How to Make Phimpme Android App Crash Free

Now Phimpme Android app is almost ready with lots of social sharing options. A user can upload images on multiple platforms like Tumblr, Flickr, Imgur, OwnCloud (open source), Nextcloud, dropbox, pinterest, etc. Apart from Sharing, Phimpme app also allow user to click image from own custom camera with different filters and various editing options. As everything is now almost ready so It also important to make app stable and crash free. To make app stable to compatible with all types of device, we can write instrumentation test cases. So in this post I will be explaining how I made Phimpme android app crash free. To do so I have integrated crash reporting service in Phimpme using Firebase Crash report service and Crashlytics.

Using Firebase Crash Reporting service:

Firebase is free of cost and provide various features along with crash reporting. To integrate firebase crash service there is step by step guide.

Step 1:

First, step is to register your app on firebase developer console. To register your Android app on firebase click here.  Add your app name and select your country.

 

Step 2:

Next, click on the Add Firebase to your Android app 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. Type this command in your terminal to generate SHA-1

keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

On successful completion of the command, the SHA-1 key will be displayed on the terminal.

Step 3:

Now add the SHA-1 and package name in firebase console. After that download googleservice.json file and place in app folder of your project.

Step 4:

Add following dependency in your android project and plugin in build.gradle

dependencies {
    classpath 'com.google.gms:google-services:3.1.0'
  }
 apply plugin: 'com.google.gms.google-services'


Step 5:

Once you have done the above four steps your app will be visible in firebase console and now you can add crash service. Now you can see crash in your firebase console

After this add the following dependency in build.gradle. This is very important.

compile 'com.google.firebase:firebase-crash:9.4.0'

Resources:

Continue ReadingHow to Make Phimpme Android App Crash Free

Binding Images Dynamically in Open Event Orga App

In Open Event Orga App (Github Repo), we used Picasso to load images from URLs and display in ImageViews. Picasso is easy to use, lightweight, and extremely configurable but there has been no new release of the library since 2015. We were using Picasso in binding adapters in order to dynamically load images using POJO properties in the layout XML itself using Android Data Binding. But this configuration was a little buggy.

The first time the app was opened, Picasso fetched the image but it was not applied to the ImageView. When the device was rotated or the activity was resumed, it loaded just fine. This was a critical issue and we tried many things to fix it but none of it quite fit our needs. We considered moving on to other Image Loading libraries like Glide, etc but it was too heavy on the size and functionality for our needs. The last resort was to update the library to develop version using Sonatype’s snapshots Repository. Now, the Picasso v2.6.0-SNAPSHOT is very stable but not released to the maven central repository, and a newer develop version v3.0.0-SNAPSHOT was launched too. So we figured we should use that. This blog will outline the steps to include the develop version of Picasso, configuring it for our needs and making it work with Android Data Binding.

Setting up Dependencies

Firstly, we need to include the sonatype repository in the repositories block of our app/build.gradle

repositories {
   ...
   maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}

 

Then we need to replace the Picasso dependency entry to this:

compile 'com.squareup.picasso:picasso:3.0.0-SNAPSHOT'

 

Note that if you used Jake Wharton’s OkHttp3 Downloader for Picasso, you won’t need it now, so you need to remove it from the dependency block

And you need to use this to import the downloader

import com.squareup.picasso.OkHttp3Downloader;

 

Next, we set up our Picasso DI this way

Picasso providesPicasso(Context context, OkHttpClient client) {
   Picasso picasso = new Picasso.Builder(context)
       .downloader(new OkHttp3Downloader(client))
       .build();
   picasso.setLoggingEnabled(true);
   return picasso;
}

 

Set the singleton instance in our application:

Picasso.setSingletonInstance(picasso);

 

And we are ready to use it.

Creating Adapters

Circular Image Adapter

We show event logos as circular images, so we needed to create a binding adapter for that:

@BindingAdapter("circleImageUrl")
public static void bindCircularImage(ImageView imageView, String url) {
   if(TextUtils.isEmpty(url)) {
       imageView.setImageResource(R.drawable.ic_photo_shutter);
       return;
   }

   Picasso.with()
       .load(Uri.parse(url))
       .error(R.drawable.ic_photo_shutter)
       .placeholder(R.drawable.ic_photo_shutter)
       .transform(new CircleTransform())
       .tag(MainActivity.class)
       .into(imageView);
}

 

If the URL is empty, we just show the default photo, and otherwise we load the image into the view using standard CircleTransform

Note that there is no context argument in the with method. This was implemented in Picasso recently where they removed the need for context for loading images. Now, they use a Dummy ContentProvider to get application context, which is inspired by how Firebase does it.

Now, we can just normally use this binding in layout to load the event thumbnail like this

<ImageView
   android:layout_width="@dimen/image_small"
   android:layout_height="@dimen/image_small"
   android:contentDescription="@string/event_thumbnail"
   app:circleImageUrl="@{event.thumbnailImageUrl}" />

 

This gives us a layout like this:

Next we need to load the header image with a deafult image.

Default Image Adapter

For this, we write a very simple adapter without CircleTransform

@BindingAdapter(value = { "imageUrl", "placeholder" }, requireAll = false)
public static void bindDefaultImage(ImageView imageView, String url, Drawable drawable) {
   if(TextUtils.isEmpty(url)) {
       if (drawable != null)
           imageView.setImageDrawable(drawable);
       return;
   }

   RequestCreator requestCreator = Picasso.with().load(Uri.parse(url));

   if (drawable != null) {
       requestCreator
           .placeholder(drawable)
           .error(drawable);
   }

   requestCreator
       .tag(MainActivity.class)
       .into(imageView);
}

 

As imageUrl or placeholder can be null, we check for both, and setting correct images if they are not. We use this in our header layout with both the url and default image we need to show:

<ImageView
   android:scaleType="centerCrop"
   app:imageUrl="@{ event.largeImageUrl }"
   app:placeholder="@{ @drawable/header }"
   android:contentDescription="@string/event_background" />

 

And this gives us a nice dynamic header like this:

This wraps up the blog on Picasso’s latest develop version and Binding Adapters. If you want to know more about Picasso and Android Data Binding, check these links:

Continue ReadingBinding Images Dynamically in Open Event Orga App

Implementing Tree View in PSLab Android App

When a task expands over sub tasks, it can be easily represented by a stem and leaf diagram. In the context of android it can be implemented using an expandable list view. But in a scenario where the subtasks has mini tasks appended to it, it is hard to implement it using the general two level expandable list views. PSLab android application supports many experiments to perform using the PSLab device. These experiments are divided into major sections and each experiments are listed under them.

The best way to implement this functionality in the android application is using a multi layer treeview implementation. In this context three layers are enough as follows;


This was implemented with the help from a library called AndroidTreeView. This blog will outline how to modify and implement it in PSLab android application.

Basic Idea

Tree view implementation simply follows the data structure “Tree” used in algorithms. Every tree has a root where it starts and from the root there will be branches which are connected using edges. Every edge will have a parent and child. To reach a child, one has to traverse through only one route.

Setting Up Dependencies

Implementing tree view begins with setting up dependencies in the gradle file in the project.

compile 'com.github.bmelnychuk:atv:1.2.+'

Creating UI for tree view

The speciality about this implementation is that it can be loaded into any kind of a layout such as a linearlayout, relativelayout, framelayout etc.

final TreeNode Root = TreeNode.root();
Root.addChildren(
       // Add child nodes here
);
// Set up the tree view
AndroidTreeView experimentsListTree = new AndroidTreeView(getActivity(), Root);
experimentsListTree.setDefaultAnimation(true);
[LinearLayout/RelativeLayout].addView(experimentsListTree.getView());

Creating a node holder

Trees are made of a collection of tree nodes. A holder for a tree node can be created using an object which extends the BaseNodeViewHolder class provided by the library. BaseNodeViewHolder requires a holder class which is generally static so that it can be accessed without creating an instance which nests textviews, imageviews and buttons.

Once the holder extends the BaseNodeViewHolder, it should override two methods as follows;

@Override
public View createNodeView(final TreeNode node, ClassContainingNodeData header) {

}

@Override
public void toggle(boolean active) {

}

createNodeView() which inflate the view and toggle() method which can be used to toggle clicks on the tree node in the UI.

The following code snippet shows how to create an object which extends the above mentioned class with the overridden methods.

public class ExperimentHeaderHolder extends TreeNode.BaseNodeViewHolder<ExperimentHeaderHolder.ExperimentHeader> {

    private ImageView arrow;

    public ExperimentHeaderHolder(Context context) {
            super(context);
    }

    @Override
    public View createNodeView(final TreeNode node, ExperimentHeader header) {

            final LayoutInflater inflater = LayoutInflater.from(context);
            final View view = inflater.inflate(R.layout.header_holder, null, false);

            TextView title = (TextView) view.findViewById(R.id.title);
            title.setText(header.title);

            arrow = (ImageView) view.findViewById(R.id.experiment_arrow);
        
            return view;
    }

    @Override
    public void toggle(boolean active) {
            arrow.setImageResource(active ? arrow_drop_up : arrow_drop_down);
    }

    public static class ExperimentHeader {

            public String title;

            public ExperimentHeader(String title) {
               this.title = title;
            }
    }
}

Creating a TreeNode

Once the holder is complete, we can move on to creating an actual tree node. TreeNode class requires an object which extends the BaseNodeViewHolder class as mentioned earlier. Also it requires a viewholder which it can use to inflate the view in the tree layout. The viewholder can be a different class. The importance of this different implementation can be explained as follows;

TreeNode treeNode = new TreeNode(new ExperimentHeaderHolder.ExperimentHeader(“Title”))
       .setViewHolder(new ExperimentHeaderHolder(context));

In the Saved Experiments section of PSLab android application, all the three levels shouldn’t implement the toggle behavior as a user clicks on the experiment (last level item), he doesn’t expect the icon to change like the ones in headers where an arrow points up and down when he clicks on it. In this case we can reuse a holder which has the title attribute while creating only a holder which does not override the toggle function to ignore icon toggling at the last level of the tree view. This explanation can be illustrated using a code snippet as follows;

new TreeNode(new ExperimentHeaderHolder.ExperimentHeader(“Title”))
       .setViewHolder(new IndividualExperimentHolder(context));

Creating parent nodes and finally the Root node

The final part of the implementation is to create parent nodes to group up similar experiments together. The TreeNode object supports a method call addChild() and addChildren(). addChild() method allows adding one tree node to the specific tree node and addChildren() method allows adding many tree nodes at the same time. Following code snippet illustrates how to add many tree nodes to a node and make it a parent node.

treeDiodeExperiments.addChildren(treeZener, treeDiode, treeDiodeClamp, treeDiodeClip, treeHalfRectifier, treeFullWave);

Setting a click listener

Click listener is a very important implementation. Each tree node can be attached with a click listener using the interface provided by the library as follows;

treeNode.setClickListener(new TreeNode.TreeNodeClickListener() {
   @Override
   public void onClick(TreeNode node, Object value) {

   }
});

The value object is the class attached to the holder and its attributes can be retireved by casting it to the specific class using casting methods;

String title = ((ExperimentHeaderHolder.ExperimentHeader) value).title;

Resources:

Continue ReadingImplementing Tree View in PSLab Android App

Implementing Tweet Search feature in Loklak Wok Android

Loklak Wok Android is a peer harvester that posts collected tweets to the Loklak Server. Along with that tweets can be searched using the app. This post describes how search API endpoint and TabLayout is used to implement the tweet searching feature.

Adding Dependencies to the project

This feature uses Retrofit2, Reactive extensions(RxJava2, RxAndroid and Retrofit RxJava adapter) and RetroLambda (for Java lambda support in Android).

In app/build.gradle:

apply plugin: 'com.android.application'
apply plugin: 'me.tatarka.retrolambda'

android {
   ...
   packagingOptions {
       exclude 'META-INF/rxjava.properties'
   }
}

dependencies {
   ...
   compile 'com.google.code.gson:gson:2.8.1'

   compile 'com.squareup.retrofit2:retrofit:2.3.0'
   compile 'com.squareup.retrofit2:converter-gson:2.3.0'
   compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'

   compile 'io.reactivex.rxjava2:rxjava:2.0.5'
   compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
}

 

In build.gradle project level:

dependencies {
   classpath 'com.android.tools.build:gradle:2.3.3'
   classpath 'me.tatarka:gradle-retrolambda:3.2.0'
}

 

Implementation

The search API endpoint is defined in LoklakApi interface which would provide the tweet search result.

public interface LoklakApi {

   @GET("api/search.json")
   Observable<Search> getSearchedTweets(
           @Query("q") String query,
           @Query("filter") String filter,
           @Query("count") int count);
}

 

The POJOs (Plain Old Java Objects) for the result of search API endpoint are obtained using jsonschema2pojo, Gson uses POJOs to convert JSON to Java objects and vice-versa.

The REST client is created by Retrofit2 and is implemented in RestClient class. The Gson converter and RxJava adapter for retrofit is added in the retrofit builder. create method is called to generate the API methods(retrofit implements LoklakApi Interface).

public class RestClient {

   private RestClient() {
   }

   private static void createRestClient() {
       sRetrofit = new Retrofit.Builder()
               .baseUrl(BASE_URL)
               // gson converter
               .addConverterFactory(GsonConverterFactory.create(gson))
               // retrofit adapter for rxjava
               .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
               .build();
   }

   private static Retrofit getRetrofitInstance() {
       if (sRetrofit == null) {
           createRestClient();
       }
       return sRetrofit;
   }

   public static <T> T createApi(Class<T> apiInterface) {
       // create method to generate API methods
       return getRetrofitInstance().create(apiInterface);
   }

}

 

As search API endpoint provides filter parameter which can be used to filter out tweets containing images and videos. So, the tweets are displayed in three categories i.e. latest, images and videos.

The tweets of different category are displayed using a ViewPager. The fragments in ViewPager are inflated by a class that extends FragmentPagerAdapter. SearchFragmentPagerAdapter extends FragmentPagerAdapter, at least two methods getItem and getCount needs to be overridden. Going by the name of methods, getItem provides ith fragment to the  ViewPager and based on the value returned by getCount number of tabs are inflated in TabLayout, a ViewGroup to display fragments in ViewPager in an elegant way. For better UI, the names (here the category of tweets) are displayed, for which we override getPageTitle method.

public class SearchFragmentPagerAdapter extends FragmentPagerAdapter {

   private List<Fragment>  mFragmentList = new ArrayList<>();
   private List<String> mFragmentNameList = new ArrayList<>();

   public SearchFragmentPagerAdapter(FragmentManager fm) {
       super(fm);
   }

   @Override
   public Fragment getItem(int position) {
       return mFragmentList.get(position);
   }

   @Override
   public int getCount() {
       return mFragmentList.size();
   }

   @Override
   public CharSequence getPageTitle(int position) {
       return mFragmentNameList.get(position);
   }

   public void addFragment(Fragment fragment, String pageTitle) {
       mFragmentList.add(fragment);
       mFragmentNameList.add(pageTitle);
   }
}

 

For easy understanding an analogy with RecyclerView can be made. The TabLayout here functions as a RecyclerView, ViewPager does the work of LayoutManager and FragmentPagerAdapter is analogous to RecyclerView.Adapter.

Now, the fragments which contain the categorical tweets are inflated in the parent fragment. Firstly, the ViewPager of TabLayout is set. Then fragments and their names are added to the FragmentPagerAdapter using the addFragment method implemented in SearchFragmentAdapter class above and finally the created adapter is set as the adapter of ViewPager, which is implemented in setupWithViewPager method.

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                        Bundle savedInstanceState) {
   // Inflate the layout for this fragment
   View rootView = inflater.inflate(R.layout.fragment_search, container, false);
   ButterKnife.bind(this, rootView);
   ...
   tabLayout.setupWithViewPager(viewPager);
   setupViewPager(viewPager);

   return rootView;
}

private void setupViewPager(ViewPager viewPager) {
   FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
   SearchFragmentPagerAdapter pagerAdapter = new SearchFragmentPagerAdapter(fragmentManager);
   pagerAdapter.addFragment(SearchCategoryFragment.newInstance("", mQuery), "LATEST");
   pagerAdapter.addFragment(SearchCategoryFragment.newInstance("image", mQuery), "PHOTOS");
   pagerAdapter.addFragment(SearchCategoryFragment.newInstance("video", mQuery), "VIDEOS");
   viewPager.setAdapter(pagerAdapter);
}

 

SearchCategoryFragment are child fragments displayed as tabs in TabLayout. These child fragments are created using newInstance method which takes two parameters, category of tweets and the tweet search query respectively, the reason a constructor with these parameters are not used is that during a orientation change only the default constructor i.e. with no parameters is restored by Android system. So, these parameters are stored in a data structure called Bundle, once the fragment object is created using the default parameter the arguments present in the bundle are passed to fragment using setArguments method. These parameter are retrieved in onCreate lifecycle callback method of fragment which are used to fetch search results.

public static SearchCategoryFragment newInstance(String category, String query) {
   Bundle args = new Bundle();
   // query and category stored in bundle
   args.putString(Constants.TWEET_SEARCH_SUGGESTION_QUERY_KEY, query);
   args.putString(TWEET_SEARCH_CATEGORY_KEY, category);
   // fragment with default constructor created
   SearchCategoryFragment fragment = new SearchCategoryFragment();
   // arguments in bundle are passed to fragment
   fragment.setArguments(args);
   return fragment;
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   Bundle bundle = getArguments();
   if (bundle != null) {
       // arguments retrieved
       mTweetSearchCategory = bundle.getString(TWEET_SEARCH_CATEGORY_KEY);
       mSearchQuery = bundle.getString(Constants.TWEET_SEARCH_SUGGESTION_QUERY_KEY);
   }
}

 

As we have search query and category we can now obtain the search result and pass the obtained result – a List of type Status – to the adapter of RecyclerView which shows the tweets beautifully inside a CardView. The adapter and LayoutManager of RecyclerView are instantiated and set in onCreateView lifecycle callback method. Finally, network request is sent by calling fetchSearchedTweets method to obtain the search results.

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                        Bundle savedInstanceState) {
   // Inflate the layout for this fragment
   View view = inflater.inflate(R.layout.fragment_search_category, container, false);
   ButterKnife.bind(this, view);

   mSearchCategoryAdapter = new SearchCategoryAdapter(getActivity(), new ArrayList<>());
   recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
   recyclerView.setAdapter(mSearchCategoryAdapter);

   // request sent to obtain search result.
   fetchSearchedTweets();

   return view;
}

 

The LoklakApi interface is implemented using the created Rest client and then getSearchedTweets method is invoked which takes in search query, category of tweets and maximum number of results in the mentioned order. If the network request is successful then setSearchResultView is invoked else setNetworkErrorView.

private void fetchSearchedTweets() {
   LoklakApi loklakApi = RestClient.createApi(LoklakApi.class);
   loklakApi.getSearchedTweets(mSearchQuery, mTweetSearchCategory, 30)
           .subscribeOn(Schedulers.io()) // network request sent in a background thread
           .observeOn(AndroidSchedulers.mainThread())
           .subscribe(this::setSearchResultView, this::setNetworkErrorView);
}

 

setSearchResultView displays the obtained result if any in RecyclerView else shows a message that there is no result for the search query.

private void setSearchResultView(Search search) {
   List<Status> statusList = search.getStatuses();
   networkErrorTextView.setVisibility(View.GONE);
   if (statusList.size() == 0) { // request successful but no results
       recyclerView.setVisibility(View.GONE);

       Resources res = getResources();
       String noSearchResultMessage = res.getString(R.string.no_search_match, mSearchQuery); // no result matched message
       // no result message displayed
       noSearchResultFoundTextView.setVisibility(View.VISIBLE);
       noSearchResultFoundTextView.setText(noSearchResultMessage);
   } else { // there are some results, so display them in RecyclerView
       recyclerView.setVisibility(View.VISIBLE);
       mSearchCategoryAdapter.setStatuses(statusList);
   }
}

 

In case of a failed network request, a TextView is displayed asking the user to check the network connections and click on it to retry.

private void setNetworkErrorView(Throwable throwable) {
   Log.e(LOG_TAG, throwable.toString());
   recyclerView.setVisibility(View.GONE);
   networkErrorTextView.setVisibility(View.VISIBLE);
}

 

When the TextView, networkErrorTextView, is clicked a network request is sent again and the visibility of networkErrorTextView is changed to GONE, as implemented in setOnClickNetworkErrorTextViewListner

@OnClick(R.id.network_error)
public void setOnClickNetworkErrorTextViewListener() {
   networkErrorTextView.setVisibility(View.GONE);
   fetchSearchedTweets();
}

 

References:

Resources

Continue ReadingImplementing Tweet Search feature in Loklak Wok Android

Upload Images to OwnCloud and NextCloud in Phimpme Android

As increasing the stack of account manager in Phimpme Android. We have now two new items OwnCloud and NextCloud to add. Both are open source storage services. Provides complete source code of their official apps and libraries on Github. You can check below

OwnCloud: https://github.com/owncloud

NextCloud: https://github.com/nextcloud

This requires a hosting server, where you can deploy it and access it through their web app and Mobile apps. I added a feature in Phimpme to upload images directly to the server right from the app using their android-library.

Steps (How I did in Phimpme)

  • Add library in Application gradle file

Firstly, to work with, we need to add the android-library they provide.

compile "com.github.nextcloud:android-library:$rootProject.nextCloudVersion"

Check the new version from here and apply over it: https://github.com/nextcloud/android-library/releases

  • Login from Account Manager

As per our Phimpme app flow, User first connect itself from the account manager and then share image from app using these credentials. Added a new Login activity for OwnCloud and NextCloud both.

          

  • Saved credentials in Database

To use that further in android-library, I store the credentials in Realm database.

account.setServerUrl(data.getStringExtra(getString(R.string.server_url)));
account.setUsername(data.getStringExtra(getString(R.string.auth_username)));
account.setPassword(data.getStringExtra(getString(R.string.auth_password)));
  • Uploading image using library

As per the official guide of OwnCloud, used Created an object of OwnCloudClient. Set the username and password.

private OwnCloudClient mClient;
mClient = OwnCloudClientFactory.createOwnCloudClient(serverUri, this, true);
mClient.setCredentials(
       OwnCloudCredentialsFactory.newBasicCredentials(
               username,
               password
       )
);

Passed the image path which we are getting in the SharingActivity. Modified with adding the separator.

File fileToUpload = new File(saveFilePath);
String remotePath = FileUtils.PATH_SEPARATOR + fileToUpload.getName();

Used the UploadRemoteOperation Class and just need to pass the path, mimeType and timeStamp. The library have already defined functions to execute the upload operations.

UploadRemoteFileOperation uploadOperation =
       new UploadRemoteFileOperation(fileToUpload.getAbsolutePath(), remotePath, mimeType, timeStamp);
uploadOperation.execute(mClient, this, mHandler);

  • Setup Account using Docker and Digital Ocean

I have already a previous blog post on how to setup NextCloud or OwnCloud account on server using Digital Ocean and Docker.

Link: https://blog.fossasia.org/how-to-use-digital-ocean-and-docker-to-setup-test-cms-for-phimpme/

Resource:

  1. NextCloud Developer Mannual: https://docs.nextcloud.com/server/9/developer_manual/index.html
  2. OwnCloud Library installation: https://doc.owncloud.org/server/9.0/developer_manual/android_library/library_installation.html
  3. Examples: https://doc.owncloud.org/server/9.0/developer_manual/android_library/examples.html
Continue ReadingUpload Images to OwnCloud and NextCloud in Phimpme Android

Common Utility classes Progress Bar and Snack Bar in Phimpme Android

As the Phimpme Android is scaling very fast on its features, code gets redundant sometimes. Some of the widely used design widgets in Android are Progress Bar and Snack Bar. Progress Bar is shown to user when some process is happening in the background. Snackbar is a feedback operation to user of its recent process. In other words we can say Snackbar is the new toast in Android with a cool feature of setting action on them. So that User can interact with the feedback received on the process.

As In Phimpme lots of account Login and Logout progress happens. Uploading success and failure required Snackbar to show to the Users. So to remove the redundancy of the boilerplate of these codes, I added two Utilities class one is Phimpme ProgressbarHandler and other is SnackbarHandler in the app. Below is one by one code and explanation of both.

Progress Bar Handler

In the constructor I passed Context as parameter. Created a ViewGroup object and set view of android. Setting the progress bar style and length using Android core attributes such as progressBarStyleLarge and duration to setIndeterminate true.

private ProgressBar mProgressBar;

public PhimpmeProgressBarHandler(Context context) {
   ViewGroup layout = (ViewGroup) ((Activity) context).findViewById(android.R.id.content)
           .getRootView();

   mProgressBar = new ProgressBar(context, null, android.R.attr.progressBarStyleLarge);
   mProgressBar.setIndeterminate(true);



   RelativeLayout.LayoutParams params = new
           RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
           RelativeLayout.LayoutParams.MATCH_PARENT);

   RelativeLayout rl = new RelativeLayout(context);

   rl.setGravity(Gravity.CENTER);
   rl.addView(mProgressBar);

   layout.addView(rl, params);

   hide();

}

Next is used dynamically created Relative Layout object and setup the parameters for width and height as MATCH_PARENT. Setting gravity of the layout to center and added the progress bar view on it using the addView method. So basically we have a progress bar ready and we dynamically created a relative layout and added the view over it.

The function used in setting up the views and progress bar are from AOSP only.

After that a Progressbar is set, we now need functions to show and hide the progress bar in the code. Created two functions show() and hide().

public void show() {
   mProgressBar.setVisibility(View.VISIBLE);
}

public void hide() {
   mProgressBar.setVisibility(View.INVISIBLE);
}

These functions set the visibility of the the progress bar.

Usage:

Now in any class we can create object of our Progressbar handler class pass the context on it and use the show() and hide() methods wherever we want to show this and hide. Below is the code snippet to show the illustration.

phimpmeProgressBarHandler = new PhimpmeProgressBarHandler(this);

phimpmeProgressBarHandler.show();

phimpmeProgressBarHandler.hide();

Snackbar Handler

To do this, I created a separate class as Snackbar Handler. What we can do is to create a static function show() and inside the declaration, we can create an object of Snackbar and apply the styles to that.

As you can see in the code snippet below, I Created a static function with parameters such as View (to take the view instance), String (to show the message) and duration  of the Snackbar. Set Up the text, textsize and action on the snackbar. An “OK” action is predefined in the function only.

public static void show(View view, String text, int duration) {
   final Snackbar snackbar = Snackbar.make(view, text, duration);
   View sbView = snackbar.getView();
   TextView textView = (TextView)sbView.findViewById(android

.support.design.R.id.snackbar_text);
   textView.setTextColor(Color.WHITE);
   textView.setTextSize(12);
   snackbar.setAction("OK", new View.OnClickListener() {
       @Override
       public void onClick(View view) {
           snackbar.dismiss();
       }
   });
   snackbar.show();
}

Usage:

To use this directly call the show method pass the view and String of the message which you want to show on Snackbar. There are overloaded methods as well in which you can pass the durations. See the below code as example.

SnackBarHandler.show(parentLayout, getString(R.string.no_account_signed_in));

Resources

Continue ReadingCommon Utility classes Progress Bar and Snack Bar in Phimpme Android

Encoding and Saving Images as Strings in Preferences in SUSI Android App

In this blog post, I’ll be telling about how to store images in preferences by encoding them into Strings and also how to retrieve them back. Many a times, you need to store an image in preferences for various purposes and then need to retrieve it back when required. In SUSI Android App, we need to store an image in preference to set the chat background. We just simply select image from gallery, convert image to a byte array, then do a Base 64 encoding to string, store it in preferences and later decode it and set the chat background.

Base64 Encoding-Decoding in Java

You may already know what Base 64 is but still here is a link to Wikipedia article explaining it. So, how to do a Base64 encoding-decoding in java? For that java has a class with all such methods already present. https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html

According to the docs:

This class consists exclusively of static methods for obtaining encoders and decoders for the Base64 encoding scheme. The implementation of this class supports the following types of Base64 as specified in RFC 4648 and RFC 2045.

  • Basic
  • URL and Filename safe
  • MIME

So, you may just use Base64.encode to encode a byte array and Base64.decode to decode a byte array.

Implementation

1. Selecting image from gallery

    

Start Image Picker Intent and pick an image from gallery. After selecting you may also start a Crop Intent to crop image also. After selecting and cropping image, you will get a URI of the image.

override fun openImagePickerActivity() {
   val i = Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
   startActivityForResult(i, SELECT_PICTURE)
}
val thePic = data.extras.getParcelable<Bitmap>("data")
val encodedImage = ImageUtils.Companion.cropImage(thePic)
chatPresenter.cropPicture(encodedImage)

2. Getting image from the URI using inputstream

Next step is to get the image from file using URI from the previous step and InputStream class and store it in a BitMap variable.

val imageStream: InputStream = context.contentResolver.openInputStream(selectedImageUri)
   val selectedImage: Bitmap
   val filePathColumn = arrayOf(MediaStore.Images.Media.DATA)
   val cursor = context.contentResolver.query(getImageUrl(context.applicationContext, selectedImageUri), filePathColumn, null, null, null)
   cursor?.moveToFirst()
   selectedImage = BitmapFactory.decodeStream(imageStream)

3. Converting the bitmap to ByteArray

Now, just convert the Bitmap thus obtained to a ByteArray using below code.

val baos = ByteArrayOutputStream()
   selectedImage.compress(Bitmap.CompressFormat.JPEG, 100, baos)
   val b = baos.toByteArray()

4. Base64 encode the ByteArray and store in preference

Encode the the byte array obtained in last step to a String and store it in preferences.

 val encodedImage = Base64.encodeToString(b, Base64.DEFAULT)
//now you have a string. You can store it in preferences

5. Decoding the String to image

Now whenever you want, you can just decode the stored Base64 encoded string to a byte array and then from byte array to a bitmap and use wherever you want.

fun decodeImage(context: Context, previouslyChatImage: String): Drawable {
   val b = Base64.decode(previouslyChatImage, Base64.DEFAULT)
   val bitmap = BitmapFactory.decodeByteArray(b, 0, b.size)
   return BitmapDrawable(context.resources, bitmap)
}

Summary

So, the main aim of this blog was to give an idea about how can you store images in preferences. There is no way to store them directly. So, you have to convert them to String by encoding them in Base64 format and then decoding it to use it. You also have other ways to store images like storing it in database etc but this one is simpler and fast.

Resources

  1. Stackoverflow answer to “How to save image as String” https://stackoverflow.com/questions/31502566/save-image-as-string-with-sharedpreferences
  2. Other Stackoverflow answer about “Saving Images in preferences” https://stackoverflow.com/questions/18072448/how-to-save-image-in-shared-preference-in-android-shared-preference-issue-in-a
  3. Official docs of Base64 class https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html
  4. Wikipedia link for learning about Base64 https://en.wikipedia.org/wiki/Base64
  5. Stackoverflow answer for “What is Base64 encoding used for?” https://stackoverflow.com/questions/201479/what-is-base-64-encoding-used-for
Continue ReadingEncoding and Saving Images as Strings in Preferences in SUSI Android App

Adding Tumblr Upload Feature in Phimpme

The Phimpme Android application along with various other cloud storage and social media upload features provides an option to upload the images on Tumblr without having to download any other applications. In this post, I will be explaining how I integrated Tumblr in phimpme as there is no proper guide on the web how to integrate to Tumblr in Android. Tumblr provides an Android-SDK but there is no proper documentation to it and is not enough to authenticate and upload the images to it. After so much research I came to a solution. So read this article to know how to integrate Tumblr in Android.

Step 1:

First, add two dependencies to your project one is for Android SDK of Tumblr and one is for loglr which help you to get login on Tumblr.

dependencies {

compile 'com.daksh:loglr:1.2.1'

compile 'com.tumblr:jumblr:0.0.11'

}

Step 2:

  1. Register your app on Tumblr to obtain developer keys.
  2. Enter callback URL it is important to get keys.
  3. Generate CONSUMER_KEY & CONSUMER_SECRET from the official developer console of Tumblr.

Register your application

Step 3:

Now we use Loglr library to log in to Tumblr. Tumblr doesn’t provide any library to login so I am using Loglr library for login Tumblr. After successfully log in Loglr will return API_KEY and API_SECRET. We will use these keys later to upload the image. Save these keys as constant variables.

public final static String TUMBLR_CONSUMER_KEY = "ENTER-CONSUMER-KEY";

public final static String TUMBLR_CONSUMER_SECRET = "ENTER-CONSUMER-SECRET";

Step 4:

To authenticate the Tumblr use loglr login instance and it can be done as follows.

Loglr.getInstance()

     .setConsumerKey(Constants.TUMBLR_CONSUMER_KEY)

     .setConsumerSecretKey(Constants.TUMBLR_CONSUMER_SECRET)

     .setLoginListener(loginListener)

     .setExceptionHandler(exceptionHandler)

     .enable2FA(true)

     .setUrlCallBack(Constants.CALL_BACK_TUMBLR)

     .initiateInActivity(AccountActivity.this);

After that you will be prompt to enter your tumblr credentials to authenticate the phimpme Android app. Once you have done it will return api_token and api_secret. Now save this in database.

