Add the app notifications filter

This commit is contained in:
Tommaso Berlose 2020-10-14 12:54:39 +02:00
parent 5259a81cfb
commit 1c1f55e20a
19 changed files with 376 additions and 41 deletions

Binary file not shown.

View File

@ -23,8 +23,8 @@ android {
applicationId "com.tommasoberlose.anotherwidget" applicationId "com.tommasoberlose.anotherwidget"
minSdkVersion 23 minSdkVersion 23
targetSdkVersion 29 targetSdkVersion 29
versionCode 106 versionCode 107
versionName "2.0.14" versionName "2.0.15"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("String", "GOOGLE_API_KEY", apikeyProperties['GOOGLE_API_KEY']) buildConfigField("String", "GOOGLE_API_KEY", apikeyProperties['GOOGLE_API_KEY'])
@ -64,13 +64,13 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.13' testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
// UI // UI
implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
implementation 'com.google.android.material:material:1.3.0-alpha03' implementation 'com.google.android.material:material:1.3.0-alpha03'
implementation 'androidx.browser:browser:1.2.0' implementation 'androidx.browser:browser:1.2.0'
implementation 'net.idik:slimadapter:2.1.2' implementation 'net.idik:slimadapter:2.1.2'
@ -84,15 +84,15 @@ dependencies {
implementation "androidx.work:work-runtime-ktx:2.4.0" implementation "androidx.work:work-runtime-ktx:2.4.0"
// EventBus // EventBus
implementation 'org.greenrobot:eventbus:3.1.1' implementation 'org.greenrobot:eventbus:3.2.0'
// Navigation // Navigation
implementation 'androidx.navigation:navigation-fragment:2.3.0' implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.0' implementation 'androidx.navigation:navigation-ui-ktx:2.3.0'
// Other // Other
implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.multidex:multidex:2.0.1'
implementation 'joda-time:joda-time:2.10.3' implementation 'joda-time:joda-time:2.10.6'
implementation 'me.everything:providers-android:1.0.1' implementation 'me.everything:providers-android:1.0.1'
implementation 'com.github.warkiz.widget:indicatorseekbar:2.1.2' implementation 'com.github.warkiz.widget:indicatorseekbar:2.1.2'
@ -121,8 +121,8 @@ dependencies {
//Retrofit //Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.squareup.retrofit2:converter-gson:2.8.1' implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.0' implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0'
implementation "com.github.haroldadmin:NetworkResponseAdapter:4.0.1" implementation "com.github.haroldadmin:NetworkResponseAdapter:4.0.1"
//Coroutines //Coroutines
@ -137,7 +137,7 @@ dependencies {
implementation 'androidx.preference:preference-ktx:1.1.1' implementation 'androidx.preference:preference-ktx:1.1.1'
// Permissions // Permissions
implementation 'com.karumi:dexter:6.1.0' implementation 'com.karumi:dexter:6.2.1'
// Fonts // Fonts
implementation 'com.github.firatkarababa:downloadable-font-list-library:1.0.2' implementation 'com.github.firatkarababa:downloadable-font-list-library:1.0.2'

View File

@ -40,6 +40,7 @@
<activity android:name=".ui.activities.CustomDateActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" /> <activity android:name=".ui.activities.CustomDateActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.IntegrationsActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" /> <activity android:name=".ui.activities.IntegrationsActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.MusicPlayersFilterActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" /> <activity android:name=".ui.activities.MusicPlayersFilterActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<activity android:name=".ui.activities.AppNotificationsFilterActivity" android:launchMode="singleInstance" android:screenOrientation="portrait" />
<receiver android:name=".ui.widgets.MainWidget"> <receiver android:name=".ui.widgets.MainWidget">

View File

@ -23,9 +23,11 @@ import com.karumi.dexter.listener.multi.MultiplePermissionsListener
import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.global.Constants import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper
import com.tommasoberlose.anotherwidget.helpers.AlarmHelper import com.tommasoberlose.anotherwidget.helpers.AlarmHelper
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver import com.tommasoberlose.anotherwidget.receivers.ActivityDetectionReceiver
import com.tommasoberlose.anotherwidget.ui.activities.AppNotificationsFilterActivity
import com.tommasoberlose.anotherwidget.ui.activities.MusicPlayersFilterActivity import com.tommasoberlose.anotherwidget.ui.activities.MusicPlayersFilterActivity
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import kotlinx.android.synthetic.main.glance_provider_settings_layout.view.* import kotlinx.android.synthetic.main.glance_provider_settings_layout.view.*
@ -94,6 +96,9 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
view.action_filter_notifications_app.isVisible = provider == Constants.GlanceProviderId.NOTIFICATIONS view.action_filter_notifications_app.isVisible = provider == Constants.GlanceProviderId.NOTIFICATIONS
if (provider == Constants.GlanceProviderId.NOTIFICATIONS) { if (provider == Constants.GlanceProviderId.NOTIFICATIONS) {
checkLastNotificationsPermission(view) checkLastNotificationsPermission(view)
view.action_filter_notifications_app.setOnClickListener {
context.startActivity(Intent(context, AppNotificationsFilterActivity::class.java))
}
} }
/* GREETINGS */ /* GREETINGS */
@ -200,7 +205,7 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
private fun checkNotificationPermission(view: View) { private fun checkNotificationPermission(view: View) {
when { when {
NotificationManagerCompat.getEnabledListenerPackages(context).contains(context.packageName) -> { ActiveNotificationsHelper.checkNotificationAccess(context) -> {
view.warning_container.isVisible = false view.warning_container.isVisible = false
MediaPlayerHelper.updatePlayingMediaInfo(context) MediaPlayerHelper.updatePlayingMediaInfo(context)
} }
@ -220,7 +225,7 @@ class GlanceSettingsDialog(val context: Activity, val provider: Constants.Glance
private fun checkLastNotificationsPermission(view: View) { private fun checkLastNotificationsPermission(view: View) {
when { when {
NotificationManagerCompat.getEnabledListenerPackages(context).contains(context.packageName) -> { ActiveNotificationsHelper.checkNotificationAccess(context) -> {
view.warning_container.isVisible = false view.warning_container.isVisible = false
} }
Preferences.showNotifications -> { Preferences.showNotifications -> {

View File

@ -138,6 +138,7 @@ object Preferences : KotprefModel() {
var mediaPlayerArtist by stringPref(default = "") var mediaPlayerArtist by stringPref(default = "")
var mediaPlayerPackage by stringPref(default = "") var mediaPlayerPackage by stringPref(default = "")
var musicPlayersFilter by stringPref(default = "") var musicPlayersFilter by stringPref(default = "")
var appNotificationsFilter by stringPref(default = "")
// Integrations // Integrations
var installedIntegrations by intPref(default = 0) var installedIntegrations by intPref(default = 0)

View File

@ -1,15 +1,14 @@
package com.tommasoberlose.anotherwidget.helpers package com.tommasoberlose.anotherwidget.helpers
import android.app.Notification import android.content.ContentResolver
import android.app.NotificationManager
import android.content.Context import android.content.Context
import android.service.notification.StatusBarNotification import android.provider.Settings
import android.util.Log import android.util.Log
import androidx.core.app.NotificationManagerCompat
import com.chibatching.kotpref.Kotpref import com.chibatching.kotpref.Kotpref
import com.chibatching.kotpref.blockingBulk import com.chibatching.kotpref.blockingBulk
import com.google.gson.Gson
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import java.lang.Exception import com.tommasoberlose.anotherwidget.receivers.NotificationListener
object ActiveNotificationsHelper { object ActiveNotificationsHelper {
fun showLastNotification(): Boolean { fun showLastNotification(): Boolean {
@ -25,4 +24,22 @@ object ActiveNotificationsHelper {
remove(Preferences::lastNotificationIcon) remove(Preferences::lastNotificationIcon)
} }
} }
fun checkNotificationAccess(context: Context): Boolean {
val contentResolver: ContentResolver = context.contentResolver
val enabledNotificationListeners =
Settings.Secure.getString(contentResolver, "enabled_notification_listeners")
val packageName: String = context.packageName
return NotificationManagerCompat.getEnabledListenerPackages(context).contains(packageName) && (enabledNotificationListeners != null && enabledNotificationListeners.contains(NotificationListener::class.java.name))
}
fun isAppAccepted(appPkg: String): Boolean = Preferences.appNotificationsFilter == "" || Preferences.appNotificationsFilter.contains(appPkg)
fun toggleAppFilter(appPkg: String) {
if (Preferences.appNotificationsFilter == "" || !Preferences.appNotificationsFilter.contains(appPkg)) {
Preferences.appNotificationsFilter = Preferences.appNotificationsFilter.split(",").union(listOf(appPkg)).joinToString(separator = ",")
} else {
Preferences.appNotificationsFilter = Preferences.appNotificationsFilter.split(",").filter { it != appPkg }.joinToString(separator = ",")
}
}
} }

View File

@ -6,7 +6,6 @@ import android.media.MediaMetadata
import android.media.session.MediaController import android.media.session.MediaController
import android.media.session.MediaSessionManager import android.media.session.MediaSessionManager
import android.media.session.PlaybackState import android.media.session.PlaybackState
import androidx.core.app.NotificationManagerCompat
import com.chibatching.kotpref.Kotpref import com.chibatching.kotpref.Kotpref
import com.chibatching.kotpref.blockingBulk import com.chibatching.kotpref.blockingBulk
import com.chibatching.kotpref.bulk import com.chibatching.kotpref.bulk
@ -16,7 +15,7 @@ import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import java.lang.Exception import java.lang.Exception
object MediaPlayerHelper { object MediaPlayerHelper {
fun isSomeonePlaying(context: Context) = Preferences.showMusic && NotificationManagerCompat.getEnabledListenerPackages(context).contains(context.packageName) && Preferences.mediaPlayerTitle != "" fun isSomeonePlaying(context: Context) = Preferences.showMusic && ActiveNotificationsHelper.checkNotificationAccess(context) && Preferences.mediaPlayerTitle != ""
fun getMediaInfo(): String { fun getMediaInfo(): String {
return if (Preferences.mediaPlayerArtist == "") { return if (Preferences.mediaPlayerArtist == "") {
@ -28,7 +27,7 @@ object MediaPlayerHelper {
fun updatePlayingMediaInfo(context: Context) { fun updatePlayingMediaInfo(context: Context) {
Kotpref.init(context) Kotpref.init(context)
if (NotificationManagerCompat.getEnabledListenerPackages(context).contains(context.packageName)) { if (ActiveNotificationsHelper.checkNotificationAccess(context)) {
val list = try { val list = try {
(context.getSystemService(Context.MEDIA_SESSION_SERVICE) as MediaSessionManager).getActiveSessions( (context.getSystemService(Context.MEDIA_SESSION_SERVICE) as MediaSessionManager).getActiveSessions(
ComponentName(context.packageName, NotificationListener::class.java.name) ComponentName(context.packageName, NotificationListener::class.java.name)
@ -89,7 +88,7 @@ object MediaPlayerHelper {
} }
} }
fun isMusicPlayerAccepted(appPkg: String): Boolean = Preferences.musicPlayersFilter.contains(appPkg) fun isMusicPlayerAccepted(appPkg: String): Boolean = Preferences.musicPlayersFilter == "" || Preferences.musicPlayersFilter.contains(appPkg)
fun toggleMusicPlayerFilter(appPkg: String) { fun toggleMusicPlayerFilter(appPkg: String) {
if (Preferences.musicPlayersFilter == "" || !Preferences.musicPlayersFilter.contains(appPkg)) { if (Preferences.musicPlayersFilter == "" || !Preferences.musicPlayersFilter.contains(appPkg)) {

View File

@ -7,6 +7,7 @@ import android.media.session.MediaSession
import android.os.Build import android.os.Build
import android.service.notification.NotificationListenerService import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification import android.service.notification.StatusBarNotification
import android.util.Log
import com.tommasoberlose.anotherwidget.global.Actions import com.tommasoberlose.anotherwidget.global.Actions
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper
@ -30,7 +31,7 @@ class NotificationListener : NotificationListenerService() {
val isGroupHeader = sbn.notification.flags and Notification.FLAG_GROUP_SUMMARY != 0 val isGroupHeader = sbn.notification.flags and Notification.FLAG_GROUP_SUMMARY != 0
val isOngoing = sbn.notification.flags and Notification.FLAG_ONGOING_EVENT != 0 val isOngoing = sbn.notification.flags and Notification.FLAG_ONGOING_EVENT != 0
if (bundle.containsKey(Notification.EXTRA_TITLE) && !isGroupHeader && !isOngoing) { if (bundle.containsKey(Notification.EXTRA_TITLE) && !isGroupHeader && !isOngoing && ActiveNotificationsHelper.isAppAccepted(sbn.packageName)) {
Preferences.lastNotificationId = sbn.id Preferences.lastNotificationId = sbn.id
Preferences.lastNotificationTitle = bundle.getString(Notification.EXTRA_TITLE) ?: "" Preferences.lastNotificationTitle = bundle.getString(Notification.EXTRA_TITLE) ?: ""
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {

View File

@ -0,0 +1,143 @@
package com.tommasoberlose.anotherwidget.ui.activities
import android.content.pm.ResolveInfo
import android.os.Bundle
import android.view.View
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide
import com.tommasoberlose.anotherwidget.R
import com.tommasoberlose.anotherwidget.databinding.ActivityAppNotificationsFilterBinding
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper
import com.tommasoberlose.anotherwidget.ui.viewmodels.AppNotificationsViewModel
import kotlinx.android.synthetic.main.activity_app_notifications_filter.*
import kotlinx.coroutines.*
import net.idik.lib.slimadapter.SlimAdapter
class AppNotificationsFilterActivity : AppCompatActivity() {
private lateinit var adapter: SlimAdapter
private lateinit var viewModel: AppNotificationsViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this).get(AppNotificationsViewModel::class.java)
val binding = DataBindingUtil.setContentView<ActivityAppNotificationsFilterBinding>(this, R.layout.activity_app_notifications_filter)
list_view.setHasFixedSize(true)
val mLayoutManager = LinearLayoutManager(this)
list_view.layoutManager = mLayoutManager
adapter = SlimAdapter.create()
adapter
.register<ResolveInfo>(R.layout.application_info_layout) { item, injector ->
injector
.text(R.id.text, item.loadLabel(viewModel.pm))
.with<ImageView>(R.id.icon) {
Glide
.with(this)
.load(item.loadIcon(viewModel.pm))
.centerCrop()
.into(it)
}
.visible(R.id.checkBox)
.clicked(R.id.item) {
toggleApp(item)
adapter.notifyItemRangeChanged(0, adapter.data.size)
}
.clicked(R.id.checkBox) {
toggleApp(item)
adapter.notifyItemRangeChanged(0, adapter.data.size)
}
.checked(R.id.checkBox, ActiveNotificationsHelper.isAppAccepted(item.activityInfo.packageName))
}
.attachTo(list_view)
setupListener()
subscribeUi(binding, viewModel)
search.requestFocus()
}
private var filterJob: Job? = null
private fun subscribeUi(binding: ActivityAppNotificationsFilterBinding, viewModel: AppNotificationsViewModel) {
binding.viewModel = viewModel
binding.lifecycleOwner = this
viewModel.appList.observe(this, Observer {
updateList(list = it)
loader.visibility = View.INVISIBLE
})
viewModel.searchInput.observe(this, Observer { search ->
updateList(search = search)
clear_search.isVisible = search.isNotBlank()
})
viewModel.appNotificationsFilter.observe(this, {
updateList()
clear_selection.isVisible = Preferences.appNotificationsFilter != ""
})
}
private fun updateList(list: List<ResolveInfo>? = viewModel.appList.value, search: String? = viewModel.searchInput.value) {
loader.visibility = View.VISIBLE
filterJob?.cancel()
filterJob = lifecycleScope.launch(Dispatchers.IO) {
if (list != null && list.isNotEmpty()) {
delay(200)
val filteredList: List<ResolveInfo> = if (search == null || search == "") {
list
} else {
list.filter {
it.loadLabel(viewModel.pm).contains(search, true)
}
}.sortedWith { app1, app2 ->
if (ActiveNotificationsHelper.isAppAccepted(app1.activityInfo.packageName) && ActiveNotificationsHelper.isAppAccepted(app2.activityInfo.packageName)) {
app1.loadLabel(viewModel.pm).toString().compareTo(app2.loadLabel(viewModel.pm).toString(), ignoreCase = true)
} else if (ActiveNotificationsHelper.isAppAccepted(app1.activityInfo.packageName)) {
-1
} else if (ActiveNotificationsHelper.isAppAccepted(app2.activityInfo.packageName)) {
1
} else {
app1.loadLabel(viewModel.pm).toString().compareTo(app2.loadLabel(viewModel.pm).toString(), ignoreCase = true)
}
}
withContext(Dispatchers.Main) {
adapter.updateData(filteredList)
loader.visibility = View.INVISIBLE
}
}
}
}
private fun setupListener() {
action_back.setOnClickListener {
onBackPressed()
}
clear_search.setOnClickListener {
viewModel.searchInput.value = ""
}
clear_selection.setOnClickListener {
Preferences.appNotificationsFilter = ""
}
}
private fun toggleApp(app: ResolveInfo) {
ActiveNotificationsHelper.toggleAppFilter(app.activityInfo.packageName)
}
}

View File

@ -142,7 +142,7 @@ class MusicPlayersFilterActivity : AppCompatActivity() {
} }
clear_selection.setOnClickListener { clear_selection.setOnClickListener {
Preferences.musicPlayersFilter = "," Preferences.musicPlayersFilter = ""
} }
} }

View File

@ -36,6 +36,7 @@ import com.tommasoberlose.anotherwidget.components.GlanceSettingsDialog
import com.tommasoberlose.anotherwidget.databinding.FragmentGlanceSettingsBinding import com.tommasoberlose.anotherwidget.databinding.FragmentGlanceSettingsBinding
import com.tommasoberlose.anotherwidget.global.Constants import com.tommasoberlose.anotherwidget.global.Constants
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.ActiveNotificationsHelper
import com.tommasoberlose.anotherwidget.helpers.AlarmHelper import com.tommasoberlose.anotherwidget.helpers.AlarmHelper
import com.tommasoberlose.anotherwidget.helpers.GlanceProviderHelper import com.tommasoberlose.anotherwidget.helpers.GlanceProviderHelper
import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper import com.tommasoberlose.anotherwidget.helpers.MediaPlayerHelper
@ -121,9 +122,7 @@ class GlanceTabFragment : Fragment() {
when (provider) { when (provider) {
Constants.GlanceProviderId.PLAYING_SONG -> { Constants.GlanceProviderId.PLAYING_SONG -> {
when { when {
NotificationManagerCompat.getEnabledListenerPackages(requireContext()) ActiveNotificationsHelper.checkNotificationAccess(requireContext()) -> {
.contains(
requireContext().packageName) -> {
MediaPlayerHelper.updatePlayingMediaInfo(requireContext()) MediaPlayerHelper.updatePlayingMediaInfo(requireContext())
injector.visibility(R.id.error_icon, View.GONE) injector.visibility(R.id.error_icon, View.GONE)
injector.visibility(R.id.info_icon, View.VISIBLE) injector.visibility(R.id.info_icon, View.VISIBLE)
@ -167,9 +166,7 @@ class GlanceTabFragment : Fragment() {
} }
Constants.GlanceProviderId.NOTIFICATIONS -> { Constants.GlanceProviderId.NOTIFICATIONS -> {
when { when {
NotificationManagerCompat.getEnabledListenerPackages(requireContext()) ActiveNotificationsHelper.checkNotificationAccess(requireContext()) -> {
.contains(
requireContext().packageName) -> {
injector.visibility(R.id.error_icon, View.GONE) injector.visibility(R.id.error_icon, View.GONE)
injector.visibility(R.id.info_icon, View.VISIBLE) injector.visibility(R.id.info_icon, View.VISIBLE)
injector.text(R.id.label, injector.text(R.id.label,
@ -305,6 +302,7 @@ class GlanceTabFragment : Fragment() {
adapter.updateData( adapter.updateData(
GlanceProviderHelper.getGlanceProviders(requireContext()) GlanceProviderHelper.getGlanceProviders(requireContext())
.mapNotNull { GlanceProviderHelper.getGlanceProviderById(requireContext(), it) } .mapNotNull { GlanceProviderHelper.getGlanceProviderById(requireContext(), it) }
.filterNot { it.id == Constants.GlanceProviderId.GREETINGS.id }
) )
providers_list.isNestedScrollingEnabled = false providers_list.isNestedScrollingEnabled = false

View File

@ -72,8 +72,6 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
subscribeUi(viewModel)
// Viewpager // Viewpager
pager.adapter = ViewPagerAdapter(requireActivity()) pager.adapter = ViewPagerAdapter(requireActivity())
pager.offscreenPageLimit = 4 pager.offscreenPageLimit = 4
@ -89,17 +87,20 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
}.attach() }.attach()
// Init clock // Init clock
time.setTextColor(ColorHelper.getClockFontColor(activity?.isDarkTheme() == true)) if (Preferences.showClock) {
time.setTextSize(TypedValue.COMPLEX_UNIT_SP, Preferences.clockTextSize.toPixel(requireContext())) time.setTextColor(ColorHelper.getClockFontColor(activity?.isDarkTheme() == true))
time_am_pm.setTextColor(ColorHelper.getClockFontColor(activity?.isDarkTheme() == true)) time.setTextSize(TypedValue.COMPLEX_UNIT_SP,
time_am_pm.setTextSize(TypedValue.COMPLEX_UNIT_SP, Preferences.clockTextSize.toPixel(requireContext()) / 5 * 2) Preferences.clockTextSize.toPixel(requireContext()))
time_am_pm.setTextColor(ColorHelper.getClockFontColor(activity?.isDarkTheme() == true))
time_am_pm.setTextSize(TypedValue.COMPLEX_UNIT_SP,
Preferences.clockTextSize.toPixel(requireContext()) / 5 * 2)
}
time_container.isVisible = Preferences.showClock time_container.isVisible = Preferences.showClock
preview.layoutParams = preview.layoutParams.apply { preview.layoutParams = preview.layoutParams.apply {
height = PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel(requireContext()) else 0 height = PREVIEW_BASE_HEIGHT.toPixel(requireContext()) + if (Preferences.showClock) 100.toPixel(requireContext()) else 0
} }
subscribeUi(viewModel) subscribeUi(viewModel)
updateUI()
// Warnings // Warnings
if (getString(R.string.xiaomi_manufacturer).equals(Build.MANUFACTURER, ignoreCase = true) && Preferences.showXiaomiWarning) { if (getString(R.string.xiaomi_manufacturer).equals(Build.MANUFACTURER, ignoreCase = true) && Preferences.showXiaomiWarning) {
@ -361,7 +362,7 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList
tabs?.getTabAt(4)?.orCreateBadge?.apply { tabs?.getTabAt(4)?.orCreateBadge?.apply {
backgroundColor = ContextCompat.getColor(requireContext(), R.color.errorColorText) backgroundColor = ContextCompat.getColor(requireContext(), R.color.errorColorText)
badgeGravity = BadgeDrawable.TOP_END badgeGravity = BadgeDrawable.TOP_END
}?.isVisible = ((Preferences.showMusic || Preferences.showNotifications) && !NotificationManagerCompat.getEnabledListenerPackages(requireContext()).contains(requireContext().packageName)) || }?.isVisible = ((Preferences.showMusic || Preferences.showNotifications) && !ActiveNotificationsHelper.checkNotificationAccess(requireContext())) ||
(Preferences.showDailySteps && !(Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || requireActivity().checkGrantedPermission(Manifest.permission.ACTIVITY_RECOGNITION))) || (Preferences.showDailySteps && !(Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || requireActivity().checkGrantedPermission(Manifest.permission.ACTIVITY_RECOGNITION))) ||
(AlarmHelper.isAlarmProbablyWrong(requireContext())) (AlarmHelper.isAlarmProbablyWrong(requireContext()))
} }

View File

@ -0,0 +1,39 @@
package com.tommasoberlose.anotherwidget.ui.viewmodels
import android.app.Application
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.chibatching.kotpref.livedata.asLiveData
import com.tommasoberlose.anotherwidget.global.Preferences
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class AppNotificationsViewModel(application: Application) : AndroidViewModel(application) {
val pm: PackageManager by lazy { application.packageManager }
val appList: MutableLiveData<List<ResolveInfo>> = MutableLiveData()
val searchInput: MutableLiveData<String> = MutableLiveData("")
var appNotificationsFilter = Preferences.asLiveData(Preferences::appNotificationsFilter)
init {
viewModelScope.launch(Dispatchers.IO) {
val mainIntent = Intent(Intent.ACTION_MAIN, null).apply {
addCategory(Intent.CATEGORY_LAUNCHER)
}
val app = application.packageManager.queryIntentActivities(mainIntent, 0)
val sortedApp = app.sortedWith(Comparator { app1: ResolveInfo, app2: ResolveInfo ->
app1.loadLabel(pm).toString().compareTo(app2.loadLabel(pm).toString())
})
withContext(Dispatchers.Main) {
appList.postValue(sortedApp)
}
}
}
}

View File

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="viewModel"
type="com.tommasoberlose.anotherwidget.ui.viewmodels.AppNotificationsViewModel" />
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/colorPrimaryDark"
tools:context="com.tommasoberlose.anotherwidget.ui.activities.AppNotificationsFilterActivity">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardElevation="2dp"
app:cardCornerRadius="0dp"
android:id="@+id/toolbar"
app:cardBackgroundColor="@color/colorPrimary">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="56dp"
android:gravity="center_vertical"
android:paddingLeft="8dp"
android:paddingRight="8dp">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="12dp"
android:layout_marginTop="2dp"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:id="@+id/action_back"
app:tint="@color/colorPrimaryText"
android:src="@drawable/round_arrow_back" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:text="@string/applications_filter_title"
android:gravity="center"
style="@style/AnotherWidget.Main.Title"/>
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="12dp"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/round_clear_all"
app:tint="@color/colorPrimaryText"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:id="@+id/clear_selection" />
</RelativeLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardElevation="2dp"
app:cardCornerRadius="0dp"
app:cardBackgroundColor="@color/colorPrimary">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="@color/colorPrimaryDark"
app:cardCornerRadius="9dp"
app:cardElevation="0dp"
android:layout_marginTop="8dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp">
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@color/colorPrimaryDark"
android:paddingStart="16dp"
android:paddingEnd="64dp"
android:inputType="textCapWords"
android:textColor="@color/colorPrimaryText"
android:textColorHint="@color/colorSecondaryText"
android:textAlignment="viewStart"
android:id="@+id/search"
android:fontFamily="@font/google_sans"
android:gravity="center_vertical|start"
android:hint="@string/search"
android:text="@={viewModel.searchInput}"
android:autofillHints="" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="12dp"
android:src="@drawable/round_close"
app:tint="@color/colorPrimaryText"
android:layout_gravity="center_vertical|end"
android:id="@+id/clear_search" />
</com.google.android.material.card.MaterialCardView>
</com.google.android.material.card.MaterialCardView>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<androidx.core.widget.ContentLoadingProgressBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
android:indeterminateTint="@color/colorAccent"
android:layout_marginTop="-7dp"
android:id="@+id/loader"
style="@style/Widget.AppCompat.ProgressBar.Horizontal" />
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/list_view" />
</RelativeLayout>
</LinearLayout>
</layout>

View File

@ -49,6 +49,9 @@
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:padding="12dp" android:padding="12dp"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/round_clear_all" android:src="@drawable/round_clear_all"
app:tint="@color/colorPrimaryText" app:tint="@color/colorPrimaryText"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"

View File

@ -197,12 +197,12 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="16sp" android:textSize="16sp"
style="@style/AnotherWidget.Settings.Title" style="@style/AnotherWidget.Settings.Title"
android:text="App notifications filter"/> android:text="@string/applications_filter_title"/>
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="14sp" android:textSize="14sp"
android:text="@string/settings_music_players_filter_subtitle" android:text="@string/applications_filter_subtitle"
style="@style/AnotherWidget.Settings.Subtitle"/> style="@style/AnotherWidget.Settings.Subtitle"/>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -209,6 +209,8 @@
<string name="google_fit">Google Fit</string> <string name="google_fit">Google Fit</string>
<string name="action_connect">Collega</string> <string name="action_connect">Collega</string>
<string name="action_disconnect">Scollega</string> <string name="action_disconnect">Scollega</string>
<string name="applications_filter_title">Applicazioni</string>
<string name="applications_filter_subtitle">Scegli le notifiche che vuoi vedere</string>
<!-- Settings --> <!-- Settings -->
<string name="action_share">Condividi</string> <string name="action_share">Condividi</string>

View File

@ -226,6 +226,8 @@
<string name="google_fit">Google Fit</string> <string name="google_fit">Google Fit</string>
<string name="action_connect">Connect</string> <string name="action_connect">Connect</string>
<string name="action_disconnect">Disconnect</string> <string name="action_disconnect">Disconnect</string>
<string name="applications_filter_title">Applications</string>
<string name="applications_filter_subtitle">Choose which notifications to view</string>
<!-- Settings --> <!-- Settings -->
<string name="action_share">Share</string> <string name="action_share">Share</string>

View File

@ -1,6 +1,6 @@
#Tue Oct 13 10:06:21 CEST 2020 #Wed Oct 14 12:52:53 CEST 2020
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip