Skip to content
JavocSoft edited this page Mar 4, 2018 · 17 revisions

#Index#

ToolBox

Is the main class of the library. It contains a large set of useful methods when programming for Android. The utility methods are ordered by functionalities:

  • Storage
  • System
  • Animation
  • Media
  • Web
  • View
  • Share
  • Screen
  • Preferences
  • Powersaving
  • Notifications
  • Net
  • Intent
  • Media
  • Market
  • IO
  • Graphics
  • Font
  • Dialogs
  • Device
  • Crypto
  • Application
  • Application Information
  • Analytics
  • AdMob
  • Activity
  • ...

To locate a method just use ToolBox.functionality_method().

Now the library is adapted to the last Android notification creation API so is possible to get notifications with a progress bar or with custom actions.

Notification with Progress Bar
Only for Android 4.0+ (API Level 14+). Determinate or indeterminate can be used. A runnable class does operations while informing to the notification about its progress. The class NotificationProgressBarRunnable must be extended to create a progress bar notification. Once runnable finishes, a text can be shown and notification can then be discarded. An example implementation:

public static class NotificationProgressBarDeterminateImpl extends ToolBox.NotificationProgressBarRunnable {
	@Override
	protected void doTask() {
		for (int i = 0; i <= 100; i+=5) {
            try {
                Thread.sleep(5*1000);
                //pBarProgress=i;
                //notify(pBarProgress);
            } catch (InterruptedException e) {
                Log.d(NotificationModule.TAG, "sleep failure");
            }                
		}			
		notifyFinish(null);
	}
}

Notification with Custom Actions
For Android 4.1+ (API Level 16+). If set, such actions will be presented in the notification. Remember that to close the notification after action is clicked, you should use the "notifyID" parameter and set it as an extra of your action to be able to close the notification from your application.

PackageManager manager = context.getPackageManager();<br>
Intent intent = manager.getLaunchIntentForPackage("<app_package>");<br>
intent.addCategory(Intent.CATEGORY_LAUNCHER);<br>
PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, 0);<br><br>

Action action = new Action(R.drawable.ic_lib, getString(R.string.app_name), pIntent);<br>
action.getExtras().putInt("NOTIFICATION_ID", "<not_id>");

Then, in the opened application:

NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.cancel(getIntent().getIntExtra(NOTIFICATION_ID, -1));

to cancel the notification.

Facebook

FB - Login

Useful links

Create Facebook Application

Create your Application in the Facebook developer portal at https://developers.facebook.com/, configure it properly. Save the facebook application id.

Prepare your application

Edit AndroidManifest.xml

Add in the "application" XML tag the following:

<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/fb_app_id" />  
<activity android:name="com.facebook.LoginActivity" />  

And before the "application" XML tag the required permissions:

<uses-permission android:name="android.permission.INTERNET"/>  
Set your Facebook Application id

In your "strings.xml" file add a new string called "fb_app_id" containing the id of the application in Facebook Developer portal https://developers.facebook.com/.

Decide where to put the Facebook Login in your Activity

Paste the layout code bellow in your activity layout where you want it:

<fragment android:id="@+id/fbLoginFragment"
      android:name="es.javocsoft.android.lib.toolbox.facebook.FacebookLoginFragment"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"          
      tools:layout="@layout/fragment_fb_logn" />
Iniitialize the Facebook Login Button

To do so you must:

Prepare facebook Event listeners:

  • Create a login action listener by creating a class extending FacebookLoginFragment.OnLoginActionCallback, implementing the required methods.
  • Create a logout action listener by creating a class extending FacebookLoginFragment.OnLogoutActionCallback, implementing the required methods.

Now, add in your Activity's onCreate:

FragmentManager fragmentManager = getSupportFragmentManager();  
FacebookLoginFragment fbLoginFragment = (FacebookLoginFragment)fragmentManager.findFragmentById(R.id.fbLoginFragment);
fbLoginFragment.initialize("public_profile,email", login_action_listener, logout_action_listener);

Whenever you need to show the Facebook Login button follow this step.

FB - Share

Note:To share, a valid active Facebook logged session must exists, use the this library Facebook Login procedure to get a valid session. See Facebook Login.

The steps once we have a valid session are:

Decide where to put the Facebook Share in your Activity

Paste the layout code bellow in your activity layout where you want it:

<fragment android:id="@+id/fbShareFragment"
      android:name="es.javocsoft.android.lib.toolbox.facebook.FacebookShareFragment"
      android:layout_width="match_parent"
      android:layout_height="wrap_content" 
      tools:layout="@layout/fragment_fb_share" />

Iniitialize the Facebook Share Button

To do so you must:

Prepare Facebook Event listeners:

  • Create a login action listener by creating a class extending OnLoginActionCallback, implementing the required methods.
  • Create a logout action listener by creating a class extending OnLogoutActionCallback, implementing the required methods.
  • Create a sucess share action listener by creating a class extending OnShareSuccessActionCallback, implementing the required methods.
  • Create a fail share action listener by creating a class extending OnShareFailActionCallback, implementing the required methods.
  • Create a cancel share action listener by creating a class extending OnShareCancelledActionCallback, implementing the required methods.

Now, add in your Activity's onCreate:

FragmentManager fragmentManager = getSupportFragmentManager();  
FacebookShareFragment fbShareFragment = (FacebookShareFragment)fragmentManager.findFragmentById(R.id.fbShareFragment);  
fbShareFragment.initialize("app_Name", "some_caption", "some_description", "some_link", "some_picture_url", login_action_listener, logout_action_listener, share_sucess_listener, share_fail_listener, share_cancel_listener);

Ads Module

Generate your banners, interstitial and Video Rewarded ad

Generate your Ads configuration in the AdMob portal at https://apps.admob.com/ (to do so you need to have an AdSense, http://www.adsense.com, account and also an AdWords, https://adwords.google.com, accounts properly configured)

Save the banner, interstitial and video rewarded ad ids.

Prepare your application

Edit AndroidManifest.xml

Add in the "application" XML tag the following:

<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/>
<meta-data android:value="true" android:name="ADMOB_ALLOW_LOCATION_FOR_ADS" />
<activity android:name="com.google.android.gms.ads.AdActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"  android:theme="@android:style/Theme.Translucent" />

And before "application" XML tag the required permissions:

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

Create a Fragment extending AdFragment and set your AdMob Banner Id

Create a class called for example "your.package.MyAdsFragment" extending es.javocsoft.android.lib.toolbox.ads.AdFragment and modify the onCreateView() method:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
              Bundle savedInstanceState) {
                            
          //Set your AdMob banner id.
          super.setAdUnitId("ca-app-pub-XXXX/XXXXX" );
                            
          return super .onCreateView(inflater, container, savedInstanceState );
}

Decide where to put the Banner in your Activity

Paste the layout code bellow, for example, in a Relative Layout, in the bottom:

<fragment android:id="@+id/adFragment"
      android:name="your.package.MyAdsFragment"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_alignParentBottom="true"
      tools:layout="@layout/fragment_ad" />

Be sure to set correctly the rest of elements of the layout. One has to have:

android:layout_above="@+id/adFragment"

Show the banner

First, put in your onCreate() method:

FragmentManager fragmentManager = getSupportFragmentManager();
ads = (AdFragment) fragmentManager.findFragmentById(R.id.adFragment);

And finally, in your onResume() method:

ads.showAds();

Hide the banner

Simply, use::

ads.hideAds();

Show an Interstitial

It is quite easy, just put this line when you want it:

AdInterstitial.getInstance().requestInterstitial("<your_admob_interstitial_id>", this, null);

It is possible also to set an interstitial click listener to be able to do something when user clicks on an interstitial ad. To achieve this just create a class that implements InterstitialAdsListener.OnInterstitialClickCallback. Implement the required methods and pass an instance of it to the interstitial call:

InterstitialClickCallback interstitialClickcallback = new InterstitialClickCallback();
AdInterstitial.getInstance().requestInterstitial("<your_admob_interstitial_id>", this, interstitialClickcallback);

Notes

You can use the "ca-app-pub-3940256099942544/6300978111" ads unit id just for testing.

Show a Video Rewarded Ad

It is quite easy, just put this line when you want it:

AdInterstitial.getInstance().requestInterstitial("<your_admob_interstitial_id>", this, null);

It is possible also to show a Video Rewarded ad click listener to be able to do something when user clicks on the ad. To achieve this just create a class that implements InterstitialRewardedVideoAdsListener.OnRewardedVideoInterstitialClickCallback. Implement the required methods and pass an instance of it to the interstitial call.

public class InterstitialRewardedVideoClickCallback extends InterstitialRewardedVideoAdsListener.OnRewardedVideoInterstitialClickCallback {
    @Override
    protected void pre_task() {
        // TODO Auto-generated method stub
    }
    @Override
    protected void task() {
        Log.i(ToolBox.TAG, "Interstitial rewarded video cliked.");
    }
    @Override
    protected void post_task() {
        // TODO Auto-generated method stub
    }
}

Also we have the callback to process the reward when the user do not close the video, to use it implement InterstitialRewardedVideoAdsListener.OnRewardedVideoInterstitialRewardCallback and pass an instance of it to the interstitial call.

public class InterstitialRewardedVideoRewardCallback extends InterstitialRewardedVideoAdsListener.OnRewardedVideoInterstitialRewardCallback {
    @Override
    protected void pre_task() {
        // TODO Auto-generated method stub
    }
    @Override
    protected void task() {
        Log.i(ToolBox.TAG, "Interstitial rewarded video reward obtained (type: " + rewardItem.getType() + "/amount: " + rewardItem.getAmount());
    }
    @Override
    protected void post_task() {
        // TODO Auto-generated method stub
    }
}

InterstitialRewardedVideoClickCallback interstitialRewardedVideoClickcallback = new InterstitialRewardedVideoClickCallback();
InterstitialRewardedVideoRewardCallback interstitialRewardedVideoRewardcallback = InterstitialRewardedVideoRewardCallback();

AdRewardedVideoInterstitial.getInstance().requestInterstitial(ApplicationBase.module_ads_rewardedvideo_interstitial_id, this, interstitialRewardedVideoClickcallback, interstitialRewardedVideoRewardcallback);

Notes

You can use the "ca-app-pub-3940256099942544/5224354917" ads unit id just for testing.

Useful Links

Google Ads - Quick-Start

Google Cloud Messaging (GCM) Module

You must follow the following steps:

  1. Configure your application in Google Cloud Console
  2. Create a project for your application .
  3. Activate the Google cloud Messaging.
  4. Generate Keys to send notifications from your server.
  5. Prepare your application.
  6. Prepare AndroidManifest.xml.
  7. Prepare your code.
  8. Use the included GCM Delivery Java classes to send notification under the package es.javocsoft.android.lib.toolbox.gcm.send.

Note: Under a WiFi connection, If your organization has a firewall that restricts the traffic to or from the Internet, you'll need to configure it to allow connectivity with GCM. The ports to open are: 5228, 5229, and 5230. GCM typically only uses 5228, but it sometimes uses 5229 and 5230. GCM doesn't provide specific IPs. It changes IPs frequently..

Configure your application in Google Cloud Console

Create your Project

  1. Go to http://console.developers.google.com and log in. Locate the "Create Project" button and press it to create a new one.
    GCM - Google Cloud API - Step1
  2. Set your project name in the window that appears and press "Create" button. Now Google will process the request, it can take some time.
    GCM - Google Cloud API - Step2 When Google Cloud Console finishes, it will show a verification mark in the bottom right corner.
    GCM - Google Cloud API - Step3
  3. Save the "Project Number" number somewhere. This number will be used in your Android application to tell it from which apps can receive notifications.
    GCM - Google Cloud API - Step4

Enable GCM in your project

Now, we need to enable the Google Cloud Messaging system in order to be able to send and receive notifications in our application. To do so:

Go to "APIs & auth" section and select "APIs" option. Locate the "Google Cloud Messaging for Android" API and activate it by pressing the ON/IOFF button.

GCM - Google Cloud API - Step5

Generate Keys to send notifications from your server

These keys will allow us to send notifications to our Apps by using the Google Cloud Messaging API. To get this key:

  1. Go to "APIs & auth" -> "Credentials" and press the button "Create new key".
    GCM - Google Cloud API - Step6
  2. Select "Server key" if we are going to send notification within a server application, that is our case.
    GCM - Google Cloud API - Step7
  3. Just put now the IP address or the subnet of the server that your are going to use to send the notifications to your application.
    GCM - Google Cloud API - Step8
  4. Once done, we shoukd see a new informative box like this:
    GCM - Google Cloud API - Step9
    Save the API KEY somewhere. This is your key for sending notifications.

Prepare your application

Depending of the used IDE you have two options:

  • Android Studio. **You need to add these lines to the file "build.gradle" in your "app" module. compile 'com.google.android.gms:play-services:5.2.08' compile files('libs/javocsoft_toolbox.jar') ...the "javocsoft-toolbox.jar" must be copied into the libs folder.
  • Eclipse. ** Add "javocsoft-toolbox.jar" to the libs folder.
Prepare AndroidManifest.xml

Add before "application" XML tag the required permissions:

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

<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

<permission android:name="<your_application_package>.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="<your_application_package>.permission.C2D_MESSAGE" />

And in "application" XML tag the following:

<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
<receiver android:name="es.javocsoft.android.lib.toolbox.gcm.core.CustomGCMBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND" >
   <intent-filter>
      <action android:name="com.google.android.c2dm.intent.RECEIVE" />
      <category android:name="<your_application_package>" />
   </intent-filter>
</receiver>
<service android:name="es.javocsoft.android.lib.toolbox.gcm.core.GCMIntentService" />

Where "your_application_package" is the package of your Android application.

Prepare your code

Declare these variables in your Main class:

//GCM Module -------------------------------------------------------------------------------
private NotificationModule notificationModule;
private CustomNotificationReceiver notReceiver;
private final String gcmAppSenderId = "<gcm_project_id>";
//END GCM Notification Module --------------------------------------------------------------

Put in method onCreate() the following:

    //GCM Module -------------------------------------------------------------------------------
    final boolean multipleNotifications = false;
    final String groupNotificationsKey = "basetestapp";

    notificationModule = NotificationModule.getInstance(this, EnvironmentType.SANDBOX,
                                gcmAppSenderId,
                                new GCMRegCallback(),
                                new GCMNotOpenCallback(),
                                new GCMUnregCallback(),
                                new GCMNotReceivedCallback(),
                                multipleNotifications,
                                groupNotificationsKey);
    if(notReceiver==null) {
        notReceiver = new CustomNotificationReceiver();
        registerReceiver(notReceiver,
                new IntentFilter(NotificationModule.NEW_NOTIFICATION_ACTION));
    }
    //END GCM Notification Module --------------------------------------------------------------

In your onResume() method:

   //GCM Module -------------------------------------------------------------------------------
    try {
        notificationModule.gcmRegisterDevice(this,"Testing GCM", MainActivity.class);
    } catch (GCMException e) {
        Log.e(NotificationModule.TAG, "Error registering with GCM: " + e.getMessage() + ".", e);
    }
    notificationModule.gcmCheckForNotificationReceival(this, getIntent());
    //END GCM Notification Module --------------------------------------------------------------

Also in onNewIntent() method:

    //GCM Module -------------------------------------------------------------------------------
    notificationModule.gcmCheckForNotificationReceival(this, intent);
    //END GCM Notification Module --------------------------------------------------------------

And finally, in onDestroy() method:

    //GCM Module -------------------------------------------------------------------------------
    unregisterReceiver(notReceiver);
    //END GCM Notification Module --------------------------------------------------------------

Now, to handle GCM events:

  • On new Notification Received -> Event class: GCMIntentService.OnNewNotificationCallback
  • On Notification Opened -> Event class: CustomNotificationReceiver.OnAckCallback
  • On Registration with GCM -> Event class: GCMIntentService.OnRegistrationCallback
  • On Unregistration from GCM -> Event class: GCMIntentService.OnUnregistrationCallback

Create your event classes by extending the specified event classes. The implemented event classes are set when the Notification module is initialized.

Notes

The notification content must be sent through GCM under a key called "message".

Useful Links

GCM - Client
GCM - Advanced
[GCM - Server](Server - Send: https://developer.android.com/google/gcm/http.html)
GCM - Server - Error Codes

Google Analytics Campaigns & Tracking

Note: This only work if the application is installed through the Google Play application.

Prepare your application

Edit AndroidManifest.xml

Add before "application" XML tag the required permissions:

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

And in "application" XML tag the following:

 <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
 <meta-data android:name="com.google.android.gms.analytics.globalConfigResource" android:resource="@xml/global_tracker"/>
 
 <service android:name="com.google.android.gms.analytics.CampaignTrackingService" />
    <receiver android:name="es.javocsoft.android.lib.toolbox.analytics.CustomCampaignTrackingReceiver" android:exported="true">
        <intent-filter>
            <action android:name="com.android.vending.INSTALL_REFERRER" />
    </intent-filter>
 </receiver>

Analytics Configuration files

First, create the Analytics global configuration file "global_tracker.xml" in a folder called "xml" (create it if does not exists) under your "res" folder. Paste the text bellow:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- 
	This is the global analytics configuration. Required.
	(it is set in the AndroidManifest.xml)
	
	See:
		https://developers.google.com/analytics/devguides/collection/android/v4/
		https://developer.android.com/reference/com/google/android/gms/analytics/GoogleAnalytics.html
    -->
 
    <integer name="ga_sessionTimeout">300</integer>
    <!-- Enable automatic Activity measurement -->
    <bool name="ga_autoActivityTracking">true</bool>
    <bool name="ga_reportUncaughtExceptions">true</bool>

    <!-- Frequency of automatic dispatch (in seconds). Default is 1800 -->
    <integer name="ga_dispatchPeriod">60</integer>
    <string name="ga_logLevel">verbose</string>
    <!-- Toggles dry run mode. In dry run mode, the normal code paths are executed 
	locally, but hits are not sent to Google Analytics servers. This is useful 
	for debugging calls to the Google Analytics SDK without polluting recorded data.
    -->
    <bool name="ga_dryRun">false</bool>

</resources>

Second, create the application Analytics configuration file "app_tracker.xml" in the same folder called "xml" (create it if does not exists) under your "res" folder. Paste the text bellow, replacing "ga_trackingId" value with your analytics tracking code:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--  The following value should be replaced with correct property id. -->
    <string name="ga_trackingId">UA-XXXXXXXX-X</string>

    <integer name="ga_sessionTimeout">300</integer>
    <!-- Enable automatic Activity measurement -->
    <bool name="ga_autoActivityTracking">true</bool>        

    <!-- The screen names that will appear in reports -->
    <screenName name="es.javocsoft.basetestapp.MainActivity">MainActivity</screenName>
</resources>

Generate your application campaign

You can generate a campaign URL by using the Google provide tool Google Campaign URL Tool

Testing

You can test easily if all the system is well mounted by executing from the shell or prompt:

 adb shell am broadcast
              -a com.android.vending.INSTALL_REFERRER
              -n es.javocsoft.basetestapp
                     /es.javocsoft.android.lib.toolbox.analytics.CustomCampaignTrackingReceiver
              --es "referrer" "utm_source%3Dfacebook%26utm_medium%3Dbanner%26utm_content%3Dbanner1%26utm_campaign%3DcampaignOne"

..if the test goes well an output like this should be seen:

Broadcasting: Intent { act=com.android.vending.INSTALL_REFERRER cmp=es.javocsoft.basetestapp/.CustomCampaignTrackingReceiver (has extras) }
Broadcast completed: result=0

Execute something when Campign data is received

You can also do something with the received campaign info. To do so, just create a class that extends CustomCampaignTrackingReceiver.OnProcessCampaignDataCallback and implement the class methods. Set an instance of your class in "onCreate()" method of your application main activity:

//Your analytics campaign custom class callback
AnalyticsCampaignCallback analyticsCampaignCallback = new AnalyticsCampaignCallback();
//Set your callback to the campaign analytics tracker.
CustomCampaignTrackingReceiver.onCampaignInfoReceivedCallback = analyticsCampaignCallback;

Tracking of application events in Analytics

If you declined to use the auto-tracking by setting "ga_autoActivityTracking" to FALSE, in the activity that you want to track, put in onStart():

 GoogleAnalytics.getInstance(this).reportActivityStart(this);

And finally, in onStop():

 GoogleAnalytics.getInstance(this).reportActivityStop(this);

Useful Links

Usage

Android Campaigns
Android Analytics

iOS Campaigns
iOS Analytics

How To Test
Analytics Tracker Class
Analytics Protocol Parameters Reference
Issues

Google Drive

Now your applications can store data in the Cloud through Google Drive storage. This is quite useful to be able for example, to recover the user configuration or some files if the application is unninstalled and installed again. Library also allows to store this data in the "application folder" in the cloud, a place that can not be tampered.

Set-Up the Drive module

1. Authorize your application to use Google Drive API

See Google Drive API AUTH to know how to achieve this step.

2. Get an instance of TBDrive Library Google Drive object

TBDrive is a singleton so save this call somewehere and call it whenever you need to do something with Google Drive. A goo place to get the instance of TBDrive could be in the onCreate() method of your activity.

TBDrive.getInstance(this, 
    new TBDriveConnectionFailureListener(getApplicationContext()), 
    new TBDriveConnectionListener(getApplicationContext()),
    new TBDriveConnectionSuspendedListener(getApplicationContext()));

The callbacks TBDriveConnectionFailureListener, TBDriveConnectionListener and TBDriveConnectionSuspendedListener extend TBDriveConnectionCallback. This allows to trigger something when connection is done, fails or is suspended. You can leave them null if not required but at least a TBDriveConnectionListener should be used to check if connection is successfully done or not.

3. Configure onPause()

Add to your activity onPause() a call to TBDrive.drive_onPause().

4. Configure your Activity's onActivityResult()

Add in your Activity's onActivityResult the call to TBDrive.drive_checkForResolutionResult(). This allows to log-in in Google Drive if the user still has not granted the access to the application to its Google Drive.

5. Connect to Google Drive

Call TBDrive.drive_connect() to connect with Google Drive. A good place could be onStart() of your activity. To disconnect from Drive use TBDrive.drive_disconnect().

Usage

There is a set of methods to operate with Google Drive. These methods can be done in a synchronous way or asynchrnously.

  • Synchronous, the call to a method does not end until the operation with Google Drive is done.
  • Asynchronous, the operation needs a callback. This callback will be triggered when operation finishes to let you do your stuff. See the package es.javocsoft.android.lib.toolbox.drive.callback for a list of available callbacks to implement.

The module allows you to:

  • Create a file (with the posibility of creating it in the secure application folder).
  • Search for files.
  • Get a file given its name or title
  • Get the file contents.
  • Pin a file (this makes Google Drive to store a copy of its metadata locally in the Device).
  • Trash/Un-trash a file.
  • Be notified when a file changes (see TBDrive#addFileChangeListener() and TBChangeListener.java)
  • Subscribe to file changes (notifications are received even if the application is not running. See TBDrive#subscribeToFile() and TBDriveEventService.java).
  • Commit/Discard changes to a file (also be notified when Google Drive servers finish applying changes, see TBDriveEventService.java)
  • Get file contents as an String.
  • Append/Replace text to a file.
  • Get a DriveFile object from an encoded DriveId string.

Notes

When subscribing to changes in a file or when we want Google Drive to notify us whenever finishes applying changes in the remote data, we need to add:

<service android:name=".TBDriveEventListener" android:exported="true">
    <intent-filter>
        <action android:name="com.google.android.gms.drive.events.HANDLE_EVENT"/>
    </intent-filter>
</service>

...where TBDriveEventListener is a class that must extends TBDriveEventService.java.

Useful Links

For more information, see:

Google Drive API
Google Drive API Reference

SMS Observer

The device must have telephony capabilities in order to be able to sent/receive SMSs.

This class allows to be able of doing something when a new SMS is received or sent. To use it, simply initialize it by using this piece of code:

Handler smsHandler = new Handler();      
if(ToolBox.device_isHardwareFeatureAvailable(this, PackageManager.FEATURE_TELEPHONY)){
    smsObserver = new SMSObserver(smsHandler, getApplicationContext(), <b>msgReceivedCallback</b>, <b>msgSentCallback</b>);              
    ContentResolver mContentResolver = getContentResolver();
    mContentResolver.registerContentObserver(Uri.parse("content:// sms/"),true, smsObserver);
}

The object msgReceivedCallback and msgSentCallback are classes that must implement SMSObserver.SMSRunnableTask class. Whenever a SMS is received/sent, these two classes will allow you to do something. SMSRunnableTask provides you with the SMS data and context.

CMT Info Helper

By using this class you will be able to get the owner company info for the given short-number. The information is obtained from the official CMT web page at http://www.cmt.es.

Usage

Get an instance of the class:

CMTInfoHelper cmtInfoHelper = CMTInfoHelper.getInstance(this, cmtInfoReceivedCallback);
//you can also get an instance that will keep updated the downloaded info from the CMT:  
//CMTInfoHelper cmtInfoHelper = CMTInfoHelper.getInstance(this, cmtInfoReceivedCallback, true, 1440);  

Once instantiated, it will download the information from the official CMT page. To get the number information just use:

Map<String,String> cmtSNInfo = cmtInfoHelper.getShortNumbersInformation();  
String company = cmtSNInfo.get("795XXX");  

The class saves in shared preferences the list, just in case no internet is not available when applications starts.

Media Scanner Notifier

This class allows notifying to Android about a new image so it can appear in the Gallery. To use simply:

 String res = "path to the resource";
 String filePath = getFileStreamPath(res).getAbsolutePath();
 new MediaScannerNotifier(context,filePath,"image/*", false);

Unzipper

Use this class to unzip a file.

Usage

String zipFile = Environment.getExternalStorageDirectory() + "/files.zip";
String unzipLocation = Environment.getExternalStorageDirectory() + "/unzipped/";  

//Unzip      
Unzipper d = new Unzipper(zipFile, unzipLocation, true);
d.unzip();

Android SQLite

Use this set of classes to work with SQLite in Android.

Usage

It is quite simple:

  1. Think about your schema and create classes extending base class DBTable.java for each table you have or need. Each class must have your table fields.
  2. Create a class extending DBSQLite class. This class will have your database creation DDL and database versions upgrade scripts.
  3. Create a class extending DBHelper.java. DBHelper class is a class that contains a set of common operations that you can use in your own extending class. Put in this class also any custom method for your needs.
  4. Initialize your database and open it.
  5. Do any usage of it and when you do not need it, close it.

Examples

A table class:

import es.javocsoft.android.lib.toolbox.db.DBTable;

public class Item extends DBTable {

public Integer id;
public String name;
public Integer inStore;
public Integer enabled;


public Item(String tableName) {
	super(tableName);		
}
}

Your database creation/upgrade class:

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import es.javocsoft.android.lib.toolbox.db.DBSQLite;

public class DatabaseSQLite extends DBSQLite {	

public DatabaseSQLite(Context context, String dbName, int dbVersion) {
	super(context, dbName, dbVersion);		
}	

@Override
public void doOnCreate(SQLiteDatabase db) {
	//TABLES
	db.execSQL("CREATE TABLE store (id INTEGER PRIMARY KEY, name VARCHAR(255), inStore INTEGER, enabled TINYINT(1))");
	
	//INDEXES
	db.execSQL("CREATE INDEX store_id_idx ON store (id)");
}

@Override
public void doOnUpgrade(SQLiteDatabase db) {
	// TODO Auto-generated method stub
	
}
}

Your database operations helper:

import android.database.Cursor;
import es.javocsoft.android.lib.toolbox.db.DBHelper;
import es.javocsoft.android.lib.toolbox.db.DBTable;

public class DatabaseHelper extends DBHelper<DatabaseSQLite> {

public DatabaseHelper(DatabaseSQLite mDbHelper) {
	super(mDbHelper);		
}

public int insertItem(Item item) {		
	return insert(item, null);
}

public Item getItem(int id){
	Item item = null;
	
	Cursor c = get("store", DBTable.getColumnsAsArray(Item.class), "id=" + id, null, null, null, null, null);
	if(c.moveToFirst()) {
		item = new Item("store");
		item.id = c.getInt(c.getColumnIndex("id"));
		item.name = c.getString(c.getColumnIndex("name"));
		item.inStore = c.getInt(c.getColumnIndex("inStore"));
		item.enabled = c.getInt(c.getColumnIndex("enabled"));
	}
	c.close();
	
	return item;
}
}

Once you have all required classes ready, use them like follows:

//Initialize your Database
DatabaseSQLite myDB = new DatabaseSQLite(getApplicationContext(), "MYDB", 1);
//Create/upgrade the database
DatabaseHelper mDatabaseHelper = new DatabaseHelper(myDB);

//Open it
mDatabaseHelper.open();

//Create a new item and insert it		
Item item1 = new Item("<table>");
item1.id=1;
item1.name="item_name_1";
item1.inStore=1;
item1.enabled=1;		
int res = mDatabaseHelper.insertItem(item1);

//Get an item from the database
Item i = mDatabaseHelper.getItem(1);

//Delete an item
res = mDatabaseHelper.deleteById("store", "id", 1);

//Check for the deleted item
i = mDatabaseHelper.getItem(1); //should return not found
		
//Create a new item
Item item2 = new Item("store");
item2.id=2;
item2.name="item_name_2";
item2.inStore=3;
item2.enabled=1;		
res = mDatabaseHelper.insertItem(item2);		

//Get the new item
i = mDatabaseHelper.getItem(2);

//Delete with conditions
res = mDatabaseHelper.deleteWhere("store", new String[]{"id"}, new String[]{"2"});

//Delete all of a table
res = mDatabaseHelper.deleteAll("store");

//Delete using an IN SQL clause.
//res = mDatabaseHelper.deleteIn("store", "id", new Integer[]{1,2});
	
mDatabaseHelper.close();

As you can see, it is quite easy.

Messenger

To connect with an application messenger service and send message use the class Mezzenger.java. To use it:

Mezzenger messenger = new Mezzenger("Inner", getApplicationContext());
messenger.connect("application.package.service.ACTION");
messenger.sendMessage(MSG_EVT_HI, 0, 0, null);

Messenger Service

To have a messenger service in your app follow these steps:

First create the messenger service message handler by extending the class MessengerIncomingHandler.java and implement the method "doWork(Message msg)" of MessengerIncomingHandler.java:

public class MyMessengerIncomingHandler extends MessengerIncomingHandler {
    //Messenger WHAT possible values
    public static final int MSG_EVT_HI = 1000;
    
    public MyMessengerIncomingHandler(Context context) {
        super(context);
    }

    @Override
    protected void doWork(Message msg) {
        switch (msg.what) {
            case MSG_EVT_HI:
                Log.i(Constants.TAG, "Messenger received: Hi!!");
                break;
            default:
                super.handleMessage(msg);
        }
    }
}

Second, create your messenger service by extending MessengerService.java and implement the method "getMessageIncomingHandler()":

public class MyMessengerService extends MessengerService {
    @Override
    protected MessengerIncomingHandler getMessageIncomingHandler() {
        return new MyMessengerIncomingHandler(context);
    }
}

And third, add the messenger service to your application AndroidManifest.xml.

<!-- Application messenger -->
<service android:name="your.application.package.messenger.MyMessengerService" 
         android:enabled="true" android:exported="true" >
    <intent-filter>
        <action android:name="your.application.package.service.messenger.ACTION_BIND" />
    </intent-filter>
</service>

Android 6+ Permissions handle

Description

Beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app. If the device is running Android 5.1 (API level 22) or lower, or the app's targetSdkVersion is 22 or lower, the system asks the user to grant the permissions at install time.

System permissions are divided into two categories, normal and dangerous (more info.):

  • Normal permissions do not directly risk the user's privacy. If your app lists a normal permission in its manifest, the system grants the permission automatically. See the list here.
  • Dangerous permissions can give the app access to the user's confidential data. If your app lists a normal permission in its manifest, the system grants the permission automatically. If you list a dangerous permission, the user has to explicitly give approval to your app. If an app requests a dangerous permission listed in its manifest, and the app already has another dangerous permission in the same permission group, the system immediately grants the permission without any interaction with the user.

See Google Developer Permissions to know more about it.

An application prepared for Android 6+ has to be able to deal with these use cases:

  • Start the App. Ask for permission (showing a message telling why do you need before ask for them)
    • Accept -> App runs normally with the service that requires the permissions
    • Not accept -> App runs normally without the service that requires the permissions
  • With previously granted permissions, start the App:
    • Start the App -> App runs normally with the service that requires the permissions
  • In a running App with granted permissions:
    • Deny the permissions -> When returning to the App, it Asks for permissions (showing a message telling why do you need before ask for them):
      • Accept -> App runs normally with the service that requires the permissions
      • Not accept -> App runs normally without the service that requires the permissions
  • User denies the permission and marks "Never ask again"
    • Start the App:
      • Show a message to the user about the required permissions (showing a message telling why do you need before ask for them)
        • Accept. Ask for permissions
          • Accept -> App runs normally with the service that requires the permissions
          • Not accept -> App runs normally without the service that requires the permissions
        • Cancel. App runs normally without the service that requires the permissions

Android < 6

Start the App:

  • Do not ask for permissions and runs normally

Usage

ToolBox provides methods to check for permissions allowing us to present to the user an informative dialog and also ask to allow them if they are not already granted. ToolBox uses the Android Support Library for backward Android compatibility.

The functions to handle permissions are:

  • permission_askFor
  • permission_checkAskPermissionsresult
  • permission_isGranted
  • permission_areGranted

ToolBox also provides a set of permissions packages according with Google permissions group. This makes easy for you to ask for permissions for a service that requires a set of permissions.

  • PERMISSION_CALENDAR
  • PERMISSION_CAMERA
  • PERMISSION_LOCATION
  • PERMISSION_MICROPHONE
  • PERMISSION_PHONE
  • PERMISSION_SENSORS
  • PERMISSION_SMS
  • PERMISSION_STORAGE

Here is an example of usage. In this case we are going to use the localization service. This service requires two permissions that are not automatically granted (not in Google NORMAL permissions group):

  • ACCESS_COARSE_LOCATION
  • ACCESS_FINE_LOCATION

ToolBox has these two permissions in the PERMISSION_LOCATION set. We will use this set to ask for permission to the user:

<!-- Before use it, we check if the permissions are already granted. -->
if(!ToolBox.permission_areGranted(TheActivity.this, ToolBox.PERMISSION_LOCATION.keySet())) {
    //Permissions not yet granted, we need to be ask.
    ToolBox.permission_askFor(TheActivity.this, ToolBox.PERMISSION_LOCATION,     
            ToolBox.PERMISSIONS_REQUEST_CODE_ASK_LOCATION, 
    	getString(R.string.permissions_required_title), 
       	getString(R.string.permissions_button_ok),
       	getString(R.string.permissions_button_deny),
       	getString(R.string.permissions_location_required_text),
            true,
            false,
       	null);
}else{
     //Permissions are already granted, continue using the localization service...
     
}

To handle the permissions ask response, we must do the following:

<!-- This method handles the response -->
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    permissionsLocationGranted = checkAskPermissionsresult(requestCode, permissions, grantResults);
    Log.i(Constants.TAG, "Location permissions granted? " + permissionsLocationGranted);
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    
    if(permissionsLocationGranted) {
        //Continue with the usage of the service that needs the permissions...
        
    }else{
        //Do something if required...
    }        
}

private boolean checkAskPermissionsresult(int requestCode, String[] permissions, int[] grantResults) {
	boolean res = false;   
    if(requestCode == ToolBox.PERMISSIONS_REQUEST_CODE_ASK_LOCATION) {
        //We could check the permissions in our system
	    //res = ToolBox.permission_areGranted(SplashActivity.this, Arrays.asList(permissions));    		
        //We check the returned result. Only returns TRUE if all permissions are granted.
 	    res = ToolBox.permission_checkAskPermissionsresult(permissions, grantResults);
	}
	return res;
}

Have in mind that every time you use a service o function that requires permissions, you should check the current permissions by using:

ToolBox.permission_areGranted(TheActivity.this, ToolBox.PERMISSION_LOCATION.keySet());

Location Service

Description

ToolBox provides a localization service (class) ready to use in a project by declaring it properly in your Androidmanifest.xml. Enabling it makes your application to be aware of any location change even if the application is not started. This is deprecated, use Google API client Location Services (see com.google.android.gms.location.LocationServices) with methods in ToolBox for Google API Client, see ToolBox#googleAPI_getApiClient(Context, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, Api, List) and so.

This service informs to an application about these events:

  • LOCATION_SERVICE_STARTED. Intent filter name: es.javocsoft.android.lib.toolbox.location.service.intent.action.LOCATION_SERVICE_STARTED
  • LOCATION_SERVICE_SHUTDOWN. Intent filter name: es.javocsoft.android.lib.toolbox.location.service.intent.action.LOCATION_SERVICE_SHUTDOWN
  • LOCATION_CHANGED. Intent filter name: es.javocsoft.android.lib.toolbox.location.service.intent.action.LOCATION_CHANGED
  • LOCATION_GPS_ENABLED. Intent filter name: es.javocsoft.android.lib.toolbox.location.service.intent.action.LOCATION_GPS_ENABLED
  • LOCATION_GPS_DISABLED. Intent filter name: es.javocsoft.android.lib.toolbox.location.service.intent.action.LOCATION_GPS_DISABLED

Usage

Just declare the localization service in your AndroidManifest.xml:

<service android:name="es.javocsoft.android.lib.toolbox.location.service.LocationService" 
         android:label="Location Service" android:enabled="true"/>

And launch it:

Intent mLocServiceIntent = new Intent(getBaseContext(), LocationService.class);
Bundle extras = new Bundle();
extras.putInt(LocationService.LOCATION_SERVICE_PARAM_MIN_DISTANCE, 2);
extras.putInt(LocationService.LOCATION_SERVICE_PARAM_MIN_TIME, 4000);
mLocServiceIntent.putExtras(extras);
startService(mLocServiceIntent);

And to stop it, when required:

Intent mLocServiceIntent = new Intent(getBaseContext(), LocationService.class);
stopService(mLocServiceIntent);

And implement in your application a receiver that listens for the desired events in order to react to them properly. For example:

public class LocationChangedReceiver extends WakefulBroadcastReceiver { 
    protected static String TAG = "LocationChangedReceiver"; 

    public LocationChangedReceiver() {}

    @Override
    public void onReceive(Context context, Intent intent) { 
        Bundle bundle = intent.getExtras(); 
        if(intent.getAction().equals(LocationService.ACTION_LOCATION_CHANGED)) { 
            //The ToolBox Location service leaves the location in the extras using
            //the key LocationService.LOCATION_KEY.
            Location location = bundle.getParcelable(LocationService.LOCATION_KEY);
            //Do something
        }else if(intent.getAction().equals(LocationService.ACTION_LOCATION_GPS_ENABLED)){
            //Do something
        }else if(intent.getAction().equals(LocationService.ACTION_LOCATION_GPS_DISABLED)){
            //Do something
        }else if(intent.getAction().equals(LocationService.ACTION_LOCATION_SERVICE_STARTED))
            //Do something 
        }else if(intent.getAction().equals(LocationService.ACTION_LOCATION_SERVICE_SHUTDOWN)){
            //Do something
        }
    }
}

Declaring it in your AndroidManifest.xml:

<receiver android:name="your_application_package.LocationReceiver" android:enabled="true" android:exported="false"/>
  <intent-filter>
      <action  android:name="es.javocsoft.android.lib.toolbox.location.service.intent.action.LOCATION_SERVICE_STARTED"/>
   <action android:name="es.javocsoft.android.lib.toolbox.location.service.intent.action.LOCATION_SERVICE_SHUTDOWN"/>
   <action android:name="es.javocsoft.android.lib.toolbox.location.service.intent.action.LOCATION_CHANGED"/>
   <action android:name="es.javocsoft.android.lib.toolbox.location.service.intent.action.LOCATION_GPS_DISABLED"/>
   <action android:name="es.javocsoft.android.lib.toolbox.location.service.intent.action.LOCATION_GPS_ENABLED"/>
  </intent-filter>
<receiver/>

Notes

  • The localization service can be customized when starting by setting:
    • Time between localization changes. Default is 4 seconds (4000 milliseconds).
    • Distance between localization changes. Default is 2 meters.
    To set these values, set them through the service starting intent by using these keys in the bundle:
    • LOCATION_SERVICE_PARAM_MIN_DISTANCE
    • LOCATION_SERVICE_PARAM_MIN_TIME
  • If service gets stopped, it will automatically run again.

Firebase

Firebase is the new Google platform for applications and services. It includes a variety of services. The library implements Cloud Messaging and Analytics.

Cloud Messaging

Replaces the old GCM

Configuration of the project in Firebase

1.- Go to the Firebase web console at https://console.firebase.google.com. Create an account if you do not have one and the project if is a new one or do not have yet none. 2.- Select the project. 2.- Add the application (iOS, Android) or the web service to the project. Follow the web instructions. 3.- Download the resulting JSON "google-services.json".

Configuration of the Android Project

4.- Open the application in the Android Studio framwork and select the "Project" view. 5.- Go to the "app" module folder and paste the downloaded JSON into it. Open the JSON and in the "client" section, delete other clients that are not the application we are configuring if there are more clients thant the current app (locate your client by looking for your application package). 6.- Return to the "Android" view. 7.- Configure the file "AndroidManifest.xml" adding the following in the "Application" section:

<!-- Firebase Notification Module (FCM) -->
<service android:name="es.javocsoft.android.lib.toolbox.firebase.core.FirebaseCustomTokenReceiverService">
    <intent-filter>
        <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
    </intent-filter>
</service>
<service android:name="es.javocsoft.android.lib.toolbox.firebase.core.FirebaseCustomNotificationReceiver">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT"/>
    </intent-filter>
</service>
<!-- Toucan Client  -->
<service android:name="es.javocsoft.android.lib.toucan.client.service.PendingOperationsDeliveryService" />
<!-- End Firebase Notification Module -->

8.- Initialize now the Firebase module:

8.1.- Create the **callback classes**; FCMRegCallback, FCMUnregCallback, FCMNotOpenCallback and FCMNotReceivedCallback. These will allow us to handle events such registration, un-registration, notification receival and opening events.

public class FCMRegCallback extends OnRegistrationCallback { 
    @Override
    protected void pre_task() {
        // TODO Auto-generated method stub
    }
    @Override
    protected void task() {
        Log.i(NotificationModule.TAG, "GCM Registered with GCM, RegId: " + getFirebaseRegistrationToken());					
        // TODO Auto-generated method stub
    }
    @Override
    protected void post_task() {
    // TODO Auto-generated method stub
    }
}
		
public class FCMUnregCallback extends OnUnregistrationCallback {
    @Override
    protected void pre_task() {
        // TODO Auto-generated method stub
    }
    @Override
    protected void task() {
        Log.i(NotificationModule.TAG, "Unregistered from GCM");
    }
    @Override
    protected void post_task() {
        // TODO Auto-generated method stub
    }
}

public class FCMNotReceivedCallback extends OnNewNotificationCallback {
    @Override
    protected void pre_task() {
        // TODO Auto-generated method stub
    }
    @Override
    protected void task() {
        Log.i(NotificationModule.TAG, "GCM Notification received.");										
    }
    @Override
    protected void post_task() {
        // TODO Auto-generated method stub
    }
}

public class FCMNotOpenCallback extends OnOpenAckCallback {
    @Override
    protected void pre_task() {
        // TODO Auto-generated method stub
    }
    @Override
    protected void task() {
        Log.i(NotificationModule.TAG, "GCM Notification opened.");										
    }
    @Override
    protected void post_task() {
        // TODO Auto-generated method stub
    }
}

8.2.- In the activity add the following:

private static NotificationModule notificationModule;
private static NotificationOpenedReceiver notReceiver;
//Callbacks
public static FCMRegCallback regCbk = new FCMRegCallback();
public static FCMNotOpenCallback openCbk = new FCMNotOpenCallback();
public static FCMUnregCallback unregCbk = new FCMUnregCallback();
public static FCMNotReceivedCallback newNotCbk = new FCMNotReceivedCallback();

/**
* Initializes the JavocSoft Toolbox Firebase notification module.
*
* @param context
* @param activityToCall        The activity that must be called once the user clicks in the notification.
* @param notificationTitle     The default notification title.
* @param multipleNotifications Set to TRUE to enable multiple notifications.
* @param groupNotificationsKey Multiple notifications are grouped by the specified notification group key.
* @param notBackgroundColor    The background color for the notification icon background.
* @param vibrate               Set to TRUE to enable vibration when notification arrives.
*/
public void initializeFirebaseNotificationsModule(Context context, Class activityToCall, String notificationTitle, boolean multipleNotifications, String groupNotificationsKey, String notBackgroundColor, boolean vibrate) {

EnvironmentType env = EnvironmentType.PRODUCTION;
boolean enableLog = false;
if(ApplicationBase.debugMode) { //A way to know if your application is in debug mode or not
    env = EnvironmentType.SANDBOX;
    enableLog = true;
}

notificationModule = NotificationModule.getInstance(context, env, regCbk, openCbk, unregCbk, newNotCbk,
					multipleNotifications, groupNotificationsKey, 						notBackgroundColor, vibrate, enableLog );

try {				                
    notificationModule.firebaseInitializeModule(context,null,notificationTitle,activityToCall);
    if(notOpenedReceiver==null) {
        notOpenedReceiver = new NotificationOpenedReceiver();
        context.registerReceiver(notOpenedReceiver, new IntentFilter(NotificationModule.NEW_NOTIFICATION_ACTION));
        Log.i(Constants.TAG, "FCM INIT: Notification opened receiver set to [" + NotificationModule.NEW_NOTIFICATION_ACTION + "] action.");
    }

    if(notificationModule.firebaseGetRegistrationToken(this)!=null) {
        Log.i(Constants.TAG, "FCM INIT: Token: " + notificationModule.firebaseGetRegistrationToken(this));
    }
} catch (FirebaseException e) {
    Log.e(Constants.TAG, "FCM INIT: Error initializing Firebase notification module (" + e.getMessage() + ").", e);
} catch (Exception e) {
    Log.e(Constants.TAG, "FCM INIT: Un-expected error initializing Firebase notification module (" + e.getMessage() +     ).", e);
}

}

8.3.- In the activity, in the "onCreate" method add:

//Initialize Firebase Notifications module
initializeFirebaseNotificationsModule(getApplicationContext(), MainActivity.class, ToolBox.application_nameInfo(getApplicationContext(), MainActivity.class.getPackage().getName()), false, "testingfirebase", "#000000", true, true);

8.4.- In the activity, in the "onDestroy" method add:

if(notOpenedReceiver!=null) {
    try {
        unregisterReceiver(notOpenedReceiver);
    }catch (Exception e){}
}

8.5.- In the activity, in the "onResume" method add:

notificationModule.firebaseCheckForNotificationReceival(this, getIntent());

8.6.- In the activity, in the "onNewIntent" method add::

notificationModule.firebaseCheckForNotificationReceival(this, intent);

That is it! Now you can test sending notifications though the Google Firebase console :)

Analytics

To use it, just add the following into your activity or application:

AnalyticsModule analyticsModule = new AnalyticsModule(this); //where "this" is your application context.

Job Service Scheduler Helper

Android Tasks execution

Before Android JobScheduler, we had AlarmManager API to schedule jobs, see android.app.AlarmManager which was present since Android version 1. Although significant changes and improvements were made to this API from version 19, to save battery of the device and boost the performance, the biggest drawback of Alarm Manager was that it solely works on the basis of time. To overcome this drawback, Google introduced JobScheduler ine API 21 which works on various conditions like availability of network, charging of device.

Why to use Job Scheduler?

Since Android Oreo (26+), what can be done and how is done in background has changed and there are limitations. As a resume:

  • Background Service Limitations: While an app is idle, there are limits to its use of background services. This does not apply to foreground services, which are more noticeable to the user.
  • Broadcast Limitations: With limited exceptions, apps cannot use their manifest to register for implicit broadcasts. They can still register for these broadcasts at runtime, and they can use the manifest to register for explicit broadcasts targeted specifically at their app.
In summary, you can only start background Services from something that is considered to be in the foreground - foreground is defined in the link below. These backgound execution limits do not affect Bound Services. If you declare a Bound Service, other components can bind to that service whether or not your app is in the foreground

