Skip to content

Commit 51c4194

Browse files
committed
Added Quick Settings tile
1 parent a3a297a commit 51c4194

13 files changed

Lines changed: 270 additions & 18 deletions

File tree

floatstat/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ plugins {
44
}
55

66
android {
7-
compileSdk 31
7+
compileSdk 33
88

99
defaultConfig {
1010
applicationId "id.psw.floatstat"
1111
minSdk 19
12-
targetSdk 31
12+
targetSdk 33
1313
versionCode 2
1414
versionName '1.0.1'
1515

floatstat/src/main/AndroidManifest.xml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
77
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
88
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
9+
<!-- No Android Go or Android devices identifying itself as Low RAM which is introduced since API 27,
10+
Since they usually did not support floating window -->
11+
<uses-feature android:name="android.hardware.ram.normal" android:required="true"/>
12+
913
<application
1014
android:allowBackup="true"
1115
android:name=".App"
@@ -23,6 +27,26 @@
2327
<category android:name="android.intent.category.LAUNCHER" />
2428
</intent-filter>
2529
</activity>
30+
<activity android:name=".TileLongClickActivity"
31+
android:exported="true"
32+
>
33+
<intent-filter>
34+
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
35+
</intent-filter>
36+
</activity>
37+
38+
<receiver android:name=".BootStartReceiver"
39+
android:label="@string/selector_bootstart"
40+
android:exported="true"
41+
android:enabled="true"
42+
>
43+
<intent-filter>
44+
<action android:name="android.intent.action.BOOT_COMPLETED"/>
45+
<action android:name="android.intent.action.REBOOT"/>
46+
<action android:name="com.htc.action.QUICKBOOT_POWERON"/>
47+
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
48+
</intent-filter>
49+
</receiver>
2650
<service
2751
android:name=".FloatWindowService"
2852
android:enabled="true"
@@ -35,6 +59,17 @@
3559
<action android:name="id.psw.temperamon.action.CLOSE"/>
3660
</intent-filter>
3761
</service>
62+
<service android:name=".SettingTileService"
63+
android:label="@string/app_name"
64+
android:exported="true"
65+
android:icon="@drawable/ic_main_notification"
66+
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
67+
>
68+
<meta-data android:name="android.service.quicksettings.ACTIVE_TILE" android:value="true"/>
69+
<intent-filter>
70+
<action android:name="android.service.quicksettings.action.QS_TILE" />
71+
</intent-filter>
72+
</service>
3873
<service
3974
android:name=".InternalStatProviderService"
4075
android:enabled="true"

floatstat/src/main/java/id/psw/floatstat/App.kt

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ package id.psw.floatstat
33
import android.annotation.SuppressLint
44
import android.app.Application
55
import android.content.*
6+
import android.content.pm.PackageManager
67
import android.graphics.Bitmap
78
import android.graphics.BitmapFactory
89
import android.graphics.Color
910
import android.net.Uri
11+
import android.os.Build
1012
import android.os.IBinder
1113
import android.util.Log
1214
import id.psw.floatstat.plugins.PluginData
@@ -20,6 +22,8 @@ class App : Application() {
2022

2123
private var keepRunning = true
2224
lateinit var iconProvider : InternalIconProvider
25+
var isFloatServiceRunning = false
26+
var isFloatWindowVisible = true
2327

2428
companion object {
2529
private val speedClassByte = arrayOf("B","k","MB","GB","TB","PB","EB","ZB","YB")
@@ -41,6 +45,7 @@ class App : Application() {
4145
const val PK_DEFAULT_PLUGIN_DISPLAY = "TetambahanTampil"
4246
const val PK_RUN_ON_STARTUP = "MulaiPasHidup"
4347
const val PK_HIDE_ON_TAP = "HideTap"
48+
const val PK_TILE_ADDED = "BeliPorselenGanWkwkwkwk"
4449
}
4550

4651

@@ -136,8 +141,8 @@ class App : Application() {
136141
hasBound = true
137142
if(service != null && name != null){
138143
val asPlugin = IFloatStatDataPlugin.Stub.asInterface(service)
139-
if(pluginList.count { it.name == name } == 0){
140-
val pluginDspName = displayNames[name] ?: name.toString() ?: "???"
144+
if(pluginList.indexOfFirst { it.name == name } < 0){
145+
val pluginDspName = displayNames[name] ?: name.toString()
141146
Log.d(TAG, "$name -> $pluginDspName")
142147
val vInfo = PluginInfo(asPlugin, name, pluginDspName)
143148
asPlugin.dataIds.split(',').forEach {
@@ -163,7 +168,11 @@ class App : Application() {
163168

164169
private fun listPlugins(){
165170
val i = Intent(ACTION_START_PLUGIN).addCategory(CATEGORY_PLUGIN)
166-
val dPkg = packageManager.queryIntentServices(i, 0)
171+
val dPkg = if (Build.VERSION.SDK_INT >= 33) {
172+
packageManager.queryIntentServices(i, PackageManager.ResolveInfoFlags.of(0L))
173+
} else {
174+
packageManager.queryIntentServices(i, 0)
175+
}
167176
if(pluginConnector.hasBound){
168177
unbindService(pluginConnector)
169178
pluginConnector.hasBound = false
@@ -187,7 +196,23 @@ class App : Application() {
187196
}
188197

189198
var shouldUpdate = false
190-
var startOnBoot = false
199+
var startOnBoot : Boolean
200+
get() {
201+
val cmp = ComponentName(applicationContext, BootStartReceiver::class.java)
202+
val pm = applicationContext.packageManager
203+
return pm.getComponentEnabledSetting(cmp) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED
204+
}
205+
set(value) {
206+
val cmp = ComponentName(applicationContext, BootStartReceiver::class.java)
207+
val pm = applicationContext.packageManager
208+
pm.setComponentEnabledSetting(
209+
cmp,
210+
value.select(
211+
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
212+
PackageManager.COMPONENT_ENABLED_STATE_DISABLED
213+
), PackageManager.DONT_KILL_APP)
214+
}
215+
191216

192217
var reReadPreference = false
193218
val activePlugins = arrayListOf<PluginId>()
@@ -272,11 +297,7 @@ class App : Application() {
272297
override fun onTerminate() {
273298
savePreferences()
274299
keepRunning = false
275-
iconProvider.clearMemFile()
276-
pluginList.forEach {
277-
it.binder.requestStop()
278-
}
279-
unbindService(pluginConnector)
300+
clearPlugins()
280301
Log.d("App", "Temperamon terminating...")
281302
super.onTerminate()
282303
}
@@ -291,4 +312,12 @@ class App : Application() {
291312
startActivity(i)
292313
}
293314

315+
fun clearPlugins() {
316+
iconProvider.clearMemFile()
317+
unbindService(pluginConnector)
318+
pluginList.forEach {
319+
it.binder.requestStop()
320+
}
321+
}
322+
294323
}

floatstat/src/main/java/id/psw/floatstat/BootStartReceiver.kt

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,20 @@ import android.content.Intent
66
import android.os.Build
77

88
class BootStartReceiver : BroadcastReceiver() {
9+
companion object {
10+
private val bootActions : Array<String> = arrayOf(
11+
Intent.ACTION_BOOT_COMPLETED,
12+
"android.intent.action.QUICKBOOT_POWERON",
13+
Intent.ACTION_REBOOT, // MIUI
14+
"com.htc.action.QUICKBOOT_POWERON", // HTC
15+
)
16+
}
17+
918
override fun onReceive(context: Context?, intent: Intent?) {
10-
if(context != null){
11-
if(context.app().startOnBoot){
12-
if(intent?.action == Intent.ACTION_BOOT_COMPLETED){
13-
FloatWindowService.startServiceS(context)
14-
}
19+
if(context != null && intent != null){
20+
val a = intent.action
21+
if(bootActions.indexOf(a) >= 0){
22+
FloatWindowService.startServiceS(context)
1523
}
1624
}
1725
}

floatstat/src/main/java/id/psw/floatstat/FloatWindowService.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ package id.psw.floatstat
22

33
import android.annotation.SuppressLint
44
import android.app.*
5+
import android.content.ComponentName
56
import android.content.Context
67
import android.content.Intent
78
import android.content.res.Configuration
89
import android.graphics.*
910
import android.os.*
11+
import android.service.quicksettings.TileService
1012
import android.view.*
1113
import android.widget.ImageButton
1214
import android.widget.PopupMenu
@@ -184,6 +186,8 @@ class FloatWindowService : Service() {
184186

185187
override fun onCreate() {
186188
super.onCreate()
189+
app().isFloatServiceRunning = true
190+
app().refreshPluginList()
187191
createNotification()
188192
createView()
189193
}
@@ -192,7 +196,8 @@ class FloatWindowService : Service() {
192196
when(intent?.action){
193197
ACTION_CLOSE -> {
194198
stopSelf()
195-
exitProcess(0)
199+
if(sdkAtLeast(Build.VERSION_CODES.N)) SettingTileService.update(app())
200+
app().clearPlugins()
196201
}
197202
ACTION_EDIT -> {
198203
openEditWindow()
@@ -207,8 +212,10 @@ class FloatWindowService : Service() {
207212
else -> View.VISIBLE
208213
}
209214
}
215+
app().isFloatWindowVisible= vMainView?.visibility == View.VISIBLE
210216
}
211217
}
218+
if(sdkAtLeast(Build.VERSION_CODES.N)) SettingTileService.update(app())
212219
return super.onStartCommand(intent, flags, startId)
213220
}
214221

@@ -349,6 +356,7 @@ class FloatWindowService : Service() {
349356

350357
override fun onDestroy() {
351358
super.onDestroy()
359+
app().isFloatServiceRunning = false
352360
Toast.makeText(applicationContext, "Closing View", Toast.LENGTH_SHORT).show()
353361
if(vMainView != null){
354362
vWinMan?.removeView(vMainView)

floatstat/src/main/java/id/psw/floatstat/MainActivity.kt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package id.psw.floatstat
22

3+
import android.annotation.SuppressLint
34
import android.app.Activity
5+
import android.app.StatusBarManager
6+
import android.content.ComponentName
7+
import android.content.Context
48
import android.content.Intent
5-
import android.content.pm.PackageManager
9+
import android.graphics.drawable.Icon
610
import android.net.Uri
711
import android.os.Build
812
import android.os.Bundle
@@ -22,9 +26,29 @@ class MainActivity : Activity() {
2226
}else{
2327
askPermission()
2428
}
29+
askAddTile()
2530
finish()
2631
}
2732

33+
@SuppressLint("WrongConstant") // Android 13+, to ask registration for new system bar tile
34+
private fun askAddTile(){
35+
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU){
36+
if(app().pref.getBoolean(App.PK_TILE_ADDED, true)){
37+
val sbm = getSystemService(Context.STATUS_BAR_SERVICE) as StatusBarManager
38+
sbm.requestAddTileService(
39+
ComponentName(this, SettingTileService::class.java),
40+
getString(R.string.app_name),
41+
Icon.createWithResource(this, R.drawable.ic_launcher_foreground),
42+
mainExecutor,
43+
){
44+
if(it == StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ADDED || it == StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ALREADY_ADDED){
45+
app().pref.edit().putBoolean(App.PK_TILE_ADDED, true).apply()
46+
}
47+
}
48+
}
49+
}
50+
}
51+
2852
private fun startServices(){
2953
Toast.makeText(applicationContext, getString(R.string.temperamon_start), Toast.LENGTH_LONG).show()
3054
startWindowService()
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package id.psw.floatstat
2+
3+
import android.content.ComponentName
4+
import android.content.Context
5+
import android.content.Intent
6+
import android.graphics.drawable.Icon
7+
import android.os.Build
8+
import android.os.IBinder
9+
import android.service.quicksettings.Tile
10+
import android.service.quicksettings.TileService
11+
import androidx.annotation.RequiresApi
12+
import java.util.*
13+
import kotlin.concurrent.timer
14+
15+
@RequiresApi(Build.VERSION_CODES.N)
16+
class SettingTileService : TileService() {
17+
18+
companion object{
19+
fun update(ctx:Context){
20+
TileService.requestListeningState(ctx, ComponentName(ctx, SettingTileService::class.java))
21+
}
22+
}
23+
24+
private val t get()= qsTile
25+
private lateinit var _cachedIcon : Icon
26+
private var canUpdate = false
27+
private lateinit var updateTimer : Timer
28+
private var isChangePending = false
29+
30+
override fun onCreate() {
31+
super.onCreate()
32+
_cachedIcon =Icon.createWithResource(this, R.drawable.ic_main_notification)
33+
}
34+
35+
override fun onBind(intent: Intent?): IBinder? {
36+
requestListeningState(this, ComponentName(this, SettingTileService::class.java))
37+
return super.onBind(intent)
38+
}
39+
40+
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
41+
return super.onStartCommand(intent, flags, startId)
42+
}
43+
44+
override fun onStartListening() {
45+
canUpdate = true
46+
isChangePending = false
47+
internalUpdateTile()
48+
}
49+
50+
override fun onStopListening() {
51+
canUpdate = false
52+
}
53+
54+
override fun onTileRemoved() {
55+
canUpdate = false
56+
}
57+
58+
private fun internalUpdateTile(){
59+
if(!canUpdate) return
60+
val pending = isChangePending
61+
t.icon = _cachedIcon
62+
63+
if(app().isFloatServiceRunning){
64+
val isVisible =app().isFloatWindowVisible
65+
t.state = isVisible.select(Tile.STATE_ACTIVE, Tile.STATE_INACTIVE)
66+
setDescription(
67+
pending.select(getString(R.string.tile_visibility_pending), isVisible.select(getString(
68+
R.string.tile_visibility_visible), getString(R.string.tile_visibility_hidden))))
69+
}else{
70+
t.state = Tile.STATE_UNAVAILABLE
71+
setDescription(pending.select(getString(R.string.tile_started_pending), getString(R.string.tile_started_no)))
72+
}
73+
t.updateTile()
74+
isChangePending = false
75+
}
76+
77+
private fun setDescription(str:String){
78+
if(sdkAtLeast(Build.VERSION_CODES.Q)) t.subtitle = str
79+
}
80+
81+
override fun onClick() {
82+
unlockAndRun {
83+
if(app().isFloatServiceRunning){
84+
startService(Intent(FloatWindowService.ACTION_VISIBILITY).setClass(app(), FloatWindowService::class.java))
85+
}else{
86+
FloatWindowService.startServiceS(app())
87+
}
88+
isChangePending = true
89+
internalUpdateTile()
90+
}
91+
}
92+
}

0 commit comments

Comments
 (0)