Thursday 3 August 2017

Android Text with Custom color Outline

I was working on some sample Android App where i tried adding a small utility i.e adding a custom border to text within app with a custom color. Some times we get requirement's in apps where we need to implement such text's in views. So here's a small and useful code snippet to implement something like this (for Android Devs).

Override onDraw method for custom Textview
@Override
public void onDraw(Canvas canvas)
{
    int textColor = getTextColors().getDefaultColor();
    setTextColor(Color.BLACK);
    getPaint().setStrokeWidth(10);
    getPaint().setStyle(Paint.Style.STROKE);
    super.onDraw(canvas);
    setTextColor(textColor);
    getPaint().setStrokeWidth(0);
    getPaint().setStyle(Paint.Style.FILL);
    super.onDraw(canvas);
}


Cheers!!

Thursday 27 July 2017

Loading custom headers on every page load inside a web view

Sometimes we have a requirement to send custom headers while opening a webview and also pass same headers whenever user navigates to another webpage within the webview. So here's how we can achieve that :


private class MyWebViewClient extends WebViewClient {
       @Override
       public void onPageStarted(WebView webView, String url, Bitmap favicon) {
           super.onPageStarted(webView, url, favicon);
       }

       @Override
       public void onPageFinished(WebView view, String url) {
           super.onPageFinished(view, url);
       }

       @Override
       public void onLoadResource(WebView view, String url) {
       }

       // api less then 24
       @SuppressWarnings("deprecation")
       @Override
       public boolean shouldOverrideUrlLoading(WebView view, String url) {
           return view.load(url,yourheaders);
       }

       // api greater than 24
       @TargetApi(Build.VERSION_CODES.N)
       @Override
       public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
           return view.load(request.getUrl(),yourheaders);
   }

   }

Monday 29 May 2017

Difference between aar vs jar. And how to implement aar in Android

Although .jar and .aar files are very similar but still there are few differences between them :

1. .jar stands for Java Archive and .aar stands for Android archive.

2.  A Jar file is basically a zip-file which contains one or many class files and text files containing some sort of meta data. Jar files can be used to bundle java library containing many classes into single file. Where as .aar files are same as .jar but their also contains android stuff like source files, layouts, drawable and manifest files as a would module contains.

3. Except Android Studio the rest of the world does not know what is that thing aar file unlike jar files.

How to compile a .aar file in your android project

Add .aar file to libs folders under your module

1. Add below code snippet to your project level gradle file

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
        flatDir {
            dirs 'aars','libs'
        }
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.2'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}


2. Below code to your module level gradle

  compile(name:'name of the aar file here', ext:'aar')


Cheers!!

Thursday 25 May 2017

How to add a common header across all activities using Parent-Child Relationship

This blog post demonstrates how to add a common header layout in all our Android Activities using simple Parent-Child Relationship.

Note : you can add Fragments as well in the container i am using in the parent class to have one activity and multiple fragments as per your app architecture.

My use case was to implement a common header in an existing project with 50+ activities which shows a to line header with a message whether a user is online or offline without changing to much in the existing code.  


1. BaseActivity.java


package kamal.com.headtest;

import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

public class BaseActivity extends AppCompatActivity {

    private TextView tvHeader,tvClose;
    private FrameLayout activityContainer;
    private RelativeLayout rlHeaderView;

    @Override
    public void setContentView(int layoutResID) {
        LinearLayout llparentView = (LinearLayout) getLayoutInflater().inflate(R.layout.activity_main, null);
        initViews(llparentView);
        getLayoutInflater().inflate(layoutResID, activityContainer, true);
        super.setContentView(llparentView);
    }

    /**
     *  Initialize BaseActivity Views
     * @param view : parent use to initialize child views
     */
    private void initViews(View view){
        tvHeader=(TextView)view.findViewById(R.id.headertext);
        tvClose=(TextView)view.findViewById(R.id.closebtn);
        activityContainer = (FrameLayout) view.findViewById(R.id.activity_content);
        rlHeaderView=(RelativeLayout)view.findViewById(R.id.headerviewrl);
        tvClose.setOnClickListener(closeBtnListener);
    }


    /**
     *  anonymous onclicklistener to handle a click event for Close button in header.
     */
    private View.OnClickListener closeBtnListener=new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            finish();
        }
    };

    /**
     *  Method to be overiden by child classes if need to hide header view
     * @param show : true or false as to handle visibility of the header
     */
    public void showHeader(boolean show){
        rlHeaderView.setVisibility(show ? View.VISIBLE : View.GONE);
    }

    /**
     *  Method to be overiden by child classes if need to hide header view
     * @param value : of the header
     */
    public void setHeaderText(String value){
        tvHeader.setText(value);
    }
}


