diff options
3 files changed, 181 insertions, 0 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index 4db77abacd69..8012dea643fb 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -43,6 +43,7 @@ import com.android.systemui.shortcut.ShortcutKeyDispatcher import com.android.systemui.statusbar.notification.fsi.FsiChromeRepo import com.android.systemui.statusbar.notification.InstantAppNotifier import com.android.systemui.statusbar.notification.fsi.FsiChromeViewModelFactory +import com.android.systemui.statusbar.notification.fsi.FsiChromeViewBinder import com.android.systemui.statusbar.phone.KeyguardLiftController import com.android.systemui.stylus.StylusUsiPowerStartable import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator @@ -93,6 +94,12 @@ abstract class SystemUICoreStartableModule { @ClassKey(FsiChromeViewModelFactory::class) abstract fun bindFSIChromeWindowViewModel(sysui: FsiChromeViewModelFactory): CoreStartable + /** Inject into FsiChromeWindowBinder. */ + @Binds + @IntoMap + @ClassKey(FsiChromeViewBinder::class) + abstract fun bindFsiChromeWindowBinder(sysui: FsiChromeViewBinder): CoreStartable + /** Inject into GarbageMonitor.Service. */ @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewBinder.kt new file mode 100644 index 000000000000..1a3927ba9b06 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewBinder.kt @@ -0,0 +1,99 @@ +package com.android.systemui.statusbar.notification.fsi + +import android.content.Context +import android.view.LayoutInflater +import android.view.WindowManager +import com.android.systemui.CoreStartable +import com.android.systemui.R +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.statusbar.notification.fsi.FsiDebug.Companion.log +import com.android.systemui.statusbar.phone.CentralSurfaces +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import java.util.concurrent.Executor +import javax.inject.Inject + +@SysUISingleton +class FsiChromeViewBinder +@Inject +constructor( + val context: Context, + val windowManager: WindowManager, + val viewModelFactory: FsiChromeViewModelFactory, + val layoutInflater: LayoutInflater, + val centralSurfaces: CentralSurfaces, + @Main val mainExecutor: Executor, + @Application val scope: CoroutineScope, +) : CoreStartable { + + companion object { + private const val classTag = "FsiChromeViewBinder" + } + + private val fsiChromeView = + layoutInflater.inflate(R.layout.fsi_chrome_view, null /* root */, false /* attachToRoot */) + as FsiChromeView + + var addedToWindowManager = false + var cornerRadius: Int = context.resources.getDimensionPixelSize( + R.dimen.notification_corner_radius) + + override fun start() { + val methodTag = "start" + log("$classTag $methodTag ") + + scope.launch { + log("$classTag $methodTag launch ") + viewModelFactory.viewModelFlow.collect { vm -> updateForViewModel(vm) } + } + } + + private fun updateForViewModel(vm: FsiChromeViewModel?) { + val methodTag = "updateForViewModel" + + if (vm == null) { + log("$classTag $methodTag viewModel is null, removing from window manager") + + if (addedToWindowManager) { + windowManager.removeView(fsiChromeView) + addedToWindowManager = false + } + return + } + + bindViewModel(vm, windowManager) + + if (addedToWindowManager) { + log("$classTag $methodTag already addedToWindowManager") + } else { + windowManager.addView(fsiChromeView, FsiTaskViewConfig.getWmLayoutParams("PackageName")) + addedToWindowManager = true + } + } + + private fun bindViewModel( + vm: FsiChromeViewModel, + windowManager: WindowManager, + ) { + log("$classTag bindViewModel") + + fsiChromeView.appIconImageView.setImageDrawable(vm.appIcon) + fsiChromeView.appNameTextView.text = vm.appName + + fsiChromeView.dismissButton.setOnClickListener { vm.onDismiss() } + fsiChromeView.fullscreenButton.setOnClickListener { vm.onFullscreen() } + + vm.taskView.cornerRadius = cornerRadius.toFloat() + vm.taskView.startActivity( + vm.fsi, + FsiTaskViewConfig.getFillInIntent(), + FsiTaskViewConfig.getActivityOptions(context, windowManager), + FsiTaskViewConfig.getLaunchBounds(windowManager) + ) + + log("$classTag bindViewModel started taskview activity") + fsiChromeView.addView(vm.taskView) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiTaskViewConfig.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiTaskViewConfig.kt new file mode 100644 index 000000000000..034ab56d5a65 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiTaskViewConfig.kt @@ -0,0 +1,75 @@ +package com.android.systemui.statusbar.notification.fsi + +import android.app.ActivityOptions +import android.content.Context +import android.content.Intent +import android.graphics.PixelFormat +import android.graphics.Rect +import android.os.Binder +import android.view.ViewGroup +import android.view.WindowManager + +/** + * Config for adding the FsiChromeView window to WindowManager and starting the FSI activity. + */ +class FsiTaskViewConfig { + + companion object { + + private const val classTag = "FsiTaskViewConfig" + + fun getWmLayoutParams(packageName: String): WindowManager.LayoutParams { + val params: WindowManager.LayoutParams? + params = + WindowManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, + WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or + WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED or + WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER, + PixelFormat.TRANSLUCENT + ) + params.setTrustedOverlay() + params.fitInsetsTypes = 0 + params.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE + params.token = Binder() + params.packageName = packageName + params.layoutInDisplayCutoutMode = + WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS + params.privateFlags = + params.privateFlags or WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS + return params + } + + fun getFillInIntent(): Intent { + val fillInIntent = Intent() + fillInIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT) + fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) + // FLAG_ACTIVITY_NEW_TASK is auto-applied because + // we're starting the FSI activity from a non-Activity context + return fillInIntent + } + + fun getLaunchBounds(windowManager: WindowManager): Rect { + // TODO(b/243421660) check this works for non-resizeable activity + return Rect() + } + + fun getActivityOptions(context: Context, windowManager: WindowManager): ActivityOptions { + // Custom options so there is no activity transition animation + val options = + ActivityOptions.makeCustomAnimation(context, 0 /* enterResId */, 0 /* exitResId */) + + options.taskAlwaysOnTop = true + + options.pendingIntentLaunchFlags = + Intent.FLAG_ACTIVITY_NEW_DOCUMENT or + Intent.FLAG_ACTIVITY_MULTIPLE_TASK or + Intent.FLAG_ACTIVITY_NEW_TASK + + options.launchBounds = getLaunchBounds(windowManager) + return options + } + } +} |