Social SignIn [Part - 2] - Fabric SDK For Twitter Login

Fabric SDK is newer SDK introduced by Twitter for Mobile Integration. Fabric SDK comes with many combined projects like Twitter Authentication For Twitter API, Crashlytics For crash reports and analysis, MoPub for advertisement etc. Before this sdk or alternative of this is Twitter4J library for Twitter API.

androprogrammer.com


This is 2nd Part of tutorial series so i am not going through all project setup steps you can find it at Social SignIn [Part - 1] - Integrate Google plus in Android App. To add Twitter authentication in your application you have to create application at Fabric.io and you can configure about which project you want to integrate into your project from Fabric sdk. Once you completely create application it will even help you to integrate Fabric sdk using Android Studio Plugins.

Once it gets installed  you can see icons like this image in toolbar. 

Just click on it and it will start like setup wizard you just have to click next. if you don't have developer account it will even create for you from Android Studio Plugin.


Wow That's easy... right ?

No, once you integrate all those things into project you might come across gradle error like this.
Error:Execution failed for task ':app:fabricGenerateResourcesDebug'.
> Crashlytics Developer Tools error.
Don't worry just comment this line in app > build.gradle file.
apply plugin: 'io.fabric'

If you only want to add Twitter login then only comment it. if you want other packages also then you have to use Fabric sdk. So now setup is done now let's see how you can add twitter button in your layout. If you want same twitter button added by Fabric sdk in your project you can skip this step. In this tutorial i have created TwitterLoginHelper class which actually does all things for me and for my view it just return result of that. But with TwitterLoginButton you don't have to do any thing. it has setCallBack method which allows you to get result into activity. but i am following MVP where my view don't know about callbacks and all it just consume result after all manipulation. Have look at my TwitterLoginHelper class.

TwitterLoginHelper.java 
package com.androprogrammer.socialsignin.util.helpers;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import com.androprogrammer.socialsignin.listeners.SocialConnectListener;
import com.androprogrammer.socialsignin.model.UserLoginDetails;
import com.twitter.sdk.android.Twitter;
import com.twitter.sdk.android.core.Callback;
import com.twitter.sdk.android.core.Result;
import com.twitter.sdk.android.core.TwitterException;
import com.twitter.sdk.android.core.TwitterSession;
import com.twitter.sdk.android.core.identity.TwitterAuthClient;
import com.twitter.sdk.android.core.services.AccountService;

/**
 * Created by Wasim on 24-Jan-16.
 */
public class TwitterLoginHelper {

    private TwitterAuthClient client;

    private AppCompatActivity activity;
    private UserLoginDetails userData;

    private SocialConnectListener userCallbackListener;

    private int identifier;
    private static final String TAG = "TwitterLoginHelper";

    public void createConnection(AppCompatActivity mActivity) {

        this.activity = mActivity;
        userData = new UserLoginDetails();
        client = new TwitterAuthClient();

    }

    public TwitterAuthClient getClient() {
        return client;
    }

    public void onActivityResult(int requestCode,int resultCode, Intent data) {

        client.onActivityResult(requestCode,resultCode,data);

    }

    public void signIn(final int mIdentifier) {

        if (client != null)
        {
            identifier = mIdentifier;
            client.authorize(activity, new Callback <twittersession>() {
                @Override
                public void success(Result <twittersession> twitterSessionResult) {
                    Log.d(TAG, "Logged with twitter");
                    final TwitterSession session = twitterSessionResult.data;

                    AccountService ac = Twitter.getApiClient(twitterSessionResult.data).getAccountService();
                    ac.verifyCredentials(true, true, new Callback <com .twitter.sdk.android.core.models.user>() {
                        @Override
                        public void success(Result <com .twitter.sdk.android.core.models.user> result) {

                            userData.setIsSocial(true);
                            userData.setIsAndroid(true);
                            userData.setTwittersLogin(true);
                            userData.setTwitterID(String.valueOf(session.getUserId()));
                            userData.setFullName(result.data.name);
                            //userData.setEmail(result.data.email);
                            userData.setEmail(getUserEmail(session));
                            userData.setUserImageUrl(result.data.profileImageUrl);

                            if (userCallbackListener != null)
                            {
                                userCallbackListener.onUserConnected(identifier,userData);
                            }
                        }

                        @Override
                        public void failure(TwitterException e) {

                            if (userCallbackListener != null)
                            {
                                userCallbackListener.onConnectionError(identifier,e.getMessage());
                            }

                        }
                    });

                    /**/


                }

                @Override
                public void failure(TwitterException e) {
                    Log.e(TAG, "Failed login with twitter");
                    e.printStackTrace();
                    if (userCallbackListener != null)
                    {
                        userCallbackListener.onConnectionError(identifier,e.getMessage());
                    }
                }
            });
        }

    }

    public void signOut()
    {
        Twitter.getSessionManager().clearActiveSession();
    }

    public void setUserCallbackListener(SocialConnectListener userCallbackListener) {
        this.userCallbackListener = userCallbackListener;
    }

    private String getUserEmail(TwitterSession session)
    {
        final String[] userEmail = new String[1];

        client.requestEmail(session, new Callback <string>() {
            @Override
            public void success(Result <string> result) {
                // Do something with the result, which provides the email address
                Log.d(TAG,"Email found - " + result.toString());
                userEmail[0] = result.data;
            }

            @Override
            public void failure(TwitterException exception) {
                // Do something on failure
                Log.d(TAG,"Email not found - " + exception.getMessage());
                userEmail[0] = "";
            }
        });

        return userEmail[0];
    }
}


This class actually does every thing for me and it is like central class for all operation of Twitter. you can create method here for getTweets or user timeline etc. benefits of such way is you don't have to create Twitter client object for different task into different classes. To get user email your app has to be on their whitlist applications.  To request email you have to submit this form.

Well now have look at below code where you can see how i have user this class.

private void startTwitterLogin() {
 Log.d("onclick", "clicked");
 if (Utility.isConnectivityAvailable(LoginActivity.this)) {
  runOnUiThread(new Runnable() {
   @Override
   public void run() {
    layout_full_twitter.setBackgroundColor(getResources().getColor(R.color.view_disable));
    layout_full_twitter.setEnabled(false);
    layout_twitter.setVisibility(View.GONE);
    pb_twitterLoader.setVisibility(View.VISIBLE);
   }
  });

  twitterHelper.createConnection(LoginActivity.this);
  twitterHelper.signIn(TWITTER_SIGN_IN);

 } else {
  Utility.showToast(LoginActivity.this,getString(R.string.msg_noInternet));
 }
}

So That's it from my side. this tutorial only covers Twitter authentication but for more like get tweets or timeline or post tweet etc subscribe to our news latter so you can get more tutorials in your inbox.

I have created this project in Kotlin language also so if you want please check out here.

Source Code

Keep coding... :)

Social SignIn [Part - 1] - Integrate Google plus in Android App

Signup with different social sites are now common. I think almost 50% applications now have options to login with different social sites like Google Plus, Facebook, Twitter etc. And one more thing makes this things required or integrated in so much apps are Rest Api or SDK provided by such platforms is very handy and easy to integrate. They even now handle sessions by them self so now you don't have to worry about user session.

androprogrammer.com


Pr-requisite
1. Project setup in Google Developer console. more details
2. Enable Google plus Api for your project. more details


So Let's start project, i hope you have already setup project which is going to use Google plus(Google Account) login. So add below code in your app/build.gradle file. you only require this part of Google play service in dependency tag so just add this only. Google play service is huge library so if you are adding full library, make sure you require all those things in your app. if not just add parts of it which is required.

app/build.gradle
compile 'com.google.android.gms:play-services-auth:8.3.0'
That's it. In Google Play Service 8.3 they have added this part which is for just google login and getting basic user data. before Google Play Service 8.3 you have to use Google plus part of play service to login and get user data but now it is separated from it. so if you want user data like cover photo, user dob, user friend circle etc then you have to add compile 'com.google.android.gms:play-services-plus:8.3.0' also in build file.

To Login with Google and get user data i have create a helper class which actually do all the staff for me and return user data or error if any by SocialConnectListener interface. go through below code and you will see how you can write less code in your activity class and get desired result.

GooglePlusLoginHelper.java
package com.androprogrammer.socialsignin.util.helpers;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import com.androprogrammer.socialsignin.listeners.SocialConnectListener;
import com.androprogrammer.socialsignin.model.UserLoginDetails;
import com.androprogrammer.socialsignin.util.Utility;
import com.google.android.gms.auth.api.Auth;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.auth.api.signin.GoogleSignInResult;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.Scopes;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.OptionalPendingResult;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Scope;