These restrictions only apply to apps that target Android 8.0 (API level 26) or higher. However, users can enable most of these restrictions for any app from the Settings screen, even if the app targets an API level lower than 26.

In most cases, apps can work around these limitations by using JobScheduler jobs, available since Android 21+. This approach lets an app arrange to perform work when the app isn't actively running, but still gives the system the leeway to schedule these jobs in a way that doesn't affect the user experience

Android JobScheduler allows you to use background executions with less impact on system resources such as battery or memory, but this is not all. Before you use it, keep the followings in mind:

  • By default, JobService runs on the main thread. Therefore, if you need a relatively long runtime or complex operation, you must create a new thread or some other way to implement asynchronous behavior.
  • id is a important key for identifying your job. Whether you use a static id or a dynamic id, it depends on the scenario, but in most cases a static id is enough.
  • Changing any of the execution conditions would stop current Job.
  • More complex conditions of a job are make job harder to be executed and maintained . It is important to determine the appropriate scenario between the consumption of the battery and the frequency of its execution.
  • In addition, you should have a clear pause / resume scenarios to implement functionality like a transaction, or to be able to resume at the last point in progress, in case the job is canceled suddenly.
  • Job execution shares their life with Wakelock. The number of WakeLock and the time are critical value to calculate the battery drain for that app. This can result in a negative experience in the battery consumption that is monitored by the user if the job is eventually triggered too often

