Preparing a release for Phimpme Android

Most of the essential features are now in a stable state in our Phimpme Android app. So we decided to release a beta version of the app. In FOSSASIA we follow branch policy where in development all current development will take place and in master branch the stable code resides.

Releasing an app is not just building an apk and submitting to the distribution platform, certain guidelines should follow.

How I prepare a released apk for Phimpme

List down the feature

We discussed on our public channel what features are now in stable state and can be released. Features such as account manager and Share Activity is excluded because it is not complete and in under development mode. We don’t want to show and under development feature. So excluded them. And made a list of available features in different category of Camera, Gallery and Share.

Follow the branch policy.

The releasable and stable codebase should be on master branch. It is good to follow the branch policy because it helps if we encounter any problem with the released apk. We can directly go to our master branch and check there. Development branch have very volatile because of active development going on.

Every Contributor’s contribution is important

When we browse our old branches such as master in case of ours. We see generally it is behind 100s of commits to the development. In case of that when we create a PR for that then it generally contains all the old commits to make the branch up to the latest.

In this case while opening and merging do not squash the commits.

Testing from Developer’s end

Testing is very essential part in development. Before releasing it is a good practice that Developer should test the app from their end. We tested app features in different devices with varying Android OS version and screen size.

  • If there is any compatibility issue, report it right away and there are several tools in Android to fix.
  • Support variety of devices and screen sizes

Changing package name, application ID

Package name, application ID are vitals of an app. Which uniquely identifies them as app in world. Like I changed the package name of Phimpme app to org.fossasia.phimpme. Check all the permission app required.

Create Release build type

Build types are great to way categorize the apps. Debug and Release are two. There are various things in codebase which we want only in Debug modes. So when we create Release mode it neglect that part of the code.

Add build types in you application build.gradle

