Saturday, 16 June 2018

Say No More To TextWatcher Callback


As Android Developer, we have used TextWatcher callback to listen any of the changes in the EditText.


Something like this :
editText.addTextChangedListener(object : TextWatcher {

override fun afterTextChanged(p0: Editable?) {

}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {

}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {

}
})

Data Binding Implementation


In Your xml Layout use OnTextChanged property

android:onTextChanged="@{model.onPasswordTextChanged}"


And call the view model user defined method to handle text changes in EditText. Full Snippet is below :


<EditText
android:id="@+id/password_et"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/username_et"
android:layout_marginTop="10dp"
android:hint="Password"
android:onTextChanged="@{model.onPasswordTextChanged}"
android:inputType="textPassword" />


ViewModel User defined method can be like this (implement as per your implementation):


fun onPasswordTextChanged(s: CharSequence,start: Int,before : Int,
count :Int){
    //TODO write your implementation here ...
}



Now no more of TextWatcher callback in your View(Activity/Fragment), Instead use data binding to avoid boilerplate code.

Use MVVM, implement user defined method’s in ViewModel to handle OnTextChanged in xml.

Hope to see more of this implementation in future :-)

Cheers!!

Monday, 11 June 2018

Architecture Components In Android

What are Architecture Components? 


A Set Libraries from Google which will help Developer’s Design Applications :

1. Testable

2. Maintainable

3. Robust

Moreover, Handling UI part of the applications I.e Activity/Fragment Lifecycle and managing persistence stage. Introducing concept’s Like ViewModel to easily handle UI data when there are orientation changes.
There are various things to learn in Architecture Components as it makes Life easy for the Developer.Let’s Dig deep into these components and understand functionality of Each Component included here.


1. ViewModel

 Responsibility to Manage/Store UI (Activity/Fragment) related DATA, So that data is persist whenever there are configuration changes like Screen Orientation. ViewModel should not contains UI (Activity/Fragment) references.

Note : We do not need worry about Memory Leaks as ViewModel is LIFECYCLE aware.


2. LiveData 

As name is depicting, this will deal with Live data changes. This component hold’s value which can be observed by UI for changes or Can also be observed by ViewModel again depends upon your requirement.

Note : It help’s controlling crashes as it is LIFECYCLE aware I.e if UI (Activity/Fragment) is Alive to observe changes or Not.

There is LifeCycle Owner, In Android activity is Lifecycle owner. So whenever a activity changes states i.e pause, stop etc it is responsibility of the LiveData to decide whether to send data from observer or not.

Usage Of LiveData Types :


 1. LiveData : 

is very useful as using this we can listen to every change made to Room database I.e on every Sql query executed.

2. MutableLiveData : 

Is wrapping a data type inside LiveData. This must be used whenever you want to communicate between ViewModel and Activity. Good thing about this type is there no leak as it does not required any context.

3. MediatorLiveData : 

This is used to listen Other LiveData types and connects Repository to UI (Activity/Fragment) via ViewModel.

Room

Room Persistence Lib The Room persistence library provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite.

Learn more about Room from Below Link :

https://developer.android.com/training/data-storage/room/

Source Codehttps://github.com/CammyKamal/ArchComponentsKotlin

Cheers!!

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!!