Android password manager development
Welcome 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, architecture and tools used will be discussed.
Of course, I didn’t start writing it right away, before that I went through various Android development classes for several months, read books, articles and actively absorbed information from other useful sources. Anyone who is going (or already) to do development for Android I want to pay attention to the most useful for me:
The first two, you have probably already met, if you were interested in how to start developing mobile applications, here you can get acquainted with the very basics of development for android. More interesting - androidweekly weekly e-mail newsletter with news and articles about everything interesting that happens in the Android world. And then two very useful chats, in the first you can get answers to your questions about development for android, in the second specifically about architecture. And the last, but not least, link samples from Google.
Long before the start of development, I already decided on the application theme and took on a very urgent problem for myself storing a large number of logins, passwords, etc.
There are a bunch of similar applications in the Play Market, and I highlighted the main disadvantages for myself, took them into account, and made a list of the main features that I wanted to implement:
And now, actually about my application, so that you can approximately understand the level of the application that will be discussed here is the project structure in Android Studio:
As the 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 it under MVP (I had time to change my mind in time).
There are several options for dividing a project into packages, the closest thing to me is dividing by screens (features). That is, almost all packages contain classes for implementing a single screen. Except for a few:
Oh yes, I forgot to list the main tools, and the approaches that I used in the development. Already mentioned MVP. Dependency Injection (DI) implemented with Dagger 2 is probably also the standard for Android development. Where MVP and DI are there, of course, and unit testing. Unit tests cover all the logic in the presenters package model and partially data. Version control system (VCS) is another must have, again, even pet projects, I would not have developed without it. I use Git, at first I used GitHub, but when it came time to think about a private repository I migrated to Bitbucket, everything happened in a few clicks with the history of all commits being saved.
For those who crave at least some kind of code, below is a brief summary of how the password list screen in the application is implemented. Here's how it looks in the end:
Classes of the passwords package:
As a markup, almost standard scrolling activity is used, which Android Studio creates itself.
The full size of the CollapsingToolbarLayout is a picture of the category in which the entry is located, below the title.
The first line, onCreate (), calls a method that, depending on user settings, prohibits or allows screenshots to be taken.
Through intent, we get the category id in which all the information that needs to be displayed on this screen is stored. PasswordMvpController detects a smartphone or tablet and creates the corresponding fragments.
In onStop (), we check whether Activity closes forever, and if so, remove the component of this screen. Another interesting callback from the fragment (about the fragment itself below), in which we set the picture and the name of the category.
In my case, the fragment is 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, according to an article that appeared just in time in Android Weekly. Thanks to this approach, the adapter is very lightweight and not overloaded with unnecessary logic, which in turn is located in the ViewHolder.
Now about the most interesting - Presenter. In the same PasswordMvpController class, an Inject presenter is created. For the presentation itself, we need: passwordView our fragment, categoryId is the unique identifier of the category, the records from which we want to show and dataRepository where we will get this category from by identifier.
The presenter start () method is called in the onResume () fragment, and already in it we turn to the DataRepository and request our category upon receipt of which we tell view to show it and the records that are stored in it. In principle, all the methods are quite simple (again we will honor MVP) I don’t see any reason to explain them.
It just somehow did not work out in one article, very little time was devoted directly to the code. Therefore, if you are interested, write your opinions in the comments, and I will take them into account 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).
Training
Of course, I didn’t start writing it right away, before that I went through various Android development classes for several months, read books, articles and actively absorbed information from other useful sources. Anyone who is going (or already) to do development for Android I want to pay attention to the most useful for me:
- startandroid.ru
- developer.alexanderklimov.ru
- androidweekly.net
- androiddev.apptractor.ru
- t.me/android_ru
- t.me/Android_Architecture
- github.com/googlesamples
The first two, you have probably already met, if you were interested in how to start developing mobile applications, here you can get acquainted with the very basics of development for android. More interesting - androidweekly weekly e-mail newsletter with news and articles about everything interesting that happens in the Android world. And then two very useful chats, in the first you can get answers to your questions about development for android, in the second specifically about architecture. And the last, but not least, link samples from Google.
About app
Long before the start of development, I already decided on the application theme and took on a very urgent problem for myself storing a large number of logins, passwords, etc.
There are a bunch of similar applications in the Play Market, and I highlighted the main disadvantages for myself, took them into account, and made a list of the main features that I wanted to implement:
- Several independent repositories (Accounts). Each with its own master password;
- The ability to create an unlimited number of categories (folders) for grouping records;
- The ability to create an unlimited number of fields (login, password, etc.) for each record, adjust the visibility of the field and their arbitrary sorting;
- Large set of icons for notes;
- The ability to quickly create a new record and a set of fields for it;
- It does not have Internet access, all data is stored only offline in encrypted form on the device. It follows that there is no question of backend and synchronization in the cloud (which I won’t pull anyway).
Development
And now, actually about my application, so that you can approximately understand the level of the application that will be discussed here is the project structure in Android Studio:
As the 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 it under MVP (I had time to change my mind in time).
There are several options for dividing a project into packages, the closest thing to me is dividing by screens (features). That is, almost all packages contain classes for implementing a single screen. Except for a few:
- base - base classes for activity, fragments, presenters
- data - repository classes
- global _di - the main ApplicationComponent for dependency injection, scopes
- model - pojo classes of business entities (Category, list, field, etc.)
- util - well, there are just helper classes that did not fit into other packages
Oh yes, I forgot to list the main tools, and the approaches that I used in the development. Already mentioned MVP. Dependency Injection (DI) implemented with Dagger 2 is probably also the standard for Android development. Where MVP and DI are there, of course, and unit testing. Unit tests cover all the logic in the presenters package model and partially data. Version control system (VCS) is another must have, again, even pet projects, I would not have developed without it. I use Git, at first I used GitHub, but when it came time to think about a private repository I migrated to Bitbucket, everything happened in a few clicks with the history of all commits being saved.
For those who crave at least some kind of code, below is a brief summary of how the password list screen in the application is implemented. Here's how it looks in the end:
Classes of the passwords package:
As a markup, almost standard scrolling activity is used, which Android Studio creates itself.
The full size of the CollapsingToolbarLayout 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 a method that, depending on user settings, prohibits or allows screenshots to be taken.
Through intent, we get the category id in which all the information that needs to be displayed on this screen is stored. PasswordMvpController detects a smartphone or tablet and creates the corresponding fragments.
In onStop (), we check whether Activity closes forever, and if so, remove the component of this screen. Another interesting callback from the fragment (about the fragment itself below), in which we set the picture and the name of the category.
In my case, the fragment is 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, according to an article that appeared just in time in Android Weekly. Thanks to this approach, the adapter is very lightweight and not overloaded with unnecessary logic, which in turn is located in the ViewHolder.
Now about the most interesting - Presenter. In the same PasswordMvpController class, an Inject presenter is created. For the presentation itself, we need: passwordView our fragment, categoryId is the unique identifier of the category, the records from which we want to show and dataRepository where we will get this category from 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();
}
}
The presenter start () method is called in the onResume () fragment, and already in it we turn to the DataRepository and request our category upon receipt of which we tell view to show it and the records that are stored in it. In principle, all the methods are quite simple (again we will honor MVP) I don’t see any reason to explain them.
Conclusion
It just somehow did not work out in one article, very little time was devoted directly to the code. Therefore, if you are interested, write your opinions in the comments, and I will take them into account 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).