Creating a basic Gallery in Phimpme Android

In the process of optimizing our Phimpme Android photo application, I had to change the implementation of the gallery in the application, as its loading time and smoothness is not satisfactory. How did I implement it?

In any gallery application, the primary necessity is having a details’ list of all available images on the device. For creating such a list, it is not necessary to iterate in all folders of the device storage for images. It is very time consuming task if we perform every time the app opens. Instead we can use database created by Android system for storing details of all available media on the device which can be accessed by any application. If we query the database with proper arguments, we get a cursor pointing to our target, using which we can form a list for our need.

Querying the mediastore for images can be implemented as shown below

Uri uri;
Cursor cursor;
int column_index;
String path = null,sortOrder;
ArrayList<String> imageList = new ArrayList<>();
uri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] projection = { MediaStore.MediaColumns.DATA }; 
//DATA is the path to the corresponding image. We only need this for loading //image into a recyclerview

sortOrder = MediaStore.Images.ImageColumns.DATE_ADDED + “ DESC”;
//This sorts all images such that recent ones appear first

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

try {
       if (null != cursor) {
           column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
           while (cursor.moveToNext()) {
                 path = cursor.getString(column_index_data);
                 imageList.add(path);
            }
            cursor.close(); 
//imageList gets populated with paths to images by here
       }
}catch (Exception e){
       e.printStackTrace();
}

As we now have the list of paths to all images, we can proceed with displaying images using image file path. We can use RecyclerView for displaying grid of images as it is more optimized that default GridView. The whole process of displaying images can be summarized in following simple steps.

  1. Add RecyclerView widget to main layout and create a layout for gallery item.
  2. Create an adapter for populating RecyclerView.
  3. Initialize adapter with imageList and attach it to the recyclerView.

Setting Layout: 

Add the below code to the layout resource for your activity(activity_main.xml).

<android.support.v7.widget.RecyclerView
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:id=”@+id/gallery_grid”/>

Inorder to use RecyclerView in the project, the following dependency for recyclerView has to be added in the build.gradle file

compile "com.android.support:recyclerview-v7:$supportLibVer"
//change supportLibVer to its corresponding value.

 

Now create a layout for item in the gallery. For this, you can create something like below.

gallery_item.xml

<?xml version="1.0" encoding="utf-8"?>
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
               android:layout_width="match_parent"
               android:layout_height="match_parent">
     <ImageView
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:scaleType="centerCrop"
         android:id="@+id/image"/>
 </RelativeLayout>

Setting the Adapter:

Adapter populates the recyclerView with the list of resources given to it. For displaying images in RecyclerView, if we use normal method for setting image i.e imageview.setImageDrawable(xyz); the recyclerView would load extremely slow everytime we open the app. So, we can use an open-source image caching library Glide, for this purpose. It caches the images on its first load and lazy loads images into imageView giving a smooth experience. It can be used only if the following dependency is added to build.gradle.

compile 'com.github.bumptech.glide:glide:4.0.0-RC0'

Its sample implementation is also shown in the adapter code below.

OnItemClickListener() cannot be used with recyclerView directly, like gridView. So, a method for implementing such kind of listener is also shown in below implementation of adapter.

import android.content.Context;
import android.net.Uri;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import java.io.File;
import java.util.ArrayList;

public class GalleryAdapter extends RecyclerView.Adapter<GalleryAdapter.mViewHolder> {

static ArrayList<String> galleryImageList;
private Context context;

public GalleryAdapter(ArrayList<String> imageList){
     galleryImageList = imageList;
}

public class mViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener,View.OnLongClickListener {

     ImageView imageView;
     ItemClickListener itemClickListener;

     public mViewHolder(View view) {
         super(view);
         imageView = (ImageView)view.findViewById(R.id.image);
         view.setOnClickListener(this);
         view.setOnLongClickListener(this);
     }

     public void setClickListener(ItemClickListener itemClickListener) {
         this.itemClickListener = itemClickListener;
     }

     @Override
     public void onClick(View v) {
         itemClickListener.onClick(v, getPosition(), false);
     }

     @Override
     public boolean onLongClick(View v) {
         itemClickListener.onClick(v, getPosition(), true);
         return true;
     }
}

@Override
public GalleryAdapter.mViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
     context = parent.getContext();
     View view = LayoutInflater.from(context).inflate(R.layout.gallery_item,parent,false);

//this gallery item is the one that we created above.

     DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);

     RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(metrics.widthPixels/3,metrics.widthPixels/3);    //3 columns

     view.setLayoutParams(layoutParams);
     return new mViewHolder(view);
}

@Override
public void onBindViewHolder(GalleryAdapter.mViewHolder holder, final int position) {
     Glide.with(context)
             .load(Uri.fromFile(new File(galleryImageList.get(position)))
             //get path from list and covertin to URI
             .override(100,100) //final image will be this size(100x100)
             .thumbnail(0.1f)   //instead of empty placeholder, a                                              //thumbnail of 0.1th size of original image is used as //placeholder before complete loading of image
             .into(holder.imageView);
     holder.setClickListener(new ItemClickListener() {
         @Override
         public void onClick(View view,int position, boolean isLongClick) {
             if (isLongClick)
                 Toast.makeText(context, "Long Clicked " + position, Toast.LENGTH_SHORT).show();

             else
                 Toast.makeText(context, "Clicked " + position , Toast.LENGTH_SHORT).show();
         }
     });
}

@Override
public int getItemCount() {
     return (null != galleryImageList) ? galleryImageList.size() : 0;
}
}

With this, we reached the final step. 

Attaching the adapter to RecyclerView:

A grid layout manager with a required number of columns is made and attached to recyclerView.

The adapter is also initialized with the created Image List and is set to the recyclerView as shown below.

recyclerView = (RecyclerView)findViewById(R.id.gallery_grid);
RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(this,3);
// 3 columns
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setAdapter(new GalleryAdapter(createAndGetImageList()));

createAndGetImageList() is the first method discussed in post.

Continue ReadingCreating a basic Gallery in Phimpme Android

Sharing the Phimpme Android App using Firebase deep-linking

The share feature in the Android application is really important for the growth of your Android application and to increase the user base. In the Phimpme Android application, we have made use of the Firebase deep link to share the application as it is the most recommended way to do it. In this tutorial, I am discussing two ways to share your Android application with the help of code snippets below. The following are:

Generate the APK and share:

Sharing the APK of the application directly can be achieved very easily but it is the least recommended way to do this. To share the APK of the application via bluetooth or other applications, we can make use of the package manager to generate the apk and pass it to the share intent to get things done smoothly. The following code snippet will be used to generate the apk and to pass it on to the share intent.

PackageManager pm = getPackageManager();
ApplicationInfo ai = pm.getApplicationInfo(getPackageName(), 0);
File srcFile = new File(ai.publicSourceDir);
Intent share = new Intent();
share.setAction(Intent.ACTION_SEND);
share.setType("application/vnd.android.package-archive");
share.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(srcFile));
startActivity(Intent.createChooser(share, "APK"));

Sharing the application’s apk directly is not the most recommended way to do it as sometimes the apk generated by the PackageManager might not install properly in another device.

Using the Firebase Deep links:

The best thing about this is that it can behave differently when clicked on different devices. For example, you have Phimpme Android application installed in your mobile phone and someone else might send you the invitation link to download it again. So when you click on the deep link, rather than opening play store or any other storage from where you can download the apk, it will open up the Phimpme Application directly.

Due to these advantages, in the Phimpme Application, we have made use of this service to share the application. In this tutorial, I will discuss on how to achieve this step by step:

Step 1:

Sign in to your google account and go the Firebase console then click on the Add project button and fill in the Project name and your country and press Create Project button to proceed.

Step 2:

Click on Add firebase to your Android application button and fill in the your Android application’s package name and the SHA-1 key. You can generate this key very easily with the help of Android studio.

For this, import and open your Android application in Android studio and then press the Gradle button at the right hand side of your screen. It will open up the screen like that mentioned in the screenshot below:

Go to app>android> and double click on the signingReport and wait for the build to complete. On successful completion of the build, the SHA-1 key will be displayed on the Gradle console below.

Step 3:

Click on the register application button below to proceed. It will provide an option to download googleServices.json file but you can skip that as it is not necessary for sharing the application.

Step 4:

Click on the Dynamic links section and add create a new dynamic link. Fill in the link to locate your application and select the behaviour of how you want the link to behave on different devices. For example, in Android if the application is installed I want to directly open the application so I have made the configuration as seen from the screenshot below:

After doing this, click on the option to Create dynamic link from below and copy the generated link.

Step 5:

After generating the URL, you can simply create a share button in your android application and can share the application with the help of code snippet provided below:

Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.install_phimpme) + "\n "+ getString(R.string.invitation_deep_link));
sendIntent.setType("text/plain");
startActivity(sendIntent);

Now go to your String.xml and create a new String resource with name invitation_deep_link and copy the dynamic link you generated using the steps mentioned above.

Resources:

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

https://console.firebase.google.com/

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

Continue ReadingSharing the Phimpme Android App using Firebase deep-linking

Designing Control UI of PSLab Android using Moqups

Mockups are an essential part of app development cycle. With numerous mock-up tools available for android apps (both offline and online), choosing the right mock-up tool becomes quite essential. The developers need a tool that supports the latest features like drag & drop elements, support collaboration upto some extent and allow easy sharing of mockups. So, Moqups was chosen as the mockups tool for the PSLab Android team.

Like other mock-up tools available in the market, using moqups is quite simple and it’s neat & simple user interface makes the job easier. This blog discusses some of the important aspects that need to be taken care of while designing mockups.

A typical online mock-up tool would look like this having a palette to drag & drop UI elements like Buttons, Text boxes, Check boxes etc. Additionally a palette to modify the features of each element ( here on the right ) and other options at the top related to prototyping, previewing etc.

    • The foremost challenge while designing any mock-up is to keep the design neat and simple such that even a layman doesn’t face problems while using it. A simple UI is always appealing and the current trend of UIs is creating flat & crisp UIs.

    • For example, the above mock-up design has numerous advantages for both a user and also as a programmer. There are seek bars as well as text boxes to input the values along with the feature of displaying the value that actually gets implemented and it’s much simpler to use. From the developer’s perspective, presence of seven identical views allows code reuse. A simple layout can be designed for one functionality and since all of them are identical, the layout can be reused in a Recyclerview.
    • The above design is a portion of the Control UI which displays the functionalities for  using PSLab as a function generator and as a voltage/current source.

    • The other section of the UI is of the Read portion. This has the functionalities to measure various parameters like voltage, resistance, capacitance, frequency and counting pulses. Here, drop-down boxes have been provided at places where channel selection is required. Since voltages are most commonly measured values in any experiment, voltages of all the channels have been displayed simultaneously.
    • Attempts should always be made to keep the smaller views as identical as possible since it becomes easier for the developer to implement it and also for the user to understand.

 

The Control UI has an Advanced Section which has features like Waveform Generators allows to generate sine/square waves of a given frequency & phase, Configuring Pulse Width Modulation (PWM)  and selecting the Digital output channel. Since, the use of such features are limited to higher level experiments, they have been separately placed in the Advanced section.

Even here drop-down boxes, text boxes & check boxes have been used to make UI look interactive.

The common dilemma faced while writing the XML file is regarding the view type to be chosen as Android provides a lot of them like LinearLayout, ConstraintLayout, ScrollView, RecyclerView, ListView etc. So, although there are several possible ways of designing a view. Certain things like using ListView or RecyclerView where there is repetition of elements is easier and when the elements are quite distinct from each other, it is better to stick to LinearLayout and ConstraintLayout.

Continue ReadingDesigning Control UI of PSLab Android using Moqups

Making Open Event Organizer Android App Reactive

FOSSASIA’s Open Event Organizer is an Android Application for Event Management. The core feature it provides is two way attendee check in, directly by searching name in the list of attendees or just by scanning a QR code from ticket. So as attendees of an event can be really large in number like 1000+ or more than that, it should not alter the performance of the App. Just imagine a big event with lot of attendees (lets say 1000+) and if check in feature of the app is slow what will be the mess at entrance where each attendee is waiting for his ticket to be scanned and verified. For example, a check in via QR code scan. A complete process is somewhat like this:

  1. QR scanner scans the ticket code and parse it into the ticket identifier string.
  2. Identifier string is parsed to get an attendee id from it.
  3. Using the attendee id, attendee is searched in the complete attendees list of the event.
  4. On match, attendee’s check in status is toggled by making required call to the server.
  5. On successful toggling the attendee is updated in database accordingly.
  6. And check in success message is shown on the screen.

From the above tasks 1st and 6th steps only are UI related. Remaining all are just background tasks which can be run on non-UI thread instead of carrying them on the same UI thread which is mainly responsible for all the user interaction with the app. ReactiveX, an API for asynchronous programming enables us to do this. Just for clarification asynchronous programming is not multithreading. It just means the tasks are independent and hence can be executed at same time. This will be another big topic to talk about. Here we have used ReactiveX just for running these tasks in background at the same time UI thread is running. Here is our code of barcode processing:

private void processBarcode(String barcode) {
  Observable.fromIterable(attendees)
      .filter(attendee -> attendee.getOrder() != null)
      .filter(attendee -> (attendee.getOrder().getIdentifier() + "-" + attendee.getId()).equals(barcode))
      .subscribeOn(Schedulers.computation())
      .observeOn(AndroidSchedulers.mainThread())
      .subscribe(attendee -> {
          // here we get the attendee and
          // further processing can be called here
          scanQRView.onScannedAttendee(attendee);
      });
  }

In the above code you will see the actual creation of an Observable. Observable class has a method fromIterable which takes list of items and create an Observable which emits these items. So hence we need to search the attendee in the attendees list we have already stored in database. Filter operator filters the items emitted using the function provided. Last two lines are important here which actually sets how our subscriber is going to work. You will need to apply this thread management setting to your observable while working on android. You don’t actually have to worry about it. Just remember subscribeOn sets the thread on which actually the background tasks will run and on item emission subscriber handle it on main thread which is set by observeOn method. Subscribe operator provides the function what actually we need to run on main thread after emitted item is caught. Once we find the attendee from barcode, network call is made to toggle check in status of the attendee. Here is the code of check in method:

public void toggleCheckIn() { eventRepository.toggleAttendeeCheckStatus(attendee.getEventId(), attendeeId)
      .subscribe(completed -> {
          ...
          String status = attendee.isCheckedIn() ? "Checked In" : "Checked Out";
          attendeeCheckInView.onSuccess(status);
      }, throwable -> {
          throwable.printStackTrace();
          ...
      });
}

In the above code toggleAttendeeCheckStatus method returns an Observable. As name suggests the Observable is to be observed and it emits signals (objects) which are caught by a Subscriber. So here observer is Subscriber. So in the above code toggleAttendeeCheckStatus is creating an Obseravable. Lets look into toggleAttendeeCheckStatus code:

public Observable<Attendee> toggleAttendeeCheckStatus(long eventId, long attendeeId) {
  return eventService.toggleAttendeeCheckStatus(eventId, attendeeId, getAuthorization())
      .map(attendee -> {
          ...
          // updating database logic
          ...
          return attendee;
      })
      .subscribeOn(Schedulers.io())
      .observeOn(AndroidSchedulers.mainThread());
}

We have used Retrofit+Okhttp for network calls. In the above code eventService.toggleAttendeeCheckStatus returns an Observable which emits updated Attendee object on server response. Here we have used Map operator provided by ReactiveX which applies function defined inside it on each item emitted by the observable and returns a new observable with these items. So here we have use it to make the related updates in the database. Now with ReactiveX support the complete check in process is:

(Tasks running in background in bold style)

  1. QR scanner scans the ticket code and parse it into the ticket identifier string.
  2. Using the identifier, attendee is searched in the complete attendees list of the event.
  3. On match, attendee’s check in status is toggled by making required call to the server.
  4. On successful toggling the attendee is updated in database accordingly.
  5. And check in success message is shown on the screen.

So now main thread runs only tasks related to the UI. And time consuming tasks are run in background and UI is updated on their completion accordingly. Hence check in process becomes smooth irrespective of size of the attendees. And app never crashes.

Continue ReadingMaking Open Event Organizer Android App Reactive

Using ButterKnife in PSLab Android App

ButterKnife is an Android Library which is used for View injection and binding. Unlike Dagger, ButterKnife is limited to views whereas Dagger has a much broader utility and can be used for injection of anything like views, fragments etc. Being limited to views makes it much more simpler to use compared to dagger.