More info at see Background Execution Limits

About Android Doze and App Standby

Doze and App Standby manage the behavior of all apps running on Android 6.0 or higher, regardless whether they are specifically targeting API level 23. See more at Android Doze and App Standby.

ToolBox Job Service Scheduler Helper (JobServiceSchedulerHelper)

This helper makes easier the usage of scheduled job services, see android.app.job.JobService. You only have to implement JobServiceBase, customize the methods "onStart" and "onStop" to add your logic into the job and modify your AndroidManifest.xml. You can schedule recurrent jobs and/or one shoot jobs. Also, you can cancel all pending jobs or cancel an specific job.

So, as a good practice, you should start using Job Services, by using this helper, instead usual background services for devices with Android 21+ for any background task you could need but, what happens to devices with Android minor than API level 21? For tasks we should use ToolBox "Firebase Job Service Dispatcher Helper" and for services we have to use ToolBox "JobIntentServiceBase" instead Service or IntentService.

An example of usage of JobServiceSchedulerHelper:

1.- Prepare our custom scheduled Job Service events receiver handler. Initialize the Job Service Scheduler Helper.

   Handler jeh = new Handler(getApplicationContext().getMainLooper()){
        &#64;Override
        public void handleMessage(Message msg) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                switch (msg.what) {
                   case JobServiceSchedulerHelper.MSG_JOB_HANDLER_START:
                        Log.i("Job", "Custom Handler.START (jobId: " + (Integer)msg.obj + ")");
                        break;
                    case JobServiceSchedulerHelper.MSG_JOB_HANDLER_STOP:
                        Log.i("Job", "Custom Handler.STOP (jobId: " + (Integer)msg.obj + ")");
                        break;
                    }
                }
        }
    };
 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
       //Initialize the Job Executor Helper
       jobExecutorHelper = new JobServiceSchedulerHelper(getApplicationContext(), jeh);
    }

