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