Practical How-To Recipes for Android

Rick Boyer

January 2016

In this article by Rick Boyer and Kyle Merrifield Mew, the author of Android Application Development Cookbook - Second Edition, we'll take a look at the following recipes:

  • Making a Flashlight with a Heads-up notification
  • Scaling down large images to avoid out-of-memory exceptions
  • How to get the last location
  • Push notification using Google Cloud Messaging

(For more resources related to this topic, see here.)

Making a Flashlight with a Heads-up notification

Android 5.0—Lollipop (API 21)—introduced a new type of notification called the Heads-up notification. Many people do not care about this new notification as it can be extremely intrusive. This is because the notification forces its way on top of other apps. (Take a look at the following screenshot.) Keep this in mind when using this type of notification. We're going to demonstrate the Heads-up notification with a Flashlight as this demonstrates a good use case scenario.

Here's a screenshot showing the Heads-up notification that we'll create:

If you have a device running Android 6.0, you may have noticed the new Flashlight settings option. As a demonstration, we're going to create something similar in this recipe.

Getting ready

Create a new project in Android Studio and call it FlashlightWithHeadsUp. When prompted for the API level, we need API 23 (or higher) for this project. Select Empty Activity when prompted for Activity Type.

How to do it...

Our activity layout will consist of just ToggleButton to control the flashlight mode. We'll use the setTorchMode() code and add a Heads-up notification. We'll need permission to use the vibrate option; so, start by opening the Android Manifest and follow these steps:

  1. Add the following permission:
    <uses-permission android:name="android.permission.VIBRATE"/>
  2. Specify that we only want a single instance of MainActivity by adding android:launchMode="singleInstance" to the <MainActivity> element. It will look like this:
    <activity android:name=".MainActivity"
    
        android:launchMode="singleInstance">
  3. With the changes made to the Manifest, open the activity_main.xml layout, and replace the existing <TextView> element with this <ToggleButton> code:
    <ToggleButton
    
        android:id="@+id/buttonLight"
    
        android:layout_width="wrap_content"
    
        android:layout_height="wrap_content"
    
        android:text="Flashlight"
    
        android:layout_centerVertical="true"
    
        android:layout_centerHorizontal="true"
    
        android:onClick="clickLight"/>
  4. Now, open ActivityMain.java and add the following global variables:
    private static final String ACTION_STOP="STOP";
    
    private CameraManager mCameraManager;
    
    private String mCameraId=null;
    
    private ToggleButton mButtonLight;
  5. Add the following code to onCreate() to set up the camera:
    mButtonLight = (ToggleButton)findViewById(R.id.buttonLight);
    
    mCameraManager = (CameraManager)
    
    this.getSystemService(Context.CAMERA_SERVICE);
    
    mCameraId = getCameraId();
    
    if (mCameraId==null) {
    
        mButtonLight.setEnabled(false);
    
    } else {
    
        mButtonLight.setEnabled(true);
    
    }
  6. Add the following method to handle the response when the user presses the notification:
    @Override
    
    protected void onNewIntent(Intent intent) {
    
        super.onNewIntent(intent);
    
        if (ACTION_STOP.equals(intent.getAction())) {
    
            setFlashlight(false);
    
        }
    
    }
  7. Add the method to get the camera ID:
    private String getCameraId()  {
    
        try {
    
            String[] ids = mCameraManager.getCameraIdList();
    
            for (String id : ids) {
    
                CameraCharacteristics c =
    
    mCameraManager.getCameraCharacteristics(id);
    
                Boolean flashAvailable =
    
    c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
    
                Integer facingDirection =
    
    c.get(CameraCharacteristics.LENS_FACING);
    
                if (flashAvailable != null &&
    
    flashAvailable && facingDirection != null
    
    && facingDirection ==
    
    CameraCharacteristics.LENS_FACING_BACK) {
    
                    return id;
    
                }
    
            }
    
        } catch (CameraAccessException e) {
    
            e.printStackTrace();
    
        }
    
        return null;
    
    }
  8. Add these two methods to handle the flashlight mode:
    public void clickLight(View view) {
    
        setFlashlight(mButtonLight.isChecked());
    
        if (mButtonLight.isChecked()) {
    
            showNotification();
    
        }
    
    }
    
     
    
    private void setFlashlight(boolean enabled) {
    
        mButtonLight.setChecked(enabled);
    
        try {
    
            mCameraManager.setTorchMode(mCameraId, enabled);
    
        } catch (CameraAccessException e) {
    
            e.printStackTrace();
    
        }
    
    }
  9. Finally, add this method to create the notification:
    private void showNotification() {
    
        Intent activityIntent = new
    
    Intent(this,MainActivity.class);
    
        activityIntent.setAction(ACTION_STOP);
    
        PendingIntent pendingIntent =
    
    PendingIntent.getActivity(this,0,activityIntent,0);
    
        final Builder notificationBuilder = new Builder(this)
    
                .setContentTitle("Flashlight")
    
                .setContentText("Press to turn off the
    
    flashlight")
    
                .setSmallIcon(R.mipmap.ic_launcher)
    
                .setLargeIcon(BitmapFactory.decodeResource(getResources(),
    
    R.mipmap.ic_launcher))
    
                .setContentIntent(pendingIntent)
    
                .setVibrate(new long[]{DEFAULT_VIBRATE})
    
                .setPriority(PRIORITY_MAX);
    
        NotificationManager notificationManager =
    
    (NotificationManager)
    
    this.getSystemService(Context.NOTIFICATION_SERVICE);
    
        notificationManager.notify(0,
    
    notificationBuilder.build());
    
    }

You're ready to run the application on a physical device. As seen in the preceding steps, you'll need an Android 6.0 (or higher) device, with an outward facing camera flash.

How it works...

Since this recipe uses the same flashlight code, we'll jump into the showNotification() method. Most of the notification builder calls are the same as the ones seen in previous examples, but there are two significant differences:

.setVibrate()

.setPriority(PRIORITY_MAX)

Notifications will not be escalated to Heads-up notifications unless the priority is high (or above) and uses either vibrate or sound.

Take a look at this from the developer documentation (http://developer.android.com/reference/android/app/Notification.html#hea...):

"At its discretion, the system UI may choose to show this as a heads-up notification".

We create the PendingIntent method as we've done previously, but, here, we set the action using this code:

activityIntent.setAction(ACTION_STOP);

We set the app to only allow a single instance in the AndroidManifest method as we don't want to start a new instance of the app when the user presses the notification. The PendingIntent method we created sets the action, which we can check out in the onNewIntent() callback. If the user opens the app without pressing the notification, they can still disable the flashlight using the ToggleButton.

There's more...

We can use a custom layout with notifications. Use the following method on the builder to specify its layout:

headsupContentView()

Scaling down large images to avoid out-of-memory exceptions

Working with images can be very memory-intensive, often resulting in your application crashing due to an out-of-memory exception. This is especially true for pictures taken with the device camera as they often have a much higher resolution than the device itself.

Since loading a higher-resolution image than the UI supports doesn't provide any visual benefit, this recipe will demonstrate how to take smaller samples of the image for display. We'll use BitmapFactory to first check the image size, and we'll then load a scaled down image.

Here's a screenshot from this recipe, showing a thumbnail of a very large image:

Getting ready

Create a new project in Android Studio and call it LoadLargeImage. Use the default Phone & Tablet options, and select Empty Activity when prompted for the Activity Type.

We'll need a large image for this recipe, so we've referred to https://pixabay.com/ for an image. Since the image itself doesn't matter, we downloaded the first image that shown at the time. (The full size of image is 6000 x 4000 and 3.4 MB.)

How to do it...

As stated previously, we need a large image to demonstrate the scaling. Once you have the image, follow these steps:

  1. Copy the image to res/drawable as image_large.jpg (use the appropriate extension if you choose a different file type)
  2. Open activity_main.xml and replace the existing TextView with the following ImageView:
    <ImageView
    
        android:id="@+id/imageViewThumbnail"
    
        android:layout_width="100dp"
    
        android:layout_height="100dp"
    
        android:layout_centerInParent="true" />
  3. Now, open MainActivity.java and add this method, which we'll explain as follows:
    public Bitmap loadSampledResource(int imageID, int 
    targetHeight, int targetWidth) {
    
        final BitmapFactory.Options options = new 
    BitmapFactory.Options();
    
        options.inJustDecodeBounds = true;
    
        BitmapFactory.decodeResource(getResources(), imageID, 
    options);
    
        final int originalHeight = options.outHeight;
    
        final int originalWidth = options.outWidth;
    
        int inSampleSize = 1;
    
        while ((originalHeight / (inSampleSize *2)) > 
    targetHeight && (originalWidth / (inSampleSize *2)) > 
    targetWidth) {
    
            inSampleSize *= 2;
    
        }
    
        options.inSampleSize=inSampleSize;
    
        options.inJustDecodeBounds = false;
    
        return BitmapFactory.decodeResource(getResources(), 
    imageID, options);
    
    }
  4. Add the following code to the existing onCreate() method:
    ImageView imageView = 
    (ImageView)findViewById(R.id.imageViewThumbnail);
    
    imageView.setImageBitmap(loadSampledResource(R.drawable.image_large, 100, 100));
  5. Run the application on a device or emulator.

How it works...

The purpose of the loadSampledResource() method is to load a smaller image to reduce the memory consumption of the image. If we attempted to load the full image chosen from https://pixabay.com/, the app would require over 3 MB of RAM to load. That's more memory than most devices can handle (at the moment, anyway), and even if it could be loaded completely, it would provide no visual benefit to our thumbnail view.

To avoid an out-of-memory situation, we use the inSampleSize property of BitmapFactory. You will find options to reduce or subsample the image. (If we set inSampleSize=2, it will reduce the image in half. If we use inSampleSize=4, it will reduce the image by ¼.) To calculate inSampleSize, we first need to know the image size. We can use the inJustDecodeBounds property, as follows:

options.inJustDecodeBounds = true;

This tells BitmapFactory to get the image dimensions without actually storing the contents of the image. Once we know the image size, we calculate the sample using this code:

while ((originalHeight / (inSampleSize *2)) > targetHeight && 
            (originalWidth / (inSampleSize *2)) > targetWidth) {

        inSampleSize *= 2;

    }

The purpose of this code is to determine the largest sample size that does not reduce the image below the target dimensions. To do this, we double the sample size, and check whether the size exceeds the target size dimensions. If it doesn't, we save the doubled sample size and repeat the process. Once the reduced size falls below the target dimensions, we use the last saved inSampleSize.

From the inSampleSize documentation:

Note that the decoder uses a final value that's based on powers of 2; any other value will be rounded down to the nearest power of 2.

Once we have the sample size, we set the inSampleSize property. We also set inJustDecodeBounds to false in order to make it load normally. Here is the code to do this:

options.inSampleSize = inSampleSize;

options.inJustDecodeBounds = false;

It's important to note that this recipe illustrates the concept of applying a task in your own application. Loading and processing images can be a long operation, which could cause your application to stop responding. This is not a good thing and could cause Android to show the Application Not Responding (ANR) dialog. It is recommended that you perform long tasks on a background thread to keep your UI thread responsive.

There's more...

It's important to note that the targetHeight and targetWidth parameters we pass to the loadSampledResource()method do not actually set the size of the image. If you run the application using the same sized image we used earlier, the sample size will be 32, resulting in a loaded image that is 187 x 125 in size.

If your layout needs an image of a specific size, either set the size in the layout file; otherwise, you can modify the size directly using the Bitmap class.

See also

How to get the last Location

We'll start this with a simple recipe that is commonly needed: how to get the last known location. This is an easy-to-use API with very little overhead resource drain (which means that your app won't be responsible for killing the battery life).

This recipe also provides a good introduction to setting up the Google Location APIs.

Getting ready

Create a new project in Android Studio and call it GetLastLocation. Use the default Phone & Tablet options, and select Empty Activity when prompted for the Activity Type.

How to do it...

First, we'll add the necessary permissions to the Android Manifest. We'll then create a layout with a Button and TextView. Finally, we'll create GoogleAPIClient to access the previous location. Open the Android Manifest and follow these steps:

  1. Add the following permission:
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
  2. Open the build.gradle file (Module: app), as shown in this screenshot:

  3. Add the following statement to the dependencies section:
    compile 'com.google.android.gms:play-services:8.4.0'
  4. Open activity_main.xml and replace the existing TextView with the following XML:
    <TextView 
        android:id="@+id/textView" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" />
    
    <Button 
        android:id="@+id/button" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:text="Get Location" 
    android:layout_centerInParent="true" 
        android:onClick="getLocation"/>
  5. Open MainActivity.java and add the following global variables:
    GoogleApiClient mGoogleApiClient;
    
    TextView mTextView;
    
    Button mButton;
  6. Add the class for ConnectionCallbacks:
    GoogleApiClient.ConnectionCallbacks mConnectionCallbacks = new GoogleApiClient.ConnectionCallbacks() {
    
        @Override
    
        public void onConnected(Bundle bundle) {
    
            mButton.setEnabled(true);
    
        }
    
        @Override
    
        public void onConnectionSuspended(int i) {}
    
    };
  7. Add the class to handle the OnConnectionFailedListener callback:
    GoogleApiClient.OnConnectionFailedListener mOnConnectionFailedListener = new GoogleApiClient.OnConnectionFailedListener() {
    
        @Override
    
        public void onConnectionFailed(ConnectionResult connectionResult) {
    
            Toast.makeText(MainActivity.this, connectionResult.toString(), Toast.LENGTH_LONG).show();
    
        }
    
    };
  8. Add the following code to the existing onCreate() method:
    mTextView = (TextView) findViewById(R.id.textView);
    
    mButton = (Button) findViewById(R.id.button);
    
    mButton.setEnabled(false);
    
    setupGoogleApiClient();
  9. Add the method to set up GoogleAPIClient:
    protected synchronized void setupGoogleApiClient() {
    
        mGoogleApiClient = new GoogleApiClient.Builder(this)
    
                .addConnectionCallbacks(mConnectionCallbacks)
    
                .addOnConnectionFailedListener(mOnConnectionFailedListener)
    
                .addApi(LocationServices.API)
    
                .build();
    
        mGoogleApiClient.connect();
    
    }
  10. Add the method for the button click:
    public void getLocation(View view) {
    
        try {
    
            Location lastLocation = LocationServices.FusedLocationApi.getLastLocation(
    
                    mGoogleApiClient);
    
            if (lastLocation != null) {
    
                mTextView.setText(
    
                        DateFormat.getTimeInstance().format(lastLocation.getTime()) + "\n" +
    
                                "Latitude="+lastLocation.getLatitude()+"\n"+
    
                                "Longitude="+lastLocation.getLongitude());
    
            } else {
    
                Toast.makeText(MainActivity.this, "null", Toast.LENGTH_LONG).show();
    
            }
    
        }
    
        catch (SecurityException e) {}
    
    }
  11. You're ready to run the application on a device or emulator.

How it works...

Before we can call the getLastLocation() method, we need to set up GoogleApiClient. We call GoogleApiClient.Builder in our setupGoogleApiClient() method, and then connect to the library. When the library is ready, it calls our ConnectionCallbacks.onConnected() method. For demonstration purposes, this is where we enable the button.

We used a button to show that we can call getLastLocation() on demand; it's not a one-time call. The system is responsible for updating the location and may return the same previous location on repeated calls. (This can be seen in the timestamp—it's the location timestamp, not the timestamp that appears when the button is pressed.)

