1 /*
<lambda>null2  * Copyright 2021 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 androidx.glance.appwidget.action
18 
19 import android.content.BroadcastReceiver
20 import android.content.Context
21 import android.content.Intent
22 import android.widget.RemoteViews
23 import androidx.annotation.RestrictTo
24 import androidx.core.os.bundleOf
25 import androidx.glance.action.ActionParameters
26 import androidx.glance.action.mutableActionParametersOf
27 import androidx.glance.appwidget.AppWidgetId
28 import androidx.glance.appwidget.TranslationContext
29 import androidx.glance.appwidget.goAsync
30 import androidx.glance.appwidget.logException
31 import kotlinx.coroutines.CancellationException
32 
33 /** Responds to broadcasts from [RunCallbackAction] clicks by executing the associated action. */
34 open class ActionCallbackBroadcastReceiver : BroadcastReceiver() {
35 
36     @Suppress("DEPRECATION")
37     override fun onReceive(context: Context?, intent: Intent?) {
38         goAsync {
39             try {
40                 requireNotNull(context) { "Context is null" }
41                 requireNotNull(intent) { "Intent is null" }
42                 val extras =
43                     requireNotNull(intent.extras) {
44                         "The intent must have action parameters extras."
45                     }
46                 val paramsBundle =
47                     requireNotNull(extras.getBundle(ExtraParameters)) {
48                         "The intent must contain a parameters bundle using extra: $ExtraParameters"
49                     }
50                 val parameters =
51                     mutableActionParametersOf().apply {
52                         paramsBundle.keySet().forEach { key ->
53                             set(ActionParameters.Key(key), paramsBundle[key])
54                         }
55                         if (extras.containsKey(RemoteViews.EXTRA_CHECKED)) {
56                             set(ToggleableStateKey, extras.getBoolean(RemoteViews.EXTRA_CHECKED))
57                         }
58                     }
59                 val className =
60                     requireNotNull(extras.getString(ExtraCallbackClassName)) {
61                         "The intent must contain a work class name string using " +
62                             "extra: $ExtraCallbackClassName"
63                     }
64                 require(intent.hasExtra(AppWidgetId)) {
65                     "To update the widget, the intent must contain the AppWidgetId integer using" +
66                         " extra: $AppWidgetId"
67                 }
68                 val glanceId = AppWidgetId(extras.getInt(AppWidgetId))
69 
70                 RunCallbackAction.run(context, className, glanceId, parameters)
71             } catch (ex: CancellationException) {
72                 throw ex
73             } catch (throwable: Throwable) {
74                 logException(throwable)
75             }
76         }
77     }
78 
79     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
80     internal companion object {
81         private const val AppWidgetId = "ActionCallbackBroadcastReceiver:appWidgetId"
82         private const val ExtraCallbackClassName = "ActionCallbackBroadcastReceiver:callbackClass"
83         private const val ExtraParameters = "ActionCallbackBroadcastReceiver:parameters"
84 
85         internal fun createIntent(
86             translationContext: TranslationContext,
87             callbackClass: Class<out ActionCallback>,
88             parameters: ActionParameters
89         ) =
90             Intent()
91                 .setComponent(translationContext.glanceComponents.actionCallbackBroadcastReceiver)
92                 .putExtra(ExtraCallbackClassName, callbackClass.canonicalName)
93                 .putExtra(AppWidgetId, translationContext.appWidgetId)
94                 .putParameterExtras(parameters)
95 
96         private fun Intent.putParameterExtras(parameters: ActionParameters): Intent {
97             val parametersPairs =
98                 parameters.asMap().map { (key, value) -> key.name to value }.toTypedArray()
99             putExtra(ExtraParameters, bundleOf(*parametersPairs))
100             return this
101         }
102     }
103 }
104