📜 ⬆️ ⬇️

Development of a password manager for Android

Greetings to all. I want to tell you the story of the development of my first mobile application for Android and share its various details. In particular, the architecture and the tools used will be discussed.



Training


Of course, I didn’t start writing it right away, before that I had been taking all sorts of lessons on Android development for several months, read books, articles and actively absorbed information from other useful sources. All who are going (or already) to do development for Android I want to draw attention to the most useful for me:


The first two, you, most likely, have already met, if you were interested in how to start developing mobile applications, here you can get acquainted with the very basics of developing for android. More interesting - androidweekly weekly e-mail newsletter with news and articles about everything interesting that happens in the world of Android. And then there are two very useful chat rooms, in the first one you can get answers to your questions about the development for android, in the second one, specifically about the architecture. And last but not least, reference samples from Google.
')

About the application


Long before the start of development, I had already decided on the subject of the application and took up the very urgent problem of storing a large number of logins, passwords, and so on.

In the Play Market, a bunch of similar applications and I identified the main shortcomings for myself, took them into account, and made a list of the main features that I wanted to implement:


Development


And now about my application itself, so that you can roughly understand the level of the application that will be discussed here is the structure of the project in Android Studio:

image alt As architecture, I chose MVP, in my opinion, now it is a standard in Android development. There is also Clean Architecture, more flexible, even more abstractions, great for large projects. But for me it was already too much, since I started developing all the code in Activity / Fragment, and only then rewrote under MVP (I had time to think again).

There are several options for dividing a project into packages: the closest division to screens (features) is closest to me. That is, almost all packages contain classes for implementing a single screen. Except for a few:


Oh, I forgot to list the main tools and approaches that I used in the development. Already mentioned MVP. Dependency Injection (DI), implemented using Dagger 2, is probably also a standard for Android development. Where MVP and DI are there, of course, unit testing. Unit tests cover all the logic in the model package and partly data. Version control system (VCS) is another must have, again, I would not even develop pet projects, without it. I use Git, first I used GitHub, but when it came time to think about the private repository I migrated to Bitbucket, everything happens in a few clicks while preserving the history of all commits.

For those who crave at least some kind of code, below is a brief description of how the password list screen is implemented in the application. Here is what it looks like in the end:



Passwords package classes:



The markup is used almost standard scrolling activity that Android Studio creates itself.

For the entire size of CollapsingToolbarLayout there is a picture of the category in which the entry is located, below the title.

PasswordsActivity code:
public class PasswordsActivity extends AppCompatActivity implements PasswordsFragment.OnPasswordListFragmentInteractionListener, PasswordFieldsFragment.OnPasswordFragmentInteractionListener { PasswordsContract.Presenter mPasswordsPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityUtils.enableScreenshots(this); setContentView(R.layout.activity_scrolling_passwords_list); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mPasswordsPresenter.onAddNewPasswordButtonClick(); } }); //Open passwords of category with that id String categoryId = getIntent().getStringExtra(PasswordsFragment.ARG_EXTRA_CATEGORY_ID); //In tablet mode open details of password with id String passwordId = getIntent().getStringExtra(PasswordFieldsFragment.ARG_EXTRA_PASSWORD_ID); PasswordsMvpController.createPasswordsViews(this, categoryId, passwordId); } @Override protected void onStop() { super.onStop(); if (isFinishing()) { Injector injector = Injector.getInstance(); injector.destroyPasswordsComponent(); if (ActivityUtils.isTablet(this)) { injector.destroyPasswordFieldsComponent(); } } } public void setPasswordsPresenter(PasswordsContract.Presenter passwordsPresenter) { mPasswordsPresenter = passwordsPresenter; } /** * Methods implemented from {@link PasswordsFragment.OnPasswordListFragmentInteractionListener} * * @param parentCategory category that must be displayed */ @Override public void showCategoryInfo(Category parentCategory) { setTitle(parentCategory.getTitle()); CollapsingToolbarLayout collapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.toolbar_layout); VectorDrawableCompat vectorDrawableCompat = VectorDrawableCompat.create(getResources(), parentCategory.getPicture().getResId(), getTheme()); collapsingToolbarLayout.setContentScrim(vectorDrawableCompat); ImageView categoryImageView = (ImageView) findViewById(R.id.category_picture_imageView); categoryImageView.setImageResource(parentCategory.getPicture().getResId()); } 


The first line oncreate () calls the method that, depending on the user settings, prohibits or allows screenshots to be taken.

Through intent we get the id of the category, which stores all the information that needs to be displayed on this screen. PasswordMvpController determines our smartphone or tablet and creates the corresponding fragments.

In onStop (), we check if the Activity is closed forever, and if so, remove the component of this screen. Another interesting callback from the fragment (about the fragment below), in which we set the picture and the name of the category.

In my case, the fragment is a View and, accordingly, provides Presenter with the necessary methods for displaying data, except for them there is nothing interesting in the fragment, and the methods themselves are quite simple.

The list of entries is implemented using RecyclerView, for an article that just appeared on Android Weekly. Thanks to this approach, the adapter is very lightweight and not overloaded with unnecessary logic, which in turn is in the ViewHolder.

Now about the most interesting - Presenter. In the same class PasswordMvpController, the creation and inject of the presenter takes place. For the presenter itself, we need: passwordView our fragment, categoryId a unique identifier for the category, the records from which we want to show, and the dataRepository from where we will receive this category by identifier.

Presenter Code
 public class PasswordsPresenter implements PasswordsContract.Presenter { private final DataRepository mDataRepository; private PasswordsContract.View mPasswordsView; // On tablets we can have null categoryId when @Nullable String mCategoryId; Category mCurrentCategory; PasswordsPresenter(DataRepository dataRepository, PasswordsContract.View passwordsView, @CategoryId @Nullable String categoryId) { mDataRepository = dataRepository; mPasswordsView = passwordsView; mCategoryId = categoryId; } public void setupListeners(PasswordsContract.View passwordsView) { mPasswordsView = passwordsView; mPasswordsView.setPresenter(this); } /* * Methods implemented from {@Link PasswordsContract.Presenter} * */ @Override public void start() { if (Strings.isNullOrEmpty(mCategoryId)) { // TODO: 25.02.2017 on tablets we can have null category id when don't pick any category, need to show message about it return; } mDataRepository.getCategory(mCategoryId, new DataSource.LoadCategoryCallback() { @Override public void onCategoryLoaded(Category category) { mCurrentCategory = category; mPasswordsView.showPasswordsInList(category.mPasswords); mPasswordsView.showCategoryInfo(mCurrentCategory); } @Override public void onDataNotAvailable() { //RecycleView show empty data message by itself } }); } @Override public void onPasswordClick(Password password) { mPasswordsView.showDetailPasswordUi(password); } @Override public void onAddNewPasswordButtonClick() { mPasswordsView.showNewPasswordUi(mCategoryId); } @Override public void onSwapPasswords(int firstPosition, int secondPosition) { mDataRepository.swapPasswords(mCurrentCategory, firstPosition, secondPosition); mPasswordsView.swapPasswordInList(firstPosition, secondPosition); } @Override public void loadCategory(Category category) { mCurrentCategory = category; mPasswordsView.showCategoryInfo(category); mPasswordsView.showPasswordsInList(category.mPasswords); } /* * Helper methods that can be called from {@Link CategoriesTabletPresenter} to manipulate view in tablet mode * */ public void onCurrentCategoryDeleted() { mPasswordsView.showEmptyUi(); } } 


In the onResume () of the fragment, the start () method of the presenter is called, and in it we refer to the DataRepository and request our category when received, we tell the view to show it and the records that are stored in it. In principle, all methods are simple enough (again, let's give up the honor of MVP), I see no reason to explain them.

Conclusion


In one article, it all somehow somehow did not work out; very little time was given directly to the code. Therefore, if you are interested, write your opinions in the comments, and I will consider them when writing an article about how data is stored in the application, how it is encrypted and, in general, about everything related to security (there will be a lot of code).

Source: https://habr.com/ru/post/328708/


All Articles