2.- Set your Job Service, extending {@link JobServiceBase} into your AndroidManifest.xml, in our example, "JobWork":

   <service android:name=".JobWork"
                android:permission="android.permission.BIND_JOB_SERVICE" />

3.- Add the WAKE_LOCK permission (for older Android devices) also in our AndroidManifest.xml:

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

4.- Now, you can start scheduling your Job Services, always implementing {@link JobServiceBase}:

   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        jobExecutorHelper.scheduleJob(1, null, null, null, null, null, null, null,
                                    null, null, JobWork.class, 10);
   }

And that is all! Your first Job Service scheduled for Android to run some background stuff in Android devices with API Level 21+!

Firebase Job Service Dispatcher Helper

Description

As an a more backwards compatibility Job scheduling solution, Google presented Firebase Job Dispatcher.

Until Firebase Job Dispatcher, most of us were familiar with JobScheduler API (ToolBox JobServiceSchedulerHelper), see android.app.job.JobScheduler, which was introduced in API Level 21. The disadvantage of Job Service Scheduler was that was available only for devices with Android 21+.

JobScheduler was good news for all the developers whose app have minimum SDK version set to 21 or greater as they can use this API directly. But what about the apps whose minimum SDK is less than 21?. If this is your case, there are a couple of options available to the developers:

  • GcmNetworkManager. Android bundled this module in Google Play services version 7.5. If your application is running on Lollipop or above, GcmNetworkManager will use the framework’s JobScheduler, so there is almost no difference between GcmNetworkManager and JobScheduler. For those platforms below Lollipop, this class will only work if the Google Play services is installed on the device and is having a version greater or equal to 7.5.
    Note: Google has stopped active development on this module and instead recommends using Firebase Job Dispatcher for scheduling any task.
  • Firebase Job Dispatcher. This library is a wrapper over GcmNetworkManager. But there is one big advantage it holds over GcmNetworkManager. If Google Play Services app is not installed or the app version is less than 7.5, then this library internally uses AlarmManager to schedule the tasks. This can work up to minimum SDK 9. Similar to GcmNetworkManager, this library uses the framework’s JobScheduler if the application is running on Lollipop and above.

About Android Doze and App Standby

Doze and App Standby manage the behavior of all apps running on Android 6.0 or higher, regardless whether they are specifically targeting API level 23. See more at Android Doze and App Standby.

More info about this library Google Firebase Library on GitHub GitHub firebase-jobdispatcher-android

ToolBox Firebase Job Dispatcher Helper (FirebaseJobDispatcherHelper)

This helper class makes easier scheduling Job Services with Firebase Job Dispatcher. Use this class to create scheduled recurrent/non-recurrent tasks. For using a common service instead of a Job, see ToolBox JobIntentServiceBase.So this class is a helper to make easier for you the usage of Firebase Job Dispatcher. Follow these steps in order to successfully schedule a job:

1.- Add the dependency to Firebase Job Dispatcher:

   implementation 'com.firebase:firebase-jobdispatcher:0.8.5'

2.- Create your Firebase Dispatcher Job Service, see {@link com.firebase.jobdispatcher.JobService}. Extracted from Google docs:

   import com.firebase.jobdispatcher.JobParameters;
   import com.firebase.jobdispatcher.JobService;
 
   public class MyJobService extends com.firebase.jobdispatcher.JobService {
       @Override
       //When your job is called by the framework, onStartJob() method will be invoked which runs
       //on the main thread. This method returns a boolean which tells the framework whether there
       //is more work remaining. We should consider offloading the code in onStartJob() to a new
       //thread and return true. Returning true here tells the framework that more work is
       //remaining. As soon as the job is completed we can call jobFinished() method which will
       //indicate that the work for this job cycle is completed. If the code involves only some
       //basic operations, then you can complete it in the main thread itself and returns false
       //which means no more work is remaining.
       public boolean onStartJob(JobParameters job) {
           // Do some work here
 
           return false; // Answers the question: "Is there still work going on?"
       }
       @Override
       //This method is called when your job is stopped by the framework. The job can be stopped
       //due to various reasons like the running constraints associated with the job are no longer
       //satisfied. The method returns a boolean variable which tells whether the job should be
       //tried again or not. If returned true, then the framework will put up this job again for
       //execution.
       public boolean onStopJob(JobParameters job) {
           return false; // Answers the question: "Should this job be retried?"
       }
    }

NOTE: When a job had been offloaded to a new thread and onStartJob() method returns true, when the job in the new thread completes, we need to call jobFinished(JobParameters params, boolean needsReschedule) method. Always make sure to inform the framework explicitly that the work for this job has been completed using jobFinished() method . If you fail to do so, the framework will consider the job as still running even if the job is not running.

3.- Add your Job Service to your application AndroidManifest.xml:

   <service
       android:exported="false"
       android:name=".MyJobService">
           <intent-filter>
               <action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE"/>
           </intent-filter>
   </service>

4.- Add the WAKE_LOCK permission also in our AndroidManifest.xml:

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

5.- Create your Firebase Job Dispatcher helper

   FirebaseJobDispatcherHelper taskDispatcher = new FirebaseJobDispatcherHelper(getApplicationContext());

