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