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.Context 23 import android.content.DialogInterface 24 import android.content.Intent 25 import android.os.Bundle 26 import android.os.UserHandle 27 import android.service.controls.Control 28 import android.service.controls.ControlsProviderService 29 import android.util.Log 30 import android.view.LayoutInflater 31 import android.view.View 32 import android.widget.ImageView 33 import android.widget.TextView 34 import androidx.activity.ComponentActivity 35 import com.android.systemui.res.R 36 import com.android.systemui.controls.ControlsServiceInfo 37 import com.android.systemui.controls.controller.ControlInfo 38 import com.android.systemui.controls.controller.ControlsController 39 import com.android.systemui.controls.ui.RenderInfo 40 import com.android.systemui.dagger.qualifiers.Main 41 import com.android.systemui.settings.UserTracker 42 import com.android.systemui.statusbar.phone.SystemUIDialog 43 import java.util.concurrent.Executor 44 import javax.inject.Inject 45 46 open class ControlsRequestDialog @Inject constructor( 47 @Main private val mainExecutor: Executor, 48 private val controller: ControlsController, 49 private val userTracker: UserTracker, 50 private val controlsListingController: ControlsListingController 51 ) : ComponentActivity(), DialogInterface.OnClickListener, DialogInterface.OnCancelListener { 52 53 companion object { 54 private const val TAG = "ControlsRequestDialog" 55 } 56 57 private lateinit var controlComponent: ComponentName 58 private lateinit var control: Control 59 private var dialog: Dialog? = null 60 private val callback = object : ControlsListingController.ControlsListingCallback { onServicesUpdatednull61 override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {} 62 } 63 64 private val userTrackerCallback: UserTracker.Callback = object : UserTracker.Callback { 65 private val startingUser = controller.currentUserId 66 onUserChangednull67 override fun onUserChanged(newUser: Int, userContext: Context) { 68 if (newUser != startingUser) { 69 userTracker.removeCallback(this) 70 finish() 71 } 72 } 73 } 74 onCreatenull75 override fun onCreate(savedInstanceState: Bundle?) { 76 super.onCreate(savedInstanceState) 77 78 userTracker.addCallback(userTrackerCallback, mainExecutor) 79 controlsListingController.addCallback(callback) 80 81 val requestUser = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL) 82 val currentUser = controller.currentUserId 83 84 if (requestUser != currentUser) { 85 Log.w(TAG, "Current user ($currentUser) different from request user ($requestUser)") 86 finish() 87 } 88 89 controlComponent = intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME) ?: run { 90 Log.e(TAG, "Request did not contain componentName") 91 finish() 92 return 93 } 94 95 control = intent.getParcelableExtra(ControlsProviderService.EXTRA_CONTROL) ?: run { 96 Log.e(TAG, "Request did not contain control") 97 finish() 98 return 99 } 100 } 101 onResumenull102 override fun onResume() { 103 super.onResume() 104 val label = verifyComponentAndGetLabel() 105 if (label == null) { 106 Log.e(TAG, "The component specified (${controlComponent.flattenToString()} " + 107 "is not a valid ControlsProviderService") 108 finish() 109 return 110 } 111 112 if (isCurrentFavorite()) { 113 Log.w(TAG, "The control ${control.title} is already a favorite") 114 finish() 115 } 116 117 dialog = createDialog(label) 118 119 dialog?.show() 120 } 121 onDestroynull122 override fun onDestroy() { 123 dialog?.dismiss() 124 userTracker.removeCallback(userTrackerCallback) 125 controlsListingController.removeCallback(callback) 126 super.onDestroy() 127 } 128 verifyComponentAndGetLabelnull129 private fun verifyComponentAndGetLabel(): CharSequence? { 130 return controlsListingController.getAppLabel(controlComponent) 131 } 132 isCurrentFavoritenull133 private fun isCurrentFavorite(): Boolean { 134 val favorites = controller.getFavoritesForComponent(controlComponent) 135 return favorites.any { it.controls.any { it.controlId == control.controlId } } 136 } 137 createDialognull138 fun createDialog(label: CharSequence): Dialog { 139 val renderInfo = RenderInfo.lookup(this, controlComponent, control.deviceType) 140 val frame = LayoutInflater.from(this).inflate(R.layout.controls_dialog, null).apply { 141 requireViewById<ImageView>(R.id.icon).apply { 142 setImageDrawable(renderInfo.icon) 143 setImageTintList( 144 context.resources.getColorStateList(renderInfo.foreground, context.theme)) 145 } 146 requireViewById<TextView>(R.id.title).text = control.title 147 requireViewById<TextView>(R.id.subtitle).text = control.subtitle 148 requireViewById<View>(R.id.control).elevation = 149 resources.getFloat(R.dimen.control_card_elevation) 150 } 151 152 val dialog = AlertDialog.Builder(this) 153 .setTitle(getString(R.string.controls_dialog_title)) 154 .setMessage(getString(R.string.controls_dialog_message, label)) 155 .setPositiveButton(R.string.controls_dialog_ok, this) 156 .setNegativeButton(android.R.string.cancel, this) 157 .setOnCancelListener(this) 158 .setView(frame) 159 .create() 160 161 SystemUIDialog.registerDismissListener(dialog) 162 dialog.setCanceledOnTouchOutside(true) 163 return dialog 164 } 165 onCancelnull166 override fun onCancel(dialog: DialogInterface?) { 167 finish() 168 } 169 onClicknull170 override fun onClick(dialog: DialogInterface?, which: Int) { 171 if (which == Dialog.BUTTON_POSITIVE) { 172 controller.addFavorite( 173 controlComponent, 174 control.structure ?: "", 175 ControlInfo(control.controlId, control.title, control.subtitle, control.deviceType) 176 ) 177 } 178 finish() 179 } 180 } 181