Skip to content

Commit

Permalink
Fix: Workaround FTP tile crash on Android 14
Browse files Browse the repository at this point in the history
Bug: #1238
  • Loading branch information
zhanghai committed Jun 26, 2024
1 parent 5127250 commit 7c6e3e8
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (c) 2024 Hai Zhang <[email protected]>
* All Rights Reserved.
*/

package me.zhanghai.android.files.compat

import android.os.Build
import android.os.IBinder
import android.service.quicksettings.TileService
import androidx.annotation.RequiresApi
import me.zhanghai.android.files.hiddenapi.RestrictedHiddenApi
import me.zhanghai.android.files.util.lazyReflectedField

@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?
@RequiresApi(Build.VERSION_CODES.N)
get() = tokenField.get(this) as IBinder?
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright (c) 2024 Hai Zhang <[email protected]>
* 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
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@

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

@RequiresApi(Build.VERSION_CODES.N)
class FtpServerTileService : TileService() {
Expand All @@ -30,8 +36,8 @@ class FtpServerTileService : TileService() {
private fun onFtpServerStateChanged(state: FtpServerService.State) {
val tile = qsTile
when (state) {
FtpServerService.State.STARTING, FtpServerService.State.RUNNING ->
tile.state = Tile.STATE_ACTIVE
FtpServerService.State.STARTING,
FtpServerService.State.RUNNING -> tile.state = Tile.STATE_ACTIVE
FtpServerService.State.STOPPING -> tile.state = Tile.STATE_UNAVAILABLE
FtpServerService.State.STOPPED -> tile.state = Tile.STATE_INACTIVE
}
Expand All @@ -49,6 +55,39 @@ class FtpServerTileService : TileService() {
}

private fun toggle() {
FtpServerService.toggle(this)
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)
}
}
}
}
}
}

0 comments on commit 7c6e3e8

Please sign in to comment.