6.- Schedule your Firebase Job Service:

   taskDispatcher.scheduleRecurrentJob(JobFCMWork.class, "fcmTask01", false,
				false,
				60,
				null,
				RetryStrategy.DEFAULT_EXPONENTIAL, null,
				null);

Base JobIntentService

This class "JobIntentServiceBase" implements JobIntentService, JobIntentService works in the same way as a Service or IntentService however, it enqueues the work into the JobScheduler on compatible Android targets, handling the compatibility for you if is not present.

If you want to avoid issues with background limitations since Android Oreo (v26+) with your current services, see android.app.Service, granting compatibility between new and older versions, do the following:

1.- Add dependencies. JobIntentService needs Google Maven repository so you have to add it in your project build.gradle:

    allprojects {
        repositories {
            jcenter ()
            maven {
                url "https://maven.google.com"
            }
        }
    }

and in your dependencies, at least v26 of compatibility support library is required:

   compile 'com.android.support:support-compat:26.0.0'

2.- Create/Modifiy your service, extending now JobIntentServiceBase and implements the methods JobIntentServiceBase#onStart() and JobIntentServiceBase#onStop() to put in them your service stuff.

3.- Declare your service in your AndroidManifest.xml setting in it the permission "android.permission.BIND_JOB_SERVICE":

   <service android:name=".ExampleJobIntentService"
            android:permission="android.permission.BIND_JOB_SERVICE" />

4.- For pre-Oreo devices, you will have to add WAKE_LOCK permission in your manifest.xml:

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

Now you can start service as usual, Android will decide how to run the service for you. Have in consideration that:

  • When running on anything less than Android Oreo, the service will start almost instantly. On Android Oreo it will be subject to JobScheduler policies, in other words, it will not run while the device is dozing and may get delayed more than an usual service when the system is under heavy load.
  • On pre Android Oreo, the service can run indefinitely but on Android Oreo it will adhere to the usual JobService execution type limits. At which point it will stop (not the process) and continue execution at a later time.

More info at JobIntentService

Fingerprint Helper

Prerequisites

Since Android Marshmallow, API Level 23, fingerprint authentication is available for any Android device with a fingerprint sensor.

Description

This helper class "FingerPrintAuthHelper" interacts with the fingerprint manager to perfom:

  • Authentication
  • Encryption of data with fingerprint authentication
  • Decryption of data with fingerprint authentication

Note: method used for encryption is AES. This is not the only option to encrypt, other methods exist. In this helper the data is encrypted and decrypted in the following manner:

Encryption:

  • User gives helper the desired non-encrypted data.
  • User is required to provide fingerprint.
  • Once authenticated, the helper obtains a key from the KeyStore and encrypts the data using a Cipher.
  • Data and IV salt (IV is recreated for every encryption and is not reused) are saved to shared preferences to be used later in the decryption process.

Decryption:

  • User requests to decrypt the data.
  • User is required to provide fingerprint.
  • The helper builds a Cipher using the IV and once user is authenticated, the KeyStore obtains a key from the KeyStore and deciphers the data.

Usage of the Fingerprint Helper (FingerPrintAuthHelper)

It is quite easy:

1.- Prepare the layout by adding to your activity the fingerprint informative dialog part that shows when asking for fingerprint:

   <RelativeLayout
         android:id="@+id/askFingerprint"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_centerHorizontal="true"
         android:layout_centerVertical="true"
         android:layout_margin="10dp"
         android:visibility="gone"
         android:background="@drawable/roundedcorners_layout">
 
         <ImageView
             android:id="@+id/imageViewFP"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginTop="10dp"
             android:src="@drawable/ic_fingerprint_black_48dp"
             android:layout_centerHorizontal="true"/>
 
         <TextView
             android:id="@+id/textViewFPInfo"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:layout_below="@+id/imageViewFP"
             android:layout_centerHorizontal="true"
             android:gravity="center_horizontal"
             android:layout_marginLeft="10dp"
             android:layout_marginRight="10dp"
             android:layout_marginBottom="10dp"
             android:text="@string/fingerprint_instructions"
             android:textAppearance="@android:style/TextAppearance.Medium" />
     </RelativeLayout>

You can find "ic_fingerprint_black_48dp" in ToolBox or by looking for material resources at Material resources.

2.- Now, declare the fingerprint informative layout to be ready in your whole activity class. Add the following on your activity "onCreate" event:

  askFingerprint = (RelativeLayout) findViewById(R.id.askFingerprint);

3.- Declare required fingerprint hardware and permissions in your AndroidManifest.xml:

   <uses-feature
     android:name="android.hardware.fingerprint"
     android:required="false" />
 
   <uses-permission android:name="android.permission.USE_FINGERPRINT" />

4.- Declare your cancellation signal, accessible from the whole activity:

   CancellationSignal cancellationSignal;

5.- Initialize the fingerprint helper class:

   //Initialize fingerprint reader helper
   FingerPrintAuthHelper fingerPrintAuthHelper = new FingerPrintAuthHelper(this);
   if (!fingerPrintAuthHelper.init()) {
       //If there was an error, show it
       ToolBox.toast_createCustomToast(getApplicationContext(),fingerPrintAuthHelper.getLastError(), 
             TolBox.TOAST_TYPE.ERROR, false);
   }

6.- Create your fingerprint events callback listener, "getAuthListener(boolean)":

    @NonNull
    private FingerPrintAuthHelper.Callback getAuthListener(final boolean isGetData) {
        return new FingerPrintAuthHelper.Callback() {
            @Override
            public void onSuccess(String result) {
                ToolBox.device_vibrate(getApplicationContext(), 50l);
                askFingerprint.setVisibility(View.GONE);
 
                if(result!=null && result.equals(FingerPrintAuthHelper.FP_AUTHORIZED)) {
                    //We wanted only authorization
                    // Set here your stuff
 
                }else{
                    if (isGetData) { //Encrypted data get
                        //Your stuff with decrypted data
                    } else { //Data was encrypted.
                        //Your stuff once encrypted
                    }
                }
            }
 
            @Override
            public void onFailure(String message) {
                ToolBox.toast_createCustomToast(getApplicationContext(), message, ToolBox.TOAST_TYPE.ERROR, false);
 
                //Your stuff here
            }
 
            @Override
            public void onHelp(int helpCode, String helpString) {
                ToolBox.toast_createCustomToast(getApplicationContext(), helpCode + ": (" + helpString + ")", 
                      TolBox.TOAST_TYPE.ERROR, false);
 
                //Your stuff here
            }
        };
    }

7.- Now you can start using the fingerprint helper class:

To just authenticate with your fingerprint:

     //Show fingerprint informative dialog
     askFingerprint.setVisibility(View.VISIBLE);
     //Create your cancellation signal to be able to abort fingerprint scanner normally.
     cancellationSignal = new CancellationSignal();
     //Ask for fingerprint authentication
     fingerPrintAuthHelper.auth(cancellationSignal, getAuthListener(false));

To encrypt and save some data:

     //Show fingerprint informative dialog
     askFingerprint.setVisibility(View.VISIBLE);
     //Create your cancellation signal to be able to abort fingerprint scanner normally.
     cancellationSignal = new CancellationSignal();
     //Ask for fingerprint encryption
     fingerPrintAuthHelper.saveData(String, cancellationSignal, getAuthListener(false));

To recover some encrypted data:

     //Show fingerprint informative dialog
     askFingerprint.setVisibility(View.VISIBLE);
     //Create your cancellation signal to be able to abort fingerprint scanner normally.
     cancellationSignal = new CancellationSignal();
     //Ask for fingerprint decryption
     fingerPrintAuthHelper.getData(cancellationSignal, getAuthListener(true));

8.- You can always cancel fingerprint authentication, just use the declared cancellation signal in your activity "onKeyDown" / "onBackPressed" events as follows:

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event)  {
        if (Integer.parseInt(android.os.Build.VERSION.SDK) &#60; 5
            && keyCode == KeyEvent.KEYCODE_BACK
            && event.getRepeatCount() == 0) {
            onBackPressed();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
 
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public void onBackPressed() {
        if(cancellationSignal!=null && !cancellationSignal.isCanceled()){
            cancellationSignal.cancel();
            cancellationSignal = null;
 
            //Hide fingerprint ask layout
            askFingerprint.setVisibility(View.GONE);
        }
    }

See also:

Material Fingerprint resources
Android 6

Clone this wiki locally