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.qs.tiles.impl.irecording.domain.interactor 18 19 import android.app.AlertDialog 20 import android.app.BroadcastOptions 21 import android.app.PendingIntent 22 import android.content.Intent 23 import android.util.Log 24 import com.android.internal.jank.InteractionJankMonitor 25 import com.android.systemui.animation.DialogCuj 26 import com.android.systemui.animation.DialogTransitionAnimator 27 import com.android.systemui.animation.Expandable 28 import com.android.systemui.dagger.qualifiers.Main 29 import com.android.systemui.plugins.ActivityStarter 30 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor 31 import com.android.systemui.qs.tiles.DELAY_MS 32 import com.android.systemui.qs.tiles.INTERVAL_MS 33 import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor 34 import com.android.systemui.qs.tiles.base.domain.model.QSTileInput 35 import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction 36 import com.android.systemui.qs.tiles.impl.irecording.data.model.IssueRecordingModel 37 import com.android.systemui.recordissue.IssueRecordingService.Companion.getStartIntent 38 import com.android.systemui.recordissue.IssueRecordingService.Companion.getStopIntent 39 import com.android.systemui.recordissue.IssueRecordingState 40 import com.android.systemui.recordissue.RecordIssueDialogDelegate 41 import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC 42 import com.android.systemui.screenrecord.RecordingController 43 import com.android.systemui.screenrecord.RecordingService 44 import com.android.systemui.settings.UserContextProvider 45 import com.android.systemui.statusbar.phone.KeyguardDismissUtil 46 import com.android.systemui.statusbar.policy.KeyguardStateController 47 import javax.inject.Inject 48 import kotlin.coroutines.CoroutineContext 49 import kotlinx.coroutines.withContext 50 51 class IssueRecordingUserActionInteractor 52 @Inject 53 constructor( 54 @Main private val mainCoroutineContext: CoroutineContext, 55 private val state: IssueRecordingState, 56 private val keyguardDismissUtil: KeyguardDismissUtil, 57 private val keyguardStateController: KeyguardStateController, 58 private val dialogTransitionAnimator: DialogTransitionAnimator, 59 private val panelInteractor: PanelInteractor, 60 private val userContextProvider: UserContextProvider, 61 private val delegateFactory: RecordIssueDialogDelegate.Factory, 62 private val recordingController: RecordingController, 63 ) : QSTileUserActionInteractor<IssueRecordingModel> { 64 handleInputnull65 override suspend fun handleInput(input: QSTileInput<IssueRecordingModel>) { 66 if (input.action is QSTileUserAction.Click) { 67 if (input.data.isRecording) { 68 stopIssueRecordingService() 69 } else { 70 withContext(mainCoroutineContext) { showPrompt(input.action.expandable) } 71 } 72 } else { 73 Log.v(TAG, "the RecordIssueTile doesn't handle ${input.action} events yet.") 74 } 75 } 76 showPromptnull77 private fun showPrompt(expandable: Expandable?) { 78 val dialog: AlertDialog = 79 delegateFactory 80 .create { 81 startIssueRecordingService() 82 dialogTransitionAnimator.disableAllCurrentDialogsExitAnimations() 83 panelInteractor.collapsePanels() 84 } 85 .createDialog() 86 val dismissAction = 87 ActivityStarter.OnDismissAction { 88 // We animate from the touched view only if we are not on the keyguard, given 89 // that if we are we will dismiss it which will also collapse the shade. 90 if (expandable != null && !keyguardStateController.isShowing) { 91 expandable 92 .dialogTransitionController( 93 DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, TILE_SPEC) 94 ) 95 ?.let { dialogTransitionAnimator.show(dialog, it) } ?: dialog.show() 96 } else { 97 dialog.show() 98 } 99 false 100 } 101 keyguardDismissUtil.executeWhenUnlocked(dismissAction, false, true) 102 } 103 startIssueRecordingServicenull104 private fun startIssueRecordingService() = 105 recordingController.startCountdown( 106 DELAY_MS, 107 INTERVAL_MS, 108 pendingServiceIntent( 109 getStartIntent( 110 userContextProvider.userContext, 111 state.traceConfig, 112 state.recordScreen, 113 state.takeBugreport, 114 ) 115 ), 116 pendingServiceIntent(getStopIntent(userContextProvider.userContext)), 117 ) 118 119 private fun stopIssueRecordingService() = 120 pendingServiceIntent(getStopIntent(userContextProvider.userContext)) 121 .send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle()) 122 pendingServiceIntentnull123 private fun pendingServiceIntent(action: Intent) = 124 PendingIntent.getService( 125 userContextProvider.userContext, 126 RecordingService.REQUEST_CODE, 127 action, 128 PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, 129 ) 130 131 companion object { 132 private const val TAG = "IssueRecordingUserActionInteractor" 133 } 134 } 135