diff --git a/app/build.gradle b/app/build.gradle
index 0e98d86f32..9e1b2ce64e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -639,7 +639,7 @@ dependencies {
// https://github.com/leolin310148/ShortcutBadger
// https://mvnrepository.com/artifact/me.leolin/ShortcutBadger
- implementation "me.leolin:ShortcutBadger:$badge_version"
+ //implementation "me.leolin:ShortcutBadger:$badge_version"
// https://github.com/bugsnag/bugsnag-android
// https://mvnrepository.com/artifact/com.bugsnag/bugsnag-android
diff --git a/app/src/amazon/AndroidManifest.xml b/app/src/amazon/AndroidManifest.xml
index dfb98bc7f6..cf7475ae97 100644
--- a/app/src/amazon/AndroidManifest.xml
+++ b/app/src/amazon/AndroidManifest.xml
@@ -30,6 +30,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
getSupportLaunchers();
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/ShortcutBadgeException.java b/app/src/main/java/me/leolin/shortcutbadger/ShortcutBadgeException.java
new file mode 100644
index 0000000000..148e82d712
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/ShortcutBadgeException.java
@@ -0,0 +1,12 @@
+package me.leolin.shortcutbadger;
+
+public class ShortcutBadgeException extends Exception {
+ public ShortcutBadgeException(String message) {
+ super(message);
+ }
+
+ public ShortcutBadgeException(String message, Exception e) {
+ super(message, e);
+ }
+
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/ShortcutBadger.java b/app/src/main/java/me/leolin/shortcutbadger/ShortcutBadger.java
new file mode 100644
index 0000000000..fa0ffa11a1
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/ShortcutBadger.java
@@ -0,0 +1,280 @@
+package me.leolin.shortcutbadger;
+
+import android.app.Notification;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Build;
+import android.util.Log;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import me.leolin.shortcutbadger.impl.AdwHomeBadger;
+import me.leolin.shortcutbadger.impl.ApexHomeBadger;
+import me.leolin.shortcutbadger.impl.AsusHomeBadger;
+import me.leolin.shortcutbadger.impl.DefaultBadger;
+import me.leolin.shortcutbadger.impl.EverythingMeHomeBadger;
+import me.leolin.shortcutbadger.impl.HuaweiHomeBadger;
+import me.leolin.shortcutbadger.impl.NewHtcHomeBadger;
+import me.leolin.shortcutbadger.impl.NovaHomeBadger;
+import me.leolin.shortcutbadger.impl.OPPOHomeBader;
+import me.leolin.shortcutbadger.impl.SamsungHomeBadger;
+import me.leolin.shortcutbadger.impl.SonyHomeBadger;
+import me.leolin.shortcutbadger.impl.VivoHomeBadger;
+import me.leolin.shortcutbadger.impl.YandexLauncherBadger;
+import me.leolin.shortcutbadger.impl.ZTEHomeBadger;
+import me.leolin.shortcutbadger.impl.ZukHomeBadger;
+
+
+/**
+ * @author Leo Lin
+ */
+public final class ShortcutBadger {
+
+ private static final String LOG_TAG = "ShortcutBadger";
+ private static final int SUPPORTED_CHECK_ATTEMPTS = 3;
+
+ private static final List> BADGERS = new LinkedList>();
+
+ private volatile static Boolean sIsBadgeCounterSupported;
+ private final static Object sCounterSupportedLock = new Object();
+
+ static {
+ BADGERS.add(AdwHomeBadger.class);
+ BADGERS.add(ApexHomeBadger.class);
+ BADGERS.add(DefaultBadger.class);
+ BADGERS.add(NewHtcHomeBadger.class);
+ BADGERS.add(NovaHomeBadger.class);
+ BADGERS.add(SonyHomeBadger.class);
+ BADGERS.add(AsusHomeBadger.class);
+ BADGERS.add(HuaweiHomeBadger.class);
+ BADGERS.add(OPPOHomeBader.class);
+ BADGERS.add(SamsungHomeBadger.class);
+ BADGERS.add(ZukHomeBadger.class);
+ BADGERS.add(VivoHomeBadger.class);
+ BADGERS.add(ZTEHomeBadger.class);
+ BADGERS.add(EverythingMeHomeBadger.class);
+ BADGERS.add(YandexLauncherBadger.class);
+ }
+
+ private static Badger sShortcutBadger;
+ private static ComponentName sComponentName;
+
+ /**
+ * Tries to update the notification count
+ *
+ * @param context Caller context
+ * @param badgeCount Desired badge count
+ * @return true in case of success, false otherwise
+ */
+ public static boolean applyCount(Context context, int badgeCount) {
+ try {
+ applyCountOrThrow(context, badgeCount);
+ return true;
+ } catch (ShortcutBadgeException e) {
+ if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
+ Log.d(LOG_TAG, "Unable to execute badge", e);
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Tries to update the notification count, throw a {@link ShortcutBadgeException} if it fails
+ *
+ * @param context Caller context
+ * @param badgeCount Desired badge count
+ */
+ public static void applyCountOrThrow(Context context, int badgeCount) throws ShortcutBadgeException {
+ if (sShortcutBadger == null) {
+ boolean launcherReady = initBadger(context);
+
+ if (!launcherReady)
+ throw new ShortcutBadgeException("No default launcher available");
+ }
+
+ try {
+ sShortcutBadger.executeBadge(context, sComponentName, badgeCount);
+ } catch (Exception e) {
+ throw new ShortcutBadgeException("Unable to execute badge", e);
+ }
+ }
+
+ /**
+ * Tries to remove the notification count
+ *
+ * @param context Caller context
+ * @return true in case of success, false otherwise
+ */
+ public static boolean removeCount(Context context) {
+ return applyCount(context, 0);
+ }
+
+ /**
+ * Tries to remove the notification count, throw a {@link ShortcutBadgeException} if it fails
+ *
+ * @param context Caller context
+ */
+ public static void removeCountOrThrow(Context context) throws ShortcutBadgeException {
+ applyCountOrThrow(context, 0);
+ }
+
+ /**
+ * Whether this platform launcher supports shortcut badges. Doing this check causes the side
+ * effect of resetting the counter if it's supported, so this method should be followed by
+ * a call that actually sets the counter to the desired value, if the counter is supported.
+ */
+ public static boolean isBadgeCounterSupported(Context context) {
+ // Checking outside synchronized block to avoid synchronization in the common case (flag
+ // already set), and improve perf.
+ if (sIsBadgeCounterSupported == null) {
+ synchronized (sCounterSupportedLock) {
+ // Checking again inside synch block to avoid setting the flag twice.
+ if (sIsBadgeCounterSupported == null) {
+ String lastErrorMessage = null;
+ for (int i = 0; i < SUPPORTED_CHECK_ATTEMPTS; i++) {
+ try {
+ Log.i(LOG_TAG, "Checking if platform supports badge counters, attempt "
+ + String.format("%d/%d.", i + 1, SUPPORTED_CHECK_ATTEMPTS));
+ if (initBadger(context)) {
+ sShortcutBadger.executeBadge(context, sComponentName, 0);
+ sIsBadgeCounterSupported = true;
+ Log.i(LOG_TAG, "Badge counter is supported in this platform.");
+ break;
+ } else {
+ lastErrorMessage = "Failed to initialize the badge counter.";
+ }
+ } catch (Exception e) {
+ // Keep retrying as long as we can. No need to dump the stack trace here
+ // because this error will be the norm, not exception, for unsupported
+ // platforms. So we just save the last error message to display later.
+ lastErrorMessage = e.getMessage();
+ }
+ }
+
+ if (sIsBadgeCounterSupported == null) {
+ Log.w(LOG_TAG, "Badge counter seems not supported for this platform: "
+ + lastErrorMessage);
+ sIsBadgeCounterSupported = false;
+ }
+ }
+ }
+ }
+ return sIsBadgeCounterSupported;
+ }
+
+ /**
+ * @param context Caller context
+ * @param notification
+ * @param badgeCount
+ */
+ public static void applyNotification(Context context, Notification notification, int badgeCount) {
+ if (Build.MANUFACTURER.equalsIgnoreCase("Xiaomi")) {
+ try {
+ Field field = notification.getClass().getDeclaredField("extraNotification");
+ Object extraNotification = field.get(notification);
+ Method method = extraNotification.getClass().getDeclaredMethod("setMessageCount", int.class);
+ method.invoke(extraNotification, badgeCount);
+ } catch (Exception e) {
+ if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
+ Log.d(LOG_TAG, "Unable to execute badge", e);
+ }
+ }
+ }
+ }
+
+ // Initialize Badger if a launcher is availalble (eg. set as default on the device)
+ // Returns true if a launcher is available, in this case, the Badger will be set and sShortcutBadger will be non null.
+ private static boolean initBadger(Context context) {
+ Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
+ if (launchIntent == null) {
+ Log.e(LOG_TAG, "Unable to find launch intent for package " + context.getPackageName());
+ return false;
+ }
+
+ sComponentName = launchIntent.getComponent();
+
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_HOME);
+ List resolveInfos = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+
+ //Turns out framework does not guarantee to put DEFAULT Activity on top of the list.
+ ResolveInfo resolveInfoDefault = context.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ validateInfoList(resolveInfoDefault, resolveInfos);
+
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ String currentHomePackage = resolveInfo.activityInfo.packageName;
+
+ for (Class extends Badger> badger : BADGERS) {
+ Badger shortcutBadger = null;
+ try {
+ shortcutBadger = badger.newInstance();
+ } catch (Exception ignored) {
+ }
+ if (shortcutBadger != null && shortcutBadger.getSupportLaunchers().contains(currentHomePackage)) {
+ if (isLauncherVersionSupported(context, currentHomePackage)) {
+ sShortcutBadger = shortcutBadger;
+ }
+ break;
+ }
+ }
+ if (sShortcutBadger != null) {
+ break;
+ }
+ }
+
+ if (sShortcutBadger == null) {
+ if (Build.MANUFACTURER.equalsIgnoreCase("ZUK"))
+ sShortcutBadger = new ZukHomeBadger();
+ else if (Build.MANUFACTURER.equalsIgnoreCase("OPPO"))
+ sShortcutBadger = new OPPOHomeBader();
+ else if (Build.MANUFACTURER.equalsIgnoreCase("VIVO"))
+ sShortcutBadger = new VivoHomeBadger();
+ else if (Build.MANUFACTURER.equalsIgnoreCase("ZTE"))
+ sShortcutBadger = new ZTEHomeBadger();
+ else
+ sShortcutBadger = new DefaultBadger();
+ }
+
+ return true;
+ }
+
+ /**
+ * Making sure that launcher version that yet doesn't support badges mechanism
+ * is NOT used by sShortcutBadger.
+ */
+ private static boolean isLauncherVersionSupported(Context context, String currentHomePackage) {
+ if (!YandexLauncherBadger.PACKAGE_NAME.equals(currentHomePackage)) {
+ return true;
+ }
+ return YandexLauncherBadger.isVersionSupported(context);
+ }
+
+ /**
+ * Making sure the default Home activity is on top of the returned list
+ * @param defaultActivity default Home activity
+ * @param resolveInfos list of all Home activities in the system
+ */
+ private static void validateInfoList(ResolveInfo defaultActivity, List resolveInfos) {
+ int indexToSwapWith = 0;
+ for (int i = 0, resolveInfosSize = resolveInfos.size(); i < resolveInfosSize; i++) {
+ ResolveInfo resolveInfo = resolveInfos.get(i);
+ String currentActivityName = resolveInfo.activityInfo.packageName;
+ if (currentActivityName.equals(defaultActivity.activityInfo.packageName)) {
+ indexToSwapWith = i;
+ }
+ }
+ Collections.swap(resolveInfos, 0, indexToSwapWith);
+ }
+
+ // Avoid anybody to instantiate this class
+ private ShortcutBadger() {
+
+ }
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/impl/AdwHomeBadger.java b/app/src/main/java/me/leolin/shortcutbadger/impl/AdwHomeBadger.java
new file mode 100644
index 0000000000..5754930fd5
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/impl/AdwHomeBadger.java
@@ -0,0 +1,41 @@
+package me.leolin.shortcutbadger.impl;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+import java.util.Arrays;
+import java.util.List;
+
+import me.leolin.shortcutbadger.Badger;
+import me.leolin.shortcutbadger.ShortcutBadgeException;
+import me.leolin.shortcutbadger.util.BroadcastHelper;
+
+/**
+ * @author Gernot Pansy
+ */
+public class AdwHomeBadger implements Badger {
+
+ public static final String INTENT_UPDATE_COUNTER = "org.adw.launcher.counter.SEND";
+ public static final String PACKAGENAME = "PNAME";
+ public static final String CLASSNAME = "CNAME";
+ public static final String COUNT = "COUNT";
+
+ @Override
+ public void executeBadge(Context context, ComponentName componentName, int badgeCount) throws ShortcutBadgeException {
+ Intent intent = new Intent(INTENT_UPDATE_COUNTER);
+ intent.putExtra(PACKAGENAME, componentName.getPackageName());
+ intent.putExtra(CLASSNAME, componentName.getClassName());
+ intent.putExtra(COUNT, badgeCount);
+
+ BroadcastHelper.sendIntentExplicitly(context, intent);
+ }
+
+ @Override
+ public List getSupportLaunchers() {
+ return Arrays.asList(
+ "org.adw.launcher",
+ "org.adwfreak.launcher"
+ );
+ }
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/impl/ApexHomeBadger.java b/app/src/main/java/me/leolin/shortcutbadger/impl/ApexHomeBadger.java
new file mode 100644
index 0000000000..eb62c28727
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/impl/ApexHomeBadger.java
@@ -0,0 +1,38 @@
+package me.leolin.shortcutbadger.impl;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+import java.util.Arrays;
+import java.util.List;
+
+import me.leolin.shortcutbadger.Badger;
+import me.leolin.shortcutbadger.ShortcutBadgeException;
+import me.leolin.shortcutbadger.util.BroadcastHelper;
+
+/**
+ * @author Gernot Pansy
+ */
+public class ApexHomeBadger implements Badger {
+
+ private static final String INTENT_UPDATE_COUNTER = "com.anddoes.launcher.COUNTER_CHANGED";
+ private static final String PACKAGENAME = "package";
+ private static final String COUNT = "count";
+ private static final String CLASS = "class";
+
+ @Override
+ public void executeBadge(Context context, ComponentName componentName, int badgeCount) throws ShortcutBadgeException {
+ Intent intent = new Intent(INTENT_UPDATE_COUNTER);
+ intent.putExtra(PACKAGENAME, componentName.getPackageName());
+ intent.putExtra(COUNT, badgeCount);
+ intent.putExtra(CLASS, componentName.getClassName());
+
+ BroadcastHelper.sendIntentExplicitly(context, intent);
+ }
+
+ @Override
+ public List getSupportLaunchers() {
+ return Arrays.asList("com.anddoes.launcher");
+ }
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/impl/AsusHomeBadger.java b/app/src/main/java/me/leolin/shortcutbadger/impl/AsusHomeBadger.java
new file mode 100644
index 0000000000..7fcd1e5631
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/impl/AsusHomeBadger.java
@@ -0,0 +1,172 @@
+package me.leolin.shortcutbadger.impl;
+
+import android.annotation.TargetApi;
+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.Build;
+import android.os.Looper;
+
+import java.util.Arrays;
+import java.util.List;
+
+import me.leolin.shortcutbadger.Badger;
+import me.leolin.shortcutbadger.ShortcutBadgeException;
+import me.leolin.shortcutbadger.util.BroadcastHelper;
+
+/**
+ * @author leolin
+ */
+public class AsusHomeBadger implements Badger {
+ private static final String INTENT_ACTION = IntentConstants.DEFAULT_INTENT_ACTION;
+ private static final String INTENT_EXTRA_BADGE_COUNT = "badge_count";
+ private static final String INTENT_EXTRA_PACKAGENAME = "badge_count_package_name";
+ private static final String INTENT_EXTRA_ACTIVITY_NAME = "badge_count_class_name";
+
+ private static final String PROVIDER_CONTENT_URI = "content://com.android.badge/"; // FIXME
+ private static final String PROVIDER_COLUMNS_BADGE_COUNT = "badge_count"; // FIXME
+ private static final String PROVIDER_COLUMNS_PACKAGE_NAME = "package_name"; // FIXME
+ private static final String PROVIDER_COLUMNS_ACTIVITY_NAME = "activity_name"; // FIXME
+ private static final String ASUS_LAUNCHER_PROVIDER_NAME = "com.android.badge";
+ private final Uri BADGE_CONTENT_URI = Uri.parse(PROVIDER_CONTENT_URI);
+
+ private AsyncQueryHandler mQueryHandler;
+
+ // It seems that Asus handle Sony like badges better than old implementation...
+ private static final String SONY_INTENT_ACTION = "com.sonyericsson.home.action.UPDATE_BADGE";
+ private static final String SONY_INTENT_EXTRA_PACKAGE_NAME = "com.sonyericsson.home.intent.extra.badge.PACKAGE_NAME";
+ private static final String SONY_INTENT_EXTRA_ACTIVITY_NAME = "com.sonyericsson.home.intent.extra.badge.ACTIVITY_NAME";
+ private static final String SONY_INTENT_EXTRA_MESSAGE = "com.sonyericsson.home.intent.extra.badge.MESSAGE";
+ private static final String SONY_INTENT_EXTRA_SHOW_MESSAGE = "com.sonyericsson.home.intent.extra.badge.SHOW_MESSAGE";
+
+
+ @Override
+ public void executeBadge(Context context, ComponentName componentName, int badgeCount) throws ShortcutBadgeException {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ // FIXME: It seems that ZenUI (com.asus.launcher) declares a content provider for badges but without documentation it is hard to guess how to add badges with it. Current draft implementation gives "No yet implemented" exception.
+// if (asusBadgeContentProviderExists(context)) {
+// executeBadgeByContentProvider(context, componentName, badgeCount);
+// } else {
+ executeBadgeByBroadcast(context, componentName, badgeCount);
+// }
+ } else {
+ Intent intent = new Intent(INTENT_ACTION);
+ intent.putExtra(INTENT_EXTRA_BADGE_COUNT, badgeCount);
+ intent.putExtra(INTENT_EXTRA_PACKAGENAME, componentName.getPackageName());
+ intent.putExtra(INTENT_EXTRA_ACTIVITY_NAME, componentName.getClassName());
+ intent.putExtra("badge_vip_count", 0);
+
+ BroadcastHelper.sendDefaultIntentExplicitly(context, intent);
+ }
+ }
+
+ private void executeBadgeByBroadcast(Context context, ComponentName componentName, int badgeCount) {
+ Intent intent = new Intent(SONY_INTENT_ACTION);
+ intent.putExtra(SONY_INTENT_EXTRA_PACKAGE_NAME, componentName.getPackageName());
+ intent.putExtra(SONY_INTENT_EXTRA_ACTIVITY_NAME, componentName.getClassName());
+ intent.putExtra(SONY_INTENT_EXTRA_MESSAGE, String.valueOf(badgeCount));
+ intent.putExtra(SONY_INTENT_EXTRA_SHOW_MESSAGE, badgeCount > 0);
+ // FIXME: BroadcastHelper fail to resolve broadcast and then don't broadcast intent while it works.
+// BroadcastHelper.sendDefaultIntentExplicitly(context, intent);
+ context.sendBroadcast(intent);
+ }
+
+ @Override
+ public List getSupportLaunchers() {
+ return Arrays.asList("com.asus.launcher");
+ }
+
+ /**
+ * Send request to Asus 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
+ */
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ 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 Asus badge content provider exists.
+ *
+ * @param context the context to use
+ * @return true if Asus badge content provider exists, otherwise false.
+ */
+ private static boolean asusBadgeContentProviderExists(Context context) {
+ boolean exists = false;
+ ProviderInfo info = context.getPackageManager().resolveContentProvider(ASUS_LAUNCHER_PROVIDER_NAME, 0);
+ if (info != null) {
+ exists = true;
+ }
+ return exists;
+ }
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/impl/DefaultBadger.java b/app/src/main/java/me/leolin/shortcutbadger/impl/DefaultBadger.java
new file mode 100644
index 0000000000..b5ff4dc581
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/impl/DefaultBadger.java
@@ -0,0 +1,49 @@
+package me.leolin.shortcutbadger.impl;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+
+import java.util.Arrays;
+import java.util.List;
+
+import me.leolin.shortcutbadger.Badger;
+import me.leolin.shortcutbadger.ShortcutBadgeException;
+import me.leolin.shortcutbadger.util.BroadcastHelper;
+
+/**
+ * @author leolin
+ */
+public class DefaultBadger implements Badger {
+ private static final String INTENT_ACTION = IntentConstants.DEFAULT_INTENT_ACTION;
+ private static final String INTENT_EXTRA_BADGE_COUNT = "badge_count";
+ private static final String INTENT_EXTRA_PACKAGENAME = "badge_count_package_name";
+ private static final String INTENT_EXTRA_ACTIVITY_NAME = "badge_count_class_name";
+
+ @Override
+ public void executeBadge(Context context, ComponentName componentName, int badgeCount) throws ShortcutBadgeException {
+ Intent intent = new Intent(INTENT_ACTION);
+ intent.putExtra(INTENT_EXTRA_BADGE_COUNT, badgeCount);
+ intent.putExtra(INTENT_EXTRA_PACKAGENAME, componentName.getPackageName());
+ intent.putExtra(INTENT_EXTRA_ACTIVITY_NAME, componentName.getClassName());
+
+ BroadcastHelper.sendDefaultIntentExplicitly(context, intent);
+ }
+
+ @Override
+ public List getSupportLaunchers() {
+ return Arrays.asList(
+ "fr.neamar.kiss",
+ "com.quaap.launchtime",
+ "com.quaap.launchtime_official"
+ );
+ }
+
+ boolean isSupported(Context context) {
+ Intent intent = new Intent(INTENT_ACTION);
+ return BroadcastHelper.resolveBroadcast(context, intent).size() > 0
+ || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
+ && BroadcastHelper.resolveBroadcast(context, new Intent(IntentConstants.DEFAULT_OREO_INTENT_ACTION)).size() > 0);
+ }
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/impl/EverythingMeHomeBadger.java b/app/src/main/java/me/leolin/shortcutbadger/impl/EverythingMeHomeBadger.java
new file mode 100644
index 0000000000..b98aaa3c03
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/impl/EverythingMeHomeBadger.java
@@ -0,0 +1,39 @@
+package me.leolin.shortcutbadger.impl;
+
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Context;
+import android.net.Uri;
+
+import java.util.Arrays;
+import java.util.List;
+
+import me.leolin.shortcutbadger.Badger;
+import me.leolin.shortcutbadger.ShortcutBadgeException;
+
+
+/**
+ * @author Radko Roman
+ * @since 13.04.17.
+ */
+public class EverythingMeHomeBadger implements Badger {
+
+ private static final String CONTENT_URI = "content://me.everything.badger/apps";
+ private static final String COLUMN_PACKAGE_NAME = "package_name";
+ private static final String COLUMN_ACTIVITY_NAME = "activity_name";
+ private static final String COLUMN_COUNT = "count";
+
+ @Override
+ public void executeBadge(Context context, ComponentName componentName, int badgeCount) throws ShortcutBadgeException {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(COLUMN_PACKAGE_NAME, componentName.getPackageName());
+ contentValues.put(COLUMN_ACTIVITY_NAME, componentName.getClassName());
+ contentValues.put(COLUMN_COUNT, badgeCount);
+ context.getContentResolver().insert(Uri.parse(CONTENT_URI), contentValues);
+ }
+
+ @Override
+ public List getSupportLaunchers() {
+ return Arrays.asList("me.everything.launcher");
+ }
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/impl/HuaweiHomeBadger.java b/app/src/main/java/me/leolin/shortcutbadger/impl/HuaweiHomeBadger.java
new file mode 100644
index 0000000000..c6952efe60
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/impl/HuaweiHomeBadger.java
@@ -0,0 +1,34 @@
+package me.leolin.shortcutbadger.impl;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+
+import java.util.Arrays;
+import java.util.List;
+
+import me.leolin.shortcutbadger.Badger;
+import me.leolin.shortcutbadger.ShortcutBadgeException;
+
+/**
+ * @author Jason Ling
+ */
+public class HuaweiHomeBadger implements Badger {
+
+ @Override
+ public void executeBadge(Context context, ComponentName componentName, int badgeCount) throws ShortcutBadgeException {
+ Bundle localBundle = new Bundle();
+ localBundle.putString("package", context.getPackageName());
+ localBundle.putString("class", componentName.getClassName());
+ localBundle.putInt("badgenumber", badgeCount);
+ context.getContentResolver().call(Uri.parse("content://com.huawei.android.launcher.settings/badge/"), "change_badge", null, localBundle);
+ }
+
+ @Override
+ public List getSupportLaunchers() {
+ return Arrays.asList(
+ "com.huawei.android.launcher"
+ );
+ }
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/impl/IntentConstants.java b/app/src/main/java/me/leolin/shortcutbadger/impl/IntentConstants.java
new file mode 100644
index 0000000000..f4fd44cf5e
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/impl/IntentConstants.java
@@ -0,0 +1,6 @@
+package me.leolin.shortcutbadger.impl;
+
+public interface IntentConstants {
+ String DEFAULT_INTENT_ACTION = "android.intent.action.BADGE_COUNT_UPDATE";
+ String DEFAULT_OREO_INTENT_ACTION = "me.leolin.shortcutbadger.BADGE_COUNT_UPDATE";
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/impl/LGHomeBadger.java b/app/src/main/java/me/leolin/shortcutbadger/impl/LGHomeBadger.java
new file mode 100644
index 0000000000..2ffc6d930c
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/impl/LGHomeBadger.java
@@ -0,0 +1,43 @@
+package me.leolin.shortcutbadger.impl;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+import java.util.Arrays;
+import java.util.List;
+
+import me.leolin.shortcutbadger.Badger;
+import me.leolin.shortcutbadger.ShortcutBadgeException;
+import me.leolin.shortcutbadger.util.BroadcastHelper;
+
+/**
+ * @author Leo Lin
+ * Deprecated, LG devices will use DefaultBadger
+ */
+@Deprecated
+public class LGHomeBadger implements Badger {
+
+ private static final String INTENT_ACTION = IntentConstants.DEFAULT_INTENT_ACTION;
+ private static final String INTENT_EXTRA_BADGE_COUNT = "badge_count";
+ private static final String INTENT_EXTRA_PACKAGENAME = "badge_count_package_name";
+ private static final String INTENT_EXTRA_ACTIVITY_NAME = "badge_count_class_name";
+
+ @Override
+ public void executeBadge(Context context, ComponentName componentName, int badgeCount) throws ShortcutBadgeException {
+ Intent intent = new Intent(INTENT_ACTION);
+ intent.putExtra(INTENT_EXTRA_BADGE_COUNT, badgeCount);
+ intent.putExtra(INTENT_EXTRA_PACKAGENAME, componentName.getPackageName());
+ intent.putExtra(INTENT_EXTRA_ACTIVITY_NAME, componentName.getClassName());
+
+ BroadcastHelper.sendDefaultIntentExplicitly(context, intent);
+ }
+
+ @Override
+ public List getSupportLaunchers() {
+ return Arrays.asList(
+ "com.lge.launcher",
+ "com.lge.launcher2"
+ );
+ }
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/impl/NewHtcHomeBadger.java b/app/src/main/java/me/leolin/shortcutbadger/impl/NewHtcHomeBadger.java
new file mode 100644
index 0000000000..94312dfe14
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/impl/NewHtcHomeBadger.java
@@ -0,0 +1,64 @@
+package me.leolin.shortcutbadger.impl;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+import java.util.Collections;
+import java.util.List;
+
+import me.leolin.shortcutbadger.Badger;
+import me.leolin.shortcutbadger.ShortcutBadgeException;
+import me.leolin.shortcutbadger.util.BroadcastHelper;
+
+/**
+ * @author Leo Lin
+ */
+public class NewHtcHomeBadger implements Badger {
+
+ public static final String INTENT_UPDATE_SHORTCUT = "com.htc.launcher.action.UPDATE_SHORTCUT";
+ public static final String INTENT_SET_NOTIFICATION = "com.htc.launcher.action.SET_NOTIFICATION";
+ public static final String PACKAGENAME = "packagename";
+ public static final String COUNT = "count";
+ public static final String EXTRA_COMPONENT = "com.htc.launcher.extra.COMPONENT";
+ public static final String EXTRA_COUNT = "com.htc.launcher.extra.COUNT";
+
+ @Override
+ public void executeBadge(Context context, ComponentName componentName, int badgeCount) throws ShortcutBadgeException {
+
+ Intent intent1 = new Intent(INTENT_SET_NOTIFICATION);
+ boolean intent1Success;
+
+ intent1.putExtra(EXTRA_COMPONENT, componentName.flattenToShortString());
+ intent1.putExtra(EXTRA_COUNT, badgeCount);
+
+ Intent intent = new Intent(INTENT_UPDATE_SHORTCUT);
+ boolean intentSuccess;
+
+ intent.putExtra(PACKAGENAME, componentName.getPackageName());
+ intent.putExtra(COUNT, badgeCount);
+
+ try {
+ BroadcastHelper.sendIntentExplicitly(context, intent1);
+ intent1Success = true;
+ } catch (ShortcutBadgeException e) {
+ intent1Success = false;
+ }
+
+ try {
+ BroadcastHelper.sendIntentExplicitly(context, intent);
+ intentSuccess = true;
+ } catch (ShortcutBadgeException e) {
+ intentSuccess = false;
+ }
+
+ if (!intent1Success && !intentSuccess) {
+ throw new ShortcutBadgeException("unable to resolve intent: " + intent.toString());
+ }
+ }
+
+ @Override
+ public List getSupportLaunchers() {
+ return Collections.singletonList("com.htc.launcher");
+ }
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/impl/NovaHomeBadger.java b/app/src/main/java/me/leolin/shortcutbadger/impl/NovaHomeBadger.java
new file mode 100644
index 0000000000..a643c7eff8
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/impl/NovaHomeBadger.java
@@ -0,0 +1,39 @@
+package me.leolin.shortcutbadger.impl;
+
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Context;
+import android.net.Uri;
+
+import java.util.Arrays;
+import java.util.List;
+
+import me.leolin.shortcutbadger.Badger;
+import me.leolin.shortcutbadger.ShortcutBadgeException;
+
+/**
+ * Shortcut Badger support for Nova Launcher.
+ * TeslaUnread must be installed.
+ * User: Gernot Pansy
+ * Date: 2014/11/03
+ * Time: 7:15
+ */
+public class NovaHomeBadger implements Badger {
+
+ private static final String CONTENT_URI = "content://com.teslacoilsw.notifier/unread_count";
+ private static final String COUNT = "count";
+ private static final String TAG = "tag";
+
+ @Override
+ public void executeBadge(Context context, ComponentName componentName, int badgeCount) throws ShortcutBadgeException {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(TAG, componentName.getPackageName() + "/" + componentName.getClassName());
+ contentValues.put(COUNT, badgeCount);
+ context.getContentResolver().insert(Uri.parse(CONTENT_URI), contentValues);
+ }
+
+ @Override
+ public List getSupportLaunchers() {
+ return Arrays.asList("com.teslacoilsw.launcher");
+ }
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/impl/OPPOHomeBader.java b/app/src/main/java/me/leolin/shortcutbadger/impl/OPPOHomeBader.java
new file mode 100644
index 0000000000..febe3b7fa8
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/impl/OPPOHomeBader.java
@@ -0,0 +1,82 @@
+package me.leolin.shortcutbadger.impl;
+
+import android.annotation.TargetApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ProviderInfo;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import java.util.Collections;
+import java.util.List;
+
+import me.leolin.shortcutbadger.Badger;
+import me.leolin.shortcutbadger.ShortcutBadgeException;
+import me.leolin.shortcutbadger.util.BroadcastHelper;
+
+/**
+ * Created by NingSo on 2016/10/14.上午10:09
+ *
+ * @author: NingSo
+ * Email: ningso.ping@gmail.com
+ */
+
+public class OPPOHomeBader implements Badger {
+
+ private static final String PROVIDER_CONTENT_URI = "content://com.android.badge/badge";
+ private static final String INTENT_ACTION = "com.oppo.unsettledevent";
+ private static final String INTENT_EXTRA_PACKAGENAME = "pakeageName";
+ private static final String INTENT_EXTRA_BADGE_COUNT = "number";
+ private static final String INTENT_EXTRA_BADGE_UPGRADENUMBER = "upgradeNumber";
+ private static final String INTENT_EXTRA_BADGEUPGRADE_COUNT = "app_badge_count";
+ private int mCurrentTotalCount = -1;
+
+ @Override
+ public void executeBadge(Context context, ComponentName componentName, int badgeCount) throws ShortcutBadgeException {
+ if (mCurrentTotalCount == badgeCount) {
+ return;
+ }
+ mCurrentTotalCount = badgeCount;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
+ executeBadgeByContentProvider(context, badgeCount);
+ } else {
+ executeBadgeByBroadcast(context, componentName, badgeCount);
+ }
+ }
+
+ @Override
+ public List getSupportLaunchers() {
+ return Collections.singletonList("com.oppo.launcher");
+ }
+
+ private void executeBadgeByBroadcast(Context context, ComponentName componentName,
+ int badgeCount) throws ShortcutBadgeException {
+ if (badgeCount == 0) {
+ badgeCount = -1;
+ }
+ Intent intent = new Intent(INTENT_ACTION);
+ intent.putExtra(INTENT_EXTRA_PACKAGENAME, componentName.getPackageName());
+ intent.putExtra(INTENT_EXTRA_BADGE_COUNT, badgeCount);
+ intent.putExtra(INTENT_EXTRA_BADGE_UPGRADENUMBER, badgeCount);
+
+ BroadcastHelper.sendIntentExplicitly(context, intent);
+ }
+
+ /**
+ * Send request to OPPO badge content provider to set badge in OPPO home launcher.
+ *
+ * @param context the context to use
+ * @param badgeCount the badge count
+ */
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ private void executeBadgeByContentProvider(Context context, int badgeCount) throws ShortcutBadgeException {
+ try {
+ Bundle extras = new Bundle();
+ extras.putInt(INTENT_EXTRA_BADGEUPGRADE_COUNT, badgeCount);
+ context.getContentResolver().call(Uri.parse(PROVIDER_CONTENT_URI), "setAppBadgeCount", null, extras);
+ } catch (Throwable ignored) {
+ throw new ShortcutBadgeException("Unable to execute Badge By Content Provider");
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/me/leolin/shortcutbadger/impl/SamsungHomeBadger.java b/app/src/main/java/me/leolin/shortcutbadger/impl/SamsungHomeBadger.java
new file mode 100644
index 0000000000..a7125b5933
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/impl/SamsungHomeBadger.java
@@ -0,0 +1,85 @@
+package me.leolin.shortcutbadger.impl;
+
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+
+import java.util.Arrays;
+import java.util.List;
+
+import me.leolin.shortcutbadger.Badger;
+import me.leolin.shortcutbadger.ShortcutBadgeException;
+import me.leolin.shortcutbadger.util.CloseHelper;
+
+/**
+ * @author Leo Lin
+ */
+public class SamsungHomeBadger implements Badger {
+ private static final String CONTENT_URI = "content://com.sec.badge/apps?notify=true";
+ private static final String[] CONTENT_PROJECTION = new String[]{"_id", "class"};
+
+ private DefaultBadger defaultBadger;
+
+ public SamsungHomeBadger() {
+ if (Build.VERSION.SDK_INT >= 21) {
+ defaultBadger = new DefaultBadger();
+ }
+ }
+
+ @Override
+ public void executeBadge(Context context, ComponentName componentName, int badgeCount) throws ShortcutBadgeException {
+ if (defaultBadger != null && defaultBadger.isSupported(context)) {
+ defaultBadger.executeBadge(context, componentName, badgeCount);
+ } else {
+ Uri mUri = Uri.parse(CONTENT_URI);
+ ContentResolver contentResolver = context.getContentResolver();
+ Cursor cursor = null;
+ try {
+ cursor = contentResolver.query(mUri, CONTENT_PROJECTION, "package=?", new String[]{componentName.getPackageName()}, null);
+ if (cursor != null) {
+ String entryActivityName = componentName.getClassName();
+ boolean entryActivityExist = false;
+ while (cursor.moveToNext()) {
+ int id = cursor.getInt(0);
+ ContentValues contentValues = getContentValues(componentName, badgeCount, false);
+ contentResolver.update(mUri, contentValues, "_id=?", new String[]{String.valueOf(id)});
+ if (entryActivityName.equals(cursor.getString(cursor.getColumnIndex("class")))) {
+ entryActivityExist = true;
+ }
+ }
+
+ if (!entryActivityExist) {
+ ContentValues contentValues = getContentValues(componentName, badgeCount, true);
+ contentResolver.insert(mUri, contentValues);
+ }
+ }
+ } finally {
+ CloseHelper.close(cursor);
+ }
+ }
+ }
+
+ private ContentValues getContentValues(ComponentName componentName, int badgeCount, boolean isInsert) {
+ ContentValues contentValues = new ContentValues();
+ if (isInsert) {
+ contentValues.put("package", componentName.getPackageName());
+ contentValues.put("class", componentName.getClassName());
+ }
+
+ contentValues.put("badgecount", badgeCount);
+
+ return contentValues;
+ }
+
+ @Override
+ public List getSupportLaunchers() {
+ return Arrays.asList(
+ "com.sec.android.app.launcher",
+ "com.sec.android.app.twlauncher"
+ );
+ }
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/impl/SonyHomeBadger.java b/app/src/main/java/me/leolin/shortcutbadger/impl/SonyHomeBadger.java
new file mode 100644
index 0000000000..ef53b3257b
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/impl/SonyHomeBadger.java
@@ -0,0 +1,153 @@
+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 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;
+ }
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/impl/VivoHomeBadger.java b/app/src/main/java/me/leolin/shortcutbadger/impl/VivoHomeBadger.java
new file mode 100644
index 0000000000..f7e43d0d12
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/impl/VivoHomeBadger.java
@@ -0,0 +1,31 @@
+package me.leolin.shortcutbadger.impl;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+import java.util.Arrays;
+import java.util.List;
+
+import me.leolin.shortcutbadger.Badger;
+import me.leolin.shortcutbadger.ShortcutBadgeException;
+
+/**
+ * @author leolin
+ */
+public class VivoHomeBadger implements Badger {
+
+ @Override
+ public void executeBadge(Context context, ComponentName componentName, int badgeCount) throws ShortcutBadgeException {
+ Intent intent = new Intent("launcher.action.CHANGE_APPLICATION_NOTIFICATION_NUM");
+ intent.putExtra("packageName", context.getPackageName());
+ intent.putExtra("className", componentName.getClassName());
+ intent.putExtra("notificationNum", badgeCount);
+ context.sendBroadcast(intent);
+ }
+
+ @Override
+ public List getSupportLaunchers() {
+ return Arrays.asList("com.vivo.launcher");
+ }
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/impl/XiaomiHomeBadger.java b/app/src/main/java/me/leolin/shortcutbadger/impl/XiaomiHomeBadger.java
new file mode 100644
index 0000000000..c639625397
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/impl/XiaomiHomeBadger.java
@@ -0,0 +1,101 @@
+package me.leolin.shortcutbadger.impl;
+
+import android.annotation.TargetApi;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Build;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+
+import me.leolin.shortcutbadger.Badger;
+import me.leolin.shortcutbadger.ShortcutBadgeException;
+import me.leolin.shortcutbadger.util.BroadcastHelper;
+
+
+/**
+ * @author leolin
+ */
+@Deprecated
+public class XiaomiHomeBadger implements Badger {
+
+ public static final String INTENT_ACTION = "android.intent.action.APPLICATION_MESSAGE_UPDATE";
+ public static final String EXTRA_UPDATE_APP_COMPONENT_NAME = "android.intent.extra.update_application_component_name";
+ public static final String EXTRA_UPDATE_APP_MSG_TEXT = "android.intent.extra.update_application_message_text";
+ private ResolveInfo resolveInfo;
+
+ @Override
+ public void executeBadge(Context context, ComponentName componentName, int badgeCount) throws ShortcutBadgeException {
+ try {
+ Class miuiNotificationClass = Class.forName("android.app.MiuiNotification");
+ Object miuiNotification = miuiNotificationClass.newInstance();
+ Field field = miuiNotification.getClass().getDeclaredField("messageCount");
+ field.setAccessible(true);
+ try {
+ field.set(miuiNotification, String.valueOf(badgeCount == 0 ? "" : badgeCount));
+ } catch (Exception e) {
+ field.set(miuiNotification, badgeCount);
+ }
+ } catch (Exception e) {
+ Intent localIntent = new Intent(
+ INTENT_ACTION);
+ localIntent.putExtra(EXTRA_UPDATE_APP_COMPONENT_NAME, componentName.getPackageName() + "/" + componentName.getClassName());
+ localIntent.putExtra(EXTRA_UPDATE_APP_MSG_TEXT, String.valueOf(badgeCount == 0 ? "" : badgeCount));
+
+ try {
+ BroadcastHelper.sendIntentExplicitly(context, localIntent);
+ } catch (ShortcutBadgeException ignored) {}
+ }
+ if (Build.MANUFACTURER.equalsIgnoreCase("Xiaomi")) {
+ tryNewMiuiBadge(context, badgeCount);
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ private void tryNewMiuiBadge(Context context, int badgeCount) throws ShortcutBadgeException {
+ if (resolveInfo == null) {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_HOME);
+ resolveInfo = context.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ }
+
+ if (resolveInfo != null) {
+ NotificationManager mNotificationManager = (NotificationManager) context
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ Notification.Builder builder = new Notification.Builder(context)
+ .setContentTitle("")
+ .setContentText("")
+ .setSmallIcon(resolveInfo.getIconResource());
+ Notification notification = builder.build();
+ try {
+ Field field = notification.getClass().getDeclaredField("extraNotification");
+ Object extraNotification = field.get(notification);
+ Method method = extraNotification.getClass().getDeclaredMethod("setMessageCount", int.class);
+ method.invoke(extraNotification, badgeCount);
+ mNotificationManager.notify(0, notification);
+ } catch (Exception e) {
+ throw new ShortcutBadgeException("not able to set badge", e);
+ }
+ }
+ }
+
+ @Override
+ public List getSupportLaunchers() {
+ return Arrays.asList(
+ "com.miui.miuilite",
+ "com.miui.home",
+ "com.miui.miuihome",
+ "com.miui.miuihome2",
+ "com.miui.mihome",
+ "com.miui.mihome2",
+ "com.i.miui.launcher"
+ );
+ }
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/impl/YandexLauncherBadger.java b/app/src/main/java/me/leolin/shortcutbadger/impl/YandexLauncherBadger.java
new file mode 100644
index 0000000000..330f3d2006
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/impl/YandexLauncherBadger.java
@@ -0,0 +1,58 @@
+package me.leolin.shortcutbadger.impl;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+
+import java.util.Collections;
+import java.util.List;
+
+import me.leolin.shortcutbadger.Badger;
+import me.leolin.shortcutbadger.ShortcutBadgeException;
+
+/**
+ * @author Nikolay Pakhomov
+ * created 16/04/2018
+ */
+public class YandexLauncherBadger implements Badger {
+
+ public static final String PACKAGE_NAME = "com.yandex.launcher";
+
+ private static final String AUTHORITY = "com.yandex.launcher.badges_external";
+ private static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
+ private static final String METHOD_TO_CALL = "setBadgeNumber";
+
+ private static final String COLUMN_CLASS = "class";
+ private static final String COLUMN_PACKAGE = "package";
+ private static final String COLUMN_BADGES_COUNT = "badges_count";
+
+ @Override
+ public void executeBadge(Context context, ComponentName componentName, int badgeCount) throws ShortcutBadgeException {
+ Bundle extras = new Bundle();
+ extras.putString(COLUMN_CLASS, componentName.getClassName());
+ extras.putString(COLUMN_PACKAGE, componentName.getPackageName());
+ extras.putString(COLUMN_BADGES_COUNT, String.valueOf(badgeCount));
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ context.getContentResolver().call(CONTENT_URI, METHOD_TO_CALL, null, extras);
+ }
+ }
+
+ public static boolean isVersionSupported(Context context) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ try {
+ context.getContentResolver().call(CONTENT_URI, "", null, null);
+ return true;
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public List getSupportLaunchers() {
+ return Collections.singletonList(PACKAGE_NAME);
+ }
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/impl/ZTEHomeBadger.java b/app/src/main/java/me/leolin/shortcutbadger/impl/ZTEHomeBadger.java
new file mode 100644
index 0000000000..7f9d78d4be
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/impl/ZTEHomeBadger.java
@@ -0,0 +1,36 @@
+package me.leolin.shortcutbadger.impl;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import me.leolin.shortcutbadger.Badger;
+import me.leolin.shortcutbadger.ShortcutBadgeException;
+
+public class ZTEHomeBadger implements Badger {
+
+ @Override
+ public void executeBadge(Context context, ComponentName componentName, int badgeCount)
+ throws ShortcutBadgeException {
+ Bundle extra = new Bundle();
+ extra.putInt("app_badge_count", badgeCount);
+ extra.putString("app_badge_component_name", componentName.flattenToString());
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ context.getContentResolver().call(
+ Uri.parse("content://com.android.launcher3.cornermark.unreadbadge"),
+ "setAppUnreadCount", null, extra);
+ }
+ }
+
+ @Override
+ public List getSupportLaunchers() {
+ return new ArrayList(0);
+ }
+}
+
diff --git a/app/src/main/java/me/leolin/shortcutbadger/impl/ZukHomeBadger.java b/app/src/main/java/me/leolin/shortcutbadger/impl/ZukHomeBadger.java
new file mode 100644
index 0000000000..05e691d317
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/impl/ZukHomeBadger.java
@@ -0,0 +1,37 @@
+package me.leolin.shortcutbadger.impl;
+
+import android.annotation.TargetApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+
+import java.util.Collections;
+import java.util.List;
+
+import me.leolin.shortcutbadger.Badger;
+import me.leolin.shortcutbadger.ShortcutBadgeException;
+
+/**
+ * Created by wuxuejian on 2016/10/9.
+ * 需在设置 -- 通知和状态栏 -- 应用角标管理 中开启应用
+ */
+
+public class ZukHomeBadger implements Badger {
+
+ private final Uri CONTENT_URI = Uri.parse("content://com.android.badge/badge");
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ @Override
+ public void executeBadge(Context context, ComponentName componentName, int badgeCount) throws ShortcutBadgeException {
+ Bundle extra = new Bundle();
+ extra.putInt("app_badge_count", badgeCount);
+ context.getContentResolver().call(CONTENT_URI, "setAppBadgeCount", null, extra);
+ }
+
+ @Override
+ public List getSupportLaunchers() {
+ return Collections.singletonList("com.zui.launcher");
+ }
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/util/BroadcastHelper.java b/app/src/main/java/me/leolin/shortcutbadger/util/BroadcastHelper.java
new file mode 100644
index 0000000000..db505d6d48
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/util/BroadcastHelper.java
@@ -0,0 +1,67 @@
+package me.leolin.shortcutbadger.util;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Build;
+
+import java.util.Collections;
+import java.util.List;
+
+import me.leolin.shortcutbadger.ShortcutBadgeException;
+import me.leolin.shortcutbadger.impl.IntentConstants;
+
+/**
+ * Created by mahijazi on 17/05/16.
+ */
+public class BroadcastHelper {
+
+ public static List resolveBroadcast(Context context, Intent intent) {
+ PackageManager packageManager = context.getPackageManager();
+ List receivers = packageManager.queryBroadcastReceivers(intent, 0);
+
+ return receivers != null ? receivers : Collections.emptyList();
+ }
+
+ public static void sendIntentExplicitly(Context context, Intent intent) throws ShortcutBadgeException {
+ List resolveInfos = resolveBroadcast(context, intent);
+
+ if (resolveInfos.size() == 0) {
+ throw new ShortcutBadgeException("unable to resolve intent: " + intent.toString());
+ }
+
+ for (ResolveInfo info : resolveInfos) {
+ Intent actualIntent = new Intent(intent);
+
+ if (info != null) {
+ actualIntent.setPackage(info.resolvePackageName);
+ context.sendBroadcast(actualIntent);
+ }
+ }
+ }
+
+ public static void sendDefaultIntentExplicitly(Context context, Intent intent) throws ShortcutBadgeException {
+ boolean oreoIntentSuccess = false;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ Intent oreoIntent = new Intent(intent);
+
+ oreoIntent.setAction(IntentConstants.DEFAULT_OREO_INTENT_ACTION);
+
+ try {
+ sendIntentExplicitly(context, oreoIntent);
+ oreoIntentSuccess = true;
+ } catch (ShortcutBadgeException e) {
+ oreoIntentSuccess = false;
+ }
+ }
+
+ if (oreoIntentSuccess) {
+ return;
+ }
+
+ // try pre-Oreo default intent
+ sendIntentExplicitly(context, intent);
+ }
+}
diff --git a/app/src/main/java/me/leolin/shortcutbadger/util/CloseHelper.java b/app/src/main/java/me/leolin/shortcutbadger/util/CloseHelper.java
new file mode 100644
index 0000000000..e646118e1f
--- /dev/null
+++ b/app/src/main/java/me/leolin/shortcutbadger/util/CloseHelper.java
@@ -0,0 +1,29 @@
+package me.leolin.shortcutbadger.util;
+
+import android.database.Cursor;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * @author leolin
+ */
+public class CloseHelper {
+
+ public static void close(Cursor cursor) {
+ if (cursor != null && !cursor.isClosed()) {
+ cursor.close();
+ }
+ }
+
+
+ public static void closeQuietly(Closeable closeable) {
+ try {
+ if (closeable != null) {
+ closeable.close();
+ }
+ } catch (IOException var2) {
+
+ }
+ }
+}
diff --git a/app/src/play/AndroidManifest.xml b/app/src/play/AndroidManifest.xml
index 637fea29b3..2f0be1c3e2 100644
--- a/app/src/play/AndroidManifest.xml
+++ b/app/src/play/AndroidManifest.xml
@@ -30,6 +30,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+