diff --git a/app/src/main/java/me/zhanghai/android/files/compat/TileServiceCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/TileServiceCompat.kt index c1df2aad9..d820bd494 100644 --- a/app/src/main/java/me/zhanghai/android/files/compat/TileServiceCompat.kt +++ b/app/src/main/java/me/zhanghai/android/files/compat/TileServiceCompat.kt @@ -5,18 +5,56 @@ package me.zhanghai.android.files.compat +import android.graphics.PixelFormat import android.os.Build import android.os.IBinder import android.service.quicksettings.TileService +import android.view.View +import android.view.WindowManager import androidx.annotation.RequiresApi +import androidx.core.view.doOnPreDraw import me.zhanghai.android.files.hiddenapi.RestrictedHiddenApi import me.zhanghai.android.files.util.lazyReflectedField +fun TileService.doWithStartForegroundServiceAllowed(action: () -> Unit) { + if (Build.VERSION.SDK_INT != Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + action() + return + } + val windowManager = getSystemService(WindowManager::class.java) + val view = View(this) + val layoutParams = + WindowManager.LayoutParams().apply { + type = WindowManager_LayoutParams_TYPE_QS_DIALOG + format = PixelFormat.TRANSLUCENT + token = this@doWithStartForegroundServiceAllowed.token + } + windowManager.addView(view, layoutParams) + // We need to wait for WindowState.onSurfaceShownChanged(), basically when the first draw has + // finished and the surface is about to be shown to the user. However there's no good callback + // for that, while waiting for the second pre-draw seems to work. + view.doOnPreDraw { + view.post { + view.invalidate() + view.doOnPreDraw { + try { + action() + } finally { + windowManager.removeView(view) + } + } + } + } +} + +private const val WindowManager_LayoutParams_TYPE_QS_DIALOG = + WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW + 35 + @delegate:RequiresApi(Build.VERSION_CODES.N) @get:RequiresApi(Build.VERSION_CODES.N) @RestrictedHiddenApi private val tokenField by lazyReflectedField(TileService::class.qualifiedName!!, "mToken") -val TileService.token: IBinder? +private val TileService.token: IBinder? @RequiresApi(Build.VERSION_CODES.N) get() = tokenField.get(this) as IBinder? diff --git a/app/src/main/java/me/zhanghai/android/files/compat/WindowManagerLayoutParamsCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/WindowManagerLayoutParamsCompat.kt deleted file mode 100644 index 83277ba71..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/WindowManagerLayoutParamsCompat.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2024 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.view.WindowManager - -object WindowManagerLayoutParamsCompat { - const val TYPE_QS_DIALOG = WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW + 35 -} diff --git a/app/src/main/java/me/zhanghai/android/files/ftpserver/FtpServerTileService.kt b/app/src/main/java/me/zhanghai/android/files/ftpserver/FtpServerTileService.kt index 744cf56d0..145f1d1c4 100644 --- a/app/src/main/java/me/zhanghai/android/files/ftpserver/FtpServerTileService.kt +++ b/app/src/main/java/me/zhanghai/android/files/ftpserver/FtpServerTileService.kt @@ -5,17 +5,12 @@ package me.zhanghai.android.files.ftpserver -import android.graphics.PixelFormat import android.os.Build import android.service.quicksettings.Tile import android.service.quicksettings.TileService -import android.view.View -import android.view.WindowManager import androidx.annotation.RequiresApi -import androidx.core.view.doOnPreDraw import androidx.lifecycle.Observer -import me.zhanghai.android.files.compat.WindowManagerLayoutParamsCompat -import me.zhanghai.android.files.compat.token +import me.zhanghai.android.files.compat.doWithStartForegroundServiceAllowed @RequiresApi(Build.VERSION_CODES.N) class FtpServerTileService : TileService() { @@ -55,39 +50,6 @@ class FtpServerTileService : TileService() { } private fun toggle() { - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { - runWithForegroundWindow { FtpServerService.toggle(this) } - } else { - FtpServerService.toggle(this) - } - } - - // Work around https://issuetracker.google.com/issues/299506164 on U which is fixed in V. - @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - private fun runWithForegroundWindow(block: () -> Unit) { - val windowManager = getSystemService(WindowManager::class.java) - val view = View(this) - val layoutParams = - WindowManager.LayoutParams().apply { - type = WindowManagerLayoutParamsCompat.TYPE_QS_DIALOG - format = PixelFormat.TRANSLUCENT - token = this@FtpServerTileService.token - } - windowManager.addView(view, layoutParams) - // We need to wait for WindowState.onSurfaceShownChanged(), basically when the first draw - // has finished and the surface is about to be shown to the user. However there's no good - // callback for that, while waiting for the second pre-draw seems to work. - view.doOnPreDraw { - view.post { - view.invalidate() - view.doOnPreDraw { - try { - block() - } finally { - windowManager.removeView(view) - } - } - } - } + doWithStartForegroundServiceAllowed { FtpServerService.toggle(this) } } }