1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.communal.widgets 18 19 import android.app.Activity 20 import android.app.ActivityOptions 21 import android.content.ActivityNotFoundException 22 import android.window.SplashScreen 23 import androidx.activity.ComponentActivity 24 import com.android.systemui.dagger.qualifiers.Background 25 import com.android.systemui.util.nullableAtomicReference 26 import dagger.assisted.Assisted 27 import dagger.assisted.AssistedFactory 28 import dagger.assisted.AssistedInject 29 import kotlinx.coroutines.CompletableDeferred 30 import kotlinx.coroutines.CoroutineDispatcher 31 import kotlinx.coroutines.withContext 32 33 /** 34 * Handles starting widget configuration activities and receiving the response to determine if 35 * configuration was successful. 36 */ 37 class WidgetConfigurationController 38 @AssistedInject 39 constructor( 40 @Assisted private val activity: ComponentActivity, 41 private val appWidgetHost: CommunalAppWidgetHost, 42 @Background private val bgDispatcher: CoroutineDispatcher 43 ) : WidgetConfigurator { 44 @AssistedFactory interfacenull45 fun interface Factory { 46 fun create(activity: ComponentActivity): WidgetConfigurationController 47 } 48 49 private var result: CompletableDeferred<Boolean>? by nullableAtomicReference() 50 configureWidgetnull51 override suspend fun configureWidget(appWidgetId: Int): Boolean = 52 withContext(bgDispatcher) { 53 if (result != null) { 54 throw IllegalStateException("There is already a pending configuration") 55 } 56 result = CompletableDeferred() 57 val options = 58 ActivityOptions.makeBasic().apply { 59 pendingIntentBackgroundActivityStartMode = 60 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED 61 splashScreenStyle = SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR 62 } 63 64 try { 65 appWidgetHost.startAppWidgetConfigureActivityForResult( 66 activity, 67 appWidgetId, 68 0, 69 REQUEST_CODE, 70 options.toBundle() 71 ) 72 } catch (e: ActivityNotFoundException) { 73 setConfigurationResult(Activity.RESULT_CANCELED) 74 } 75 val value = result?.await() ?: false 76 result = null 77 return@withContext value 78 } 79 setConfigurationResultnull80 fun setConfigurationResult(resultCode: Int) { 81 result?.complete(resultCode == Activity.RESULT_OK) 82 } 83 84 companion object { 85 const val REQUEST_CODE = 100 86 } 87 } 88