/**
 * Created by Wasim on 06-Dec-15.
 */
public class GooglePlusLoginHelper implements GoogleApiClient.OnConnectionFailedListener {


 private AppCompatActivity activity;
 private UserLoginDetails userData;

 private SocialConnectListener userCallbackListener;

 /* Client for accessing Google APIs */
 private static GoogleApiClient mGoogleApiClient;

 private int requestIdentifier = 0;

 private static final String TAG = "GooglePlusLoginHelper";

 public void createConnection(AppCompatActivity mActivity)
 {

  this.activity = mActivity;
  userData = new UserLoginDetails();

  if (Utility.checkPlayServices(mActivity)) {

   GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
     .requestScopes(new Scope(Scopes.PROFILE))
     .requestScopes(new Scope(Scopes.PLUS_LOGIN))
     .requestProfile()
     .requestEmail()
     .build();

   if (mGoogleApiClient == null) {
    // START create_google_api_client
    // Build GoogleApiClient with access to basic profile
    mGoogleApiClient = new GoogleApiClient.Builder(mActivity)
      .enableAutoManage(mActivity,this)
      .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
      .build();
   }
  }
 }

 public void onActivityResult(int resultCode,Intent data)
 {

  GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
  handleSignInResult(result);

 }

 // START signIn
 public void signIn(int identifier) {
  requestIdentifier = identifier;
  Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
  activity.startActivityForResult(signInIntent, requestIdentifier);
 }
 

 // Sign out
 public void signOut() {
  if (mGoogleApiClient != null) {

   if(!mGoogleApiClient.isConnecting()){
    mGoogleApiClient.connect();
   }

   mGoogleApiClient.disconnect();

   Utility.showToast(activity, "You are logged out successfully");
  }
 }

 public SocialConnectListener getUserCallbackListener() {
  return userCallbackListener;
 }

 public void setUserCallbackListener(SocialConnectListener userCallbackListener) {
  this.userCallbackListener = userCallbackListener;
 }


 public void handleSignInResult(GoogleSignInResult result) {

  Log.d(TAG, "handleSignInResult:" + result.isSuccess());
  if (result.isSuccess()) {
   // Signed in successfully, show authenticated UI.
   GoogleSignInAccount acct = result.getSignInAccount();

   if (acct != null) {
    userData.setFullName(acct.getDisplayName());

    userData.setGoogleID(acct.getId());
    userData.setEmail(acct.getEmail());
    if (acct.getPhotoUrl() != null) {
     userData.setUserImageUri(acct.getPhotoUrl().toString());
    }

    userData.setIsGplusLogin(true);
    userData.setIsSocial(true);

    if (userCallbackListener != null) {
     userCallbackListener.onUserConnected(requestIdentifier,userData);
    }

   }
  }
 }

 @Override
 public void onConnectionFailed(ConnectionResult connectionResult) {

  Log.d(TAG, "onConnectionFailed:" + connectionResult);

  if (userCallbackListener != null)
  {
   userCallbackListener.onConnectionError(requestIdentifier,connectionResult.getErrorMessage());
  }
 }
}


In above code i have passed activity object in place of context but when you call connect method you will get response in onActivityResult for which you have to cast context to activity. check below code for LoginActivty in which i require user data. which actually initiate login request.

LoginActivity.java
package com.androprogrammer.socialsignin.activities;

import android.content.Intent;
import android.graphics.PorterDuff;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.ProgressBar;

import com.androprogrammer.socialsignin.R;
import com.androprogrammer.socialsignin.listeners.SocialConnectListener;
import com.androprogrammer.socialsignin.model.UserLoginDetails;
import com.androprogrammer.socialsignin.util.PreferenceManager;
import com.androprogrammer.socialsignin.util.Utility;
import com.androprogrammer.socialsignin.util.helpers.GooglePlusLoginHelper;

import java.util.ArrayList;

import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class LoginActivity extends AppCompatActivity implements SocialConnectListener {

 // Google Plus Login
 @Bind(R.id.layout_gplustext) LinearLayout layout_gplus;
 @Bind(R.id.layout_full_gplus) LinearLayout layout_full_gplus;
 @Bind(R.id.pb_gplusloading) ProgressBar pb_gpLoader;

 /* RequestCode for sign-in */
 private static final int GPLUS_SIGN_IN = 1001;

 private GooglePlusLoginHelper gplusHelper;


 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  getWindow().setFlags(
    WindowManager.LayoutParams.FLAG_FULLSCREEN,
    WindowManager.LayoutParams.FLAG_FULLSCREEN);

  setContentView(R.layout.activity_login);
  ButterKnife.bind(this);

  gplusHelper = new GooglePlusLoginHelper();
  gplusHelper.setUserCallbackListener(this);

  pb_gpLoader.getIndeterminateDrawable().setColorFilter(getResources().getColor(R.color.colorPrimary),
    PorterDuff.Mode.SRC_IN);

 }


 @OnClick(R.id.layout_full_gplus) void clicked() {

  // clicked on google plus login
  if (Utility.isConnectivityAvailable(LoginActivity.this)) {
   runOnUiThread(new Runnable() {
    @Override
    public void run() {
     layout_full_gplus.setBackgroundColor(getResources().getColor(R.color.view_disable));
     layout_full_gplus.setEnabled(false);
     layout_gplus.setVisibility(View.GONE);
     pb_gpLoader.setVisibility(View.VISIBLE);
    }
   });

   gplusHelper.createConnection(LoginActivity.this);
   gplusHelper.signIn(GPLUS_SIGN_IN);

  } else {
   Utility.showToast(LoginActivity.this,"No internet connection available...");
  }
 }


 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {

  if (requestCode == GPLUS_SIGN_IN) {
  
   gplusHelper.onActivityResult(resultCode, data);
  }
 }

 @Override
 public void onUserConnected(int requestIdentifier,UserLoginDetails userData) {

  if (requestIdentifier == GPLUS_SIGN_IN) {
   runOnUiThread(new Runnable() {
    @Override
    public void run() {
     layout_full_gplus.setBackgroundColor(getResources().getColor(R.color.bg_gplus));
     layout_full_gplus.setEnabled(true);
     layout_gplus.setVisibility(View.VISIBLE);
     pb_gpLoader.setVisibility(View.GONE);

     // For sign out 
     PreferenceManager.preferencePutInteger(getResources().getString(R.string.pref_login_identifier),
       GPLUS_SIGN_IN);
    }
   });
  }

  Utility.showToast(LoginActivity.this, "connected");
  PreferenceManager.preferencePutBoolean(getResources().getString(R.string.pref_is_loggedin),true);

  Intent MainScreen = new Intent(LoginActivity.this,MainActivity.class);

  Bundle b = new Bundle();
  b.putSerializable(getResources().getString(R.string.key_userModel),userData);

  MainScreen.putExtra(getResources().getString(R.string.key_userBundle), b);

  startActivity(MainScreen);
  overridePendingTransition(R.anim.right_slide_in, 0);
 }

 @Override
 public void onConnectionError(int requestIdentifier,String message) {
  Utility.showToast(LoginActivity.this, message);

  if (requestIdentifier == GPLUS_SIGN_IN) {
   runOnUiThread(new Runnable() {
    @Override
    public void run() {
     layout_full_gplus.setBackgroundColor(getResources().getColor(R.color.bg_gplus));
     layout_full_gplus.setEnabled(true);
     layout_gplus.setVisibility(View.VISIBLE);
     pb_gpLoader.setVisibility(View.GONE);
    }
   });
  }
 }


 @Override
 public void onCancelled(int requestIdentifier,String message) {
  Utility.showToast(LoginActivity.this, message);
 }
}



I hope this is how social connect libraries works on mobile platform. they allow us to implement some interface and they pass data through it. so you can even now create own library or framework if your using more then one platform. if you want other social connect tutorials also please subscribe to our news latter because my new article will be on facebook or twitter. so i hope you can find latest code in your inbox.

From coding point of view it is done but still you have to add Get Account permission in your manifest. and if you want your app works on Android new version Marshmallow you have to ask for permission runtime. because GET_ACCOUNTS permission is in danger permissions list. 

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.GET_ACCOUNTS"/>

I have created this project in Kotlin language also so if you want please check out here.

Source Code

Keep coding... :)

Data Caching in Android Application like Gmail, What's app