buildTypes {
   release {
       minifyEnabled false
   }

Rebuild app again and verify from the left tab bar

Generate Signed apk and Create keystore (.jks) file

Navigate to Build → Generate Signed APK

Fill all details and proceed further to generate the signed apk in your home directory.

Adding Signing configurations in build.gradle

Copy the keystore (.jks) file to the root of the project and add signing configurations

signingConfigs {
       config {
           keyAlias 'phimpme'
           keyPassword 'phimpme'
           storeFile file('../org.fossasia.phimpme.jks')
           storePassword 'XXXXXXX'
       }
   }

InstallRelease Gradle task

Navigate to the right sidebar of Android Studio click on Gradle


Click on installRelease to install the released apk. It take all the credentials from the signing configurations.

Resources

Continue Reading

Adding Twitter Integration with MVP Architecture in Phimpme Android

The Account manager layout part of Phimpme is set. Now we need to start adding different account to make it functional. We first start with twitter. Twitter functionality is integrated with the help of Twitter Kit provided by Twitter itself. We followed the steps provided on the installation guide.

Note: Before Starting this first go to apps.twitter.com and create new app, add the relevant information such as name, description, URL etc.

How twitter works in Phimpme

A dialog box appear when user selected the add account option in Account manager. Select Twitter option from it.

Twitter guides to use custom TwitterLoginButton for sign in. But as we are using a common dialog box. How to initiate login from there?

Using TwitterAuthClient

Twitter Auth Client invoke the Twitter callback and popup the login window. On authorized the correct user it goes inside the onSuccess method and start a Twitter session which helps us to get the information which we want to store in database such as username, access token.

client.authorize(getActivity(), new Callback<TwitterSession>() {
   @Override
   public void success(Result<TwitterSession> result) {

       // Creating twitter session, after user authenticate
       // in twitter popup
       TwitterSession session = TwitterCore.getInstance()
               .getSessionManager().getActiveSession();
       TwitterAuthToken authToken = session.getAuthToken();


       // Writing values in Realm database
       account.setUsername(session.getUserName());
       account.setToken(String.valueOf(session.getAuthToken()));

   }

Working with MVP architecture to show Twitter data to User in a recyclerView

Finally after successful login from Twitter, we also need to show user that you are successfully logged in Phimpme app and also provide a sign out feature so that user can logout from Twitter anytime.

Account manager has a recyclerView which takes data from the database and show it to the User.

Steps:

class AccountContract {
   internal interface View : MvpView{

       /**
        * Setting up the recyclerView. The layout manager, decorator etc.
        */
       fun setUpRecyclerView()

       /**
        * Account Presenter calls this function after taking data from Database Helper Class
        */
       fun setUpAdapter(accountDetails: RealmResults<AccountDatabase>)

       /**
        * Shows the error log
        */
       fun showError()
   }

   internal interface Presenter {

       /**
        * function to load data from database, using Database Helper class
        */
       fun loadFromDatabase()

       /**
        * setting up the recyclerView adapter from here
        */
       fun handleResults(accountDetails: RealmResults<AccountDatabase>)
   }
}

This class clearly show what functions are in View and what are in Presenter. The View interface extended to MvpView which actually holds some common functions such as onComplete()

  • Implement View interface in AccountActivity

class AccountActivity : ThemedActivity(), AccountContract.View

And perform all the action which are happening on the View such as setting up the RecyclerView

override fun setUpRecyclerView() {
   val layoutManager = LinearLayoutManager(this)
   accountsRecyclerView!!.setLayoutManager(layoutManager)
   accountsRecyclerView!!.setAdapter(accountAdapter)
}
  • Main Business Logic should not be in Activity class

That’s why using MVP we have very less number of lines of code in our Main Activity because it separate the work on different zones which help developer to easily work, maintain and other user to contribute.

So like in our case I need to update the RecyclerView adapter by taking data from database. That work should not be in activity that’s why I create a class AccountPresenter and extend this to our Presenter interface in contract class

class AccountPresenter extends BasePresenter<AccountContract.View>
       implements AccountContract.Presenter

I added the function which take care of loading data from database

@Override
public void loadFromDatabase() {
   handleResults(databaseHelper.fetchAccountDetails());
}
  • Always consider the future and keep an eye for future development

Right now I not need to do alot on Database, I just need to pick the whole data and show it on View. But I need to take case of future development in this part as well. There might be more complex operation on Database in future, then it will create complexity in the codebase, if it is not architectured well.

So, I created a DatabaseHelper class which takes care of all the database operations, the advantage of this is, anyone who have to contribute in Database part or debugging the databse need not to search for every activity and scroll lines of code, the work will be in DatabaseHelper for sure.

Added DatabaseHelper in data package

public class DatabaseHelper {

   private Realm realm;

   public DatabaseHelper(Realm realm) {
       this.realm = realm;
   }

   public RealmResults<AccountDatabase> fetchAccountDetails(){
       return realm.where(AccountDatabase.class).findAll();
   }

   public void deleteSignedOutAccount(String accountName){
       final RealmResults<AccountDatabase> deletionQueryResult =  realm.where(AccountDatabase.class)
               .equalTo("name", accountName).findAll();

       realm.executeTransaction(new Realm.Transaction() {
           @Override
           public void execute(Realm realm) {
               deletionQueryResult.deleteAllFromRealm();
           }
       });
   }
}

Flow Diagram:

Browse the Phimpme GitHub Repository for complete illustration.

Resources

  1. Twitter KIt overview : https://dev.twitter.com/twitterkit/android/overview
  2. Login with Twitter: https://dev.twitter.com/twitterkit/android/log-in-with-twitter
  3. MVP Architecture by Ribot: https://github.com/ribot/android-boilerplate

 

Continue Reading

Adding Image Editor in Phimpme Android app

Image editing is a core feature of our Phimpme Android Application. We are using this already existing components Image Editor. Currently our app flow is Gallery → Open images. Now we add an option menu in the top named “Edit” which redirect to the edit Image Activity with that image path. Perform the edit operations there and apply the changed and finally updated the album with new file.

The task is to first get the image path pass to the EditImageActivity and then save the edited image in different folder after user finish with editing.

Below are the high level architecture of how it is achieved with help of ImageEditor.

Steps to integrate Editor:

  • Get the selected Image URI

Firstly, add the permission in manifest file

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Get the local images in your internal or SD Card using Media Store content provider.

Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; 

String[] projection = { MediaStore.Images.Media._ID, MediaStore.Images.Media.BUCKET_ID,MediaStore.Images.Media.BUCKET_DISPLAY_NAME }; 

Cursor cursor = getContentResolver().query(uri, projection, null, null, null);

After that you can iterate the cursor to end and get details.

  • Create a separate folder where edited images will be saved.

Now after editing we have to place the edited image into a seperate folder with a new name. You can create folder like this:

File aviaryFolder = new File(baseDir, FOLDER_NAME);

Generating new file which will be saved as edited image

public static File getEmptyFile(String name) {

  File folder = FileUtils.createFolders();

  if (folder != null) {

     if (folder.exists()) {

        File file = new File(folder, name);

        return file;

     }

  }

  return null;

}
FileUtils.getEmptyFile("edited"

     + System.currentTimeMillis() + ".png");

 

It will save new image as named edited<timeinmillis> so it will be unique.

  • StartActivityforResult with the image URI and edit activity

So firstly, when user open image and click on edit. We create an empty file using the above method. Getting the image uri and pass it into the editImageActivity. Other than Uri, also pass the output file path.

And startActivityForResult with intent and request code.

You can track the editing done by user and finally save if there is some edit done, like in Phimpme we keep a counter which increased on change of Bitmap.

Update the Album by putting the image Media Store

ContentValues values = new ContentValues(2);

String extensionName = getExtensionName(dstPath);

values.put(MediaStore.Images.Media.MIME_TYPE, "image/" + (TextUtils.isEmpty(extensionName) ? "jpeg" : extensionName));

values.put(MediaStore.Images.Media.DATA, dstPath);

context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

Please visit the whole code here: github.com/fossasia/phimpme-android

Resources

Continue Reading
Using Activities with Bottom navigation view in Phimpme Android
BottonNavigationView

Using Activities with Bottom navigation view in Phimpme Android

Can we use Bottom Navigation View with activities? In the FOSSASIA Phimpme Project we integrated two big projects such as Open Camera and Leafpic. Both are activity based projects and not developed over fragment. The bottom navigation we use will work with fragment.

In Leafpic as well as in Open Camera, there are a lot of Multi level inheritance which runs the app components. The problem faced to continue to work on activities. For theme management using base activity is necessary. The base activity extend to AppcompatActivity. The leafpic code hierarchy is very complex and depend on various activities. Better way is to shift to activities for using Bottom navigation view and  work with activity similar like fragments.

Image source: https://storage.googleapis.com/material-design/

Solution:

This is possible if we copy the bottom navigation item in each of the activity and setting the clicked state of it as per the menu item ID.

Steps to Implement (How I did in Phimpme)

  • Add a Base Activity which takes care of two thing

    • Taking the layout element and setting in the activity.
    • Set up the correct navigation menu item clicked

Function selectBottomNavigationBarItem(int itemId) will take care of what item is currently active. Called this function in onStart(), getNavigationMenuItemId() function will return the menu id and selectBottomNavigagionBarItem update the bottom navigation view.

public abstract class BaseActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener

private void updateNavigationBarState() {
   int actionId = getNavigationMenuItemId();
   selectBottomNavigationBarItem(actionId);
}

void selectBottomNavigationBarItem(int itemId) {
   Menu menu = navigationView.getMenu();
   for (int i = 0, size = menu.size(); i < size; i++) {
       MenuItem item = menu.getItem(i);
       boolean shouldBeChecked = item.getItemId() == itemId;
       if (shouldBeChecked) {
           item.setChecked(true);
           break;
       }
   }
}
  • Add two abstract functions in the BaseActivity for above task
public abstract int getContentViewId();

public abstract int getNavigationMenuItemId();
  • Extend every activity to the Base Activity

For example I created a blank Activity: AccountsActivity and extend to BaseActivity

public class AccountsActivity extends BaseActivity
  • Implement the abstract function in the every activity and set the correct layout and menu item id.

@Override
public int getContentViewId() {
 return R.layout.content_accounts;
}

@Override
public int getNavigationMenuItemId() {
   return R.id.navigation_accounts;
}
  • Remove the setContentView(R.layout.activity_main);
  • Add the onNavigationItemSelected in BaseActivity

@Override
public boolean onNavigationItemSelected(@NonNull final MenuItem item) {
   switch (item.getItemId()) {
       case R.id.navigation_home:
    startActivity(new Intent(this, LFMainActivity.class));
           Break;
       case R.id.navigation_camera:
           startActivity(new Intent(this, CameraActivity.class));
           Break;
       case R.id.navigation_accounts:
           startActivity(new Intent(this, AccountsActivity.class));
           Break;
   }
   finish();
   return true;
}

Output:

The transition is as smooth as we use bottom navigation views with Fragment. The bottom navigation also appear on above of the activity.

Source: https://stackoverflow.com/a/42811144/2902518

Resources:

Continue Reading

Phimpme: Merging several Android Projects into One Project

To speed up our development of the version 2 of the Phimpme Android app, we decided to use some existing Open Source libraries and projects such as Open Camera app for Camera features and Leafpic for the photo gallery.

Integrating several big projects into one is a crucial step, and it is not difficult. Here are some points to ponder.

  1. Clone project separately. Build project and note down the features which you want to integrate. Explore the manifest file to know the Launcher, services and other permission app is taking.

I explore the Leafpic app manifest file. Found out its Launcher class, and changed it with the our code. Looked at the permissions app required such as Camera, access fine location, write external storage.

  1. Follow Bottom Up approach while integrating. It makes life easier. By the bottom up approach I mean create a new branch and then start deleting the code which is not required. Always work in branch so that no code lost or messed up.

Not everything is required at the moment. So I first integrate the whole leafpic app and then remove the splash screen. Also noted down the features needs to remove such as drawer, search bar etc.

  1. Remove and Commit, In a big project things are interlinked so removing one feature, actually made you remove most of the code. So my strategy is to focus on one feature and when it removed commit the code. This will help you in a reset hard your code anytime.

Like a lot of times I use reset –hard in my code, because it messed up.

  1. Licensing: One thing which is most important in using open source code is their License. Always follow what is written in their License. This is the way we can give credit to their hard work. There are various types of licenses, three famous are Apache, MIT and GNU General Public License. All have their pros and cons.

Apart of License there are some other condition as well. Like in leafpic which we are using in Phimpme has a condition to inform developers about the project in which we are using leafpic.

  1. Aware of File Duplication, sometimes many files have same name which results into file duplication. So refactor them already in the code.

I refactor the MainActivity in leafpic to LFMainActivity to not to clash with the existing. Resolve package name. This is also one of the tedious task. Android Studio already do a lot of refactoring but some part left.

  • Make sure your manifest file contains correct name.
  • The best way to refactor your package name in xml file is to ctrl+Shift+f in Ubuntu or Edit → Find → Find in path in Android Studio. Then search old package name and replace it with new. You can use alt+ j for multi cursor as well.
  • Clean and rebuild project.

Run app at each step so that you can catch the error at a point. Also use debugger for debugging.

Resources

Continue Reading
Set spacing in RecyclerView items by custom Item Decorator in Phimpme Android App
Spacing in RecyclerView GridLayoutManager

Set spacing in RecyclerView items by custom Item Decorator in Phimpme Android App

We have decided to shift our images Gallery code from GridView to using Grid Layout manager in RecyclerView in Phimpme Android application. RecyclerView has many advantages as compare to Grid/ List view.

  • Advantages of using layout manager either List, Grid or Staggered.
  • We can use many built in animations.
  • Item decorator for customizing the item.
  • Recycle items using the View Holder pattern

Recycler View documentation

Adding recyclerview in xml

<android.support.v7.widget.RecyclerView

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:id="@+id/rv"

    />

Setting layout manager

mLayoutManager = new GridLayoutManager(this, 3);

recyclerView.setLayoutManager(mLayoutManager);

In phimpme we have an item as an ImageView, to show into the grid. Setup the Grid using layout manager as above.

Gallery of images is set but there is no spacing in between grid items. Padding will not help in this case.

Found a way to set offset by creating a Custom item decoration class. Add a constructor with parameter as a dimension resource.

 

public class ItemOffsetDecoration extends RecyclerView.ItemDecoration {

   private int mItemOffset;

   public ItemOffsetDecoration(int itemOffset) {

       mItemOffset = itemOffset;

   }



   public ItemOffsetDecoration(@NonNull Context context, @DimenRes int itemOffsetId) {

       this(context.getResources().getDimensionPixelSize(itemOffsetId));

   }

   @Override

   public void getItemOffsets(Rect outRect, View view, RecyclerView parent,

           RecyclerView.State state) {

       super.getItemOffsets(outRect, view, parent, state);

       outRect.set(mItemOffset, mItemOffset, mItemOffset, mItemOffset);

   }

}

Author: gist.github.com/yqritc/ccca77dc42f2364777e1

Usage:

ItemOffsetDecoration itemDecoration = new ItemOffsetDecoration(context, R.dimen.item_offset);

mRecyclerView.addItemDecoration(itemDecoration)

Pass the item_offset value in the function. Go through the material design guidelines for a clear understanding of dimensions in item offset.

Continue Reading
  • 1
  • 2
Close Menu