account.setToken(loginResult.getOAuthToken());

account.setSecret(loginResult.getOAuthTokenSecret());

Step 5:

Once the authentication is done now we can upload an image directly to Tumblr from the Share activity in the Phimpme Android application. To upload an image create an async task so that uploading process will run in a background thread and not block the main UI thread. Keep in mind Tumblr require 4 variable to create Tumblr client CONSUMER_KEY, CONSUMER_SECRET, API_KEY and API_SECRET. Now we can create a Tumblr client using these 4 values. Once the client is created we are ready to get data from Tumblr and upload an image on Tumblr. Before uploading an image on Tumblr we need blog name because the user can have multiple blogs on Tumblr so we need to ask the user to choose a blog name from the list or we can provide dialog to enter blog name manually. Now enter the following code in the doInBackground() method of asynctask.

PhotoPost post = null;

try {

 post = client.newPost(user.getBlogs().get(0).getName(), PhotoPost.class);

 if (caption!=null && !caption.isEmpty())

 post.setCaption(caption);

 post.setData(new File(imagePath));

 post.save();

} catch (IllegalAccessException | InstantiationException e) {

 success = false;

}

If success variable is true that means our image is uploaded successfully. This is how I implemented the upload feature to Tumblr using two different libraries. To get the full source code, please refer to the Phimpme Android repository.

Resources:

Continue ReadingAdding Tumblr Upload Feature in Phimpme

Uploading Images to Box Storage from the Phimpme Application

The Phimpme Android application along with many other cloud storage applications integrated like Dropbox, Imgur, Pinterest has the option to upload the image to the Box storage without having to install any other applications on the device. From the Phimpme app, the user can click the photo, edit it, view any image from the gallery and then can upload multiple images to many storage services or social media without any hassle. In this tutorial, I will be discussing how we achieved the functionality to upload the images on the Box storage.

Step 1:

To integrate Box storage to the application, the first thing we need to do is create an application from the Box developers console and get the CLIENT_ID and the CLIENT_SECRET. To do this:

  1. Go to the Box developer page and log in.
  2. Create a new application from the app console.
  3. It will now give options to select what kind of application are you using. Select the option partner integration to get the image upload functionality to all the users.
  4. Give a name to your application and finally click the create application button.
  5. After this, you will be taken to the screen similar to the below screenshot from where you can copy the CLIENT_ID and CLIENT_SECRET to be used later on.

Step 2:

Now coming to the Android part, to get the image upload functionality to the box storage, we need to make use of the Android SDK provided by Box to achieve the uploading functionality easily. To add the SDK to the Android project from Gradle, copy the following line of Gradle script which will download and install the SDK from the maven repository.

maven{ url "https://mvnrepository.com/artifact/com.box/box-android-sdk" }
compile group: 'com.box', name: 'box-android-sdk', version: '4.0.8'

Now after adding the SDK, rebuild the project.

Step 3:

After this, to upload the file on the box storage, we need to login to the Box account to get the access token and the user name to store it in Realm database. For this, first we have to configure the Box client. This can be done using the following line of code.

BoxConfig.CLIENT_ID = BOX_CLIENT_ID;
BoxConfig.CLIENT_SECRET = BOX_CLIENT_SECRET;

Replace the BOX_CLIENT_ID and BOX_CLIENT_SECRET in the above code with the key received by following the step 1.

After configuring the Box API client, we can authenticate the user using the sessionBox object by following lines of code.

sessionBox = new BoxSession(AccountActivity.this);
sessionBox.authenticate();

After the authentication, we have to get the user name and the access token of the user using the authenticated session box.

account.setUsername(sessionBox.getUser().getName());
account.setToken(String.valueOf(accessToken));

After authenticating the user, we can upload the photos directly to the Box storage from the Share Activity in the Phimpme Android application. For this, we have to create a new Asynchronous task in android which will do the network operations in the background to avoid the Network on main thread exception. After this, we can make use of the Box file API to upload the photos to the Box storage and call the function getUploadRequest which takes the three parameters file input stream, the upload name of the file and the destination of the folder respectively. This can be done by the following lines of code.

mFileApi = new BoxApiFile(sessionBox);
BoxRequestsFile.UploadFile request = mFileApi.getUploadRequest(inputStream, uploadName, destinationFolderId);

This upload request throws a BoxException so we have to catch that exception using the try/catch block to avoid the application crash in case the upload request fails.

This is how we have implemented the upload to Box storage functionality in the Phimpme Android application. To get the full source code, please refer to the Phimpme Android repository.

Resources

  1. Box Developers app console : https://app.box.com/developers/console/
  2. Box Android SDK GitHub page : https://github.com/box/box-android-sdk
  3. Android’s Developer page on NetworkOnMainThreadExceoption : https://developer.android.com/reference/android/os/NetworkOnMainThreadException.html
Continue ReadingUploading Images to Box Storage from the Phimpme Application