Home
#Index#
- ToolBox
- Facebook:
- Google Play Services Modules:
- SMS related:
- SMS Observer - SMSObserver.java. Allows to be aware of sent/received SMS messages.
- CMT Short-number Information - CMTShortNumberInformation.java. Allows to know if a short number or text comes from a Premium/Subscription service and its price.
- CMT Info Helper - CMTInfoHelper.java. Allows to know the owner company of a given short-number.
- Media Scanner Notifier - MediaScannerNotifier.java. Allows to tell Android about an image so it appears in the gallery.
- Unzipper - Unzipper.java. Allows to unzip a zipped file.
- Android SQLite - Allows to use SQLite in your applications.
- Message channel
- Android 6+ Permissions handle
- Location Service - Deprecated, use ToolBox#googleAPI_getApiClient()
- Firebase
- Background Job Scheduling Helpers
- Fingerprint Helper
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 Login
- [Facebook - Getting Started](https://developers.facebook.com/docs/android/getting -started/)
- Facebook Permissions
Create your Application in the Facebook developer portal at https://developers.facebook.com/, configure it properly. Save the facebook application id.
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"/>
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/.
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" />
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.
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:
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" />
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);
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.
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 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 );
}
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"
First, put in your onCreate() method:
FragmentManager fragmentManager = getSupportFragmentManager();
ads = (AdFragment) fragmentManager.findFragmentById(R.id.adFragment);
And finally, in your onResume() method:
ads.showAds();
Simply, use::
ads.hideAds();
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);
You can use the "ca-app-pub-3940256099942544/6300978111" ads unit id just for testing.
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);
You can use the "ca-app-pub-3940256099942544/5224354917" ads unit id just for testing.
You must follow the following steps:
- Configure your application in Google Cloud Console
- Create a project for your application .
- Activate the Google cloud Messaging.
- Generate Keys to send notifications from your server.
- Prepare your application.
- Prepare AndroidManifest.xml.
- Prepare your code.
- 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..
- Go to http://console.developers.google.com and log in. Locate the "Create Project" button and press it to create a new one.
- Set your project name in the window that appears and press "Create" button. Now Google will process the request, it can take some time.
When Google Cloud Console finishes, it will show a verification mark in the bottom right corner.
- Save the "Project Number" number somewhere. This number will be used in your Android application to tell it from which apps can receive notifications.
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.
These keys will allow us to send notifications to our Apps by using the Google Cloud Messaging API. To get this key:
- Go to "APIs & auth" -> "Credentials" and press the button "Create new key".
- Select "Server key" if we are going to send notification within a server application, that is our case.
- 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.
- Once done, we shoukd see a new informative box like this:
Save the API KEY somewhere. This is your key for sending notifications.
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.
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.
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.
The notification content must be sent through GCM under a key called "message".
GCM - Client
GCM - Advanced
[GCM - Server](Server - Send: https://developer.android.com/google/gcm/http.html)
GCM - Server - Error Codes
Note: This only work if the application is installed through the Google Play application.
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>
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>
You can generate a campaign URL by using the Google provide tool Google Campaign URL Tool
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
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;
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);
Android Campaigns
Android Analytics
How To Test
Analytics Tracker Class
Analytics Protocol Parameters Reference
Issues
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.
See Google Drive API AUTH to know how to achieve this step.
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.
Add to your activity onPause() a call to TBDrive.drive_onPause().
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.
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().
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.
For more information, see:
Google Drive API
Google Drive API Reference
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.
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.
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.
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);
Use this class to unzip a file.
String zipFile = Environment.getExternalStorageDirectory() + "/files.zip";
String unzipLocation = Environment.getExternalStorageDirectory() + "/unzipped/";
//Unzip
Unzipper d = new Unzipper(zipFile, unzipLocation, true);
d.unzip();
Use this set of classes to work with SQLite in Android.
It is quite simple:
- 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.
- Create a class extending DBSQLite class. This class will have your database creation DDL and database versions upgrade scripts.
- 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.
- Initialize your database and open it.
- Do any usage of it and when you do not need it, close it.
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.
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);
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>
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
- Deny the permissions -> When returning to the App, it Asks for permissions (showing a message telling why do you need before ask for them):
- 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
- Accept. Ask for permissions
- Show a message to the user about the required permissions (showing a message telling why do you need before ask for them)
- Start the App:
Android < 6
Start the App:
- Do not ask for permissions and runs normally
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());
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
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.
- LOCATION_SERVICE_PARAM_MIN_DISTANCE
- LOCATION_SERVICE_PARAM_MIN_TIME
- If service gets stopped, it will automatically run again.
Firebase is the new Google platform for applications and services. It includes a variety of services. The library implements Cloud Messaging and Analytics.
Replaces the old GCM
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".
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 :)
To use it, just add the following into your activity or application:
AnalyticsModule analyticsModule = new AnalyticsModule(this); //where "this" is your application context.
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.
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.
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
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.
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()){
@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+!
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.
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
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);
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
Since Android Marshmallow, API Level 23, fingerprint authentication is available for any Android device with a fingerprint sensor.
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.
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) < 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