Offline access is one of the basic requirement of Applications now. All developers or Application owner wants their whole app can be usable without internet. Because some time we all face internet connection issue but still we can use some of the application offline like Gmail, What's app and much more. So how they do such things. How you can develop application so it can be used without internet also.

Data Caching can be done with following patterns.
  1. Store data (Server response) in database and access from it.
  2. Store data (Server response) in internal cache directory.
  3. Store data (Server response) in external cache directory.

androprogrammer.com


    In this tutorial i am going to use Reservoir library which allows you to store data in internal cache directory using <Key, Value> pair. There are number of libraries but this is simple and easy to integrate. With Reservoir i have used ION library for Network Request and getting data from server and GSON library for data (Pojo class to response object) mapping. Like ION you can find many networking library like Volley, Retrofit,Smash etc. Volley even supports data caching without any external library but Volley isn't available with Gradle build.

    Steps

    Step - 1
    For learning purpose create a sample app with one activity and give proper name of it. in this tutorial it is DataCachingDemo. In this activity i have used Recycler view to display list of user data. In first step we will setup layout for data. so put below xml code in respective files.

    layout/activity_datacaching_demo.xml

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v4.widget.SwipeRefreshLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/swipe_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingTop="?attr/actionBarSize"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:context="com.androprogrammer.tutorials.samples.DataCachingDemo">
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_userData"
            android:layout_width="match_parent"
            android:layout_margin="5dp"
            android:layout_height="wrap_content"
            android:scrollbars="vertical"/>
    
    </android.support.v4.widget.SwipeRefreshLayout>
    
    

    layout/row_datacaching_item
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v7.widget.CardView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_margin="5dp"
        app:cardCornerRadius="2dp"
        app:cardElevation="@dimen/elevation_normal">
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/white">
    
            <LinearLayout
                android:id="@+id/layout_header"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">
    
                <LinearLayout
                    android:layout_width="0dp"
                    android:layout_weight="0.3"
                    android:padding="@dimen/padding_small"
                    android:layout_height="90dip">
    
                    <ImageView
                        android:id="@+id/row_userImage"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:scaleType="centerInside"
                        android:src="@mipmap/user_noimage"
                        android:adjustViewBounds="true"
                        android:cropToPadding="false"
                        android:layout_gravity="fill"/>
    
                </LinearLayout>
    
                <LinearLayout
                    android:layout_width="0dp"
                    android:layout_weight="0.6"
                    android:layout_height="match_parent"
                    android:orientation="vertical">
    
    
                    <TextView
                        android:id="@+id/row_tv_userName"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:textColor="?android:textColorPrimary"
                        tools:text="@string/hello_world"
                        android:padding="@dimen/padding_small"/>
    
                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:padding="@dimen/padding_small"
                        android:orientation="horizontal">
    
                        <ImageView
                            android:id="@+id/row_icon_mail"
                            android:layout_width="20dip"
                            android:layout_height="20dip"
                            android:paddingLeft="2dp"
                            android:scaleType="fitXY"
                            android:adjustViewBounds="true"
                            tools:tint="?colorAccent"
                            tools:tintMode="add"
                            android:src="@android:drawable/ic_dialog_email"/>
    
    
                        <TextView
                            android:id="@+id/row_tv_email"
                            android:layout_width="match_parent"
                            android:layout_height="match_parent"
                            android:paddingLeft="@dimen/padding_small"
                            android:layout_gravity="center"
                            android:gravity="center_vertical"
                            android:textAppearance="?android:textAppearanceSmall"
                            tools:text="@string/hello_world"/>
    
                    </LinearLayout>
    
                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:padding="@dimen/padding_small"
                        android:orientation="horizontal">
    
                        <ImageView
                            android:id="@+id/row_icon_web"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:src="@mipmap/ic_action_web"
                            android:scaleType="fitXY"
                            android:adjustViewBounds="true"
                            tools:tint="?colorAccent"
                            tools:tintMode="add"/>
    
                        <TextView
                            android:id="@+id/row_userWebsite"
                            android:layout_width="0dp"
                            android:layout_height="match_parent"
                            android:layout_weight="0.8"
                            android:paddingLeft="@dimen/padding_small"
                            android:layout_gravity="center_vertical|center_horizontal"
                            android:gravity="center_vertical"
                            android:textAppearance="?android:textAppearanceSmall"
                            tools:text="@string/hello_world"
                            android:autoLink="web"/>
    
                    </LinearLayout>
    
                </LinearLayout>
    
            </LinearLayout>
    
        </RelativeLayout>
    
    </android.support.v7.widget.CardView>
    

    Step - 2
    In Step - 1 we have completed layout setup to display data in recycler view. and now in this step we are going to get data from the server and then we will store in cache memory and will access from it.
    Note:- When you provide offline data access you have to offer something to refresh data like refresh button in toolbar or can use SwipeRefrshLayout so user can refresh view once internet connection is available.

    I have used generic pattern for different task like to make network request call i have used HttpWebRequest class, To pass data to activity i have used ApiResponse interface and to define constants i have used Common class. Below code is of DataCachingDemo class which we have created to display sample data. so put below code in your main activity.

    sample/DataCachingDemo.java

    package com.androprogrammer.tutorials.samples;
    
    import android.os.Build;
    import android.os.Bundle;
    import android.support.v4.widget.SwipeRefreshLayout;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.transition.Explode;
    import android.transition.Transition;
    import android.view.LayoutInflater;
    import android.view.MenuItem;
    import android.view.View;
    import android.view.Window;
    
    import com.androprogrammer.tutorials.R;
    import com.androprogrammer.tutorials.activities.Baseactivity;
    import com.androprogrammer.tutorials.adapters.DataCachingListAdapter;
    import com.androprogrammer.tutorials.listners.ApiResponse;
    import com.androprogrammer.tutorials.models.UserDataResponse;
    import com.androprogrammer.tutorials.network.HTTPWebRequest;
    import com.androprogrammer.tutorials.util.Common;
    import com.anupcowkur.reservoir.Reservoir;
    import com.anupcowkur.reservoir.ReservoirPutCallback;
    import com.google.gson.GsonBuilder;
    import com.google.gson.JsonArray;
    import com.google.gson.reflect.TypeToken;
    
    import java.lang.reflect.Type;
    import java.util.ArrayList;
    import java.util.List;
    
    public class DataCachingDemo extends Baseactivity implements ApiResponse{
    
     protected View view;
     private RecyclerView recycler_data;
     private SwipeRefreshLayout layout_refresh;
     private RecyclerView.LayoutManager mLayoutManager;
     private List<UserDataResponse> mdata;
     private DataCachingListAdapter mAdapter;
    
     private static final String Cache_Key="userData";
    
     @Override
     protected void onCreate(Bundle savedInstanceState) {
      if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
      {
       getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
    
       //set the transition
       Transition ts = new Explode();
       ts.setDuration(5000);
       getWindow().setEnterTransition(ts);
       getWindow().setExitTransition(ts);
      }
    
      super.onCreate(savedInstanceState);
    
      setReference();
    
      // base class methods to set toolbar and sub title.
      setToolbarElevation(getResources().getDimension(R.dimen.elevation_normal));
      setToolbarSubTittle(this.getClass().getSimpleName());
      getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    
     }
    
     @Override
     public void setReference() {
      view = LayoutInflater.from(this).inflate(R.layout.activity_datacaching_demo, container);
    
      layout_refresh = (SwipeRefreshLayout) view.findViewById(R.id.swipe_layout);
    
      mLayoutManager = new LinearLayoutManager(DataCachingDemo.this);
    
      recycler_data = (RecyclerView) view.findViewById(R.id.recycler_userData);
      recycler_data.setLayoutManager(mLayoutManager);
    
      mdata = new ArrayList<>();
    
      layout_refresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
       @Override
       public void onRefresh() {
        if (!Common.isConnectivityAvailable(DataCachingDemo.this)) {
         if (layout_refresh.isRefreshing()) {
          layout_refresh.setRefreshing(false);
         }
         Common.showToast(DataCachingDemo.this, getResources().getString(R.string.msg_noInternet));
    
        } else
         getDataFromTheServer();
       }
      });
    
      fillAdapterData();
     }
    
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
      // Handle action bar item clicks here. The action bar will
      // automatically handle clicks on the Home/Up button, so long
      // as you specify a parent activity in AndroidManifest.xml.
      switch (item.getItemId()) {
       // Respond to the action bar's Up/Home button
       case android.R.id.home:
        finish();
        break;
      }
    
      return super.onOptionsItemSelected(item);
     }
    
     private void getDataFromTheServer()
     {
      // get data from the server
      HTTPWebRequest.createJsonRequest(DataCachingDemo.this,
                                       Common.App_Network_values.Api_call_getData,
                                       Common.App_Network_values.URL_APP_BASE,
                                       Common.App_Network_values.METHOD_GET,
                                       DataCachingDemo.this);
     }
    
    
     @Override
     public void NetworkRequestCompleted(int apiCode, JsonArray response) {
    
      try {
       switch (apiCode)
       {
        case Common.App_Network_values.Api_call_getData:
    
         if (response != null)
         {
          Type listType = new TypeToken<ArrayList<UserDataResponse>>(){}.getType();
          mdata = new GsonBuilder().create().fromJson(response, listType);
    
          //Put a user data in cache
          Reservoir.putAsync(Cache_Key, mdata, new ReservoirPutCallback() {
           @Override
           public void onSuccess() {
            //success
            Common.showToast(DataCachingDemo.this,"Data caching done...");
    
            fillAdapterData();
           }
    
           @Override
           public void onFailure(Exception e) {
            //error
           }
          });
    
          if (layout_refresh.isRefreshing())
          {
           layout_refresh.setRefreshing(false);
          }
         }
    
         break;
       }
      }catch (Exception e)
      {
       e.printStackTrace();
      }
     }
    
     private void fillAdapterData()
     {
      try {
       // check if data exist or not
       boolean objectExists = Reservoir.contains(Cache_Key);
    
       if (objectExists)
       {
        Type resultType = new TypeToken<List<UserDataResponse>>() {}.getType();
    
        mdata = Reservoir.get(Cache_Key, resultType);
    
        mAdapter = new DataCachingListAdapter(DataCachingDemo.this,mdata);
    
        recycler_data.setAdapter(mAdapter);
       }
       else
       {
        getDataFromTheServer();
       }
      } catch (Exception e) {
       e.printStackTrace();
      }
     }
    
     @Override
     public void networkError(int apiCode, String message) {
      Common.showToast(DataCachingDemo.this,message);
     }
    
     @Override
     public void responseError(int apiCode, String message) {
      Common.showToast(DataCachingDemo.this,message);
     }
    }
    
    


    Step - 3
    In step -3 we are going to setup permission and build file. in above code i have already used comments to describe what it actually do. so please go through it once so you will understand what it actually happening on the basis of different conditions.

    AndroidManifest.xml
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    

    build.gradle
    dependencies {
        compile 'com.google.code.gson:gson:2.3.1'
        compile 'com.koushikdutta.ion:ion:2.1.6'
        compile 'com.anupcowkur:reservoir:2.1'
    }

    That's it from my side. now run the code and check it. if you have problem or query please comment in below comment box.

    Keep coding...

    Source code

    Runtime/Dynamically Change Android Application Theme

    Before android lollipop most of the apps provides only two or three themes to change at run time because they have already bundled image object for that theme color. I mean images and all selector color are defined and drawable already created with all theme colors because there is no primary color and color accent attribute that allow you to change color dynamically before lollipop. In Lollipop they have add selector that change all objects like edit text and switch with the theme color and that's very cool. All object look similar and contrasting to your theme.

    So how the app developer change themes run time ?
    How to change full app theme run time?

    Well for answer of above questions go through full tutorial and you will learn how application default theme changed with newly selected color at run time.

    androprogrammer.com


    Steps

    Step - 1
    Create activity where user can select theme color. in this tutorial it is ChangeThemeDemo. I have taken only two colors but you can add as many as you want. It is really lengthy process but if you go step by step it will be so much easy for you. Now create theme.xml in values folder where you will define all themes that you want to implement. And user can change theme from it run time.

    values/themes.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
        <style name="AppTheme.Green" parent="Theme.AppCompat.Light">
            <item name="colorPrimary">@color/primary_green</item>
            <item name="colorPrimaryDark">@color/primary_dark_green</item>
            <item name="colorAccent">@color/accent_green</item>
            <item name="colorControlHighlight">@color/primary_green</item>
            <item name="android:textColorPrimary">@color/primary_text</item>
            <item name="android:textColorSecondary">@color/secondary_text</item>
        </style>
    
        <style name="AppTheme.Purple" parent="Theme.AppCompat.Light">
            <item name="colorPrimary">@color/primary_purple</item>
            <item name="colorPrimaryDark">@color/primary_dark_purple</item>
            <item name="colorAccent">@color/accent_purple</item>
            <item name="colorControlHighlight">@color/primary_purple</item>
            <item name="android:textColorPrimary">@color/primary_text</item>
            <item name="android:textColorSecondary">@color/secondary_text</item>
        </style>
    
    </resources>
    

    values/color.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <color name="primary_green">#52bf90</color>
        <color name="primary_dark_green">#398564</color>
        <color name="accent_green">#FF4081</color>
        <color name="activity_bg">#e4e5e5</color>
        <color name="primary_purple">#673AB7</color>
        <color name="primary_dark_purple">#512DA8</color>
        <color name="primary_light">#D1C4E9</color>
        <color name="accent_purple">#FF9800</color>
        <color name="primary_text">#212121</color>
        <color name="secondary_text">#727272</color>
        <color name="icons">#FFFFFF</color>
        <color name="divider">#B6B6B6</color>
        <color name="activity_bg_black">#374046</color>
    </resources>
    

    Ok, now your basic theme is set. i have created themes.xml file for only declaring base of theme. You can create your hole theme over here as i have created in styles.xml. check out below code for different themes i have created light and dark and both version of my theme in it.

    values/styles.xml


    <resources>
    
        <style name="AppTheme.Base.Green" parent="AppTheme.Green">
            <item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
            <item name="windowActionModeOverlay">true</item>
            <item name="windowActionBarOverlay">true</item>
            <item name="android:windowActionBarOverlay">true</item>
            <item name="android:windowBackground">@color/activity_bg</item>
            <item name="windowActionBar">false</item>
            <item name="windowNoTitle">true</item>
        </style>
    
        <style name="AppTheme.Base.Green.Dark" parent="AppTheme.Green">
            <item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
            <item name="windowActionModeOverlay">true</item>
            <item name="windowActionBarOverlay">true</item>
            <item name="android:windowActionBarOverlay">true</item>
            <item name="android:windowBackground">@color/activity_bg_black</item>
            <item name="windowActionBar">false</item>
            <item name="windowNoTitle">true</item>
        </style>
    
        <style name="AppTheme.Base.Purple" parent="AppTheme.Purple">
            <item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
            <item name="windowActionModeOverlay">true</item>
            <item name="windowActionBarOverlay">true</item>
            <item name="android:windowActionBarOverlay">true</item>
            <item name="android:windowBackground">@color/activity_bg</item>
            <item name="windowActionBar">false</item>
            <item name="windowNoTitle">true</item>
        </style>
    
        <style name="AppTheme.Base.Purple.Dark" parent="AppTheme.Purple">
            <item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
            <item name="windowActionModeOverlay">true</item>
            <item name="windowActionBarOverlay">true</item>
            <item name="android:windowActionBarOverlay">true</item>
            <item name="android:windowBackground">@color/activity_bg_black</item>
            <item name="windowActionBar">false</item>
            <item name="windowNoTitle">true</item>
        </style>
    
        <!-- Base application themes. -->
        <style name="ThemeApp.Green" parent="AppTheme.Base.Green"/>
        <style name="ThemeApp.Green.Dark" parent="AppTheme.Base.Green.Dark"/>
        <style name="ThemeApp.Purple" parent="AppTheme.Base.Purple"/>
        <style name="ThemeApp.Purple.Dark" parent="AppTheme.Base.Purple.Dark"/>
    
    
        <style name="DrawerArrowStyle" parent="Widget.AppCompat.DrawerArrowToggle">
            <item name="spinBars">true</item>
            <item name="color">@android:color/white</item>
        </style>
    
    </resources>
    
    

    Step - 2
    In step - 1 we have set all things for themes. now in this step i am going to create layout for theme chooser. you can create as you want but for basic setup you can put below code into your activity. it will allow user to select weather they want to apply light or dark theme. i have directly given values for text and padding and margin but for this tutorial only. it is good practice that you declare it in strings.xml and dimen.xml and then apply here. so it will be reusable.

    layout/activity_changetheme_demo.xml

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingTop="?attr/actionBarSize"
        tools:context="com.androprogrammer.tutorials.samples.ChangeThemeDemo">
    
        <LinearLayout
            android:id="@+id/layout_header"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:background="?attr/colorPrimary">
    
    
            <android.support.v7.widget.SwitchCompat
                android:id="@+id/switch_darkTheme"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="10dip"
                android:layout_marginLeft="10dip"
                android:layout_marginRight="5dip"
                android:textColor="@android:color/white"
                android:text="Dark Theme"
                tools:text="Dark Theme"/>
    
        </LinearLayout>
    
        <LinearLayout
            android:layout_below="@id/layout_header"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dip"
            android:padding="10dip"
            android:background="@android:color/white"
            android:id="@+id/Layout_green">
    
            <LinearLayout
                android:layout_width="30dip"
                android:layout_height="35dip"
                android:gravity="center"
                android:clickable="false"
                android:elevation="5dip"
                android:background="@color/primary_green"/>
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Green"
                android:gravity="center"
                android:clickable="false"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:padding="8dip"
                android:layout_marginLeft="10dip"/>
    
        </LinearLayout>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dip"
            android:padding="10dip"
            android:layout_below="@id/Layout_green"
            android:background="@android:color/white"
            android:id="@+id/Layout_purple">
    
            <LinearLayout
                android:layout_width="30dip"
                android:layout_height="35dip"
                android:gravity="center"
                android:clickable="false"
                android:elevation="5dip"
                android:background="@color/primary_purple"/>
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Purple"
                android:gravity="center"
                android:clickable="false"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:padding="8dip"
                android:layout_marginLeft="10dip"/>
    
        </LinearLayout>
    
    </RelativeLayout>
    
    

    Step - 3
    Now to change theme in overall screen in your application you have to put below code in your Base or Main Activity so when ever you start app your your new themes gets apply for whole app. in my case i have BaseActivty which i am extending in all other activities so if i put that code in that activity my whole app themes get changed. MainController is application class in my app you can find code here. you have to put below code before calling super.onCreate(savedInstanceState); because setTheme Method will change theme through out the app.

    if (!MainController.preferenceGetString("AppliedTheme","").equals(""))
            {
                if (MainController.preferenceGetString("AppliedTheme","").equals("Green"))
                {
                    setTheme(R.style.ThemeApp_Green);
                }
                else if (MainController.preferenceGetString("AppliedTheme","").equals("Green_Dark"))
                {
                    setTheme(R.style.ThemeApp_Green_Dark);
                }
                else if (MainController.preferenceGetString("AppliedTheme","").equals("Purple_Dark"))
                {
                    setTheme(R.style.ThemeApp_Purple_Dark);
                }
                else if (MainController.preferenceGetString("AppliedTheme","").equals("Purple"))
                {
                    setTheme(R.style.ThemeApp_Purple);
                }
            }
            else
            {
                setTheme(R.style.ThemeApp_Green);
            }
    

    As i have already told, you don't have to use strings directly you have to define in strings.xml so if you want to change some thing you have to change it at only one place.



    Step - 4
    Well now all set, now put below code in Java class in this tutorial it is ChangeThemeDemo.java. where you have to change preference on the basis of user selection. which theme is selected by user and weather light version or dark.

    samples/ChangeThemeDemo.java

    package com.androprogrammer.tutorials.samples;
    
    
    import android.content.Intent;
    import android.os.Build;
    import android.os.Bundle;
    import android.support.v4.app.TaskStackBuilder;
    import android.support.v7.widget.SwitchCompat;
    import android.transition.Explode;
    import android.transition.Transition;
    import android.view.LayoutInflater;
    import android.view.MenuItem;
    import android.view.View;
    import android.view.Window;
    import android.widget.CompoundButton;
    import android.widget.LinearLayout;
    
    import com.androprogrammer.tutorials.MainController;
    import com.androprogrammer.tutorials.R;
    import com.androprogrammer.tutorials.activities.Baseactivity;
    import com.androprogrammer.tutorials.activities.Listactivity;
    
    
    /**
     * Created by Wasim on 15-08-2015.
     */
    public class ChangeThemeDemo extends Baseactivity implements View.OnClickListener{
    
        protected View view;
        private LinearLayout layout_green, layout_purple;
        private SwitchCompat switch_dark;
        private boolean isDarkTheme;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
            {
                getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
    
                //set the transition
                Transition ts = new Explode();
                ts.setDuration(5000);
                getWindow().setEnterTransition(ts);
                getWindow().setExitTransition(ts);
            }
    
            super.onCreate(savedInstanceState);
    
            setReference();
    
            setToolbarElevation(0);
    
            setToolbarSubTittle(this.getClass().getSimpleName());
    
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        }
    
        @Override
        public void setReference() {
            view = LayoutInflater.from(this).inflate(R.layout.activity_changetheme_demo,container);
    
            switch_dark = (SwitchCompat) view.findViewById(R.id.switch_darkTheme);
            layout_green = (LinearLayout) view.findViewById(R.id.Layout_green);
            layout_purple = (LinearLayout) view.findViewById(R.id.Layout_purple);
    
            switch_dark.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    
                    if (isChecked)
                    {
                        isDarkTheme = true;
                        MainController.preferencePutBoolean("DarkTheme", true);
                    }
                    else {
                        isDarkTheme = false;
                        MainController.preferencePutBoolean("DarkTheme", false);
                    }
                }
            });
    
            if (MainController.preferenceGetBoolean("DarkTheme", false))
            {
                switch_dark.setChecked(true);
            }
    
            layout_green.setOnClickListener(this);
            layout_purple.setOnClickListener(this);
        }
    
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // Handle action bar item clicks here. The action bar will
            // automatically handle clicks on the Home/Up button, so long
            // as you specify a parent activity in AndroidManifest.xml.
            switch (item.getItemId()) {
                // Respond to the action bar's Up/Home button
                case android.R.id.home:
                    finish();
                    break;
            }
    
            return super.onOptionsItemSelected(item);
        }
    
        @Override
        public void onClick(View v) {
    
            switch (v.getId())
            {
                case R.id.Layout_green:
                    if (isDarkTheme)
                        MainController.preferencePutString("AppliedTheme", "Green_Dark");
                    else
                        MainController.preferencePutString("AppliedTheme", "Green");
    
                    TaskStackBuilder.create(this)
                            .addNextIntent(new Intent(this, Listactivity.class))
                            .addNextIntent(getIntent())
                            .startActivities();
                    break;
    
                case R.id.Layout_purple:
    
                    if (isDarkTheme)
                        MainController.preferencePutString("AppliedTheme", "Purple_Dark");
                    else
                        MainController.preferencePutString("AppliedTheme", "Purple");
    
                    TaskStackBuilder.create(this)
                            .addNextIntent(new Intent(this, Listactivity.class))
                            .addNextIntent(getIntent())
                            .startActivities();
                    break;
            }
    
        }
    }
    
    

    After changing theme you have to restart your activity. you also have to change themes in previous screens also. because they are already opened and in Activity stacktrace. So to change theme in it  also i have created TaskStackBuilder. which allow us to recreate Activity stack trace. in my case i have given only two intents but you have to pass activity hierarchy so user don't know that whole app is restarted.

    TaskStackBuilder.create(this)
                            .addNextIntent(new Intent(this, Listactivity.class))
                            .addNextIntent(getIntent())
                            .startActivities();
    

    That's it from my side. now run the app and see the result. if you have any query or suggestion please let me know in below comment box.

    keep coding...

    Source code

    Crop and Compress Images in Android

    Images and Icons are now one of the essential element of android application. With the new Material design more icons are used then text. But they are compressed and well created so it won't get stretched in any screen size devices. But at run time there are number of applications from where user can upload images, during this process image cropping and compression is required (must). because user may upload large file and it may take long to upload. and if network connectivity is weak your app may crash due to an error. That will create bad impact on user.

    So developers(you) has to take proper measures because even image compression and upload task increase load on mobile's ram and processor also. Apps like facebook, whatsApp, Google plus and many well known app does the same thing. so how they do and what is the proper way of doing it and even make it optimized. Go through these tutorial and you will notice different in timing and size.


    androprogrammer.com

    Steps

    Step -1
    Create an activity where user can pic image (can take new image or can chose from gallery). in this tutorial it is TakeImageDemo. now put below layout code in xml file. I have used simple layout for this tutorial but you can create as you want.

    layout/activity_takepicture_demo.xml

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="?attr/actionBarSize"
        android:paddingBottom="@dimen/activity_vertical_margin"
        tools:context="com.androprogrammer.tutorials.samples.TakePictureDemo">
    
            <Button
                android:id="@+id/btnTakePicture"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:onClick="takePicture_Click"
                android:text="Take Picture" />
    
            <ImageView
                android:id="@+id/img_camera"
                android:layout_width="200dip"
                android:layout_height="200dip"
                android:layout_below="@+id/btnTakePicture"
                android:layout_centerHorizontal="true"
                android:layout_margin="5dp"
                tools:src="@mipmap/abc1" />
    
    </RelativeLayout>
    
    

    Step - 2
    Now your layout or designing part is done. Android by default (implicit intent) doesn't provide any intent to crop images so we have to call "com.android.camera.action.CROP" package. That package takes image as data and display image crop view to user like the above image.

    TakePictureDemo.java

    package com.androprogrammer.tutorials.samples;
    
    import android.content.ActivityNotFoundException;
    import android.content.Context;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.net.Uri;
    import android.os.Bundle;
    import android.os.Environment;
    import android.provider.MediaStore;
    import android.support.v7.app.AlertDialog;
    import android.view.LayoutInflater;
    import android.view.MenuItem;
    import android.view.View;
    import android.widget.ImageView;
    
    import com.androprogrammer.tutorials.R;
    import com.androprogrammer.tutorials.activities.Baseactivity;
    import com.androprogrammer.tutorials.util.Common;
    
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    
    public class TakePictureDemo extends Baseactivity {
    
        protected View view;
        protected ImageView imgViewCamera;
        protected int LOAD_IMAGE_CAMERA = 0, CROP_IMAGE = 1, LOAD_IMAGE_GALLARY = 2;
        private Uri picUri;
        private File pic;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            setReference();
    
            setToolbarElevation(7);
    
            setToolbarSubTittle(this.getClass().getSimpleName());
    
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    
        }
    
        @Override
        public void setReference() {
            view = LayoutInflater.from(this).inflate(R.layout.activity_takepicture_demo, container);
            imgViewCamera = (ImageView) view.findViewById(R.id.img_camera);
        }
    
        public void takePicture_Click(View v) {
            final CharSequence[] options = {"Take Photo", "Choose from Gallery"};
    
            AlertDialog.Builder builder = new AlertDialog.Builder(TakePictureDemo.this);
            builder.setTitle("Select Pic Using...");
            builder.setItems(options, new DialogInterface.OnClickListener() {
    
                @Override
                public void onClick(DialogInterface dialog, int item) {
                    if (options[item].equals("Take Photo")) {
    
                        try {
                            Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
    
                            pic = new File(Environment.getExternalStorageDirectory(),
                                    "tmp_" + String.valueOf(System.currentTimeMillis()) + ".jpg");
    
                            picUri = Uri.fromFile(pic);
    
                            cameraIntent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, picUri);
    
                            cameraIntent.putExtra("return-data", true);
                            startActivityForResult(cameraIntent, LOAD_IMAGE_CAMERA);
                        } catch (ActivityNotFoundException e) {
                            e.printStackTrace();
                        }
    
                    } else if (options[item].equals("Choose from Gallery")) {
                        Intent intent = new Intent(Intent.ACTION_PICK,
                                android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                        startActivityForResult(Intent.createChooser(intent, "Select Picture"), LOAD_IMAGE_GALLARY);
                    }
                }
            });
    
            builder.show();
    
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            //super.onActivityResult(requestCode, resultCode, data);
            if (requestCode == LOAD_IMAGE_CAMERA && resultCode == RESULT_OK) {
                    CropImage();
    
            }
            else if (requestCode == LOAD_IMAGE_GALLARY) {
                if (data != null) {
    
                    picUri = data.getData();
                    CropImage();
                }
            }
            else if (requestCode == CROP_IMAGE) {
                if (data != null) {
                    // get the returned data
                    Bundle extras = data.getExtras();
    
                    // get the cropped bitmap
                    Bitmap photo = extras.getParcelable("data");
    
                    imgViewCamera.setImageBitmap(photo);
    
                    if (pic != null)
                    {
                        // To delete original image taken by camera
                        if (pic.delete())
                            Common.showToast(TakePictureDemo.this,"original image deleted...");
                    }
                }
            }
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // Handle action bar item clicks here. The action bar will
            // automatically handle clicks on the Home/Up button, so long
            // as you specify a parent activity in AndroidManifest.xml.
            int id = item.getItemId();
    
            //noinspection SimplifiableIfStatement
            if (id == android.R.id.home) {
                finish();
                return true;
            }
    
            return super.onOptionsItemSelected(item);
        }
    
        protected void CropImage() {
            try {
                Intent intent = new Intent("com.android.camera.action.CROP");
                intent.setDataAndType(picUri, "image/*");
    
                intent.putExtra("crop", "true");
                intent.putExtra("outputX", 200);
                intent.putExtra("outputY", 200);
                intent.putExtra("aspectX", 3);
                intent.putExtra("aspectY", 4);
                intent.putExtra("scaleUpIfNeeded", true);
                intent.putExtra("return-data", true);
    
                startActivityForResult(intent, CROP_IMAGE);
    
            } catch (ActivityNotFoundException e) {
                Common.showToast(this, "Your device doesn't support the crop action!");
            }
        }
    
        public Bitmap CompressResizeImage(Bitmap bm)
        {
            int bmWidth = bm.getWidth();
            int bmHeight = bm.getHeight();
            int ivWidth = imgViewCamera.getWidth();
            int ivHeight = imgViewCamera.getHeight();
    
    
            int new_height = (int) Math.floor((double) bmHeight *( (double) ivWidth / (double) bmWidth));
            Bitmap newbitMap = Bitmap.createScaledBitmap(bm, ivWidth, new_height, true);
    
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            newbitMap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
            byte[] b = baos.toByteArray();
    
            Bitmap bm1 = BitmapFactory.decodeByteArray(b, 0, b.length);
    
            return bm1;
        }
    }
    
    



    As you can see i am calling crop method from both the case when user take using camera or select image from gallery. one more thing you have to note that if user take new image using camera i am deleting that original file and just storing cropped image so it won't create space issue.

    It is best practice that first you resize image and then compress it so it won't get stretched.

    Step - 3

    AndroidManifest.xml

    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-feature android:name="android.hardware.camera" />
    

    Paste above code in manifest file. it required to use camera in your app.

    That's it from my side. now run the app and see the result. if you have any query please let me know in below comment box.

    keep coding...

    Source code

    View Pager With Circular Indicator (without any library)

    There are number of libraries available to create circular indicator for view pager. But i think you don't require that all heavy weight libraries. They all filled with so much congested and non used code in it. It may happen you may come across some odd issue with those libs. So to save your time and even with easy to understand code i have written this tutorial. Go through full tutorial and you find how easy it is.

    I have created View pager for introduction of the application (Current trend of application start screen almost in all Google apps). But you can use it as per your requirement.

    viewpager demo


    Steps

    Step -1
    Create activity (welcome screen) which holds pager to display images and give proper name to it. In this tutorial it is ViewPagerDemo Activity. now put below code inside into xml file (layout file).

    activity_viewpager_demo.xml

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        tools:context="com.androprogrammer.tutorials.samples.ViewPagerDemo">
    
        <android.support.v4.view.ViewPager
            android:id="@+id/pager_introduction"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            tools:listitem="@layout/pager_item" />
    
        <RelativeLayout
            android:id="@+id/viewPagerIndicator"
            android:layout_width="match_parent"
            android:layout_height="55dp"
            android:layout_alignParentBottom="true"
            android:layout_marginTop="5dp"
            android:gravity="center">
    
            <LinearLayout
                android:id="@+id/viewPagerCountDots"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_centerHorizontal="true"
                android:gravity="center"
                android:orientation="horizontal" />
    
            <ImageButton
                android:id="@+id/btn_next"
                android:layout_width="42dip"
                android:layout_height="42dip"
                android:layout_alignParentRight="true"
                android:layout_marginRight="15dip"
                android:background="@drawable/btn_round_semitransperant"
                android:src="@mipmap/ic_navigation_arrow_forward" />
    
            <ImageButton
                android:id="@+id/btn_finish"
                android:layout_width="42dip"
                android:layout_height="42dip"
                android:layout_alignParentRight="true"
                android:layout_marginRight="15dip"
                android:background="@drawable/btn_round_semitransperant"
                android:contentDescription="Let's start"
                android:src="@mipmap/ic_navigation_check"
                android:visibility="gone" />
    
        </RelativeLayout>
    
    </RelativeLayout>
    
    

    Step - 2
    Above code will show error but don't worry it is for drawable files. so below are drawable files put the code in appropriate file and your layout is set.

    pager_item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:fitsSystemWindows="true"
        android:layout_height="match_parent">
    
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:src="@mipmap/abc1"
            android:id="@+id/img_pager_item"
            android:scaleType="fitXY"
            android:adjustViewBounds="true"
            android:clickable="false"/>
    
    </LinearLayout>
    
    

    drawable/selecteditem_dot.xml

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="oval" android:useLevel="true"
        android:dither="true">
    
        <size android:height="12dip" android:width="12dip"/>
    
        <solid android:color="@color/primary"/>
    </shape>
    
    

    drawable/nonselecteditem_dot.xml

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="oval" android:useLevel="true"
        android:dither="true">
    
        <size android:height="6dip" android:width="6dip"/>
    
        <solid android:color="@color/activity_bg"/>
    </shape>
    
    



    Step - 3
    For View pager, layout part is done. you can find sample images from here. now let's move to code part where i have created circular dots on the basis of data size. So if you have let say 10 images it will create 10 dots with first one as selected by default. put the code in to activity and make changes as per your project implementation.

    ViewPagerDemo.java

    package com.androprogrammer.tutorials.samples;
    
    import android.os.Bundle;
    import android.support.v4.view.ViewPager;
    import android.view.LayoutInflater;
    import android.view.MenuItem;
    import android.view.View;
    import android.view.Window;
    import android.view.WindowManager;
    import android.widget.ImageButton;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    
    import com.androprogrammer.tutorials.R;
    import com.androprogrammer.tutorials.activities.Baseactivity;
    import com.androprogrammer.tutorials.adapters.ViewPagerAdapter;
    
    public class ViewPagerDemo extends Baseactivity implements ViewPager.OnPageChangeListener, View.OnClickListener{
    
        protected View view;
        private ImageButton btnNext, btnFinish;
        private ViewPager intro_images;
        private LinearLayout pager_indicator;
        private int dotsCount;
        private ImageView[] dots;
        private ViewPagerAdapter mAdapter;
    
        private int[] mImageResources = {
                R.mipmap.abc1,
                R.mipmap.abc2,
                R.mipmap.abc3,
                R.mipmap.abc4,
                R.mipmap.abc5
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            // To make activity full screen.
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                    WindowManager.LayoutParams.FLAG_FULLSCREEN);
    
            super.onCreate(savedInstanceState);
    
            setReference();
    
            toolbar.setVisibility(View.GONE);
    
        }
    
        @Override
        public void setReference() {
            view = LayoutInflater.from(this).inflate(R.layout.activity_viewpager_demo,container);
    
            intro_images = (ViewPager) view.findViewById(R.id.pager_introduction);
            btnNext = (ImageButton) view.findViewById(R.id.btn_next);
            btnFinish = (ImageButton) view.findViewById(R.id.btn_finish);
    
            pager_indicator = (LinearLayout) view.findViewById(R.id.viewPagerCountDots);
    
            btnNext.setOnClickListener(this);
            btnFinish.setOnClickListener(this);
    
            mAdapter = new ViewPagerAdapter(ViewPagerDemo.this, mImageResources);
            intro_images.setAdapter(mAdapter);
            intro_images.setCurrentItem(0);
            intro_images.setOnPageChangeListener(this);
            setUiPageViewController();
        }
    
        private void setUiPageViewController() {
    
            dotsCount = mAdapter.getCount();
            dots = new ImageView[dotsCount];
    
            for (int i = 0; i < dotsCount; i++) {
                dots[i] = new ImageView(this);
                dots[i].setImageDrawable(getResources().getDrawable(R.drawable.nonselecteditem_dot));
    
                LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                        LinearLayout.LayoutParams.WRAP_CONTENT,
                        LinearLayout.LayoutParams.WRAP_CONTENT
                );
    
                params.setMargins(4, 0, 4, 0);
    
                pager_indicator.addView(dots[i], params);
            }
    
            dots[0].setImageDrawable(getResources().getDrawable(R.drawable.selecteditem_dot));
        }
    
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.btn_next:
                    intro_images.setCurrentItem((intro_images.getCurrentItem() < dotsCount)
                            ? intro_images.getCurrentItem() + 1 : 0);
                    break;
    
                case R.id.btn_finish:
                    finish();
                    break;
            }
        }
    
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    
        }
    
        @Override
        public void onPageSelected(int position) {
            for (int i = 0; i < dotsCount; i++) {
                dots[i].setImageDrawable(getResources().getDrawable(R.drawable.nonselecteditem_dot));
            }
    
            dots[position].setImageDrawable(getResources().getDrawable(R.drawable.selecteditem_dot));
    
            if (position + 1 == dotsCount) {
                btnNext.setVisibility(View.GONE);
                btnFinish.setVisibility(View.VISIBLE);
            } else {
                btnNext.setVisibility(View.VISIBLE);
                btnFinish.setVisibility(View.GONE);
            }
        }
    
        @Override
        public void onPageScrollStateChanged(int state) {
    
        }
    }
    
    

    This is code for view pager adapter.which fills all images into view pager and display like gallery.
    adapters/ViewPagerAdapter.java

    package com.androprogrammer.tutorials.adapters;
    
    import android.content.Context;
    import android.support.v4.view.PagerAdapter;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    
    import com.androprogrammer.tutorials.R;
    
    /**
     * Created by Wasim on 11-06-2015.
     */
    public class ViewPagerAdapter extends PagerAdapter {
    
        private Context mContext;
        private int[] mResources;
    
        public ViewPagerAdapter(Context mContext, int[] mResources) {
            this.mContext = mContext;
            this.mResources = mResources;
        }
    
        @Override
        public int getCount() {
            return mResources.length;
        }
    
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == ((LinearLayout) object);
        }
    
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            View itemView = LayoutInflater.from(mContext).inflate(R.layout.pager_item, container, false);
    
            ImageView imageView = (ImageView) itemView.findViewById(R.id.img_pager_item);
            imageView.setImageResource(mResources[position]);
    
            container.addView(itemView);
    
            return itemView;
        }
    
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((LinearLayout) object);
        }
    }
    
    

    That's it from my side. now run the app and see the result. and at the end i am replacing next button with finish button. if you have any query please let me know in below comment box.

    Keep coding...

    Source code

    Android Wear: How to send data from Android Wear to Mobile Device ?

    Android Wear or Let say Smart Watches are new and trending technology. Android apps are updating and they all comes with Android wear support. if you want it is in your app you can find simple Helloworld wear app tutorials easily on Google. but what if you want to explore it more let say you want to display a List view in android wear or get some kind of input from android wear app. so these tutorial is for you. In this tutorial i have created simple list in android wear app in which user click on list item and an app in mobile device gets open. for example you want to display list of songs in android wear app and on the click of that you want to change song in your device or let say you can display to do list on android wear and on the click of list item you can open to do item details inside your mobile app. this list can be infinite and its totally depends on you and apps in android.

    Android Wear sample


    Steps

    Step - 1
    First of let i start with adding a android wear module in development. If you have already added android wear module in you app you can skip this step. To add right click on your app root folder (name of your app folder) and go to New > Module. Select Android Wear Module and click on next. Give same name in Application name (Mobile app name) and in Module Name write wear. so you can differentiate them. Make sure you give same package name also. because when you send some data through mobile app or wear app MessageApi will check for the same package and pass that data. so if you give different name you won't be able to send or receive data. On the next screen select Blank Wear Activity and on the next screen give proper name to it.

    Note - Above steps are for Android studio wizard. Sorry for eclipse developers (Please migrate to AS)

    Step - 2
    So our android wear module is added. I hope you have no issue in above steps. in the main activity or launcher activity i have created a List (basically list of my tutorials). For learning purpose you can start with same and after that you can change it is as you want. so put below code in appropriate layout files.

    wear/layout/activity_list.xml

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.wearable.view.WatchViewStub 
       xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/watch_view_stub"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#e4e5e5"
        app:rectLayout="@layout/rect_activity_list"
        app:roundLayout="@layout/round_activity_list"
        tools:context=".ListActivity"
        tools:deviceIds="wear">
    
    </android.support.wearable.view.WatchViewStub>
    
    


    wear/layout/rect_activity_list.xml & wear/layout/round_activity_list.xml

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.wearable.view.BoxInsetLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        tools:context=".ListActivity"
        tools:deviceIds="wear_round">
    
            <android.support.wearable.view.WearableListView
                android:id="@+id/listView1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="5dp"
                android:overScrollMode="never" />
    
            <TextView
                android:id="@+id/tv_header"
                android:layout_width="wrap_content"
                android:layout_height="40dp"
                android:layout_gravity="top|center"
                android:textSize="30sp"
                android:layout_marginTop="10dp"
                android:textColor="#52bf90"
                android:fontFamily="sans-serif-light"
                android:text="@string/app_name"/>
    
    </android.support.wearable.view.BoxInsetLayout>
    
    
    


    Step - 3
    List layout is done in Step -2. now let's jump to coding part where i have initialize WearableListView with Array adapter. Using GoogleApiClient i have checked if there is a connection to a android wear from an app or not. we can get all connected nodes or devices using Wearable.NodeApi.getConnectedNodes method. check below code and make sure you understand or go through it for one time so you know what is actually going on. otherwise you will get confused.

    wear/java/ListActivity.java

    package com.androprogrammer.tutorials;
    
    import android.app.Activity;
    import android.content.Context;
    import android.os.Bundle;
    import android.support.wearable.view.WatchViewStub;
    import android.support.wearable.view.WearableListView;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.ViewGroup;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import com.google.android.gms.common.ConnectionResult;
    import com.google.android.gms.common.api.GoogleApiClient;
    import com.google.android.gms.common.api.ResultCallback;
    import com.google.android.gms.wearable.MessageApi;
    import com.google.android.gms.wearable.Node;
    import com.google.android.gms.wearable.NodeApi;
    import com.google.android.gms.wearable.Wearable;
    
    import java.util.ArrayList;
    
    public class ListActivity extends Activity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,WearableListView.ClickListener {
    
        private WearableListView mListView;
    
        private ArrayList<String> listItems;
    
        Node mNode; // the connected device to send the message to
        GoogleApiClient mGoogleApiClient;
        private boolean mResolvingError=false;
    
        public static String SERVICE_CALLED_WEAR = "WearListClicked";
        public static String TAG = "WearListActivity";
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_list);
    
            //Connect the GoogleApiClient
            mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addApi(Wearable.API)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .build();
    
            initializeListItems();
            final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
            stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
                @Override
                public void onLayoutInflated(WatchViewStub stub) {
                    mListView = (WearableListView) stub.findViewById(R.id.listView1);
    
                    mListView.setAdapter(new MyAdapter(ListActivity.this, listItems));
                    mListView.setClickListener(ListActivity.this);
    
                }
            });
        }
    
        private void initializeListItems() {
    
            listItems = new ArrayList<>();
            listItems.add("Async File Read");
            listItems.add("Battery Status");
            listItems.add("Volume Setting");
            listItems.add("Frame Animation");
            listItems.add("Video Player");
            listItems.add("Circular Image View");
            listItems.add("Track User Location");
            listItems.add("Take Image");
            listItems.add("Image Grid View");
            listItems.add("Image Switcher");
            listItems.add("Tabs with Toolbar");
            listItems.add("Icon Tabs with Toolbar");
            listItems.add("Push Notification");
    
    
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            if (!mResolvingError) {
                mGoogleApiClient.connect();
            }
        }
    
        /**
         * Resolve the node = the connected device to send the message to
         */
        private void resolveNode() {
    
            Wearable.NodeApi.getConnectedNodes(mGoogleApiClient)
                    .setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() {
                        @Override
                        public void onResult(NodeApi.GetConnectedNodesResult nodes) {
                            for (Node node : nodes.getNodes()) {
                        mNode = node;
                    }
                }
            });
        }
    
        @Override
        public void onConnected(Bundle bundle) {
            resolveNode();
        }
    
        @Override
        public void onConnectionSuspended(int i) {
    
        }
    
        @Override
        public void onConnectionFailed(ConnectionResult connectionResult) {
    
        }
    
        /**
         * Send message to mobile handheld
         */
        private void sendMessage(String Key) {
    
            if (mNode != null && mGoogleApiClient!= null && mGoogleApiClient.isConnected()) {
                Log.d(TAG, "-- " + mGoogleApiClient.isConnected());
                Wearable.MessageApi.sendMessage(
                        mGoogleApiClient, mNode.getId(), SERVICE_CALLED_WEAR + "--" + Key, null).setResultCallback(
    
                        new ResultCallback<MessageApi.SendMessageResult>() {
                            @Override
                            public void onResult(MessageApi.SendMessageResult sendMessageResult) {
    
                                if (!sendMessageResult.getStatus().isSuccess()) {
                                    Log.e(TAG, "Failed to send message with status code: "
                                            + sendMessageResult.getStatus().getStatusCode());
                                }
                            }
                        }
                );
            }
    
        }
    
        @Override
        public void onClick(WearableListView.ViewHolder viewHolder) {
            TextView view = (TextView) viewHolder.itemView.findViewById(R.id.row_tv_name);
            String Key = view.getText().toString();
            Log.d(TAG, Key);
            sendMessage(Key);
        }
    
        @Override
        public void onTopEmptyRegionClick() {
            Toast.makeText(this, "You tapped on Top empty area", Toast.LENGTH_SHORT).show();
        }
    
    
        private class MyAdapter extends WearableListView.Adapter {
            private final LayoutInflater mInflater;
            private ArrayList<String> data;
    
            private MyAdapter(Context context, ArrayList<String> listItems) {
                mInflater = LayoutInflater.from(context);
                data = listItems;
            }
    
            @Override
            public WearableListView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                return new WearableListView.ViewHolder(
                        mInflater.inflate(R.layout.row_wear_list, null));
            }
    
            @Override
            public void onBindViewHolder(WearableListView.ViewHolder holder, int position) {
                TextView view = (TextView) holder.itemView.findViewById(R.id.row_tv_name);
                view.setText(data.get(position));
                holder.itemView.setTag(position);
            }
    
            @Override
            public int getItemCount() {
                return data.size();
            }
        }
    }
    
    


    wear/layout/row_wear_list.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:orientation="horizontal"
        android:padding="8dp"
        android:gravity="center">
    
        <TextView
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:text="Medium Text"
            android:textStyle="bold"
            android:ellipsize="end"
            android:maxEms="15"
            android:padding="10dp"
            android:ems="15"
            android:background="@android:color/white"
            android:textColor="@android:color/black"
            android:fontFamily="sans-serif-condensed-light"
            android:gravity="center"
            android:layout_height="match_parent"
            android:layout_width="wrap_content"
            android:textSize="16sp"
            android:id="@+id/row_tv_name" />
    
    </LinearLayout>
    


    wear/AndroidManifest.xml
    <meta-data android:name="com.google.android.gms.version"
                android:value="@integer/google_play_services_version" />
    


    Please add above lines of code in manifest file of wear module. because we are using google play service to communicate. otherwise it will give error.

    Step - 4
    Now android wear module is ready. you can run it separately on emulator and check it. now we require a bridge which can pass android wear message to android mobile app. so when user click on list item in android wear a service get some sort of message and on the basis of that message we start appropriate activity. Using WearableListenerService we will create service which will do same for our app.

    mobile/java/WearListCallListenerService.java

    package com.androprogrammer.tutorials.services;
    
    import android.content.Intent;
    import android.util.Log;
    
    import com.androprogrammer.tutorials.activities.Listactivity;
    import com.google.android.gms.wearable.MessageEvent;
    import com.google.android.gms.wearable.WearableListenerService;
    
    /**
     * Created by Wasim on 08-05-2015.
     */
    public class WearListCallListenerService extends WearableListenerService {
    
        public static String SERVICE_CALLED_WEAR = "WearListClicked";
    
    
        @Override
        public void onMessageReceived(MessageEvent messageEvent) {
            super.onMessageReceived(messageEvent);
    
            String event = messageEvent.getPath();
    
            Log.d("Listclicked", event);
    
            String [] message = event.split("--");
    
            if (message[0].equals(SERVICE_CALLED_WEAR)) {
    
                startActivity(new Intent((Intent) Listactivity.getInstance().tutorials.get(message[1]))
                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
            }
        }
    }
    
    


    Step - 5
    In Mobile app put below code to register your service.

    mobile/AndroidManifest.xml

    <service android:name=".services.WearListCallListenerService">
                <intent-filter>
                    <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
                </intent-filter>
            </service>
    


    That's it...
    Now run wear module in android wear emulator and mobile module in device. make sure you are connected. I hope you understand the full tutorial. you can get full source code from my Github repo. if you find it helpful please share this tutorial.

    Keep coding...