diff options
12 files changed, 545 insertions, 18 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt new file mode 100644 index 000000000000..2194c75dba5b --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.irecording + +import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testCase +import com.android.systemui.kosmos.testScope +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.recordissue.IssueRecordingState +import com.android.systemui.settings.fakeUserFileManager +import com.android.systemui.settings.userTracker +import com.google.common.truth.Truth +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class IssueRecordingDataInteractorTest : SysuiTestCase() { + + private val kosmos = Kosmos().also { it.testCase = this } + private val userTracker = kosmos.userTracker + private val userFileManager = kosmos.fakeUserFileManager + private val testUser = UserHandle.of(1) + + lateinit var state: IssueRecordingState + private lateinit var underTest: IssueRecordingDataInteractor + + @Before + fun setup() { + state = IssueRecordingState(userTracker, userFileManager) + underTest = IssueRecordingDataInteractor(state, kosmos.testScope.testScheduler) + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun emitsEvent_whenIsRecordingStatusChanges_correctly() { + kosmos.testScope.runTest { + val data by + collectLastValue( + underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)) + ) + runCurrent() + Truth.assertThat(data?.isRecording).isFalse() + + state.isRecording = true + runCurrent() + Truth.assertThat(data?.isRecording).isTrue() + + state.isRecording = false + runCurrent() + Truth.assertThat(data?.isRecording).isFalse() + } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt new file mode 100644 index 000000000000..244422943309 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.irecording + +import android.content.res.mainResources +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testCase +import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.qsEventLogger +import com.android.systemui.qs.tiles.viewmodel.QSTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig +import com.android.systemui.recordissue.RecordIssueModule +import com.android.systemui.res.R +import com.google.common.truth.Truth +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class IssueRecordingMapperTest : SysuiTestCase() { + private val kosmos = Kosmos().also { it.testCase = this } + private val uiConfig = + QSTileUIConfig.Resource(R.drawable.qs_record_issue_icon_off, R.string.qs_record_issue_label) + private val config = + QSTileConfig( + TileSpec.create(RecordIssueModule.TILE_SPEC), + uiConfig, + kosmos.qsEventLogger.getNewInstanceId() + ) + private val resources = kosmos.mainResources + private val theme = resources.newTheme() + + @Test + fun whenData_isRecording_useCorrectResources() { + val underTest = IssueRecordingMapper(resources, theme) + val tileState = underTest.map(config, IssueRecordingModel(true)) + Truth.assertThat(tileState.activationState).isEqualTo(QSTileState.ActivationState.ACTIVE) + } + + @Test + fun whenData_isNotRecording_useCorrectResources() { + val underTest = IssueRecordingMapper(resources, theme) + val tileState = underTest.map(config, IssueRecordingModel(false)) + Truth.assertThat(tileState.activationState).isEqualTo(QSTileState.ActivationState.INACTIVE) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt new file mode 100644 index 000000000000..4e5806902a10 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.irecording + +import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.dialogTransitionAnimator +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testCase +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.kosmos.testScope +import com.android.systemui.plugins.activityStarter +import com.android.systemui.plugins.statusbar.statusBarStateController +import com.android.systemui.qs.pipeline.domain.interactor.panelInteractor +import com.android.systemui.qs.tiles.base.interactor.QSTileInput +import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction +import com.android.systemui.recordissue.RecordIssueDialogDelegate +import com.android.systemui.settings.UserContextProvider +import com.android.systemui.settings.userTracker +import com.android.systemui.statusbar.phone.KeyguardDismissUtil +import com.android.systemui.statusbar.policy.keyguardStateController +import com.google.common.truth.Truth +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.mock + +@SmallTest +@RunWith(AndroidJUnit4::class) +class IssueRecordingUserActionInteractorTest : SysuiTestCase() { + + val user = UserHandle(1) + val kosmos = Kosmos().also { it.testCase = this } + + private lateinit var userContextProvider: UserContextProvider + private lateinit var underTest: IssueRecordingUserActionInteractor + + private var hasCreatedDialogDelegate: Boolean = false + + @Before + fun setup() { + hasCreatedDialogDelegate = false + with(kosmos) { + val factory = + object : RecordIssueDialogDelegate.Factory { + override fun create(onStarted: Runnable): RecordIssueDialogDelegate { + hasCreatedDialogDelegate = true + + // Inside some tests in presubmit, createDialog throws an error because + // the test thread's looper hasn't been prepared, and Dialog.class + // internally is creating a new handler. For testing, we only care that the + // dialog is created, so using a mock is acceptable here. + return mock(RecordIssueDialogDelegate::class.java) + } + } + + userContextProvider = userTracker + underTest = + IssueRecordingUserActionInteractor( + testDispatcher, + KeyguardDismissUtil( + keyguardStateController, + statusBarStateController, + activityStarter + ), + keyguardStateController, + dialogTransitionAnimator, + panelInteractor, + userTracker, + factory + ) + } + } + + @Test + fun handleInput_showsPromptToStartRecording_whenNotRecordingAlready() { + kosmos.testScope.runTest { + underTest.handleInput( + QSTileInput(user, QSTileUserAction.Click(null), IssueRecordingModel(false)) + ) + Truth.assertThat(hasCreatedDialogDelegate).isTrue() + } + } + + @Test + fun handleInput_attemptsToStopRecording_whenRecording() { + kosmos.testScope.runTest { + val input = QSTileInput(user, QSTileUserAction.Click(null), IssueRecordingModel(true)) + try { + underTest.handleInput(input) + } catch (e: NullPointerException) { + // As of 06/07/2024, PendingIntent.startService is not easily mockable and throws + // an NPE inside IActivityManager. Catching that here and ignore it, then verify + // mock interactions were done correctly + } + Truth.assertThat(hasCreatedDialogDelegate).isFalse() + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt index 70f3b847ce07..a3feb2b09da3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt @@ -46,6 +46,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.recordissue.IssueRecordingService import com.android.systemui.recordissue.IssueRecordingState import com.android.systemui.recordissue.RecordIssueDialogDelegate +import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC import com.android.systemui.recordissue.TraceurMessageSender import com.android.systemui.res.R import com.android.systemui.screenrecord.RecordingService @@ -197,8 +198,4 @@ constructor( expandedAccessibilityClassName = Switch::class.java.name } } - - companion object { - const val TILE_SPEC = "record_issue" - } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt new file mode 100644 index 000000000000..1af328e1e0a8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.irecording + +import android.os.UserHandle +import com.android.systemui.Flags +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor +import com.android.systemui.recordissue.IssueRecordingState +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.onStart + +class IssueRecordingDataInteractor +@Inject +constructor( + private val state: IssueRecordingState, + @Background private val bgCoroutineContext: CoroutineContext, +) : QSTileDataInteractor<IssueRecordingModel> { + + override fun tileData( + user: UserHandle, + triggers: Flow<DataUpdateTrigger> + ): Flow<IssueRecordingModel> = + conflatedCallbackFlow { + val listener = Runnable { trySend(IssueRecordingModel(state.isRecording)) } + state.addListener(listener) + awaitClose { state.removeListener(listener) } + } + .onStart { emit(IssueRecordingModel(state.isRecording)) } + .distinctUntilChanged() + .flowOn(bgCoroutineContext) + + override fun availability(user: UserHandle): Flow<Boolean> = + flowOf(android.os.Build.IS_DEBUGGABLE && Flags.recordIssueQsTile()) +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt new file mode 100644 index 000000000000..ff931b35567f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.irecording + +import android.content.res.Resources +import android.content.res.Resources.Theme +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper +import com.android.systemui.qs.tiles.viewmodel.QSTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.res.R +import javax.inject.Inject + +class IssueRecordingMapper +@Inject +constructor( + @Main private val resources: Resources, + private val theme: Theme, +) : QSTileDataToStateMapper<IssueRecordingModel> { + override fun map(config: QSTileConfig, data: IssueRecordingModel): QSTileState = + QSTileState.build(resources, theme, config.uiConfig) { + if (data.isRecording) { + activationState = QSTileState.ActivationState.ACTIVE + secondaryLabel = resources.getString(R.string.qs_record_issue_stop) + icon = { Icon.Resource(R.drawable.qs_record_issue_icon_on, null) } + } else { + icon = { Icon.Resource(R.drawable.qs_record_issue_icon_off, null) } + activationState = QSTileState.ActivationState.INACTIVE + secondaryLabel = resources.getString(R.string.qs_record_issue_start) + } + supportedActions = setOf(QSTileState.UserAction.CLICK) + contentDescription = "$label, $secondaryLabel" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingModel.kt new file mode 100644 index 000000000000..260729b6a868 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingModel.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.irecording + +@JvmInline value class IssueRecordingModel(val isRecording: Boolean) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt new file mode 100644 index 000000000000..4971fefc8f57 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.irecording + +import android.app.AlertDialog +import android.app.BroadcastOptions +import android.app.PendingIntent +import android.util.Log +import com.android.internal.jank.InteractionJankMonitor +import com.android.systemui.animation.DialogCuj +import com.android.systemui.animation.DialogTransitionAnimator +import com.android.systemui.animation.Expandable +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor +import com.android.systemui.qs.tiles.base.interactor.QSTileInput +import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor +import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction +import com.android.systemui.recordissue.IssueRecordingService +import com.android.systemui.recordissue.RecordIssueDialogDelegate +import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC +import com.android.systemui.screenrecord.RecordingService +import com.android.systemui.settings.UserContextProvider +import com.android.systemui.statusbar.phone.KeyguardDismissUtil +import com.android.systemui.statusbar.policy.KeyguardStateController +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.withContext + +private const val TAG = "IssueRecordingActionInteractor" + +class IssueRecordingUserActionInteractor +@Inject +constructor( + @Main private val mainCoroutineContext: CoroutineContext, + private val keyguardDismissUtil: KeyguardDismissUtil, + private val keyguardStateController: KeyguardStateController, + private val dialogTransitionAnimator: DialogTransitionAnimator, + private val panelInteractor: PanelInteractor, + private val userContextProvider: UserContextProvider, + private val delegateFactory: RecordIssueDialogDelegate.Factory, +) : QSTileUserActionInteractor<IssueRecordingModel> { + + override suspend fun handleInput(input: QSTileInput<IssueRecordingModel>) { + if (input.action is QSTileUserAction.Click) { + if (input.data.isRecording) { + stopIssueRecordingService() + } else { + withContext(mainCoroutineContext) { showPrompt(input.action.expandable) } + } + } else { + Log.v(TAG, "the RecordIssueTile doesn't handle ${input.action} events yet.") + } + } + + private fun showPrompt(expandable: Expandable?) { + val dialog: AlertDialog = + delegateFactory + .create { + startIssueRecordingService() + dialogTransitionAnimator.disableAllCurrentDialogsExitAnimations() + panelInteractor.collapsePanels() + } + .createDialog() + val dismissAction = + ActivityStarter.OnDismissAction { + // We animate from the touched view only if we are not on the keyguard, given + // that if we are we will dismiss it which will also collapse the shade. + if (expandable != null && !keyguardStateController.isShowing) { + expandable + .dialogTransitionController( + DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, TILE_SPEC) + ) + ?.let { dialogTransitionAnimator.show(dialog, it) } ?: dialog.show() + } else { + dialog.show() + } + false + } + keyguardDismissUtil.executeWhenUnlocked(dismissAction, false, true) + } + + private fun startIssueRecordingService() = + PendingIntent.getForegroundService( + userContextProvider.userContext, + RecordingService.REQUEST_CODE, + IssueRecordingService.getStartIntent(userContextProvider.userContext), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + .send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle()) + + private fun stopIssueRecordingService() = + PendingIntent.getService( + userContextProvider.userContext, + RecordingService.REQUEST_CODE, + IssueRecordingService.getStopIntent(userContextProvider.userContext), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + .send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle()) +} diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt index 4ea334522ad4..b077349ade74 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt @@ -18,7 +18,7 @@ package com.android.systemui.recordissue import android.content.Context import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.qs.tiles.RecordIssueTile +import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC import com.android.systemui.res.R import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker @@ -35,11 +35,7 @@ constructor( ) { private val prefs = - userFileManager.getSharedPreferences( - RecordIssueTile.TILE_SPEC, - Context.MODE_PRIVATE, - userTracker.userId - ) + userFileManager.getSharedPreferences(TILE_SPEC, Context.MODE_PRIVATE, userTracker.userId) var takeBugreport get() = prefs.getBoolean(KEY_TAKE_BUG_REPORT, false) diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt index 26af9a71de7d..907b92ce4c9b 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt @@ -16,12 +16,20 @@ package com.android.systemui.recordissue +import com.android.systemui.Flags import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.qs.tiles.RecordIssueTile +import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory +import com.android.systemui.qs.tiles.impl.irecording.IssueRecordingDataInteractor +import com.android.systemui.qs.tiles.impl.irecording.IssueRecordingMapper +import com.android.systemui.qs.tiles.impl.irecording.IssueRecordingModel +import com.android.systemui.qs.tiles.impl.irecording.IssueRecordingUserActionInteractor import com.android.systemui.qs.tiles.viewmodel.QSTileConfig import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel +import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel import com.android.systemui.res.R import dagger.Binds import dagger.Module @@ -34,19 +42,19 @@ interface RecordIssueModule { /** Inject RecordIssueTile into tileMap in QSModule */ @Binds @IntoMap - @StringKey(RecordIssueTile.TILE_SPEC) + @StringKey(TILE_SPEC) fun bindRecordIssueTile(recordIssueTile: RecordIssueTile): QSTileImpl<*> companion object { - const val RECORD_ISSUE_TILE_SPEC = "record_issue" + const val TILE_SPEC = "record_issue" @Provides @IntoMap - @StringKey(RECORD_ISSUE_TILE_SPEC) + @StringKey(TILE_SPEC) fun provideRecordIssueTileConfig(uiEventLogger: QsEventLogger): QSTileConfig = QSTileConfig( - tileSpec = TileSpec.create(RECORD_ISSUE_TILE_SPEC), + tileSpec = TileSpec.create(TILE_SPEC), uiConfig = QSTileUIConfig.Resource( iconRes = R.drawable.qs_record_issue_icon_off, @@ -54,5 +62,24 @@ interface RecordIssueModule { ), instanceId = uiEventLogger.getNewInstanceId(), ) + + /** Inject FlashlightTile into tileViewModelMap in QSModule */ + @Provides + @IntoMap + @StringKey(TILE_SPEC) + fun provideIssueRecordingTileViewModel( + factory: QSTileViewModelFactory.Static<IssueRecordingModel>, + mapper: IssueRecordingMapper, + stateInteractor: IssueRecordingDataInteractor, + userActionInteractor: IssueRecordingUserActionInteractor + ): QSTileViewModel = + if (Flags.qsNewTilesFuture()) + factory.create( + TileSpec.create(TILE_SPEC), + userActionInteractor, + stateInteractor, + mapper, + ) + else StubQSTileViewModel } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt index 503c52f89b0a..ce1a885098d9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt @@ -17,7 +17,6 @@ package com.android.systemui.recordissue import android.app.Dialog -import android.content.SharedPreferences import android.os.UserHandle import android.testing.TestableLooper import android.widget.Button @@ -57,6 +56,7 @@ import org.mockito.ArgumentMatchers.anyLong import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.spy +import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -71,7 +71,6 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() { @Mock private lateinit var mediaProjectionMetricsLogger: MediaProjectionMetricsLogger @Mock private lateinit var userTracker: UserTracker @Mock private lateinit var state: IssueRecordingState - @Mock private lateinit var sharedPreferences: SharedPreferences @Mock private lateinit var screenCaptureDisabledDialogDelegate: ScreenCaptureDisabledDialogDelegate @Mock private lateinit var screenCaptureDisabledDialog: SystemUIDialog @@ -192,7 +191,7 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() { anyInt(), eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER) ) - verify(factory).create(any<ScreenCapturePermissionDialogDelegate>()) + verify(factory, times(2)).create(any(SystemUIDialog.Delegate::class.java)) } @Test @@ -213,7 +212,7 @@ class RecordIssueDialogDelegateTest : SysuiTestCase() { anyInt(), eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER) ) - verify(factory, never()).create(any<ScreenCapturePermissionDialogDelegate>()) + verify(factory, never()).create() } @Test diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt index 1851c89ecd94..6574946726a9 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt @@ -36,3 +36,14 @@ val Kosmos.systemUIDialogFactory: SystemUIDialogFactory by val Kosmos.mockSystemUIDialogFactory: SystemUIDialog.Factory by Kosmos.Fixture { mock<SystemUIDialog.Factory>() } + +val Kosmos.systemUIDialogDotFactory by + Kosmos.Fixture { + SystemUIDialog.Factory( + applicationContext, + systemUIDialogManager, + sysUiState, + broadcastDispatcher, + dialogTransitionAnimator, + ) + } |