2. ChildActivity.java

package kamal.com.headtest;
import android.os.Bundle;
import android.support.annotation.Nullable;

public class ChildActivity extends BaseActivity{

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.setContentView(R.layout.childlayout);
        showHeader(true);;
    }
}

3. activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/parent_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <RelativeLayout
        android:id="@+id/headerviewrl"
        android:layout_width="match_parent"
        android:orientation="horizontal"
        android:layout_height="?actionBarSize">

        <TextView
            android:id="@+id/headertext"
            android:layout_width="wrap_content"
            android:layout_height="?actionBarSize"
            android:layout_centerHorizontal="true"
            android:gravity="center"
            android:padding="10dp"
            android:textSize="20sp"
            android:textStyle="bold"
            android:text="My Header View" />

        <TextView
            android:id="@+id/closebtn"
            android:layout_width="wrap_content"
            android:layout_height="?actionBarSize"
            android:gravity="center"
            android:padding="10dp"
            android:textSize="30sp"
            android:textStyle="bold"
            android:layout_alignParentRight="true"
            android:text="X" />

    </RelativeLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="2dp"
        android:background="#000000"
        />

    <FrameLayout
        android:id="@+id/activity_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>


4. childlayout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
 
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:layout_marginTop="10dp"
        android:layout_centerHorizontal="true"
        android:text="Testing child" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:layout_marginTop="10dp"
        android:layout_centerHorizontal="true"
        android:text="Testing child" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:layout_marginTop="10dp"
        android:layout_centerHorizontal="true"
        android:text="Testing child" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:layout_marginTop="10dp"
        android:layout_centerHorizontal="true"
        android:text="Testing child" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:layout_marginTop="10dp"
        android:layout_centerHorizontal="true"
        android:text="Testing child" />

</LinearLayout>


5. manifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="kamal.com.headtest">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".ChildActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>



Output:





Cheers :-)

Thursday 2 March 2017

FCM Android Integration


  FCM has replaced GCM for push Notifications in android.So we will be discussing how to    
  integrate FCM in android app and handle push notifications.

  Below are the steps to start

 1. Create a project on Firebase console.




2. Select Firebase for android apps.

3. Register app by entering the app application id and SHA1 finferprint.

4. Download the config json file and place in your app folder.


Place downloaded json to your app directory




5. Finally Add Firebase sdk  .



 App level gradle :

 // Add this line to gradle dependencies.
  classpath 'com.google.gms:google-services:3.0.0'

Below is the app level Gradle file:      

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {

    repositories {
        jcenter()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.2'
        // Add this line
        classpath 'com.google.gms:google-services:3.0.0'
       }
}



allprojects {
    repositories {
        jcenter()
    }
}


Add FirebaseMessaging dependencies to your module level gradle file
compile 'com.google.firebase:firebase-messaging:10.2.0'

Below is the module Gradle file :

apply plugin: 'com.android.application'
android {
    compileSdkVersion 25
    buildToolsVersion "25.0.0"
    defaultConfig {
        minSdkVersion 15
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        multiDexEnabled true
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:25.0.0'
    compile 'com.google.firebase:firebase-messaging:10.2.0'
    compile 'com.google.android.gms:play-services:10.2.0'
}

Now add below services to your app so as to get the required device token .


1. MyFirebaseInstanceIDService 

import android.util.Log;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;

/**
 * Created by kamal on 3/3/2017.
 */

public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {

    private static final String TAG = "MyFirebaseIIDService";

    @Override
    public void onTokenRefresh() {
        //Getting registration token for the device
        String refreshedToken = FirebaseInstanceId.getInstance().getToken();
        Log.d(TAG, "Refreshed token: " + refreshedToken);
    }

    private void sendRegistrationToServer(String token) {
    }

}


Create MyFirebaseMessagingService to handle the notifications.

2.  MyFirebaseMessagingService 


import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;


/**
 * Created by kamal on 3/3/2017.

 */


public class MyFirebaseMessagingService extends FirebaseMessagingService {

    private static final String TAG = "MyFirebaseMsgService";

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        Log.e(TAG, "From: " + remoteMessage.getFrom());
        Log.e(TAG, "Notification Message Body: " + remoteMessage.getNotification().getBody());
        handleNotification(remoteMessage.getNotification().getBody());
    }



    private void handleNotification(String message) {
        Intent intent = new Intent(this, MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,
                PendingIntent.FLAG_ONE_SHOT);

        Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle("Firebase Push Notification")
                .setContentText(messageBody)
                .setAutoCancel(true)
                .setSound(defaultSoundUri)
                .setContentIntent(pendingIntent);

        NotificationManager notificationManager =
                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(0, notificationBuilder.build());
    }

}


Finally add these services to manifest.


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="">
    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".LocationClass">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".MainActivity"
            android:windowSoftInputMode="stateAlwaysHidden"
            />

        <!-- Services -->
        <service
            android:name=".MyFirebaseMessagingService">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT"/>
            </intent-filter>
        </service>


        <service
            android:name=".MyFirebaseInstanceIDService">
            <intent-filter>
                <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
            </intent-filter>
        </service>

    </application>

</manifest>

Now you can test the notifications by going to Notifcations tab in the firebase project dashboard.
And send push notifications to devices.


Output :



Cheers!!




Tuesday 28 February 2017

Android Google Maps with Current Location Demo Part 2

In this article i'll continue google maps into your android application and adding current location using Google Api Client with Location listener using Fuse Api.

Here i'll be handling Android runtime permission to enable Fine location permission for Fetching current location of users running 6.0 or above.

Location dialog will also be available to prompt user to enable location services so that fuse api can fetch the user's current location.

1. Add latest google play services dependency to your module gradle for eg :

dependencies {
  compile fileTree(dir: 'libs', include: ['*.jar'])
  compile 'com.android.support:appcompat-v7:25.0.0' 
  compile 'com.google.android.gms:play-services:10.2.0'
}


2. Add Api keys and google play services version to the manifest file.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="kamal.com.androidv2mapsdemo">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">        <activity android:name=".MapsActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!--Defining Map Configs here. Including Google play services version and Map Key-->
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="Your Api key" />
    </application>

</manifest>

3. Add a container to add map on runtime in Xml layout.

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

    <!-- The map fragments will go here -->
    <RelativeLayout
        android:id="@+id/map_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></RelativeLayout>

</RelativeLayout>

4. MapsActivity to inflate map.
 import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentManager;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.ResultCallbacks;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.LocationSettingsResult;
import com.google.android.gms.location.LocationSettingsStates;
import com.google.android.gms.location.LocationSettingsStatusCodes;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;


public class MapsActivity extends AppCompatActivity implements
        OnMapReadyCallback , GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {

    private GoogleMap googleMap;
    private SupportMapFragment fragment;
    private GoogleApiClient mGoogleApiClient;

    /**
     * The desired interval for location updates. Inexact. Updates may be more or less frequent.
     */
    public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000;
    /**
     * The fastest rate for active location updates. Exact. Updates will never be more frequent
     * than this value.
     */
    public static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS =
            UPDATE_INTERVAL_IN_MILLISECONDS / 2;
    /**
     * Stores parameters for requests to the FusedLocationProviderApi.
     */
    protected LocationRequest mLocationRequest;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);
        setupDynamicMapFragment();
        if(checkGooglePlayServicesAvailable())
        initializeMap();
    }

    private void setupDynamicMapFragment() {
        FragmentManager fm = this.getSupportFragmentManager();
        fragment = (SupportMapFragment) fm.findFragmentById(R.id.map_container);
        if (fragment == null) {
            fragment = SupportMapFragment.newInstance();
            fm.beginTransaction().replace(R.id.map_container, fragment).commit();
        }
    }


    /**
     * function to load map. If map is not created it will create it for you
     */
    private void initializeMap() {
        if (googleMap == null) {
            fragment.getMapAsync(this);
        }
    }


    @Override
    public void onMapReady(GoogleMap googleMap) {
        this.googleMap=googleMap;
        if (googleMap != null) {
            googleMap.getUiSettings().setMapToolbarEnabled(false);
            if (Build.VERSION.SDK_INT < 23) {
                googleMap.setMyLocationEnabled(true);
                buildGoogleApiClient();
            }else{
                checkFineLocationPermission();
            }
        }
    }


    protected synchronized void buildGoogleApiClient() {
        Toast.makeText(this,"buildGoogleApiClient", Toast.LENGTH_SHORT).show();
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
        mGoogleApiClient.connect();
    }

    /**
     * Check whether Google Play Services are available.
     *
     * If not, then display dialog allowing user to update Google Play Services
     *
     * @return true if available, or false if not
     */
    private boolean checkGooglePlayServicesAvailable( )
    {
        GoogleApiAvailability googleAPI = GoogleApiAvailability.getInstance();
        final int status = googleAPI.isGooglePlayServicesAvailable(this);
        if (status == ConnectionResult.SUCCESS)
        {
            return true;
        }
        Log.e("", "Google Play Services not available: " + GooglePlayServicesUtil.getErrorString(status));

        if (googleAPI.isUserResolvableError(status))
        {
            final Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(status, this, 1);
            if (errorDialog != null)
            {
                errorDialog.show();
            }
        }
        return false;
    }


    @Override
    public void onConnected(@Nullable Bundle bundle) {
        Toast.makeText(this,"onConnected",Toast.LENGTH_SHORT).show();
        //mLocationRequest.setSmallestDisplacement(0.1F); //1/10 meter
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(5000); //5 seconds
        mLocationRequest.setFastestInterval(3000); //3 seconds
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
                .addLocationRequest(mLocationRequest);
        PendingResult<LocationSettingsResult> result =
                LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient,
                        builder.build());
        result.setResultCallback(new ResultCallbacks<LocationSettingsResult>() {
            @Override
            public void onSuccess(@NonNull LocationSettingsResult result) {
                final Status status = result.getStatus();
                final LocationSettingsStates s= result.getLocationSettingsStates();
                switch (status.getStatusCode()) {
                    case LocationSettingsStatusCodes.SUCCESS:
                        // All location settings are satisfied. The client can
                        // initialize location requests here.
                        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, MapsActivity.this);
                        break;
                    case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
                        // Location settings are not satisfied, but this can be fixed
                        // by showing the user a dialog.
                        try {
                            // Show the dialog by calling startResolutionForResult(),
                            // and check the result in onActivityResult().
                            status.startResolutionForResult(
                                    MapsActivity.this,
                                    100);
                        } catch (IntentSender.SendIntentException e) {
                            // Ignore the error.
                        }
                        break;
                    case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                        // Location settings are not satisfied. However, we have no way
                        // to fix the settings so we won't show the dialog.
                        break;
                }
            }


            @Override
            public void onFailure(@NonNull Status status) {
                Log.e("","");
                try {
                    status.startResolutionForResult(MapsActivity.this, 100);
                } catch (IntentSender.SendIntentException e) {
                    e.printStackTrace();
                }
            }
        }) ;
    }



    @Override
    public void onConnectionSuspended(int i) {
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

    }


    @Override
    public void onLocationChanged(Location location) {
        //Place current location marker
        LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
        MarkerOptions markerOptions = new MarkerOptions();
        markerOptions.position(latLng);
        markerOptions.title("Current Position");
        markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA));
        googleMap.addMarker(markerOptions);

        //move map camera
        googleMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));
        googleMap.animateCamera(CameraUpdateFactory.zoomTo(5));


        //stop location updates
        if (mGoogleApiClient != null) {
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
        }

    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(requestCode==100 ){
            LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
        }
    }


    /**
     *   Write external Permission check
     */
    private void checkFineLocationPermission(){
        if ((ContextCompat.checkSelfPermission(this,  android.Manifest.permission.ACCESS_FINE_LOCATION))
                != PackageManager.PERMISSION_GRANTED) {
            // This condition checks whether user has earlier denied the permission or not just by clicking on deny in the permission dialog.
            //Remember not on the never ask check box then deny in the permission dialog
            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    android.Manifest.permission.ACCESS_FINE_LOCATION)) {
                /**
                 * Show an explanation to user why this permission in needed by us. Here using alert dialog to show the permission use.
                 */
                new AlertDialog.Builder(this)
                        .setTitle("Permission Required")
                        .setMessage("This permission was denied earlier by you. This permission is required to use card.")
                        .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {
                                dialog.dismiss();
                                ActivityCompat.requestPermissions(MapsActivity.this,
                                        new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
                                        4);
                            }
                        })
                        .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {
                                dialog.dismiss();
                            }
                        })
                        .setIcon(android.R.drawable.ic_dialog_alert)
                        .show();
            } else {
                //Just ask for the permission for first time. This block will come into play when user is trying to use feature which requires permission grant.
                //So for the first time user will be into this else block. So just ask for the permission you need by showing default permission dialog
                ActivityCompat.requestPermissions(MapsActivity.this,
                        new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION}, 4);
            }
        }else {
            // If permission is already granted by user then we will be into this else block. So do whatever is required here
            //            Toast.makeText(this,"Permission Already granted",Toast.LENGTH_LONG).show();
            googleMap.setMyLocationEnabled(true);
            buildGoogleApiClient();
        }
    }



    /**
     *  This method will be invoked when user allows or deny's a permission from the permission dialog so take actions accordingly.
     * @param requestCode : returns requestCode
     * @param permissions : returns permissions
     * @param grantResults : returns grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 4:
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    googleMap.setMyLocationEnabled(true);
                    buildGoogleApiClient();
                } else {
                    String permission = permissions[0];
                    boolean showRationale = ActivityCompat.shouldShowRequestPermissionRationale(this, permission);
                    if (!showRationale) {
                        //We will be in this block when User has ticked the never show dialog and denied the permission from the permission dialog
                        //Here we can not request the permission again if user has denied the permission with never ask option enabled.
                        //Only way is to show a imfo dialog and ask user to grant the permission from settings.
                        Toast.makeText(this, "Write Storage Permission Denied with never show options." +
                                        "Please manually enable the permission and re-start the app to use this feature.",
                                Toast.LENGTH_LONG).show();
                    } else {
                        Toast.makeText(this, "Write Storage Permission Denied", Toast.LENGTH_LONG).show();
                    }
                }
                break;
            default:
        }
    }

}
Now you can see users current location on google maps.


Download the full source code from : Android Google Maps Demo Link

Cheers!!

Monday 27 February 2017

Android Google Map Demo part 1

In this article i'll be discussing how to easily integrate maps into your android application. So to start firstly you would need to setup your android app as normally we do. Before starting please create your project on Google web console and obtain required Api keys for Google Maps using SHA1 fingerprint of you System,

1. Add latest Google Play Services Dependency to your module gradle for eg :

dependencies {
  compile fileTree(dir: 'libs', include: ['*.jar'])
  compile 'com.android.support:appcompat-v7:25.0.0' 
  compile 'com.google.android.gms:play-services:10.2.0'
}


2. Add Api Keys and Google Play Services Version to the manifest file.


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="kamal.com.androidv2mapsdemo">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MapsActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!--Defining Map Configs here. Including Google play services version and Map Key-->
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="Your Api key" />
    </application>

</manifest>


3. Add a container to add map on runtime in Xml layout.


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

    <!-- The map fragments will go here -->
    <RelativeLayout
        android:id="@+id/map_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></RelativeLayout>

</RelativeLayout>


4. MapsActivity to inflate map.

package kamal.com.androidv2mapsdemo;

import android.app.Dialog;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;

public class MapsActivity extends AppCompatActivity implements
        OnMapReadyCallback {
    private GoogleMap googleMap;
    private SupportMapFragment fragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);
        setupDynamicMapFragment();
        if(checkGooglePlayServicesAvailable())
        initializeMap();
    }


    private void setupDynamicMapFragment() {
        FragmentManager fm = this.getSupportFragmentManager();
        fragment = (SupportMapFragment) fm.findFragmentById(R.id.map_container);
        if (fragment == null) {
            fragment = SupportMapFragment.newInstance();
            fm.beginTransaction().replace(R.id.map_container, fragment).commit();
        }
    }


    /**
     * function to load map. If map is not created it will create it for you
     */
    private void initializeMap() {
        if (googleMap == null) {
            fragment.getMapAsync(this);
        }
    }


    @Override
    public void onMapReady(GoogleMap googleMap) {
        this.googleMap=googleMap;
        if (googleMap != null) {
            googleMap.getUiSettings().setMapToolbarEnabled(false);
        }
    }


    /**
     * Check whether Google Play Services are available.
     *
     * If not, then display dialog allowing user to update Google Play Services
     *
     * @return true if available, or false if not
     */
    private boolean checkGooglePlayServicesAvailable( )
    {
        GoogleApiAvailability googleAPI = GoogleApiAvailability.getInstance();
        final int status = googleAPI.isGooglePlayServicesAvailable(this);
        if (status == ConnectionResult.SUCCESS)
        {
            return true;
        }

        Log.e("", "Google Play Services not available: " + GooglePlayServicesUtil.getErrorString(status));

        if (googleAPI.isUserResolvableError(status))
        {
            final Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(status, this, 1);
            if (errorDialog != null)
            {
                errorDialog.show();
            }
        }

        return false;
    }

}


That's it, We are done with Google maps for this article. You can see your google maps now.



Cheers!!