This approach of calling the location non-demand can be useful in situations where you only need the location when something happens in your app (such as geocoding an object). Since the system is responsible for the updates of the location, your app will not be responsible for draining your battery due to location updates.

The accuracy of the Location object we receive is based on our permission setting. We used ACCESS_COARSE_LOCATION, but if we want higher accuracy, we can request ACCESS_FINE_LOCATION instead using the following permission:

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

Lastly, to keep the code focused on GoogleApiClient, we just wrap getLastLocation() with SecurityException. In a production application, you should check and request the permission.

There's more...

If a problem occurs when establishing a connection with GoogleApiClient, OnConnectionFailedListener is called. In this example, we will display a toast.

Testing the location can be a challenge since it's difficult to actually move the device when testing and debugging it. Fortunately, we have the ability to simulate GPS data with the emulator. (It is possible to create mock locations on a physical device as well, but it's not as easy.)

Mock Locations

There are three ways to simulate locations using the emulator:

  • Android Studio
  • DDMS
  • The Geo command via Telnet

To set a mock location in Android Studio, follow these steps:

  1. Go to the Tools | Android | Android Device Monitor menu.
  2. Select the Emulator Control tab in the Devices window.
  3. Enter GPS coordinates under Location Controls.

Here's a screenshot showing Location Controls:

Important: Simulating the location works by sending GPS data. Therefore, for your app to receive the mock location, it will need to receive GPS data. Testing lastLocation() may not send the mock GPS data since it doesn't rely solely on GPS to determine the location of the device. (We can't force the system to use any specific location sensor; we can only make a request. The system will choose the optimum solution to deliver results.)

See also

Push notification using Google Cloud Messaging

Google Cloud Messaging (GCM), Google's version of a push notification, allows your application to receive messages. The idea is similar to SMS messages but much more flexible. There are three components of GCM:

  • Your server (this is where you initiate the message)
  • Google's GCM server
  • An Android device (though GCM is also available on other platforms)

When the user starts the application, your code needs to connect to the GCM server and obtain a device token, and then send this token to your server. Your server is responsible for initiating the message and passing it to the GCM server. Your server needs to track the device tokens to be sent when initiating the message (your server tells the GCM server which device tokens to send).

You can implement your own server or chose to use one of many services available (the Simple Testing Option section offers an option to verify whether your code works).

This recipe will walk you through the steps needed to add GCM using the current (version 8.3) Google Services library. Before getting to the steps, it's worth noting that GCM is supported all the way back to API 8 as long as the user has a Google account. A Google account is not required after installing Android 4.0.4.

Getting ready

Create a new project in Android Studio and call it GCM. Use the default Phone & Tablet options, and select Empty Activity when prompted for the Activity Type.

Google Cloud Messaging uses the Google Services Plugin, which requires a Google Services Configuration File, available from the Google Developer Console. To create the configuration file, you will need the following information:

Note that if you download the source files, you will need to create a new package name when following the steps as the existing package name has already been registered.

How to do it...

After completing the preceding section, follow these steps:

  1. Copy the google-services.json file you downloaded in the Getting Ready section to your app folder (<project folder>\GCM\app).
  2. Open the project Gradle build file called build.gradle (project: GCM). Add the following to the build script dependencies section:
    classpath 'com.google.gms:google-services:1.5.0-beta2'
  3. Open the Gradle app module build file, called build.gradle (module: app), and add the following statement to the beginning of the file (above the android section):
    apply plugin: 'com.google.gms.google-services'
  4. In the same module build file, as seen in step 3, add the following statement to the dependencies section:
    compile 'com.google.android.gms:play-services-auth:8.3.0'
  5. Open the Android Manifest and add the following permissions:
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    
    <permission android:name="<packageName >.permission.C2D_MESSAGE"
    
      android:protectionLevel="signature" />
    
    <uses-permission android:name="<packageName >.permission.C2D_MESSAGE" />
  6. Within the <application> element, add the following <receiver> and <service> declarations (these should be at the same level as <activity>):
    <receiver
    
        android:name="com.google.android.gms.gcm.GcmReceiver"
    
        android:exported="true"
    
        android:permission="com.google.android.c2dm.permission.SEND" >
    
    <intent-filter>
    
    <action android:name="com.google.android.c2dm.intent.RECEIVE" />
    
    <category android:name="<packageName>" />
    
    <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
    
    </intent-filter>
    
    </receiver>
    
    <service
    
        android:name=".GCMService"
    
        android:exported="false" >
    
    <intent-filter>
    
    <action android:name="com.google.android.c2dm.intent.GCM_RECEIVED_ACTION"/>
    
    <action android:name="com.google.android.c2dm.intent.RECEIVE" />
    
    </intent-filter>
    
    </service>
    
    <service
    
        android:name=".GCMInstanceService"
    
        android:exported="false">
    
    <intent-filter>
    
    <action android:name="com.google.android.gms.iid.InstanceID" />
    
    </intent-filter>
    
    </service>
    
    <service
    
        android:name=".GCMRegistrationService"
    
        android:exported="false">
    
    </service>
  7. Create a new Java class, called GCMRegistrationService, that extends IntentService, as follows:
    public class GCMRegistrationService extends IntentService {
    
        private final String SENT_TOKEN="SENT_TOKEN";
    
        public GCMRegistrationService() {
    
            super("GCMRegistrationService");
    
        }
    
        @Override
    
        protected void onHandleIntent(Intent intent) {
    
            super.onCreate();
    
            SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
    
            try {
    
                InstanceID instanceID = InstanceID.getInstance(this);
    
                String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
    
                        GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
    
                Log.i("GCMRegistrationService", "GCM Registration Token: " + token);
    
                //sendTokenToServer(token);
    
                sharedPreferences.edit().putBoolean(SENT_TOKEN, true).apply();
    
            } catch (Exception e) {
    
                sharedPreferences.edit().putBoolean(SENT_TOKEN, false).apply();
    
            }
    
        }
    
    }
  8. Create a new Java class, called GCMInstanceServicethat, that extends InstanceIDListenerServiceas, as follows:
    public class GCMInstanceService extends InstanceIDListenerService {
    
        @Override
    
             public void onTokenRefresh() {
    
            Intent intent = new Intent(this, GCMRegistrationService.class);
    
            startService(intent);
    
        }
    
    }
  9. Create a new Java class, called GCMServicethat, that extends GcmListenerServiceas, as follows:
    public class GCMService extends GcmListenerService {
    
        @Override
    
        public void onMessageReceived(String from, Bundle data) {
    
            super.onMessageReceived(from, data);
    
            Log.i("GCMService", "onMessageReceived(): " + data.toString());
    
        }
    
    }
  10. Add the following code to the existing onCreate() callback:
    Intent intent = new Intent(this, GCMRegistrationService.class);
    
    startService(intent);
  11. You're ready to run the application on a device or emulator.

How it works...

Most of the actual GCM code is encapsulated within the Google APIs, simplifying their implementation. We just have to set up the project to include the Google Services and give our app the required permissions.

Important: When adding the permissions in steps 5 and 6, replace the <packageName> placeholder with your application's package name.

The most complicated aspect of GCM is probably the multiple services that are required. Even though the code in each service is minimal, each service has a specific task. There are two main aspects of GCM:

  • Registering the app with the GCM server
  • Receiving messages

This is the code to register with the GCM server:

String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),

        GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);

We don't call getToken() in the Activity because it could block the UI thread. Instead, we call GCMRegistrationService, which handles the call in a background thread. After you receive the device token, you need to send it to your server as it is needed when initiating a message.

Receiving a GCM message is handled in GCMService, which extends GcmListenerService. Since the Google API already handles most of the work, all we have to do respond to the onMessageReceived() callback.

There's more...

To make it easier to type, we left out an important Google Services API verification, which should be included in any production application. Instead of calling GCMRegistrationService directly, as we did in onCreate() previously, first check whether the Google API Service is available. Here's an example that shows how to call the isGooglePlayServicesAvailable() method:

private boolean isGooglePlayServicesAvailable() {

    GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance();

    int resultCode = googleApiAvailability.isGooglePlayServicesAvailable(this);

    if (resultCode != ConnectionResult.SUCCESS) {

        if (googleApiAvailability.isUserResolvableError(resultCode)) {

            googleApiAvailability.getErrorDialog(this, resultCode, PLAY_SERVICES_RESOLUTION_REQUEST)

                    .show();

        } else {

            Toast.makeText(MainActivity.this, "Unsupported Device", Toast.LENGTH_SHORT).show();

            finish();

        }

        return false;

    }

    return true;

}

Then, change the onCreate() code to call this method first:

if (isGooglePlayServicesAvailable()) {

    Intent intent = new Intent(this, GCMRegistrationService.class);

    startService(intent);

}

Simple testing option

To verify whether your code is working correctly, a testing application was created and posted on Google Play. This app will run on both a physical device and an emulator. The Google Play listing also includes a link to download the source code to run the project directly, making it easier to enter the required fields.

Take a look at GCM (Push Notification) Tester at https://play.google.com/store/apps/details?id=com.eboyer.gcmtester.

See also

Summary

In this article, we learned how to make a Flashlight with a Heads-up notification, scaling down large images to avoid out-of-memory exceptions, how to get last location and using push notification with GCM.

Resources for Article:


Further resources on this subject:

 


You've been reading an excerpt of:

Android Application Development Cookbook - Second Edition

Explore Title