From 77d82c39c459d80421cd5dae5d5b9083ae5625a5 Mon Sep 17 00:00:00 2001 From: Dima Hrebeniuk Date: Thu, 6 Aug 2026 20:31:35 +0300 Subject: [PATCH] Refresh Quick Settings tile when panel opens --- README.md | 8 ---- app/src/main/AndroidManifest.xml | 29 +----------- .../chargequicktile/BootCompletedReceiver.kt | 20 -------- .../chargequicktile/ChargeQuickTileApp.kt | 40 ---------------- .../chargequicktile/ChargeQuickTileService.kt | 16 ++----- .../SettingsLaunchpadActivity.kt | 3 +- .../chargequicktile/UpdaterService.kt | 47 ------------------- app/src/main/res/values-de/strings.xml | 4 +- app/src/main/res/values-fr/strings.xml | 2 - app/src/main/res/values/strings.xml | 4 +- 10 files changed, 7 insertions(+), 166 deletions(-) delete mode 100644 app/src/main/java/de/tebbeubben/chargequicktile/BootCompletedReceiver.kt delete mode 100644 app/src/main/java/de/tebbeubben/chargequicktile/ChargeQuickTileApp.kt delete mode 100644 app/src/main/java/de/tebbeubben/chargequicktile/UpdaterService.kt diff --git a/README.md b/README.md index fa869c8..ea37adf 100644 --- a/README.md +++ b/README.md @@ -37,18 +37,11 @@ A lightweight and minimal Android app that allows Google Pixel users (starting f ```bash adb shell pm grant de.tebbeubben.chargequicktile android.permission.WRITE_SECURE_SETTINGS - adb shell pm grant de.tebbeubben.chargequicktile android.permission.POST_NOTIFICATIONS ``` 3. **Add the Quick Settings Tile** Pull down your notification shade → Tap the pencil icon to edit tiles → Drag the Charge Optimization tile into your active area. -4. **Start the Foreground Service** - Long press the tile once after adding it to initialize the foreground service. This is required to ensure reliable background operation and must only be done once. After rebooting the service should be started automatically. - -5. **(Optional) Hide the Persistent Notification** - Go to Settings → Apps → Charge Optimization → Notifications. Disable the Foreground Service notification if you want a cleaner status bar - ## ⚠️ Requirements - Google Pixel device @@ -58,7 +51,6 @@ A lightweight and minimal Android app that allows Google Pixel users (starting f ## 🛡️ Permissions Explained - `WRITE_SECURE_SETTINGS`: Allows the app to toggle secure system settings related to charging behavior. -- `POST_NOTIFICATIONS`: Enables the app to run reliably as a foreground service, improving performance and reliability. ## 🧠 Why This App? Android 15 introduced granular charging controls on Pixel phones, but switching between them still involves deep-diving into menus. This app simplifies the process by exposing it through a single, intuitive tile. diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1396ffe..96b3878 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,18 +6,12 @@ android:name="android.permission.WRITE_SECURE_SETTINGS" tools:ignore="ProtectedPermissions" /> - - - - - - - @@ -51,23 +41,6 @@ - - - - - - - - - - - - - \ No newline at end of file + diff --git a/app/src/main/java/de/tebbeubben/chargequicktile/BootCompletedReceiver.kt b/app/src/main/java/de/tebbeubben/chargequicktile/BootCompletedReceiver.kt deleted file mode 100644 index 4856d05..0000000 --- a/app/src/main/java/de/tebbeubben/chargequicktile/BootCompletedReceiver.kt +++ /dev/null @@ -1,20 +0,0 @@ -package de.tebbeubben.chargequicktile - -import android.app.ForegroundServiceStartNotAllowedException -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent - -//Make sure the app is spawned upon boot and kept alive -class BootCompletedReceiver : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - if (intent.action !in arrayOf(Intent.ACTION_BOOT_COMPLETED, Intent.ACTION_MY_PACKAGE_REPLACED)) return - try { - context.startForegroundService(Intent(context, UpdaterService::class.java)) - } catch (e: ForegroundServiceStartNotAllowedException) { - //For some reason, Android also likes to deliver the ACTION_BOOT_COMPLETED action when the app has been force-topped - //However, in those cases we aren't allowed to start a foreground service as opposed to the documentation... - e.printStackTrace() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/de/tebbeubben/chargequicktile/ChargeQuickTileApp.kt b/app/src/main/java/de/tebbeubben/chargequicktile/ChargeQuickTileApp.kt deleted file mode 100644 index ea9828e..0000000 --- a/app/src/main/java/de/tebbeubben/chargequicktile/ChargeQuickTileApp.kt +++ /dev/null @@ -1,40 +0,0 @@ -package de.tebbeubben.chargequicktile - -import android.app.Application -import android.content.ComponentName -import android.database.ContentObserver -import android.net.Uri -import android.os.Handler -import android.os.Looper -import android.provider.Settings -import android.service.quicksettings.TileService - -class ChargeQuickTileApp : Application() { - - var tileService: ChargeQuickTileService? = null - - private val settingsObserver = object : ContentObserver(Handler(Looper.getMainLooper())) { - override fun onChange(selfChange: Boolean, uri: Uri?) { - if (uri?.lastPathSegment in WATCH_FOR) { - updateTileService() - } - } - } - - override fun onCreate() { - super.onCreate() - contentResolver.registerContentObserver(Settings.Secure.CONTENT_URI, true, settingsObserver) - updateTileService() - } - - private fun updateTileService() { - tileService.let { tileService -> - if (tileService == null) { - TileService.requestListeningState(this, ComponentName(this, ChargeQuickTileService::class.java)) - } else { - tileService.updateTile() - } - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/de/tebbeubben/chargequicktile/ChargeQuickTileService.kt b/app/src/main/java/de/tebbeubben/chargequicktile/ChargeQuickTileService.kt index d7bf73c..aaf15a9 100644 --- a/app/src/main/java/de/tebbeubben/chargequicktile/ChargeQuickTileService.kt +++ b/app/src/main/java/de/tebbeubben/chargequicktile/ChargeQuickTileService.kt @@ -7,23 +7,13 @@ import android.service.quicksettings.TileService class ChargeQuickTileService : TileService() { - private lateinit var app: ChargeQuickTileApp - - override fun onCreate() { - app = application as ChargeQuickTileApp - } - - override fun onTileAdded() { - updateTile() - } - override fun onStartListening() { - app.tileService = this + super.onStartListening() updateTile() } override fun onStopListening() { - app.tileService = null + super.onStopListening() } override fun onClick() { @@ -65,4 +55,4 @@ class ChargeQuickTileService : TileService() { qsTile.updateTile() } -} \ No newline at end of file +} diff --git a/app/src/main/java/de/tebbeubben/chargequicktile/SettingsLaunchpadActivity.kt b/app/src/main/java/de/tebbeubben/chargequicktile/SettingsLaunchpadActivity.kt index da4f6c7..f321e70 100644 --- a/app/src/main/java/de/tebbeubben/chargequicktile/SettingsLaunchpadActivity.kt +++ b/app/src/main/java/de/tebbeubben/chargequicktile/SettingsLaunchpadActivity.kt @@ -9,7 +9,6 @@ class SettingsLaunchpadActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - startForegroundService(Intent(this, UpdaterService::class.java)) val intent = Intent() intent.setClassName( "com.google.android.settings.intelligence", @@ -21,4 +20,4 @@ class SettingsLaunchpadActivity : Activity() { finish() } -} \ No newline at end of file +} diff --git a/app/src/main/java/de/tebbeubben/chargequicktile/UpdaterService.kt b/app/src/main/java/de/tebbeubben/chargequicktile/UpdaterService.kt deleted file mode 100644 index 31a7c59..0000000 --- a/app/src/main/java/de/tebbeubben/chargequicktile/UpdaterService.kt +++ /dev/null @@ -1,47 +0,0 @@ -package de.tebbeubben.chargequicktile - -import android.app.Notification -import android.app.NotificationChannel -import android.app.NotificationManager -import android.app.Service -import android.content.Intent -import android.content.pm.ServiceInfo -import android.os.IBinder - -//This doesn't actually update the QS tile, but it keeps the app process alive -class UpdaterService : Service() { - - companion object { - const val NOTIFICATION_CHANNEL_ID = "foreground_channel" - const val NOTIFICATION_ID = 1 - } - - override fun onBind(intent: Intent?): IBinder? = null - - override fun onCreate() { - (getSystemService(NOTIFICATION_SERVICE) as NotificationManager) - .createNotificationChannel( - NotificationChannel( - NOTIFICATION_CHANNEL_ID, - getString(R.string.foreground_notification), - NotificationManager.IMPORTANCE_LOW - ) - ) - val notification = Notification.Builder(this, NOTIFICATION_CHANNEL_ID) - .setContentTitle(getString(R.string.foreground_notification)) - .setContentText(getString(R.string.you_can_hide_this_notification)) - .setOngoing(true) - .setSmallIcon(R.drawable.battery_android_frame_full_24px) - .build() - startForeground( - NOTIFICATION_ID, - notification, - ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE - ) - } - - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - return START_STICKY - } - -} \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 1f5cffa..d785c05 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -5,6 +5,4 @@ Bis 80% laden Aus Optimiertes Laden - Hintergrunddienst aktiv - Du kannst diese Benachrichtigung verstecken. - \ No newline at end of file + diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 2087472..40252f8 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -4,6 +4,4 @@ Limiter à 80% Désactivé Optimisation de la recharge - Service en arrière-plan actif - Vous pouvez masquer cette notification. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2a422e7..28d3aa8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,6 +4,4 @@ Limit to 80% Off Charging Optimization - Background service active - You can hide this notification. - \ No newline at end of file +