The need for using ButterKnife in our project PSLab Android was felt due to the fact binding views would be much more simpler in case of layouts like that of Oscilloscope Menu which has multiple views in the form of Textboxes, Checkboxes, Seekbars, Popups etc. Also, ButterKnife makes it possible to access the views outside the file in which they were declared. In this blog, the use of ButterKnife is limited to activities and fragments.

ButterKnife can used anywhere we would have otherwise used findViewById(). It helps in preventing code repetition while instantiating views in the layout. The ButterKnife blog has neatly listed all the possible uses of the library.

The added advantage of using Butterknife are-

  • The hassle of using Boilerplate code is not needed and the code volume is reduced significantly in some cases.
  • Setting up ButterKnife is quite easy as all it takes is adding one dependency to your gradle file.
  • It also has other features like Resource Binding (i.e binding String, Color, Drawable etc.).
  • Other uses like simplification of code while using buttons. For example, there is no need of using findViewById and setOnClickListener, simple annotation of the button ID with @OnClick does the task.

Using butterknife was essential for PSLab Android since for the views shown below which has too many elements, writing boilerplate code can be a very tedious task.

Using ButterKnife in activities

The PSLab App defines several activities like MainActivity, SplashActivity, ControlActivity etc. each of which consists of views that can be injected with ButterKnife.

After setting up ButterKnife by adding dependencies in gradle files, import these modules in every activity.

import butterknife.BindView;
import butterknife.ButterKnife;

Traditionally, views in Android are defined as follows using the ID defined in a layout file. For this findViewById is used to retrieve the widgets.

private NavigationView navigationView;
private DrawerLayout drawer;
private Toolbar toolbar;

navigationView = (NavigationView) findViewById(org.fossasia.pslab.R.id.nav_view);
drawer = (DrawerLayout) findViewById(org.fossasia.pslab.R.id.drawer_layout);
toolbar = (Toolbar) findViewById(org.fossasia.pslab.R.id.toolbar);

However, with the use of Butterknife, fields are annotated with @BindView and a View ID for finding and casting the view automatically in the layout files.
After the annotation, finding and casting of views ButterKnife.bind(this) is called to bind the views in the corresponding activity.

@BindView(R.id.nav_view) NavigationView navigationView;
@BindView(R.id.drawer_layout) DrawerLayout drawer;
@BindView(R.id.toolbar) Toolbar toolbar;
setContentView(R.layout.activity_main);
ButterKnife.bind(this);

Using ButterKnife in fragments

The PSLab Android App implements ApplicationFragment, DesignExperimentsFragment, HomeFragment etc., so ButterKnife for fragments is also used.

Using ButterKnife is fragments is slightly different from using it in activities as the binded views need to be destroyed on leaving the fragment as the fragments have different life cycle( Read about it here). For this an Unbinder needs to be defined to unbind the views before they are destroyed.

Quoting from the official documentation

When binding a fragment in OnCreateView, set the views to null in OnDestroyView. Butter Knife returns an Unbinder instance when you call bind to do this for you. Call its unbind method in the appropriate lifecycle callback.

So, an additional module of ButterKnife is imported

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;

Similar as that of activities, the code below can be replaced by the corresponding ButterKnife code.

TextView tvDeviceStatus = (TextView)view.findViewById(org.fossasia.pslab.R.id.tv_device_status);
TextView tvVersion = (TextView)view.findViewById(org.fossasia.pslab.R.id.tv_device_version);
ImageView imgViewDeviceStatus = (ImageView)view.findViewById(org.fossasia.pslab.R.id.img_device_status);
@BindView(R.id.tv_device_status) TextView tvDeviceStatus;
@BindView(R.id.tv_device_version) TextView tvVersion;
@BindView(R.id.img_device_status) ImageView imgViewDeviceStatus;
private Unbinder unbinder;

Additionally, the unbinder object is used to store the returned Unbinder instance when ButterKnife.bind is called for Fragments.

unbinder = ButterKnife.bind(this,view);

Finally, the unbind method of unbinder is called while view is destroyed.

@Override public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
Continue ReadingUsing ButterKnife in PSLab Android App

Prototyping PSLab Android App using Invision

Often, while designing apps, we need planning and proper designing before actually building the apps. This is where mock-up tools and prototyping is useful. While designing user interfaces, the first step is usually creating mockups. Mockups give quite a good idea about the appearance of various layouts of the app. However, mockups are just still images and they don’t give a clue about the user experience of the app. This is where prototyping tools are useful. Prototyping helps to get an idea about the user experience without actually building the app.

Invision is an online prototyping service which was used for initial testing of the PSLab Android app. Some of pictures below are the screenshots of our prototype taken in Invision. Since, it supports collaboration among developers, it proves to be a very useful tool. 

  • Using Invision is quite simple. Visit the Invision website and sign up for an account.Before using invision for prototyping, the mockups of the UI layouts must be ready since Invision is simply meant for prototyping and not creating mockups. There are a lot of mock-up tools available online which are quite easy to use.
  • Create a new project on Invision. Select the project type – Prototype in this case followed by selecting the platform of the project i.e. Android, iOS etc.
  • Collaborators can be added to a project for working together.
    • After project creation and adding collaborators is done with, the mock-up screens can be uploaded to the project directory
    • Select any mock-up screen, the window below appears, there are a few modes available in the bottom navbar – Preview mode, Build mode, Comment mode, Inspect Mode and History Mode.
        • Preview Mode – View your screen and test the particular screen prototype.
        • Build Mode – Assign functionality to buttons, navbars, seek bars, check boxes etc. and other features like transitions.
        • Comment Mode – Leave comments/suggestions regarding performance/improvement for other collaborators to read.
        • Inspect mode – Check for any unforeseen errors while building.
        • History Mode – Check the history of changes on the screen.
  • Switch to the build mode, it would now prompt to click & drag to create boxes around buttons, check boxes, seek bars etc,(shown above). Once a box ( called as “hotspot” in Invision ), a dialog box pops up asking to assign functionalities.
  • The hotspot/box which was selected must link to another menu/layout or result in some action like app closing. This functionality is provided by the “Link To:” option.
  • Then the desired gesture for activating the hotspot is selected which can be tap for buttons & check boxes, slide for navbars & seek bars etc from the “Gesture:” option.
  • Lastly, the transition resulting due to moving from the hotspot to the assigned window in “Link To:” is selected from the “Transition:” menu.

This process can be repeated for all the screens in the project. Finally for testing and previewing the final build, the screen which appears when the app starts is selected and further navigation, gestures etc. are tested there. So, building prototypes is quite an interesting and easy task.

Additional Resources

Continue ReadingPrototyping PSLab Android App using Invision

Using FastAdapter in Open Event Organizer Android Project

RecyclerView is an important graphical UI component in any android application. Android provides RecyclerView.Adapter class which manages all the functionality of RecyclerView. I don’t know why but android people have kept this class in a very abstract form with only basic functionalities implemented by default. On the plus side it opens many doors for custom adapters with new functionalities for example, sticky headers, scroll indicator, drag and drop actions on items, multiview types items etc. A developer should be able to make an adapter of his need by extending RecyclerView.Adapter. There are many custom adapters developers have shared which comes with built in functionalities. FastAdapter is one of them which comes with all the good functionalities built in and also it is very easy to use. I just got to use this in the Open Event Organizer Android App of which the core feature is Attendees Check In. We have used FastAdapter library to show attendees list which needs many features which are absent in plane RecyclerView.Adapter. FastAdapter is built in such way that there are many different ways of using it on developer’s need. I have found a simplest way which I will be sharing here. The first part is extending the item model to inherit AbstractItem.

public class Attendee extends AbstractItem<Attendee, AttendeeViewHolder> {
  @PrimaryKey
  private long id;
  ...
  ...

  @Override
  public long getIdentifier() {
      return id;
  }

  @Override
  public int getType() {
      return 0;
  }

  @Override
  public int getLayoutRes() {
      return R.layout.attendee_layout;
  }

  @Override
  public AttendeeViewHolder getViewHolder(View view) {
      return new AttendeeViewHolder(DataBindingUtil.bind(view));
  }

  @Override
  public void bindView(AttendeeViewHolder holder, List<Object> list) {
      super.bindView(holder, list);
      holder.bindAttendee(this);
  }

  @Override
  public void unbindView(AttendeeViewHolder holder) {
      super.unbindView(holder);
      holder.unbindAttendee();
  }
}

The methods are pretty obvious by name. Implement these methods accordingly. You may notice that we have used Databinding here to bind data to views but it is not necessary. Also you will have to create your ViewHolder for adapter. You can either use RecyclerView.ViewHolder or you can just create a custom one by inheriting it as per your need. Once this part is over you are half done as most of the things are been taken care in model itself. Now we will be writing code for adapter and setting it to your RecyclerView.

FastItemAdapter<Attendee> fastItemAdapter = new FastItemAdapter<>();
fastItemAdapter.setHasStableIds(true);
...
// functionalities related code
...
recyclerView.setAdapter(fastItemAdapter);

Initialize FastItemAdapter which will be our main adapter handling all the direct functions related to the RecyclerView. Set up some boolean constants according to the project need. In our project we have Attendee model which has id as a primary field. FastItemAdapter can take advantage of distinct field of the model called as identifier . Hence it is set true as Attendee model has id field. But you should be careful about setting it to True as then you must have implemented getIdentifier in the model to return correct field which will be used as an identifier by our adapter. And the adapter is good to set to the RecyclerView.

Now we got to decide which functionalities we will be implementing to our RecyclerView. In our case we needed: 1. Search filter for attendees, 2. Sticky header for attendees groups arranged alphabetically and 3. On click listener for attendee item.

FastItemAdapter has ItemFilter adapter wrapped inside which manages all the filtering stuff. Filtering logic can be set using it.

fastItemAdapter.getItemFilter().withFilterPredicate(this::shallFilter);

Where shallFilter is method which takes attendee object and returns boolean whether to filter the item or not. And after this you can use FastItemAdapter’s filter method to filter the items. For sticky headers you need to implement StickyRecyclerHeadersAdapter extending AbstractAdapter. In this class you will have to implement your filter logic in getHeaderId method. This must return an unique id for items of the same group.

@Override
public long getHeaderId(int position) {
   IItem item = getItem(position);
   if(item instanceof Attendee && ((Attendee)item).getFirstName() != null)
       return ((Attendee) item).getFirstName().toUpperCase().charAt(0);
   return -1;
}

Like in this case we have grouped attendees alphabetically hence just returning initial character’s ASCII value will do good. You can modify this method according to your need. For other unimplemented methods just keep their default return values. With this you will also have to implement onCreateHeaderViewHolder and onBindHeaderViewHolder methods to bind view and data to the header layout. Once this is done you are ready to set sticky headers to your RecyclerView with following code:

stickyHeaderAdapter = new StickyHeaderAdapter();
final HeaderAdapter headerAdapter = new HeaderAdapter();
recyclerView.setAdapter(stickyHeaderAdapter.wrap((headerAdapter.wrap(fastItemAdapter))));

final StickyRecyclerHeadersDecoration decoration = new StickyRecyclerHeadersDecoration(stickyHeaderAdapter);
recyclerView.addItemDecoration(decoration);
adapterDataObserver = new RecyclerView.AdapterDataObserver() {
   @Override
   public void onChanged() {
       decoration.invalidateHeaders();
   }
};
stickyHeaderAdapter.registerAdapterDataObserver(adapterDataObserver);

For click listener, the code is similar to the RecyclerView.Adapter’s one.

fastItemAdapter.withOnClickListener(new FastAdapter.OnClickListener<Item>() {
  @Override
  public boolean onClick(View v, IAdapter<Item> adapter, Item item, int position) {
     // your on click logic
   return true;
  }
});

With this now you have successfully implemented FastItemAdapter to your RecyclerView. Although there are some important points to be taken care of. If you are using filter in your application then you will have to modify your updateItem logic. As when filter is applied to the adapter its items list is filtered. And if you are updating the item using its position from original list it then it will result in exception or updating the wrong item. So you will have to change the position to the one in filtered list. For example the updateAttendee method from Organizer App code looks like this:

public void updateAttendee(int position, Attendee attendee) {
   position = fastItemAdapter.getAdapterPosition(attendee);
   fastItemAdapter.getItemFilter().set(position, attendee);
}
Continue ReadingUsing FastAdapter in Open Event Organizer Android Project

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 ReadingCombining multiple images into one image in the Phimpme Android Application

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 ReadingImplementing Travis for Testing Phimpme Android