Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
e
os
BlissLauncher
Commits
5e0f444a
Commit
5e0f444a
authored
May 23, 2022
by
Suphon Thanakornpakapong
Browse files
Respect global notification dots settings
parent
6866ac55
Pipeline
#187868
passed with stage
in 2 minutes and 55 seconds
Changes
5
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
app/src/main/java/foundation/e/blisslauncher/BlissLauncher.java
View file @
5e0f444a
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
)
{
...
...
app/src/main/java/foundation/e/blisslauncher/features/notification/NotificationService.java
View file @
5e0f444a
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
()));
}
...
...
app/src/main/java/foundation/e/blisslauncher/util/LooperExecutor.java
0 → 100644
View file @
5e0f444a
/*
* 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
<
Runnable
>
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
);
}
}
app/src/main/java/foundation/e/blisslauncher/util/MainThreadInitializedObject.java
0 → 100644
View file @
5e0f444a
/*
* 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
<
T
>
{
private
static
final
LooperExecutor
MAIN_EXECUTOR
=
new
LooperExecutor
(
Looper
.
getMainLooper
());
private
final
ObjectProvider
<
T
>
mProvider
;
private
T
mValue
;
public
MainThreadInitializedObject
(
ObjectProvider
<
T
>
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
>
{
T
get
(
Context
context
);
}
}
app/src/main/java/foundation/e/blisslauncher/util/SettingsCache.java
0 → 100644
View file @
5e0f444a
/*
* 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
<
Uri
,
Boolean
>
mKeyCache
=
new
ConcurrentHashMap
<>();
private
final
Map
<
Uri
,
CopyOnWriteArrayList
<
OnChangeListener
>>
mListenerMap
=
new
HashMap
<>();
protected
final
ContentResolver
mResolver
;
/**
* Singleton instance
*/
public
static
MainThreadInitializedObject
<
SettingsCache
>
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
<
OnChangeListener
>
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
<
OnChangeListener
>
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
<
Uri
,
Boolean
>
keyCache
)
{
mKeyCache
=
keyCache
;
}
public
interface
OnChangeListener
{
void
onSettingsChanged
(
boolean
isEnabled
);
}
}
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment