From 5e0f444a5eb9928d51c9d2ffa69583fb23c5457e Mon Sep 17 00:00:00 2001 From: Suphon Thanakornpakapong Date: Mon, 23 May 2022 09:33:31 +0700 Subject: [PATCH 1/5] Respect global notification dots settings --- .../e/blisslauncher/BlissLauncher.java | 17 ++ .../notification/NotificationService.java | 46 ++++- .../e/blisslauncher/util/LooperExecutor.java | 118 ++++++++++++ .../util/MainThreadInitializedObject.java | 65 +++++++ .../e/blisslauncher/util/SettingsCache.java | 179 ++++++++++++++++++ 5 files changed, 423 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/foundation/e/blisslauncher/util/LooperExecutor.java create mode 100644 app/src/main/java/foundation/e/blisslauncher/util/MainThreadInitializedObject.java create mode 100644 app/src/main/java/foundation/e/blisslauncher/util/SettingsCache.java diff --git a/app/src/main/java/foundation/e/blisslauncher/BlissLauncher.java b/app/src/main/java/foundation/e/blisslauncher/BlissLauncher.java index 8141d07c65..cd0786436e 100755 --- a/app/src/main/java/foundation/e/blisslauncher/BlissLauncher.java +++ b/app/src/main/java/foundation/e/blisslauncher/BlissLauncher.java @@ -1,7 +1,10 @@ package foundation.e.blisslauncher; +import static foundation.e.blisslauncher.util.SettingsCache.NOTIFICATION_BADGING_URI; + import android.app.Application; import android.appwidget.AppWidgetManager; +import android.content.ComponentName; import android.content.Context; import foundation.e.blisslauncher.core.DeviceProfile; @@ -9,6 +12,8 @@ import foundation.e.blisslauncher.core.IconsHandler; import foundation.e.blisslauncher.core.blur.BlurWallpaperProvider; import foundation.e.blisslauncher.core.customviews.WidgetHost; import foundation.e.blisslauncher.features.launcher.AppProvider; +import foundation.e.blisslauncher.features.notification.NotificationService; +import foundation.e.blisslauncher.util.SettingsCache; public class BlissLauncher extends Application { private IconsHandler iconsPackHandler; @@ -29,6 +34,18 @@ public class BlissLauncher extends Application { connectAppProvider(); BlurWallpaperProvider.Companion.getInstance(this); + + SettingsCache settingsCache = SettingsCache.INSTANCE.get(this); + SettingsCache.OnChangeListener notificationLister = this::onNotificationSettingsChanged; + settingsCache.register(NOTIFICATION_BADGING_URI, notificationLister); + onNotificationSettingsChanged(settingsCache.getValue(NOTIFICATION_BADGING_URI)); + } + + private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) { + if (areNotificationDotsEnabled) { + NotificationService.requestRebind(new ComponentName( + this, NotificationService.class)); + } } public static BlissLauncher getApplication(Context context) { diff --git a/app/src/main/java/foundation/e/blisslauncher/features/notification/NotificationService.java b/app/src/main/java/foundation/e/blisslauncher/features/notification/NotificationService.java index 046fdc2172..2c28a77e13 100755 --- a/app/src/main/java/foundation/e/blisslauncher/features/notification/NotificationService.java +++ b/app/src/main/java/foundation/e/blisslauncher/features/notification/NotificationService.java @@ -1,10 +1,15 @@ package foundation.e.blisslauncher.features.notification; +import static foundation.e.blisslauncher.util.SettingsCache.NOTIFICATION_BADGING_URI; + import android.content.Intent; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; +import java.util.Collections; + import foundation.e.blisslauncher.core.utils.ListUtil; +import foundation.e.blisslauncher.util.SettingsCache; /** * Created by falcon on 14/3/18. @@ -12,31 +17,68 @@ import foundation.e.blisslauncher.core.utils.ListUtil; public class NotificationService extends NotificationListenerService { + private static boolean sIsConnected = false; + NotificationRepository mNotificationRepository; + private boolean mDotsEnabled; + private SettingsCache mSettingsCache; + private SettingsCache.OnChangeListener mNotificationSettingsChangedListener; + @Override public void onCreate() { super.onCreate(); mNotificationRepository = NotificationRepository.getNotificationRepository(); + + // Register an observer to rebind the notification listener when dots are re-enabled. + mSettingsCache = SettingsCache.INSTANCE.get(this); + mNotificationSettingsChangedListener = this::onNotificationSettingsChanged; + mSettingsCache.register(NOTIFICATION_BADGING_URI, + mNotificationSettingsChangedListener); + onNotificationSettingsChanged(mSettingsCache.getValue(NOTIFICATION_BADGING_URI)); } @Override public void onDestroy() { super.onDestroy(); + mSettingsCache.unregister(NOTIFICATION_BADGING_URI, mNotificationSettingsChangedListener); + mNotificationRepository.updateNotification(Collections.emptyList()); + } + + private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) { + mDotsEnabled = areNotificationDotsEnabled; + if (!areNotificationDotsEnabled && sIsConnected) { + requestUnbind(); + updateNotifications(); + } } @Override public void onListenerConnected() { - mNotificationRepository.updateNotification(ListUtil.asSafeList(getActiveNotifications())); + sIsConnected = true; + updateNotifications(); + } + + @Override + public void onListenerDisconnected() { + sIsConnected = false; } @Override public void onNotificationPosted(StatusBarNotification sbn) { - mNotificationRepository.updateNotification(ListUtil.asSafeList(getActiveNotifications())); + updateNotifications(); } @Override public void onNotificationRemoved(StatusBarNotification sbn) { + updateNotifications(); + } + + private void updateNotifications() { + if (!mDotsEnabled) { + mNotificationRepository.updateNotification(Collections.emptyList()); + return; + } mNotificationRepository.updateNotification(ListUtil.asSafeList(getActiveNotifications())); } diff --git a/app/src/main/java/foundation/e/blisslauncher/util/LooperExecutor.java b/app/src/main/java/foundation/e/blisslauncher/util/LooperExecutor.java new file mode 100644 index 0000000000..560a675ac9 --- /dev/null +++ b/app/src/main/java/foundation/e/blisslauncher/util/LooperExecutor.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * 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 + */ +package foundation.e.blisslauncher.util; + +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Process; + +import java.util.List; +import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * Extension of {@link AbstractExecutorService} which executed on a provided looper. + */ +public class LooperExecutor extends AbstractExecutorService { + + private final Handler mHandler; + + public LooperExecutor(Looper looper) { + mHandler = new Handler(looper); + } + + public Handler getHandler() { + return mHandler; + } + + @Override + public void execute(Runnable runnable) { + if (getHandler().getLooper() == Looper.myLooper()) { + runnable.run(); + } else { + getHandler().post(runnable); + } + } + + /** + * Same as execute, but never runs the action inline. + */ + public void post(Runnable runnable) { + getHandler().post(runnable); + } + + /** + * Not supported and throws an exception when used. + */ + @Override + @Deprecated + public void shutdown() { + throw new UnsupportedOperationException(); + } + + /** + * Not supported and throws an exception when used. + */ + @Override + @Deprecated + public List shutdownNow() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isShutdown() { + return false; + } + + @Override + public boolean isTerminated() { + return false; + } + + /** + * Not supported and throws an exception when used. + */ + @Override + @Deprecated + public boolean awaitTermination(long l, TimeUnit timeUnit) { + throw new UnsupportedOperationException(); + } + + /** + * Returns the thread for this executor + */ + public Thread getThread() { + return getHandler().getLooper().getThread(); + } + + /** + * Returns the looper for this executor + */ + public Looper getLooper() { + return getHandler().getLooper(); + } + + /** + * Set the priority of a thread, based on Linux priorities. + * @param priority Linux priority level, from -20 for highest scheduling priority + * to 19 for lowest scheduling priority. + * @see Process#setThreadPriority(int, int) + */ + public void setThreadPriority(int priority) { + Process.setThreadPriority(((HandlerThread) getThread()).getThreadId(), priority); + } +} diff --git a/app/src/main/java/foundation/e/blisslauncher/util/MainThreadInitializedObject.java b/app/src/main/java/foundation/e/blisslauncher/util/MainThreadInitializedObject.java new file mode 100644 index 0000000000..275498550e --- /dev/null +++ b/app/src/main/java/foundation/e/blisslauncher/util/MainThreadInitializedObject.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * 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. + * + * Modifications copyright 2021, Lawnchair + */ +package foundation.e.blisslauncher.util; + +import android.content.Context; +import android.os.Looper; + +import java.util.concurrent.ExecutionException; + +/** + * Utility class for defining singletons which are initiated on main thread. + */ +public class MainThreadInitializedObject { + + private static final LooperExecutor MAIN_EXECUTOR = new LooperExecutor(Looper.getMainLooper()); + + private final ObjectProvider mProvider; + private T mValue; + + public MainThreadInitializedObject(ObjectProvider provider) { + mProvider = provider; + } + + public T get(Context context) { + if (mValue == null) { + if (Looper.myLooper() == Looper.getMainLooper()) { + mValue = mProvider.get(context.getApplicationContext()); + onPostInit(context); + } else { + try { + return MAIN_EXECUTOR.submit(() -> get(context)).get(); + } catch (InterruptedException|ExecutionException e) { + throw new RuntimeException(e); + } + } + } + return mValue; + } + + protected void onPostInit(Context context) { } + + public T getNoCreate() { + return mValue; + } + + public interface ObjectProvider { + + T get(Context context); + } +} diff --git a/app/src/main/java/foundation/e/blisslauncher/util/SettingsCache.java b/app/src/main/java/foundation/e/blisslauncher/util/SettingsCache.java new file mode 100644 index 0000000000..4f716d9d13 --- /dev/null +++ b/app/src/main/java/foundation/e/blisslauncher/util/SettingsCache.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * 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. + */ + +package foundation.e.blisslauncher.util; + +import static android.provider.Settings.System.ACCELEROMETER_ROTATION; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.provider.Settings; + +import androidx.annotation.VisibleForTesting; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * ContentObserver over Settings keys that also has a caching layer. + * Consumers can register for callbacks via {@link #register(Uri, OnChangeListener)} and + * {@link #unregister(Uri, OnChangeListener)} methods. + * + * This can be used as a normal cache without any listeners as well via the + * {@link #getValue(Uri, int)} and {@link #onChange)} to update (and subsequently call + * get) + * + * The cache will be invalidated/updated through the normal + * {@link ContentObserver#onChange(boolean)} calls + * + * Cache will also be updated if a key queried is missing (even if it has no listeners registered). + */ +public class SettingsCache extends ContentObserver implements AutoCloseable { + + /** Hidden field Settings.Secure.NOTIFICATION_BADGING */ + public static final Uri NOTIFICATION_BADGING_URI = + Settings.Secure.getUriFor("notification_badging"); + /** Hidden field Settings.Secure.ONE_HANDED_MODE_ENABLED */ + public static final String ONE_HANDED_ENABLED = "one_handed_mode_enabled"; + /** Hidden field Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED */ + public static final String ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED = + "swipe_bottom_to_notification_enabled"; + public static final Uri ROTATION_SETTING_URI = + Settings.System.getUriFor(ACCELEROMETER_ROTATION); + + private static final String SYSTEM_URI_PREFIX = Settings.System.CONTENT_URI.toString(); + + /** + * Caches the last seen value for registered keys. + */ + private Map mKeyCache = new ConcurrentHashMap<>(); + private final Map> mListenerMap = new HashMap<>(); + protected final ContentResolver mResolver; + + /** + * Singleton instance + */ + public static MainThreadInitializedObject INSTANCE = + new MainThreadInitializedObject<>(SettingsCache::new); + + private SettingsCache(Context context) { + super(new Handler()); + mResolver = context.getContentResolver(); + } + + @Override + public void close() { + mResolver.unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + // We use default of 1, but if we're getting an onChange call, can assume a non-default + // value will exist + boolean newVal = updateValue(uri, 1 /* Effectively Unused */); + if (!mListenerMap.containsKey(uri)) { + return; + } + + for (OnChangeListener listener : mListenerMap.get(uri)) { + listener.onSettingsChanged(newVal); + } + } + + /** + * Returns the value for this classes key from the cache. If not in cache, will call + * {@link #updateValue(Uri, int)} to fetch. + */ + public boolean getValue(Uri keySetting) { + return getValue(keySetting, 1); + } + + /** + * Returns the value for this classes key from the cache. If not in cache, will call + * {@link #updateValue(Uri, int)} to fetch. + */ + public boolean getValue(Uri keySetting, int defaultValue) { + if (mKeyCache.containsKey(keySetting)) { + return mKeyCache.get(keySetting); + } else { + return updateValue(keySetting, defaultValue); + } + } + + /** + * Does not de-dupe if you add same listeners for the same key multiple times. + * Unregister once complete using {@link #unregister(Uri, OnChangeListener)} + */ + public void register(Uri uri, OnChangeListener changeListener) { + if (mListenerMap.containsKey(uri)) { + mListenerMap.get(uri).add(changeListener); + } else { + CopyOnWriteArrayList l = new CopyOnWriteArrayList<>(); + l.add(changeListener); + mListenerMap.put(uri, l); + mResolver.registerContentObserver(uri, false, this); + } + } + + private boolean updateValue(Uri keyUri, int defaultValue) { + String key = keyUri.getLastPathSegment(); + boolean newVal; + if (keyUri.toString().startsWith(SYSTEM_URI_PREFIX)) { + newVal = Settings.System.getInt(mResolver, key, defaultValue) == 1; + } else { // SETTING_SECURE + newVal = Settings.Secure.getInt(mResolver, key, defaultValue) == 1; + } + + mKeyCache.put(keyUri, newVal); + return newVal; + } + + /** + * Call to stop receiving updates on the given {@param listener}. + * This Uri/Listener pair must correspond to the same pair called with for + * {@link #register(Uri, OnChangeListener)} + */ + public void unregister(Uri uri, OnChangeListener listener) { + List listenersToRemoveFrom = mListenerMap.get(uri); + if (!listenersToRemoveFrom.contains(listener)) { + return; + } + + listenersToRemoveFrom.remove(listener); + if (listenersToRemoveFrom.isEmpty()) { + mListenerMap.remove(uri); + } + } + + /** + * Don't use this. Ever. + * @param keyCache Cache to replace {@link #mKeyCache} + */ + @VisibleForTesting + void setKeyCache(Map keyCache) { + mKeyCache = keyCache; + } + + public interface OnChangeListener { + void onSettingsChanged(boolean isEnabled); + } +} -- GitLab From 079a602e2a0057a40e77a3f163d7dc7de92616b2 Mon Sep 17 00:00:00 2001 From: Suphon Thanakornpakapong Date: Mon, 23 May 2022 19:38:22 +0700 Subject: [PATCH 2/5] Add copyright header --- .../e/blisslauncher/util/MainThreadInitializedObject.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/foundation/e/blisslauncher/util/MainThreadInitializedObject.java b/app/src/main/java/foundation/e/blisslauncher/util/MainThreadInitializedObject.java index 275498550e..a6c2810e87 100644 --- a/app/src/main/java/foundation/e/blisslauncher/util/MainThreadInitializedObject.java +++ b/app/src/main/java/foundation/e/blisslauncher/util/MainThreadInitializedObject.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2018 The Android Open Source Project + * Copyright 2022 ECORP SAS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. -- GitLab From e6c5af7429f93655c7def5247ef7b05832967a0e Mon Sep 17 00:00:00 2001 From: Suphon Thanakornpakapong Date: Mon, 23 May 2022 19:51:43 +0700 Subject: [PATCH 3/5] Apply suggestions --- .../features/notification/NotificationService.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/foundation/e/blisslauncher/features/notification/NotificationService.java b/app/src/main/java/foundation/e/blisslauncher/features/notification/NotificationService.java index 2c28a77e13..e05c07f7ac 100755 --- a/app/src/main/java/foundation/e/blisslauncher/features/notification/NotificationService.java +++ b/app/src/main/java/foundation/e/blisslauncher/features/notification/NotificationService.java @@ -21,7 +21,7 @@ public class NotificationService extends NotificationListenerService { NotificationRepository mNotificationRepository; - private boolean mDotsEnabled; + private boolean mAreDotsEnabled; private SettingsCache mSettingsCache; private SettingsCache.OnChangeListener mNotificationSettingsChangedListener; @@ -30,7 +30,6 @@ public class NotificationService extends NotificationListenerService { super.onCreate(); mNotificationRepository = NotificationRepository.getNotificationRepository(); - // Register an observer to rebind the notification listener when dots are re-enabled. mSettingsCache = SettingsCache.INSTANCE.get(this); mNotificationSettingsChangedListener = this::onNotificationSettingsChanged; mSettingsCache.register(NOTIFICATION_BADGING_URI, @@ -46,7 +45,7 @@ public class NotificationService extends NotificationListenerService { } private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) { - mDotsEnabled = areNotificationDotsEnabled; + mAreDotsEnabled = areNotificationDotsEnabled; if (!areNotificationDotsEnabled && sIsConnected) { requestUnbind(); updateNotifications(); @@ -75,7 +74,7 @@ public class NotificationService extends NotificationListenerService { } private void updateNotifications() { - if (!mDotsEnabled) { + if (!mAreDotsEnabled) { mNotificationRepository.updateNotification(Collections.emptyList()); return; } -- GitLab From 74f191c38cd203729f06f244160a501405fef226 Mon Sep 17 00:00:00 2001 From: Suphon Thanakornpakapong Date: Mon, 13 Jun 2022 20:25:38 +0700 Subject: [PATCH 4/5] Remove SettingsCache --- .../e/blisslauncher/BlissLauncher.java | 26 ++- .../notification/NotificationService.java | 31 +-- .../e/blisslauncher/util/LooperExecutor.java | 118 ------------ .../util/MainThreadInitializedObject.java | 66 ------- .../e/blisslauncher/util/SettingsCache.java | 179 ------------------ 5 files changed, 36 insertions(+), 384 deletions(-) delete mode 100644 app/src/main/java/foundation/e/blisslauncher/util/LooperExecutor.java delete mode 100644 app/src/main/java/foundation/e/blisslauncher/util/MainThreadInitializedObject.java delete mode 100644 app/src/main/java/foundation/e/blisslauncher/util/SettingsCache.java diff --git a/app/src/main/java/foundation/e/blisslauncher/BlissLauncher.java b/app/src/main/java/foundation/e/blisslauncher/BlissLauncher.java index cd0786436e..c808f8158c 100755 --- a/app/src/main/java/foundation/e/blisslauncher/BlissLauncher.java +++ b/app/src/main/java/foundation/e/blisslauncher/BlissLauncher.java @@ -1,11 +1,13 @@ package foundation.e.blisslauncher; -import static foundation.e.blisslauncher.util.SettingsCache.NOTIFICATION_BADGING_URI; - import android.app.Application; import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.provider.Settings; import foundation.e.blisslauncher.core.DeviceProfile; import foundation.e.blisslauncher.core.IconsHandler; @@ -13,9 +15,11 @@ import foundation.e.blisslauncher.core.blur.BlurWallpaperProvider; import foundation.e.blisslauncher.core.customviews.WidgetHost; import foundation.e.blisslauncher.features.launcher.AppProvider; import foundation.e.blisslauncher.features.notification.NotificationService; -import foundation.e.blisslauncher.util.SettingsCache; public class BlissLauncher extends Application { + public static final Uri NOTIFICATION_BADGING_URI = + Settings.Secure.getUriFor("notification_badging"); + private IconsHandler iconsPackHandler; private DeviceProfile deviceProfile; @@ -35,13 +39,19 @@ public class BlissLauncher extends Application { connectAppProvider(); BlurWallpaperProvider.Companion.getInstance(this); - SettingsCache settingsCache = SettingsCache.INSTANCE.get(this); - SettingsCache.OnChangeListener notificationLister = this::onNotificationSettingsChanged; - settingsCache.register(NOTIFICATION_BADGING_URI, notificationLister); - onNotificationSettingsChanged(settingsCache.getValue(NOTIFICATION_BADGING_URI)); + ContentObserver notificationSettingsObserver = new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange) { + onNotificationSettingsChanged(); + } + }; + getContentResolver().registerContentObserver( + NOTIFICATION_BADGING_URI, false, notificationSettingsObserver); } - private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) { + private void onNotificationSettingsChanged() { + boolean areNotificationDotsEnabled = Settings.Secure.getInt( + getContentResolver(), NOTIFICATION_BADGING_URI.getLastPathSegment(), 1) == 1; if (areNotificationDotsEnabled) { NotificationService.requestRebind(new ComponentName( this, NotificationService.class)); diff --git a/app/src/main/java/foundation/e/blisslauncher/features/notification/NotificationService.java b/app/src/main/java/foundation/e/blisslauncher/features/notification/NotificationService.java index e05c07f7ac..e509c44bae 100755 --- a/app/src/main/java/foundation/e/blisslauncher/features/notification/NotificationService.java +++ b/app/src/main/java/foundation/e/blisslauncher/features/notification/NotificationService.java @@ -1,15 +1,17 @@ package foundation.e.blisslauncher.features.notification; -import static foundation.e.blisslauncher.util.SettingsCache.NOTIFICATION_BADGING_URI; +import static foundation.e.blisslauncher.BlissLauncher.NOTIFICATION_BADGING_URI; import android.content.Intent; +import android.database.ContentObserver; +import android.os.Handler; +import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import java.util.Collections; import foundation.e.blisslauncher.core.utils.ListUtil; -import foundation.e.blisslauncher.util.SettingsCache; /** * Created by falcon on 14/3/18. @@ -22,31 +24,34 @@ public class NotificationService extends NotificationListenerService { NotificationRepository mNotificationRepository; private boolean mAreDotsEnabled; - private SettingsCache mSettingsCache; - private SettingsCache.OnChangeListener mNotificationSettingsChangedListener; + private final ContentObserver mNotificationSettingsObserver = new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange) { + onNotificationSettingsChanged(); + } + }; @Override public void onCreate() { super.onCreate(); mNotificationRepository = NotificationRepository.getNotificationRepository(); - mSettingsCache = SettingsCache.INSTANCE.get(this); - mNotificationSettingsChangedListener = this::onNotificationSettingsChanged; - mSettingsCache.register(NOTIFICATION_BADGING_URI, - mNotificationSettingsChangedListener); - onNotificationSettingsChanged(mSettingsCache.getValue(NOTIFICATION_BADGING_URI)); + getContentResolver().registerContentObserver( + NOTIFICATION_BADGING_URI, false, mNotificationSettingsObserver); + onNotificationSettingsChanged(); } @Override public void onDestroy() { super.onDestroy(); - mSettingsCache.unregister(NOTIFICATION_BADGING_URI, mNotificationSettingsChangedListener); + getContentResolver().unregisterContentObserver(mNotificationSettingsObserver); mNotificationRepository.updateNotification(Collections.emptyList()); } - private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) { - mAreDotsEnabled = areNotificationDotsEnabled; - if (!areNotificationDotsEnabled && sIsConnected) { + private void onNotificationSettingsChanged() { + mAreDotsEnabled = Settings.Secure.getInt( + getContentResolver(), NOTIFICATION_BADGING_URI.getLastPathSegment(), 1) == 1; + if (!mAreDotsEnabled && sIsConnected) { requestUnbind(); updateNotifications(); } diff --git a/app/src/main/java/foundation/e/blisslauncher/util/LooperExecutor.java b/app/src/main/java/foundation/e/blisslauncher/util/LooperExecutor.java deleted file mode 100644 index 560a675ac9..0000000000 --- a/app/src/main/java/foundation/e/blisslauncher/util/LooperExecutor.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * 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 - */ -package foundation.e.blisslauncher.util; - -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Process; - -import java.util.List; -import java.util.concurrent.AbstractExecutorService; -import java.util.concurrent.TimeUnit; - -/** - * Extension of {@link AbstractExecutorService} which executed on a provided looper. - */ -public class LooperExecutor extends AbstractExecutorService { - - private final Handler mHandler; - - public LooperExecutor(Looper looper) { - mHandler = new Handler(looper); - } - - public Handler getHandler() { - return mHandler; - } - - @Override - public void execute(Runnable runnable) { - if (getHandler().getLooper() == Looper.myLooper()) { - runnable.run(); - } else { - getHandler().post(runnable); - } - } - - /** - * Same as execute, but never runs the action inline. - */ - public void post(Runnable runnable) { - getHandler().post(runnable); - } - - /** - * Not supported and throws an exception when used. - */ - @Override - @Deprecated - public void shutdown() { - throw new UnsupportedOperationException(); - } - - /** - * Not supported and throws an exception when used. - */ - @Override - @Deprecated - public List shutdownNow() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isShutdown() { - return false; - } - - @Override - public boolean isTerminated() { - return false; - } - - /** - * Not supported and throws an exception when used. - */ - @Override - @Deprecated - public boolean awaitTermination(long l, TimeUnit timeUnit) { - throw new UnsupportedOperationException(); - } - - /** - * Returns the thread for this executor - */ - public Thread getThread() { - return getHandler().getLooper().getThread(); - } - - /** - * Returns the looper for this executor - */ - public Looper getLooper() { - return getHandler().getLooper(); - } - - /** - * Set the priority of a thread, based on Linux priorities. - * @param priority Linux priority level, from -20 for highest scheduling priority - * to 19 for lowest scheduling priority. - * @see Process#setThreadPriority(int, int) - */ - public void setThreadPriority(int priority) { - Process.setThreadPriority(((HandlerThread) getThread()).getThreadId(), priority); - } -} diff --git a/app/src/main/java/foundation/e/blisslauncher/util/MainThreadInitializedObject.java b/app/src/main/java/foundation/e/blisslauncher/util/MainThreadInitializedObject.java deleted file mode 100644 index a6c2810e87..0000000000 --- a/app/src/main/java/foundation/e/blisslauncher/util/MainThreadInitializedObject.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * Copyright 2022 ECORP SAS - * - * 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. - * - * Modifications copyright 2021, Lawnchair - */ -package foundation.e.blisslauncher.util; - -import android.content.Context; -import android.os.Looper; - -import java.util.concurrent.ExecutionException; - -/** - * Utility class for defining singletons which are initiated on main thread. - */ -public class MainThreadInitializedObject { - - private static final LooperExecutor MAIN_EXECUTOR = new LooperExecutor(Looper.getMainLooper()); - - private final ObjectProvider mProvider; - private T mValue; - - public MainThreadInitializedObject(ObjectProvider provider) { - mProvider = provider; - } - - public T get(Context context) { - if (mValue == null) { - if (Looper.myLooper() == Looper.getMainLooper()) { - mValue = mProvider.get(context.getApplicationContext()); - onPostInit(context); - } else { - try { - return MAIN_EXECUTOR.submit(() -> get(context)).get(); - } catch (InterruptedException|ExecutionException e) { - throw new RuntimeException(e); - } - } - } - return mValue; - } - - protected void onPostInit(Context context) { } - - public T getNoCreate() { - return mValue; - } - - public interface ObjectProvider { - - T get(Context context); - } -} diff --git a/app/src/main/java/foundation/e/blisslauncher/util/SettingsCache.java b/app/src/main/java/foundation/e/blisslauncher/util/SettingsCache.java deleted file mode 100644 index 4f716d9d13..0000000000 --- a/app/src/main/java/foundation/e/blisslauncher/util/SettingsCache.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * 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. - */ - -package foundation.e.blisslauncher.util; - -import static android.provider.Settings.System.ACCELEROMETER_ROTATION; - -import android.content.ContentResolver; -import android.content.Context; -import android.database.ContentObserver; -import android.net.Uri; -import android.os.Handler; -import android.provider.Settings; - -import androidx.annotation.VisibleForTesting; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * ContentObserver over Settings keys that also has a caching layer. - * Consumers can register for callbacks via {@link #register(Uri, OnChangeListener)} and - * {@link #unregister(Uri, OnChangeListener)} methods. - * - * This can be used as a normal cache without any listeners as well via the - * {@link #getValue(Uri, int)} and {@link #onChange)} to update (and subsequently call - * get) - * - * The cache will be invalidated/updated through the normal - * {@link ContentObserver#onChange(boolean)} calls - * - * Cache will also be updated if a key queried is missing (even if it has no listeners registered). - */ -public class SettingsCache extends ContentObserver implements AutoCloseable { - - /** Hidden field Settings.Secure.NOTIFICATION_BADGING */ - public static final Uri NOTIFICATION_BADGING_URI = - Settings.Secure.getUriFor("notification_badging"); - /** Hidden field Settings.Secure.ONE_HANDED_MODE_ENABLED */ - public static final String ONE_HANDED_ENABLED = "one_handed_mode_enabled"; - /** Hidden field Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED */ - public static final String ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED = - "swipe_bottom_to_notification_enabled"; - public static final Uri ROTATION_SETTING_URI = - Settings.System.getUriFor(ACCELEROMETER_ROTATION); - - private static final String SYSTEM_URI_PREFIX = Settings.System.CONTENT_URI.toString(); - - /** - * Caches the last seen value for registered keys. - */ - private Map mKeyCache = new ConcurrentHashMap<>(); - private final Map> mListenerMap = new HashMap<>(); - protected final ContentResolver mResolver; - - /** - * Singleton instance - */ - public static MainThreadInitializedObject INSTANCE = - new MainThreadInitializedObject<>(SettingsCache::new); - - private SettingsCache(Context context) { - super(new Handler()); - mResolver = context.getContentResolver(); - } - - @Override - public void close() { - mResolver.unregisterContentObserver(this); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - // We use default of 1, but if we're getting an onChange call, can assume a non-default - // value will exist - boolean newVal = updateValue(uri, 1 /* Effectively Unused */); - if (!mListenerMap.containsKey(uri)) { - return; - } - - for (OnChangeListener listener : mListenerMap.get(uri)) { - listener.onSettingsChanged(newVal); - } - } - - /** - * Returns the value for this classes key from the cache. If not in cache, will call - * {@link #updateValue(Uri, int)} to fetch. - */ - public boolean getValue(Uri keySetting) { - return getValue(keySetting, 1); - } - - /** - * Returns the value for this classes key from the cache. If not in cache, will call - * {@link #updateValue(Uri, int)} to fetch. - */ - public boolean getValue(Uri keySetting, int defaultValue) { - if (mKeyCache.containsKey(keySetting)) { - return mKeyCache.get(keySetting); - } else { - return updateValue(keySetting, defaultValue); - } - } - - /** - * Does not de-dupe if you add same listeners for the same key multiple times. - * Unregister once complete using {@link #unregister(Uri, OnChangeListener)} - */ - public void register(Uri uri, OnChangeListener changeListener) { - if (mListenerMap.containsKey(uri)) { - mListenerMap.get(uri).add(changeListener); - } else { - CopyOnWriteArrayList l = new CopyOnWriteArrayList<>(); - l.add(changeListener); - mListenerMap.put(uri, l); - mResolver.registerContentObserver(uri, false, this); - } - } - - private boolean updateValue(Uri keyUri, int defaultValue) { - String key = keyUri.getLastPathSegment(); - boolean newVal; - if (keyUri.toString().startsWith(SYSTEM_URI_PREFIX)) { - newVal = Settings.System.getInt(mResolver, key, defaultValue) == 1; - } else { // SETTING_SECURE - newVal = Settings.Secure.getInt(mResolver, key, defaultValue) == 1; - } - - mKeyCache.put(keyUri, newVal); - return newVal; - } - - /** - * Call to stop receiving updates on the given {@param listener}. - * This Uri/Listener pair must correspond to the same pair called with for - * {@link #register(Uri, OnChangeListener)} - */ - public void unregister(Uri uri, OnChangeListener listener) { - List listenersToRemoveFrom = mListenerMap.get(uri); - if (!listenersToRemoveFrom.contains(listener)) { - return; - } - - listenersToRemoveFrom.remove(listener); - if (listenersToRemoveFrom.isEmpty()) { - mListenerMap.remove(uri); - } - } - - /** - * Don't use this. Ever. - * @param keyCache Cache to replace {@link #mKeyCache} - */ - @VisibleForTesting - void setKeyCache(Map keyCache) { - mKeyCache = keyCache; - } - - public interface OnChangeListener { - void onSettingsChanged(boolean isEnabled); - } -} -- GitLab From 6f83a6d9a8db397f2907aff878626148d8263981 Mon Sep 17 00:00:00 2001 From: Suphon Thanakornpakapong Date: Wed, 6 Jul 2022 10:45:13 +0700 Subject: [PATCH 5/5] Change mAreDotsEnabled to mAreDotsDisabled --- .../features/notification/NotificationService.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/foundation/e/blisslauncher/features/notification/NotificationService.java b/app/src/main/java/foundation/e/blisslauncher/features/notification/NotificationService.java index e509c44bae..6bd0657c67 100755 --- a/app/src/main/java/foundation/e/blisslauncher/features/notification/NotificationService.java +++ b/app/src/main/java/foundation/e/blisslauncher/features/notification/NotificationService.java @@ -23,7 +23,7 @@ public class NotificationService extends NotificationListenerService { NotificationRepository mNotificationRepository; - private boolean mAreDotsEnabled; + private boolean mAreDotsDisabled; private final ContentObserver mNotificationSettingsObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { @@ -49,9 +49,9 @@ public class NotificationService extends NotificationListenerService { } private void onNotificationSettingsChanged() { - mAreDotsEnabled = Settings.Secure.getInt( - getContentResolver(), NOTIFICATION_BADGING_URI.getLastPathSegment(), 1) == 1; - if (!mAreDotsEnabled && sIsConnected) { + mAreDotsDisabled = Settings.Secure.getInt( + getContentResolver(), NOTIFICATION_BADGING_URI.getLastPathSegment(), 1) != 1; + if (mAreDotsDisabled && sIsConnected) { requestUnbind(); updateNotifications(); } @@ -79,7 +79,7 @@ public class NotificationService extends NotificationListenerService { } private void updateNotifications() { - if (!mAreDotsEnabled) { + if (mAreDotsDisabled) { mNotificationRepository.updateNotification(Collections.emptyList()); return; } -- GitLab