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 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +