package me.leolin.shortcutbadger.impl;
import android.content.AsyncQueryHandler;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ProviderInfo;
import android.net.Uri;
import android.os.Looper;
import java.util.Arrays;
import java.util.List;
import me.leolin.shortcutbadger.Badger;
import me.leolin.shortcutbadger.ShortcutBadgeException;
/**
* @author Leo Lin
*/
public class SonyHomeBadger implements Badger {
private static final String INTENT_ACTION = "com.sonyericsson.home.action.UPDATE_BADGE";
private static final String INTENT_EXTRA_PACKAGE_NAME = "com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME";
private static final String INTENT_EXTRA_ACTIVITY_NAME = "com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME";
private static final String INTENT_EXTRA_MESSAGE = "com.sonyericsson.home.intent.extra.badge.MESSAGE";
private static final String INTENT_EXTRA_SHOW_MESSAGE = "com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE";
private static final String PROVIDER_CONTENT_URI = "content://com.sonymobile.home.resourceprovider/badge";
private static final String PROVIDER_COLUMNS_BADGE_COUNT = "badge_count";
private static final String PROVIDER_COLUMNS_PACKAGE_NAME = "package_name";
private static final String PROVIDER_COLUMNS_ACTIVITY_NAME = "activity_name";
private static final String SONY_HOME_PROVIDER_NAME = "com.sonymobile.home.resourceprovider";
private final Uri BADGE_CONTENT_URI = Uri.parse(PROVIDER_CONTENT_URI);
private AsyncQueryHandler mQueryHandler;
@Override
public void executeBadge(Context context, ComponentName componentName,
int badgeCount) throws ShortcutBadgeException {
if (sonyBadgeContentProviderExists(context)) {
executeBadgeByContentProvider(context, componentName, badgeCount);
} else {
executeBadgeByBroadcast(context, componentName, badgeCount);
}
}
@Override
public List<String> getSupportLaunchers() {
return Arrays.asList("com.sonyericsson.home", "com.sonymobile.home");
}
private static void executeBadgeByBroadcast(Context context, ComponentName componentName,
int badgeCount) {
Intent intent = new Intent(INTENT_ACTION);
intent.putExtra(INTENT_EXTRA_PACKAGE_NAME, componentName.getPackageName());
intent.putExtra(INTENT_EXTRA_ACTIVITY_NAME, componentName.getClassName());
intent.putExtra(INTENT_EXTRA_MESSAGE, String.valueOf(badgeCount));
intent.putExtra(INTENT_EXTRA_SHOW_MESSAGE, badgeCount > 0);
context.sendBroadcast(intent);
}
/**
* Send request to Sony badge content provider to set badge in Sony home launcher.
*
* @param context the context to use
* @param componentName the componentName to use
* @param badgeCount the badge count
*/
private void executeBadgeByContentProvider(Context context, ComponentName componentName,
int badgeCount) {
if (badgeCount < 0) {
return;
}
final ContentValues contentValues = createContentValues(badgeCount, componentName);
if (Looper.myLooper() == Looper.getMainLooper()) {
// We're in the main thread. Let's ensure the badge update happens in a background
// thread by using an AsyncQueryHandler and an async update.
if (mQueryHandler == null) {
mQueryHandler = new AsyncQueryHandler(
context.getApplicationContext().getContentResolver()) {
};
}
insertBadgeAsync(contentValues);
} else {
// Already in a background thread. Let's update the badge synchronously. Otherwise,
// if we use the AsyncQueryHandler, this thread may already be dead by the time the
// async execution finishes, which will lead to an IllegalStateException.
insertBadgeSync(context, contentValues);
}
}
/**
* Asynchronously inserts the badge counter.
*
* @param contentValues Content values containing the badge count, package and activity names
*/
private void insertBadgeAsync(final ContentValues contentValues) {
mQueryHandler.startInsert(0, null, BADGE_CONTENT_URI, contentValues);
}
/**
* Synchronously inserts the badge counter.
*
* @param context Caller context
* @param contentValues Content values containing the badge count, package and activity names
*/
private void insertBadgeSync(final Context context, final ContentValues contentValues) {
context.getApplicationContext().getContentResolver()
.insert(BADGE_CONTENT_URI, contentValues);
}
/**
* Creates a ContentValues object to be used in the badge counter update. The package and
* activity names must correspond to an activity that holds an intent filter with action
* "android.intent.action.MAIN" and category android.intent.category.LAUNCHER" in the manifest.
* Also, it is not allowed to publish badges on behalf of another client, so the package and
* activity names must belong to the process from which the insert is made.
* To be able to insert badges, the app must have the PROVIDER_INSERT_BADGE
* permission in the manifest file. In case these conditions are not
* fulfilled, or any content values are missing, there will be an unhandled
* exception on the background thread.
*
* @param badgeCount the badge count
* @param componentName the component name from which package and class name will be extracted
*
*/
private ContentValues createContentValues(final int badgeCount,
final ComponentName componentName) {
final ContentValues contentValues = new ContentValues();
contentValues.put(PROVIDER_COLUMNS_BADGE_COUNT, badgeCount);
contentValues.put(PROVIDER_COLUMNS_PACKAGE_NAME, componentName.getPackageName());
contentValues.put(PROVIDER_COLUMNS_ACTIVITY_NAME, componentName.getClassName());
return contentValues;
}
/**
* Check if the latest Sony badge content provider exists .
*
* @param context the context to use
* @return true if Sony badge content provider exists, otherwise false.
*/
private static boolean sonyBadgeContentProviderExists(Context context) {
boolean exists = false;
ProviderInfo info = context.getPackageManager().resolveContentProvider(SONY_HOME_PROVIDER_NAME, 0);
if (info != null) {
exists = true;
}
return exists;
}
}