diff --git a/.gitmodules b/.gitmodules index fbb9f6d48..dd823f04c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,6 +4,3 @@ [submodule "dav4android"] path = dav4android url = https://gitlab.com/tasks.org/dav4android.git -[submodule "ShortcutBadger"] - path = ShortcutBadger - url = https://github.com/tasks/ShortcutBadger.git diff --git a/ShortcutBadger b/ShortcutBadger deleted file mode 160000 index 59d0f178e..000000000 --- a/ShortcutBadger +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 59d0f178e952c40244ab02d213c99158237e078b diff --git a/app/build.gradle b/app/build.gradle index af7db6f8e..e02eb5f5b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -165,8 +165,7 @@ dependencies { implementation('com.wdullaer:materialdatetimepicker:3.6.1') { exclude group: 'com.android.support', module: 'support-v4' } -// implementation "me.leolin:ShortcutBadger:1.1.21@aar" - implementation ":ShortcutBadger:" + implementation "me.leolin:ShortcutBadger:1.1.22@aar" implementation 'com.google.apis:google-api-services-tasks:v1-rev52-1.23.0' implementation 'com.google.api-client:google-api-client-android:1.23.0' implementation 'com.android.billingclient:billing:1.1' diff --git a/app/src/main/java/org/tasks/receivers/Badger.java b/app/src/main/java/org/tasks/receivers/Badger.java index 75b2896dd..4809da3a6 100644 --- a/app/src/main/java/org/tasks/receivers/Badger.java +++ b/app/src/main/java/org/tasks/receivers/Badger.java @@ -6,7 +6,6 @@ import android.content.Intent; import com.todoroo.astrid.api.Filter; import com.todoroo.astrid.dao.TaskDao; import javax.inject.Inject; -import me.leolin.shortcutbadger.ShortcutBadger; import org.tasks.LocalBroadcastManager; import org.tasks.injection.ApplicationScope; import org.tasks.injection.ForApplication; diff --git a/app/src/main/java/org/tasks/receivers/ShortcutBadger.java b/app/src/main/java/org/tasks/receivers/ShortcutBadger.java new file mode 100644 index 000000000..1dc2bc6a1 --- /dev/null +++ b/app/src/main/java/org/tasks/receivers/ShortcutBadger.java @@ -0,0 +1,277 @@ +package org.tasks.receivers; + +/* + * Copyright 2014 Leo Lin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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.Badger; +import me.leolin.shortcutbadger.ShortcutBadgeException; +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.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); + } + + 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); + 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)) { + 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; + } + + 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() { + + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 6bb8e7cee..a8f498869 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,4 +2,3 @@ include 'app' includeBuild 'dav4android' includeBuild 'ical4android' -includeBuild 'ShortcutBadger/ShortcutBadger'