Animations

Animations in an Android app adds the wow factor to the app. It engages the user with visual treats that enhance the overall experience of your app. Android framework provides multiple ways to animate views, objects and overall screens. Please check the demo:

Let’s see one example from the demo, Alpha Animation. Create a resource file named alpha in your “res\anim” directory with the following code:

<?xml version="1.0" encoding="utf-8"?>
<alpha
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:fromAlpha="0.0"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:toAlpha="1.0">
</alpha>

Add this code where you want to animate the view in your app:

Animation animAlpha = AnimationUtils.loadAnimation(MainActivity.this, R.anim.alpha);
[YOUR_VIEW_OBJECT].startAnimation(animAlpha);

Replace the “YOUR_VIEW_OBJECT” with the view’s name which you want to animate.

Checkout the rest of the animations in this GitHub repository. In the demo project, you’ll find animations for alpha, blinking, fading, zooming, scaling, rotation, flipping & more.

Happy Coding!

Android Notifications

Notifications are a great way to remind user of his/her immediate or future attention required by the app. It enables the developers to show useful and sometimes crucial information in concise way to the users.
Lets see how we can take advantage of the NotificationCompat API:
In your app gradle file, make your target version point to 20 because from Android OS Lollipop and later, the notification icons should follow the material design (must be entirely white), in case, your notification icon doesn’t follow the requirements.
In your activity (MainActivity.java in my case) file, add this declaration:

private PendingIntent pendingIntent;
private NotificationManager notificationManager;
private NotificationCompat.Builder notificationBuilder;

In the onCreate method of the activity, add these lines:

Intent i = new Intent(MainActivity.this, CatchNotificationActivity.class);
pendingIntent = PendingIntent.getActivity(MainActivity.this, 0, i, 0);
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

Now, whenever you want to show the notification, add this code to any events in your activity or method calls:

notificationBuilder = (NotificationCompat.Builder) new NotificationCompat.Builder(MainActivity.this)
        .setContentIntent(pendingIntent)
        .setContentTitle("Notifications Demo")
        .setContentText("Notified you, don't tell me I didn't.")
        .setSmallIcon(R.mipmap.ic_launcher)
        .setSubText("Told you, I've notified already. ;)")
        .setAutoCancel(true)
        .setTicker("Notifying...");

Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
notificationBuilder.setSound(uri);
notificationBuilder.setVibrate(new long[]{300, 1000});

notificationManager.notify(1, notificationBuilder.build());

In CatchNotification.java activity file, I’ve just added the code to remove the notification once it has been consumed:

String ns = Context.NOTIFICATION_SERVICE;
NotificationManager nMgr = (NotificationManager) getApplicationContext().getSystemService(ns);
nMgr.cancel(1);

The example code is available in this GitHub repository. In the sample project, you’ll also find code to show the notification using the BigPictureStyle and BigTextStyle APIs.

Happy Coding!

Mashmallow Permission

Starting with the Android Marshmallow OS, the permissions mechanism has been updated. Now, the apps are directly installed in Android M or later OS devices without asking for permissions. In the app, when a user accesses a feature that requires a nod from the user, the app has to ask for the permission and only if the user allows that permission, can the feature work in the app. Otherwise, the app has to disable that feature for that user.
This has put us, the developers, to do more work in the app, checking for each permission that the app needs. But at the same time, it has enabled the user to view and understand what an app does with a particular feature and given him/her a right to disable access if he/she wishes to. User’s privacy and his/her rights are utmost to us, so even if it means extra coding, we should be willing to do it. Also, its a great user experience (UX), if our app enables and educates the user on each and every feature we develop for them.
Let’s check out, how to implement the permission requests and handle the situations. I’m going to show how we can ask for Location updates from user’s device. But you can use the code for any permission you like.
Let’s add dependencies in app gradle file:

    compile 'com.android.support:design:23.3.0'
    compile 'com.google.android.gms:play-services:8.4.0'

Google Play services is the one for location and Design support library is for SnackBar I’ve used in the app.

Imports in the MainActivity.java class file:

import android.Manifest;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationServices;

MainActivity class file:

public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {

    private static final int REQUEST_ACCESS_FINE_LOCATION = 1;
    private Button btnLocation;
    private TextView tvLocation;
    private GoogleApiClient mGoogleApiClient;
    private Location mLastLocation;

    private void findViews() {
        btnLocation = (Button)findViewById( R.id.btnLocation );
        tvLocation = (TextView)findViewById( R.id.tvLocation );

        btnLocation.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mGoogleApiClient != null) {
                    if (mGoogleApiClient.isConnected()) {
                        getLocation();
                    } else {
                        Snackbar.make(tvLocation, "Google Play Services not ready!", Snackbar.LENGTH_LONG).show();
                    }
                } else {
                    Snackbar.make(tvLocation, "Google Play Services not available!", Snackbar.LENGTH_LONG).show();
                }
            }
        });
    }

    private void getLocation() {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {

            ActivityCompat.requestPermissions(MainActivity.this,
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                    REQUEST_ACCESS_FINE_LOCATION);

            return;
        }

        mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
        if (mLastLocation != null) {
            String msg = "Latitude: " + String.valueOf(mLastLocation.getLatitude()) + "\n"
                    + "Longitude: " + String.valueOf(mLastLocation.getLongitude());
            tvLocation.setText(msg);
        }else{
            Snackbar.make(tvLocation, "Location unavailable!", Snackbar.LENGTH_LONG).show();
        }
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // instantiate Google API client
        if (mGoogleApiClient == null) {
            mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();
        }

        findViews();
    }

    @Override
    protected void onStart() {
        mGoogleApiClient.connect();
        super.onStart();
    }

    @Override
    protected void onStop() {
        mGoogleApiClient.disconnect();
        super.onStop();
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        getLocation();
    }

    @Override
    public void onConnectionSuspended(int i) {
        Snackbar.make(tvLocation, "Google Play Services not ready!", Snackbar.LENGTH_LONG).show();
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        Snackbar.make(tvLocation, "Google Play Services connection failed!", Snackbar.LENGTH_LONG).show();
    }

    @Override
    public void onRequestPermissionsResult(
            int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

        switch (requestCode) {
            case REQUEST_ACCESS_FINE_LOCATION: {
                // grantResults array will be empty if the user denies permission...
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Snackbar.make(tvLocation, "Permission granted!", Snackbar.LENGTH_LONG).show();
                    getLocation();

                } else {
                    Snackbar.make(tvLocation, "Permission denied!", Snackbar.LENGTH_LONG).show();
                    // you can also build a case with the user if the permission is important for the app.
                    AlertDialog.Builder builder = new AlertDialog.Builder(
                            MainActivity.this);
                    builder.setTitle("Marshmallow Permissions");
                    builder.setMessage("Location is important for the app to function " +
                            "and give you better results! Please grant location permission.");
                    builder.setPositiveButton("OK",
                            new DialogInterface.OnClickListener() {
                                public void onClick(DialogInterface dialog,
                                                    int which) {
                                    getLocation();
                                }
                            });
                    builder.show();
                }
                return;
            }

            // other permissions can be checked in Case statements
        }
    }
}

We can maintain the count of requests for a feature and show appropriate messages to the user based on the count of requests and earlier denials. App source is available on this GitHub repository.

Happy Coding!

Speech To Text

Android has a very awesome API that enables speech to be converted to text. It is available by default. All we need is an Internet connection as the API sends your speech to Google servers to convert to text and get the results back to us. There is an Offline mode but for that we need to make sure that we download the language packs for our speech to converted to words.

As a developer, it excites me to try this feature and add it to my apps if my clients ever need it. To enable speech recognition in your activity, all we need to do is the following code:

Speech recognition code:

private final int SPEECH_CODE = 1;

The code in this method triggered on event or ran on its own:

private void startConvertingSpeechToText() {
        Intent i = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        i.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault());
        i.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
                RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
        i.putExtra(RecognizerIntent.EXTRA_PROMPT,
                "Please start speaking...");
        try {
            startActivityForResult(i, SPEECH_CODE);
        } catch (ActivityNotFoundException a) {
            Snackbar.make(fabSpeak, "Speech recognition is not supported on your device. Sorry!",
                    Snackbar.LENGTH_LONG).show();
        }
    }

Handle onActivityResult method with the speech data:

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case SPEECH_CODE: {
                if (resultCode == RESULT_OK && null != data) {
                    ArrayList<String> result = data
                            .getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
                    String text = result.get(0);
                    tvConvertedText.setText(text);
                }
                break;
            }
        }
    }

That’s it! You can start getting text commands from your users in your apps using speech now. This is really good UX mechanism to save keystrokes, but only for short commands. I’ve found out that sometimes, it fails due to different accents, speed of speaking and how near or far the device you are holding when speaking. Do your own experiments!

The example project is available on GitHub.

Happy Coding!