1 /* 2 * Copyright (C) 2020 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.controls.management 18 19 import android.app.AlertDialog 20 import android.app.Dialog 21 import android.content.ComponentName 22 import android.content.DialogInterface 23 import android.content.Intent 24 import android.os.Bundle 25 import android.os.UserHandle 26 import android.service.controls.Control 27 import android.service.controls.ControlsProviderService 28 import android.util.Log 29 import android.view.LayoutInflater 30 import android.view.View 31 import android.widget.ImageView 32 import android.widget.TextView 33 import com.android.systemui.R 34 import com.android.systemui.broadcast.BroadcastDispatcher 35 import com.android.systemui.controls.ControlsServiceInfo 36 import com.android.systemui.controls.controller.ControlInfo 37 import com.android.systemui.controls.controller.ControlsController 38 import com.android.systemui.controls.ui.RenderInfo 39 import com.android.systemui.settings.CurrentUserTracker 40 import com.android.systemui.statusbar.phone.SystemUIDialog 41 import com.android.systemui.util.LifecycleActivity 42 import javax.inject.Inject 43 44 open class ControlsRequestDialog @Inject constructor( 45 private val controller: ControlsController, 46 private val broadcastDispatcher: BroadcastDispatcher, 47 private val controlsListingController: ControlsListingController 48 ) : LifecycleActivity(), DialogInterface.OnClickListener, DialogInterface.OnCancelListener { 49 50 companion object { 51 private const val TAG = "ControlsRequestDialog" 52 } 53 54 private lateinit var controlComponent: ComponentName 55 private lateinit var control: Control 56 private var dialog: Dialog? = null 57 private val callback = object : ControlsListingController.ControlsListingCallback { onServicesUpdatednull58 override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {} 59 } 60 61 private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) { 62 private val startingUser = controller.currentUserId 63 onUserSwitchednull64 override fun onUserSwitched(newUserId: Int) { 65 if (newUserId != startingUser) { 66 stopTracking() 67 finish() 68 } 69 } 70 } 71 onCreatenull72 override fun onCreate(savedInstanceState: Bundle?) { 73 super.onCreate(savedInstanceState) 74 75 currentUserTracker.startTracking() 76 controlsListingController.addCallback(callback) 77 78 val requestUser = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL) 79 val currentUser = controller.currentUserId 80 81 if (requestUser != currentUser) { 82 Log.w(TAG, "Current user ($currentUser) different from request user ($requestUser)") 83 finish() 84 } 85 86 controlComponent = intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME) ?: run { 87 Log.e(TAG, "Request did not contain componentName") 88 finish() 89 return 90 } 91 92 control = intent.getParcelableExtra(ControlsProviderService.EXTRA_CONTROL) ?: run { 93 Log.e(TAG, "Request did not contain control") 94 finish() 95 return 96 } 97 } 98 onResumenull99 override fun onResume() { 100 super.onResume() 101 val label = verifyComponentAndGetLabel() 102 if (label == null) { 103 Log.e(TAG, "The component specified (${controlComponent.flattenToString()} " + 104 "is not a valid ControlsProviderService") 105 finish() 106 return 107 } 108 109 if (isCurrentFavorite()) { 110 Log.w(TAG, "The control ${control.title} is already a favorite") 111 finish() 112 } 113 114 dialog = createDialog(label) 115 116 dialog?.show() 117 } 118 onDestroynull119 override fun onDestroy() { 120 dialog?.dismiss() 121 currentUserTracker.stopTracking() 122 controlsListingController.removeCallback(callback) 123 super.onDestroy() 124 } 125 verifyComponentAndGetLabelnull126 private fun verifyComponentAndGetLabel(): CharSequence? { 127 return controlsListingController.getAppLabel(controlComponent) 128 } 129 isCurrentFavoritenull130 private fun isCurrentFavorite(): Boolean { 131 val favorites = controller.getFavoritesForComponent(controlComponent) 132 return favorites.any { it.controls.any { it.controlId == control.controlId } } 133 } 134 createDialognull135 fun createDialog(label: CharSequence): Dialog { 136 val renderInfo = RenderInfo.lookup(this, controlComponent, control.deviceType) 137 val frame = LayoutInflater.from(this).inflate(R.layout.controls_dialog, null).apply { 138 requireViewById<ImageView>(R.id.icon).apply { 139 setImageDrawable(renderInfo.icon) 140 setImageTintList( 141 context.resources.getColorStateList(renderInfo.foreground, context.theme)) 142 } 143 requireViewById<TextView>(R.id.title).text = control.title 144 requireViewById<TextView>(R.id.subtitle).text = control.subtitle 145 requireViewById<View>(R.id.control).elevation = 146 resources.getFloat(R.dimen.control_card_elevation) 147 } 148 149 val dialog = AlertDialog.Builder(this) 150 .setTitle(getString(R.string.controls_dialog_title)) 151 .setMessage(getString(R.string.controls_dialog_message, label)) 152 .setPositiveButton(R.string.controls_dialog_ok, this) 153 .setNegativeButton(android.R.string.cancel, this) 154 .setOnCancelListener(this) 155 .setView(frame) 156 .create() 157 158 SystemUIDialog.registerDismissListener(dialog) 159 dialog.setCanceledOnTouchOutside(true) 160 return dialog 161 } 162 onCancelnull163 override fun onCancel(dialog: DialogInterface?) { 164 finish() 165 } 166 onClicknull167 override fun onClick(dialog: DialogInterface?, which: Int) { 168 if (which == Dialog.BUTTON_POSITIVE) { 169 controller.addFavorite( 170 controlComponent, 171 control.structure ?: "", 172 ControlInfo(control.controlId, control.title, control.subtitle, control.deviceType) 173 ) 174 } 175 finish() 176 } 177 } 178