Combining multiple images into one image in the Phimpme Android Application

The Phimpme app comes with a lot of image editing functionalities. One such fascinating feature Phimpme provides is to combine two or more multiple images into one with just a button click. To do this in the Phimpme Application, long click two or more medias and click the affix button to combine them into one from the menu options. This is how two images when affixed looks like:

 

In this post, I will talk about how to achieve this functionality in any Android application with some code snippets. Here is the step by step tutorial on how to achieve this:

Step 1:

The first step is to reduce the bitmap of the selected images to a smaller size to avoid the OutOFMemory Exception while affixing images. As by affixing the images we will be making the images smaller, we don’t need the high quality bitmaps for it. To reduce the bitmaps use the function provided below:

private Bitmap getBitmap(String path) {

       Uri uri = Uri.fromFile(new File(path));
       InputStream in = null;
       try {
       final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
       in = getContentResolver().openInputStream(uri);

       // Decode image size
       BitmapFactory.Options o = new BitmapFactory.Options();
       o.inJustDecodeBounds = true;
       BitmapFactory.decodeStream(in, null, o);
       in.close();

       int scale = 1;
           while ((o.outWidth * o.outHeight) * (1 / Math.pow(scale, 2)) >
               IMAGE_MAX_SIZE) {
                   scale++;
       }

       Bitmap bitmap = null;
       in = getContentResolver().openInputStream(uri);
       if (scale > 1) {
           scale--;
           // scale to max possible inSampleSize that still yields an image
           // larger than target
           o = new BitmapFactory.Options();
           o.inSampleSize = scale;
           bitmap = BitmapFactory.decodeStream(in, null, o);

           // resize to desired dimensions
           int height = bitmap.getHeight();
           int width = bitmap.getWidth();

           double y = Math.sqrt(IMAGE_MAX_SIZE
                   / (((double) width) / height));
           double x = (y / height) * width;

           Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, (int) x,
                   (int) y, true);
           bitmap.recycle();
           bitmap = scaledBitmap;

           System.gc();
       } else {
           bitmap = BitmapFactory.decodeStream(in);
       }
       in.close();

       Log.d(TAG, "bitmap size - width: " +bitmap.getWidth() + ", height: " +
               bitmap.getHeight());
       return bitmap;
   } catch (IOException e) {
       Log.e(TAG, e.getMessage(),e);
       return null;
   }
}

Step 2:

Create a bitmap array of the selected images using the following snippet:

for (int i = 0; i < selectedMedias.size(); i++) {
    bitmapArray.add(getBitmap(selectedMedias.get(i).getPath()));
}

Here the selectedMedias is the ArrayList<Media> files which is used to create a bitmap array of reduced pixels using the function getBitmap we created in the first step.

Step 3:

Create a union bitmap of the two images of particular width and height and then draw your bitmaps into a canvas using the Android Canvas class :

unionBitmap = Bitmap.createBitmap(getBitmapsWidth(bitmapArray), getMaxBitmapHeight(bitmapArray), Bitmap.Config.ARGB_8888);
Canvas comboImage = new Canvas(unionBitmap);

Step 4:

The last step is to combine the two images and get the affixed Canvas as the result. This can be done using the function given below which takes the Canvas and ArrayList of bitmaps as a parameter and returns a Canvas of images.

private static Canvas combineBitmap(Canvas cs, ArrayList bpmList){
       int width = bpmList.get(0).getWidth();
       cs.drawBitmap(bpmList.get(0), 0f, 0f, null);

       for (int i = 1; i < bpmList.size(); i++) {
           cs.drawBitmap(bpmList.get(i), width, 0f, null);
           width += bpmList.get(i).getWidth();
       }
       return cs;
   }

This is how we achieved the affixing images feature in our phimpme Android application. For the complete working source code. Refer to the Affix.java class in Phimpme Android project source code.

Resources:

  1. https://developer.android.com/guide/topics/graphics/2d-graphics.html
  2. https://github.com/fossasia/phimpme-android/
  3. https://github.com/HoraApps/LeafPic
  4. https://stackoverflow.com/questions/11740362/merge-two-bitmaps-in-android
Continue Reading

Implementing Travis for Testing Phimpme Android

What is Travis CI? It’s importance in Android Testing.

Travis CI is continuous integration service which is hosted online and makes it easy for developers to automate any kinds of test on their Github projects. In an open source project, there are many developers from around the world working together and the repository experiences many new commits in a single day. Their might be a chance that while making the changes, they have broken something in the application. In the first blog, I discussed how to add Espresso UI tests. After this the question arises is how to use these tests to check the stability at each push or a pull request.

How I ran Espresso Tests in Phimpme using Travis?

After adding the Espresso Tests in the application, we  need to configure .travis.yml file to run the tests on each push in the repository. This can be achieved by following the steps below:

Step 1:

In the .travis.yml file of the repository, check whether the existing configuration exists to check for gradle build of the application. Create one in case it is not their and add the configurations to do a build check. You can take help from the official site of Travis or from the .travis.yml file in the Phimpme repository.

Step 2:

Now in the before script part of the file add the following codes which will start the emulator in the Travis to run the Espresso Tests.

    Before_script:

    – echo no | android create avd force n test t android19 – –abi armeabiv7a

    emulator avd test noaudio nowindow &

     sleep 300

     adb shell input keyevent 82 &

The sleep 300 is used to wait for a specified time so that the emulator starts perfectly before running the test.

Step 3:

In the script part of the file, run the Espresso test written in the application by adding the following line of codes.

Script:

./gradlew connectedAndroidTest

After adding these, make a test commit on your repository. Login to your Travis account to check the status of the test.

For the complete code of how to implement Travis test, refer to this Pull request in the Phimpme Android repository. In this Pull request, I did not make use of the sleep 300, due to which sometimes the test was failing without any broken UI. Hence, make sure to add this line of script for better results.

Resources:

https://docs.travis-ci.com/user/languages/android/

https://github.com/fossasia/phimpme-android

Continue Reading

Testing User Interactions with Espresso

Espresso is a testing framework which provides the facility to write the tests for user interactions and unitary tests. Since the release of its version 2 it is now a part of Android Testing Support Library.

The android apps we build at FOSSASIA follow rigorous testing methods. See this simple UI test  in the Phimp.me app using espresso to check if button and bottom navigation are displayed in an activity. You can also find our other tests related to API and databases in the Open Event Android App.

In this blog we learn how to add this facility to your app and write a test for a simple app that takes the name of from the user and prints it on the other screen on button click.

Adding espresso support

  • Install android support repository if not already present. You do it by following Tools -> Android -> SDK Manager
Tools you need to download for testing
  • Add the following dependencies to your app’s build.gradle file
dependencies {
    androidTestCompile 'com.android.support.test:runner:0.5'
    androidTestCompile 'com.android.support.test:rules:0.5'
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'

}
  • Specify the test instrumentation runner in default config
android {

    defaultConfig {

        // ....

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }

}

Before we begin with writing our tests knowing some basic components will help in understanding the code better. Writing tests with espresso is easy as its construction is similar to English language.

The three major components are 

  • ViewActions        : Allows you to interact with views
  • ViewAssertions   : Allows you to assert the state of a view.
  • ViewMatchers     : Allows you to locate a view in the current view hierarchy.

Suppose we want to test if text is displayed in the view, we can do it by

onView(withId(R.id.textView))                              //ViewMatcher

 .perform(click())                                         //ViewAction

 .check(matches(isDisplayed()));                           //ViewAssertion

Example

Consider an app which takes a name from the user and displays it on the next screen on clicking the button.

To perform this kind of test we will write

//Locate the view with id "name" and type the text "Natalie"

onView(withId(R.id.name)).perform(typeText("Natalie"));

//Locate the view with id "next" and click on it

onView(withId(R.id.next)).perform(click());

//Locate the view with id "new_name" and check its text is equal with "Natalie"

onView(withId(R.id.new_name)).check(matches(withText("Natalie")));

You can run tests by right clicking on the class and selecting the “run test” option. If the interaction is not as expected then the message will be displayed.

Up until now unit test were in main focus but as we move towards the more complex apps where user interaction plays an essential role, UI testing becomes equally necessary.

References:

Continue Reading

Integrating an Image Editing Page in Phimpme Android

The main aim of the Phimpme is to develop image editing and sharing application as an alternative to proprietary solutions like Instagram. Any user can choose a photo from the gallery or click a picture from the camera and upload it on the various social media platform including Drupal and wordpress. As most of the image editor applications in the app store currently my team and I discussed and listed down the basic functionality of the Image editing activity. We have listed down the following features for image Editing activity:

  • Filters.
  • Stickers
  • Image tuning

Choosing the Image Editing Application

There are number of existing Open Source projects that we went through to check how they could be integrated into Phimpme. We looked into those projects which are licensed under the  MIT Licence. As per the MIT Licence the user has the liberty to modify the use the code, modify it, merge, publish it without any restrictions. Image-Editor Android is one such application which has MIT Licence. The Image-Editor Android has extensive features for manipulating and enhancing the image. The features are as follows:

  • Edit Image by drawing on it.
  • Applying stickers on the image.
  • Applying filters.
  • Crop.
  • Rotating the image.
  • Text on the image.

It is an ideal application to be implemented in our project.

The basic flow of the application

First, getting the image either by gallery or camera. The team has implemented leafPic and openCamera. Second, redirecting the image from the leafPic gallery to the Image editing activity by choosing edit option from the popup menu.

Populating the Menu in the popup menu in XML:

<menu> tag is the root node, which contains ites in the popup menu. The following code is used to populate the menu:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/action_edit"
          android:icon="@drawable/ic_edit"
          android:title="@string/Edit"
          android:showAsAction="ifRoom"/>
    <item android:id="@+id/action_use_as"
          android:icon="@drawable/ic_use_as"
          android:title="@string/useAs" />
</menu>

Setting up the Image Editing Activity

Image-Editor Android application contains two main sections.

  • MainActivity (To get the image).
  • imageeditlibrary(To edit the image)

We need to import imageeditlibrary module. Android studios gives easy method to import a module from any other project using GUI. This can be done as follows: File->new->import module then choosing the module from the desired application.

Things to remember after importing any module from any other project:

  • Making sure that the minSdkVersion and targetSdkVersion in the gradle of the imported module and the current working project is same. In Phimpme the minSdkVersion is 16 and tagetSdkVersion is 25, which is used as standard SDK version.
  • Importing all the classes in the used in the imageeditlibrary module before using them in the leadPic gallery.

Sending Image to Image Editing Activity

This includes three tasks:

  • Handling onclick listeners.
  • Sending the image from the leafPic Activity
  • Receiving the the image in EditImageActivity.

Handling onClick Listener:

public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
case R.id.action_edit:
// setOnclick listener here.
  }
}

Sending Image to EditImageActivity:

First we need to get the path of the image to be send. For this we need FileUtils class to handle the source address of the image. In FileUtils we use the function getEditFile().

public static File genEditFile(){
        return FileUtils.getEmptyFile("croppedImage"
                + System.currentTimeMillis() + ".png");
    }

Which calls the function getEmptyFile(String name):

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;
    }

After getting the path of the file we need to send the path of the file to the EditImageActivity:

Uri uri = Uri.fromFile(new File(getAlbum().getCurrentMedia().getPath()));
                File outputFile = FileUtils.genEditFile();
                EditImageActivity.start(this,String.valueOf(uri),outputFile.getAbsolutePath(),ACTION_REQUEST_EDITIMAGE);

Receiving the image in EdtiImageActivity:

This is done by calling getdata() function in onCreate function.

private void getData() {
        filePath = getIntent().getStringExtra(FILE_PATH);
        saveFilePath = getIntent().getStringExtra(EXTRA_OUTPUT);
        loadImage(filePath);
    }

EditImageActivity Layout:

Conclusion

When integrating files from another activity we have to keep the API version of both the projects same. The best way to send an image to another activity is to save the image internally and then call the image path from the other activity.

Continue Reading

Use of ViewPager in Phimpme

Previously GalleryView was used in phimpme android app but as it is now deprecated, I decided to use ViewPager instead of GalleryView.

ViewPager allows us to view data with a horizontal swipe with the help of layoutManager.

Steps to implement the viewPager:

  1. First, add the ViewPager in Activity.xml file where you want to implement the ViewPager. This can be done using the line of code below:
<android.support.v4.view.ViewPager
             android:id="@+id/view_pager"
                android:layout_width="match_parent"
               android:layout_height="match_parent">

</android.support.v4.view.ViewPager>
  1.  To display the content of viewPager we use the viewPagerAdapter. Create new java file ViewPagerAdapter and extends it to PagerAdapter.

ViewPagerAdapter.java

public class ViewPagerAdapter extends PagerAdapter {
}
  1. After extending to PagerAdaper we have to override the two basic methods of PagerAdapter.

First, implement the constructor which helps us to provide the context of activity to ViewPagerAdapter.

You can override by pressing Alt+enter combination, click on “implement methods” and then selects these two methods.

It will implement two methods  

  • getCount()
  • isViewFromObject()

getCount will return the number of items in view pager.

  1. Now we override the few methods which are required to inflate and destroy view in viewPager.

First,

Override the instantiateItem() method it creates the page for given position.

@Override

public Object instantiateItem(ViewGroup container, int position) {
 return super.instantiateItem(container, position);
}

Now we will modify this method to inflate the view for viewPager.

As we want to display imageView in viewPager first we have to inflate the imageView and set Image according to the position of ViewPager.

Next steps,

  • Implement the customView for imageView.
  • And provide the data for  ViewPager i.e Array of images.

Create new custom_layout.xml and add ImageView in it.

<ImageView

   android:layout_width="match_parent"

   android:id="@+id/image_view"

   android:layout_height="match_parent" />

And create an array for images if you want to show images from the local memory so collect path of the images which you want to show with the array name Images.

Now we will use custom_layout layout in our ViewPager instantiateItem() method.

@Override

public Object instantiateItem(ViewGroup container, int position) {

   LayoutInflater layoutInflater = (LayoutInflater)  context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

   View view=  layoutInflater.inflate(R.layout.custom_view,null);

   ImageView imageView = (ImageView)view.findViewById(R.id.image_view);

   imageView.setBackgroundResource(images[position]);

   container.addView(view,0);

   return view;

}

The above code inflates the imageView in ViewPager.

Now we have to override destroyItem() method.  This method will destroy the content of viewPager from given position.

The below code will remove the view which we added in instantiateItem() method.

@Override

public void destroyItem(ViewGroup container, int position, Object object) {
  container.removeView((View) object);
}

Now PagerAdapter is ready, we can use this in our Activity.

  1. Reference the viewPager and set the ViewPagerAdapter to ViewPager.

Activity.java

@Override

protected void onCreate(Bundle savedInstanceState) {

   super.onCreate(savedInstanceState);

   setContentView(R.layout.activity_main);

   ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);

   viewPager.setAdapter(new ViewPagerAdapter(this));

}

The above code will set the pagerAdapter to our viewPager and display the content which we defined in instantiateItem() method of pagerAdapter.

 

This is how viewPager will allow viewing images by swiping horizontally in Phimpme.

Resources:

https://developer.android.com/reference/android/support/v4/view/PagerAdapter.html

https://github.com/fossasia/phimpme-android/pull/407/files

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

UI Testing in Phimpme Android

Espresso is an Android Testing tool which helps developers to write UI based tests. After writing tests, developers can make use of Android studio to run the tests or can implement a method in various Continuous integration sites like Travis CI to run the tests on a new push or a pull request. I implemented Espresso tests in the Phimpme Android project of FOSSASIA to test the basic UI elements of the home screen, camera view and the settings activity.

Steps to Add the UI tests :

    1. The first step is to import the packages related to the instrumentation tests and configure the build.gradle file of the application to add certain dependencies. This can be done using the line of code below:
      dependencies {
      androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
      }
      
    2. To ensure best test results make sure that you turn off the device animations. For this go to developer options and disable the:
      • Window animation scale
      • Transition animation scale
      • Animator duration scale

      After doing the above, your developer options screen should look like the screenshot below with all the animation scale disabled.

    3. Create the java class for the android test with the name of the activity you want to test followed by the word Test. Suppose you want to create a test for your MainActivity. Make a test class with the name MainActivityTest.
    4. Define your tests with annotations @LargeTest @RunWith(AndroidJUnit4.class) and define the MainActivity Test.
    5. Define the Test activity rule with the annotation @Rule and mention the java class which you want to test. I’ll be explaining the codes exactly as I used to add espresso test in Phimpme Android application below. In this for testing the PhimpMe.class, I used the following codes
      
      @Rule
      public ActivityTestRule<PhimpMe> mActivityTestRule = new ActivityTestRule<>(PhimpMe.class);

      The codes from defining the tests to defining the rules are given below:

      @LargeTest
      @RunWith(AndroidJUnit4.class)
          public class MainActivityTest {
             @Rule
             public ActivityTestRule<PhimpMe> mActivityTestRule = new ActivityTestRule<>(PhimpMe.class);
      
    6. After this, start writing the tests with the @Test annotations, the code as I used to test the Load more photos button in the MainActivity of the Phimpme Application is shown below:
      
      @Test
      ViewInteraction imageView = onView(
                      allOf(withId(R.id.btnLoadMoreLocalPhotos),
                              childAtPosition(
                                      allOf(withId(R.id.titlebarLocalPhotos),
                                              childAtPosition(
                                                      IsInstanceOf.instanceOf(android.widget.LinearLayout.class),
                                                      0)),
                                      1),
                              isDisplayed()));
      imageView.check(matches(isDisplayed()));
      

 

The above code checks whether the plus button in the top right corner is visible in the UI.

For the complete code, please refer to the Phimpme Android Repository of FOSSASIA or refer to this pull request in which I added tests for all the Activities and Fragments of the Phimpme Android Application. Since we are rebuilding the application by modifying the whole view of the application, the tests are currently removed. As soon as the application becomes fairly stable, I will be adding the Tests in this in the same way.

For complete tutorial on setting up the test using the Android Studio Inbuilt functionality. Refer to :

https://developer.android.com/studio/test/espresso-test-recorder.html#run-an-espresso-test-with-firebase-test-lab

That’s it for now. Thanks!

Resources :

https://developer.android.com/training/testing/ui-testing/espresso-testing.html

https://github.com/fossasia/phimpme-android/pull/85/files

https://developer.android.com/studio/test/espresso-test-recorder.html#run-an-espresso-test-with-firebase-test-lab

Continue Reading
Close Menu