Loading packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt +4 −10 Original line number Diff line number Diff line Loading @@ -52,7 +52,6 @@ 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 com.android.traceur.TraceUtils.PresetTraceType import java.util.concurrent.Executor import javax.inject.Inject Loading Loading @@ -131,15 +130,11 @@ constructor( } } private fun startIssueRecordingService(screenRecord: Boolean, traceType: PresetTraceType) = private fun startIssueRecordingService() = PendingIntent.getForegroundService( userContextProvider.userContext, RecordingService.REQUEST_CODE, IssueRecordingService.getStartIntent( userContextProvider.userContext, screenRecord, traceType ), IssueRecordingService.getStartIntent(userContextProvider.userContext), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) .send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle()) Loading @@ -157,7 +152,7 @@ constructor( val dialog: AlertDialog = delegateFactory .create { startIssueRecordingService(it.screenRecord, it.traceType) startIssueRecordingService() dialogTransitionAnimator.disableAllCurrentDialogsExitAnimations() panelInteractor.collapsePanels() } Loading @@ -169,8 +164,7 @@ constructor( if (expandable != null && !keyguardStateController.isShowing) { expandable .dialogTransitionController(DialogCuj(CUJ_SHADE_DIALOG_OPEN, TILE_SPEC)) ?.let { dialogTransitionAnimator.show(dialog, it) } ?: dialog.show() ?.let { dialogTransitionAnimator.show(dialog, it) } ?: dialog.show() } else { dialog.show() } Loading packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.ktdeleted 100644 → 0 +0 −21 Original line number Diff line number Diff line /* * 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.recordissue import com.android.traceur.TraceUtils.PresetTraceType data class IssueRecordingConfig(val screenRecord: Boolean, val traceType: PresetTraceType) packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt +13 −20 Original line number Diff line number Diff line Loading @@ -35,8 +35,6 @@ import com.android.systemui.screenrecord.RecordingService import com.android.systemui.screenrecord.RecordingServiceStrings import com.android.systemui.settings.UserContextProvider import com.android.systemui.statusbar.phone.KeyguardDismissUtil import com.android.traceur.MessageConstants.INTENT_EXTRA_TRACE_TYPE import com.android.traceur.TraceUtils.PresetTraceType import java.util.concurrent.Executor import javax.inject.Inject Loading Loading @@ -76,15 +74,10 @@ constructor( when (intent?.action) { ACTION_START -> { bgExecutor.execute { traceurMessageSender.startTracing( intent.getSerializableExtra( INTENT_EXTRA_TRACE_TYPE, PresetTraceType::class.java ) ) traceurMessageSender.startTracing(issueRecordingState.traceType) } issueRecordingState.isRecording = true if (!intent.getBooleanExtra(EXTRA_SCREEN_RECORD, false)) { if (!issueRecordingState.recordScreen) { // If we don't want to record the screen, the ACTION_SHOW_START_NOTIF action // will circumvent the RecordingService's screen recording start code. return super.onStartCommand(Intent(ACTION_SHOW_START_NOTIF), flags, startId) Loading @@ -107,7 +100,7 @@ constructor( ) val screenRecording = intent.getParcelableExtra(EXTRA_PATH, Uri::class.java) if (issueRecordingState.takeBugReport) { if (issueRecordingState.takeBugreport) { iActivityManager.requestBugReportWithExtraAttachment(screenRecording) } else { traceurMessageSender.shareTraces(applicationContext, screenRecording) Loading @@ -130,7 +123,6 @@ constructor( companion object { private const val TAG = "IssueRecordingService" private const val CHANNEL_ID = "issue_record" private const val EXTRA_SCREEN_RECORD = "extra_screenRecord" /** * Get an intent to stop the issue recording service. Loading @@ -148,35 +140,36 @@ constructor( * * @param context Context from the requesting activity */ fun getStartIntent( context: Context, screenRecord: Boolean, traceType: PresetTraceType, ): Intent = Intent(context, IssueRecordingService::class.java) .setAction(ACTION_START) .putExtra(EXTRA_SCREEN_RECORD, screenRecord) .putExtra(INTENT_EXTRA_TRACE_TYPE, traceType) fun getStartIntent(context: Context): Intent = Intent(context, IssueRecordingService::class.java).setAction(ACTION_START) } } private class IrsStrings(private val res: Resources) : RecordingServiceStrings(res) { override val title get() = res.getString(R.string.issuerecord_title) override val notificationChannelDescription get() = res.getString(R.string.issuerecord_channel_description) override val startErrorResId get() = R.string.issuerecord_start_error override val startError get() = res.getString(R.string.issuerecord_start_error) override val saveErrorResId get() = R.string.issuerecord_save_error override val saveError get() = res.getString(R.string.issuerecord_save_error) override val ongoingRecording get() = res.getString(R.string.issuerecord_ongoing_screen_only) override val backgroundProcessingLabel get() = res.getString(R.string.issuerecord_background_processing_label) override val saveTitle get() = res.getString(R.string.issuerecord_save_title) } packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt +58 −3 Original line number Diff line number Diff line Loading @@ -16,16 +16,51 @@ 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.res.R import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.traceur.TraceUtils.PresetTraceType import java.util.concurrent.CopyOnWriteArrayList import javax.inject.Inject @SysUISingleton class IssueRecordingState @Inject constructor() { class IssueRecordingState @Inject constructor( userTracker: UserTracker, userFileManager: UserFileManager, ) { private val listeners = CopyOnWriteArrayList<Runnable>() private val prefs = userFileManager.getSharedPreferences( RecordIssueTile.TILE_SPEC, Context.MODE_PRIVATE, userTracker.userId ) var takeBugreport get() = prefs.getBoolean(KEY_TAKE_BUG_REPORT, false) set(value) = prefs.edit().putBoolean(KEY_TAKE_BUG_REPORT, value).apply() var recordScreen get() = prefs.getBoolean(KEY_RECORD_SCREEN, false) set(value) = prefs.edit().putBoolean(KEY_RECORD_SCREEN, value).apply() var hasUserApprovedScreenRecording get() = prefs.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false) private set(value) = prefs.edit().putBoolean(HAS_APPROVED_SCREEN_RECORDING, value).apply() var issueTypeRes get() = prefs.getInt(KEY_ISSUE_TYPE_RES, ISSUE_TYPE_NOT_SET) set(value) = prefs.edit().putInt(KEY_ISSUE_TYPE_RES, value).apply() val traceType: PresetTraceType get() = ALL_ISSUE_TYPES[issueTypeRes] ?: PresetTraceType.UNSET var takeBugReport: Boolean = false private val listeners = CopyOnWriteArrayList<Runnable>() var isRecording = false set(value) { Loading @@ -33,6 +68,10 @@ class IssueRecordingState @Inject constructor() { listeners.forEach(Runnable::run) } fun markUserApprovalForScreenRecording() { hasUserApprovedScreenRecording = true } fun addListener(listener: Runnable) { listeners.add(listener) } Loading @@ -40,4 +79,20 @@ class IssueRecordingState @Inject constructor() { fun removeListener(listener: Runnable) { listeners.remove(listener) } companion object { private const val KEY_TAKE_BUG_REPORT = "key_takeBugReport" private const val HAS_APPROVED_SCREEN_RECORDING = "HasApprovedScreenRecord" private const val KEY_RECORD_SCREEN = "key_recordScreen" const val KEY_ISSUE_TYPE_RES = "key_issueTypeRes" const val ISSUE_TYPE_NOT_SET = -1 val ALL_ISSUE_TYPES: Map<Int, PresetTraceType> = hashMapOf( Pair(R.string.performance, PresetTraceType.PERFORMANCE), Pair(R.string.user_interface, PresetTraceType.UI), Pair(R.string.battery, PresetTraceType.BATTERY), Pair(R.string.thermal, PresetTraceType.THERMAL) ) } } packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt +53 −69 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ package com.android.systemui.recordissue import android.annotation.SuppressLint import android.app.AlertDialog import android.app.AlertDialog.BUTTON_POSITIVE import android.content.Context import android.content.Intent import android.content.res.ColorStateList Loading @@ -41,18 +41,16 @@ import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger import com.android.systemui.mediaprojection.SessionCreationSource import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialogDelegate import com.android.systemui.qs.tiles.RecordIssueTile import com.android.systemui.recordissue.IssueRecordingState.Companion.ALL_ISSUE_TYPES import com.android.systemui.recordissue.IssueRecordingState.Companion.ISSUE_TYPE_NOT_SET import com.android.systemui.recordissue.IssueRecordingState.Companion.KEY_ISSUE_TYPE_RES import com.android.systemui.res.R import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.traceur.MessageConstants.INTENT_EXTRA_TRACE_TYPE import com.android.traceur.TraceUtils.PresetTraceType import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import java.util.concurrent.Executor import java.util.function.Consumer class RecordIssueDialogDelegate @AssistedInject Loading @@ -64,31 +62,20 @@ constructor( @Main private val mainExecutor: Executor, private val devicePolicyResolver: dagger.Lazy<ScreenCaptureDevicePolicyResolver>, private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger, private val userFileManager: UserFileManager, private val screenCaptureDisabledDialogDelegate: ScreenCaptureDisabledDialogDelegate, private val issueRecordingState: IssueRecordingState, private val state: IssueRecordingState, private val traceurMessageSender: TraceurMessageSender, @Assisted private val onStarted: Consumer<IssueRecordingConfig>, @Assisted private val onStarted: Runnable, ) : SystemUIDialog.Delegate { private val issueTypeOptions: Map<Int, PresetTraceType> = hashMapOf( Pair(R.string.performance, PresetTraceType.PERFORMANCE), Pair(R.string.user_interface, PresetTraceType.UI), Pair(R.string.battery, PresetTraceType.BATTERY), Pair(R.string.thermal, PresetTraceType.THERMAL) ) private var selectedIssueType: PresetTraceType? = null /** To inject dependencies and allow for easier testing */ @AssistedFactory interface Factory { /** Create a dialog object */ fun create(onStarted: Consumer<IssueRecordingConfig>): RecordIssueDialogDelegate fun create(onStarted: Runnable): RecordIssueDialogDelegate } @SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var screenRecordSwitch: Switch @SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var bugReportSwitch: Switch private lateinit var issueTypeButton: Button @MainThread Loading @@ -97,21 +84,8 @@ constructor( setView(LayoutInflater.from(context).inflate(R.layout.record_issue_dialog, null)) setTitle(context.getString(R.string.qs_record_issue_label)) setIcon(R.drawable.qs_record_issue_icon_off) setNegativeButton(R.string.cancel) { _, _ -> dismiss() } setPositiveButton( R.string.qs_record_issue_start, { _, _ -> issueRecordingState.takeBugReport = bugReportSwitch.isChecked onStarted.accept( IssueRecordingConfig( screenRecordSwitch.isChecked, selectedIssueType ?: PresetTraceType.UNSET ) ) dismiss() }, false ) setNegativeButton(R.string.cancel) { _, _ -> } setPositiveButton(R.string.qs_record_issue_start) { _, _ -> onStarted.run() } } bgExecutor.execute { traceurMessageSender.bindToTraceur(dialog.context) } } Loading @@ -121,23 +95,40 @@ constructor( @MainThread override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { dialog.apply { window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS) window?.setGravity(Gravity.CENTER) window?.apply { addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS) setGravity(Gravity.CENTER) } screenRecordSwitch = requireViewById(R.id.screenrecord_switch) screenRecordSwitch.setOnCheckedChangeListener { _, isEnabled -> if (isEnabled) { screenRecordSwitch = requireViewById<Switch>(R.id.screenrecord_switch).apply { isChecked = state.recordScreen setOnCheckedChangeListener { _, isChecked -> state.recordScreen = isChecked if (isChecked) { bgExecutor.execute { onScreenRecordSwitchClicked() } } } bugReportSwitch = requireViewById(R.id.bugreport_switch) val startButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE) issueTypeButton = requireViewById(R.id.issue_type_button) issueTypeButton.setOnClickListener { onIssueTypeClicked(context) { startButton.isEnabled = true } } requireViewById<Switch>(R.id.bugreport_switch).apply { isChecked = state.takeBugreport setOnCheckedChangeListener { _, isChecked -> state.takeBugreport = isChecked } } issueTypeButton = requireViewById<Button>(R.id.issue_type_button).apply { val startButton = dialog.getButton(BUTTON_POSITIVE) if (state.issueTypeRes != ISSUE_TYPE_NOT_SET) { setText(state.issueTypeRes) } else { startButton.isEnabled = false } setOnClickListener { onIssueTypeClicked(context) { startButton.isEnabled = true } } } } } @WorkerThread Loading @@ -160,45 +151,38 @@ constructor( SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER ) if (flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)) { val prefs = userFileManager.getSharedPreferences( RecordIssueTile.TILE_SPEC, Context.MODE_PRIVATE, userTracker.userId ) if (!prefs.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false)) { if ( flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING) && !state.hasUserApprovedScreenRecording ) { mainExecutor.execute { ScreenCapturePermissionDialogDelegate(factory, prefs).createDialog().apply { ScreenCapturePermissionDialogDelegate(factory, state).createDialog().apply { setOnCancelListener { screenRecordSwitch.isChecked = false } show() } } } } } @MainThread private fun onIssueTypeClicked(context: Context, onIssueTypeSelected: Runnable) { val popupMenu = PopupMenu(context, issueTypeButton) issueTypeOptions.keys.forEach { ALL_ISSUE_TYPES.keys.forEach { popupMenu.menu.add(it).apply { setIcon(R.drawable.arrow_pointing_down) if (issueTypeOptions[it] != selectedIssueType) { if (it != state.issueTypeRes) { iconTintList = ColorStateList.valueOf(Color.TRANSPARENT) } intent = Intent().putExtra(INTENT_EXTRA_TRACE_TYPE, issueTypeOptions[it]) intent = Intent().putExtra(KEY_ISSUE_TYPE_RES, it) } } popupMenu.apply { setOnMenuItemClickListener { issueTypeButton.text = it.title selectedIssueType = it.intent?.getSerializableExtra( INTENT_EXTRA_TRACE_TYPE, PresetTraceType::class.java ) state.issueTypeRes = it.intent?.getIntExtra(KEY_ISSUE_TYPE_RES, ISSUE_TYPE_NOT_SET) ?: ISSUE_TYPE_NOT_SET onIssueTypeSelected.run() true } Loading Loading
packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt +4 −10 Original line number Diff line number Diff line Loading @@ -52,7 +52,6 @@ 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 com.android.traceur.TraceUtils.PresetTraceType import java.util.concurrent.Executor import javax.inject.Inject Loading Loading @@ -131,15 +130,11 @@ constructor( } } private fun startIssueRecordingService(screenRecord: Boolean, traceType: PresetTraceType) = private fun startIssueRecordingService() = PendingIntent.getForegroundService( userContextProvider.userContext, RecordingService.REQUEST_CODE, IssueRecordingService.getStartIntent( userContextProvider.userContext, screenRecord, traceType ), IssueRecordingService.getStartIntent(userContextProvider.userContext), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) .send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle()) Loading @@ -157,7 +152,7 @@ constructor( val dialog: AlertDialog = delegateFactory .create { startIssueRecordingService(it.screenRecord, it.traceType) startIssueRecordingService() dialogTransitionAnimator.disableAllCurrentDialogsExitAnimations() panelInteractor.collapsePanels() } Loading @@ -169,8 +164,7 @@ constructor( if (expandable != null && !keyguardStateController.isShowing) { expandable .dialogTransitionController(DialogCuj(CUJ_SHADE_DIALOG_OPEN, TILE_SPEC)) ?.let { dialogTransitionAnimator.show(dialog, it) } ?: dialog.show() ?.let { dialogTransitionAnimator.show(dialog, it) } ?: dialog.show() } else { dialog.show() } Loading
packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.ktdeleted 100644 → 0 +0 −21 Original line number Diff line number Diff line /* * 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.recordissue import com.android.traceur.TraceUtils.PresetTraceType data class IssueRecordingConfig(val screenRecord: Boolean, val traceType: PresetTraceType)
packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt +13 −20 Original line number Diff line number Diff line Loading @@ -35,8 +35,6 @@ import com.android.systemui.screenrecord.RecordingService import com.android.systemui.screenrecord.RecordingServiceStrings import com.android.systemui.settings.UserContextProvider import com.android.systemui.statusbar.phone.KeyguardDismissUtil import com.android.traceur.MessageConstants.INTENT_EXTRA_TRACE_TYPE import com.android.traceur.TraceUtils.PresetTraceType import java.util.concurrent.Executor import javax.inject.Inject Loading Loading @@ -76,15 +74,10 @@ constructor( when (intent?.action) { ACTION_START -> { bgExecutor.execute { traceurMessageSender.startTracing( intent.getSerializableExtra( INTENT_EXTRA_TRACE_TYPE, PresetTraceType::class.java ) ) traceurMessageSender.startTracing(issueRecordingState.traceType) } issueRecordingState.isRecording = true if (!intent.getBooleanExtra(EXTRA_SCREEN_RECORD, false)) { if (!issueRecordingState.recordScreen) { // If we don't want to record the screen, the ACTION_SHOW_START_NOTIF action // will circumvent the RecordingService's screen recording start code. return super.onStartCommand(Intent(ACTION_SHOW_START_NOTIF), flags, startId) Loading @@ -107,7 +100,7 @@ constructor( ) val screenRecording = intent.getParcelableExtra(EXTRA_PATH, Uri::class.java) if (issueRecordingState.takeBugReport) { if (issueRecordingState.takeBugreport) { iActivityManager.requestBugReportWithExtraAttachment(screenRecording) } else { traceurMessageSender.shareTraces(applicationContext, screenRecording) Loading @@ -130,7 +123,6 @@ constructor( companion object { private const val TAG = "IssueRecordingService" private const val CHANNEL_ID = "issue_record" private const val EXTRA_SCREEN_RECORD = "extra_screenRecord" /** * Get an intent to stop the issue recording service. Loading @@ -148,35 +140,36 @@ constructor( * * @param context Context from the requesting activity */ fun getStartIntent( context: Context, screenRecord: Boolean, traceType: PresetTraceType, ): Intent = Intent(context, IssueRecordingService::class.java) .setAction(ACTION_START) .putExtra(EXTRA_SCREEN_RECORD, screenRecord) .putExtra(INTENT_EXTRA_TRACE_TYPE, traceType) fun getStartIntent(context: Context): Intent = Intent(context, IssueRecordingService::class.java).setAction(ACTION_START) } } private class IrsStrings(private val res: Resources) : RecordingServiceStrings(res) { override val title get() = res.getString(R.string.issuerecord_title) override val notificationChannelDescription get() = res.getString(R.string.issuerecord_channel_description) override val startErrorResId get() = R.string.issuerecord_start_error override val startError get() = res.getString(R.string.issuerecord_start_error) override val saveErrorResId get() = R.string.issuerecord_save_error override val saveError get() = res.getString(R.string.issuerecord_save_error) override val ongoingRecording get() = res.getString(R.string.issuerecord_ongoing_screen_only) override val backgroundProcessingLabel get() = res.getString(R.string.issuerecord_background_processing_label) override val saveTitle get() = res.getString(R.string.issuerecord_save_title) }
packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt +58 −3 Original line number Diff line number Diff line Loading @@ -16,16 +16,51 @@ 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.res.R import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.traceur.TraceUtils.PresetTraceType import java.util.concurrent.CopyOnWriteArrayList import javax.inject.Inject @SysUISingleton class IssueRecordingState @Inject constructor() { class IssueRecordingState @Inject constructor( userTracker: UserTracker, userFileManager: UserFileManager, ) { private val listeners = CopyOnWriteArrayList<Runnable>() private val prefs = userFileManager.getSharedPreferences( RecordIssueTile.TILE_SPEC, Context.MODE_PRIVATE, userTracker.userId ) var takeBugreport get() = prefs.getBoolean(KEY_TAKE_BUG_REPORT, false) set(value) = prefs.edit().putBoolean(KEY_TAKE_BUG_REPORT, value).apply() var recordScreen get() = prefs.getBoolean(KEY_RECORD_SCREEN, false) set(value) = prefs.edit().putBoolean(KEY_RECORD_SCREEN, value).apply() var hasUserApprovedScreenRecording get() = prefs.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false) private set(value) = prefs.edit().putBoolean(HAS_APPROVED_SCREEN_RECORDING, value).apply() var issueTypeRes get() = prefs.getInt(KEY_ISSUE_TYPE_RES, ISSUE_TYPE_NOT_SET) set(value) = prefs.edit().putInt(KEY_ISSUE_TYPE_RES, value).apply() val traceType: PresetTraceType get() = ALL_ISSUE_TYPES[issueTypeRes] ?: PresetTraceType.UNSET var takeBugReport: Boolean = false private val listeners = CopyOnWriteArrayList<Runnable>() var isRecording = false set(value) { Loading @@ -33,6 +68,10 @@ class IssueRecordingState @Inject constructor() { listeners.forEach(Runnable::run) } fun markUserApprovalForScreenRecording() { hasUserApprovedScreenRecording = true } fun addListener(listener: Runnable) { listeners.add(listener) } Loading @@ -40,4 +79,20 @@ class IssueRecordingState @Inject constructor() { fun removeListener(listener: Runnable) { listeners.remove(listener) } companion object { private const val KEY_TAKE_BUG_REPORT = "key_takeBugReport" private const val HAS_APPROVED_SCREEN_RECORDING = "HasApprovedScreenRecord" private const val KEY_RECORD_SCREEN = "key_recordScreen" const val KEY_ISSUE_TYPE_RES = "key_issueTypeRes" const val ISSUE_TYPE_NOT_SET = -1 val ALL_ISSUE_TYPES: Map<Int, PresetTraceType> = hashMapOf( Pair(R.string.performance, PresetTraceType.PERFORMANCE), Pair(R.string.user_interface, PresetTraceType.UI), Pair(R.string.battery, PresetTraceType.BATTERY), Pair(R.string.thermal, PresetTraceType.THERMAL) ) } }
packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt +53 −69 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ package com.android.systemui.recordissue import android.annotation.SuppressLint import android.app.AlertDialog import android.app.AlertDialog.BUTTON_POSITIVE import android.content.Context import android.content.Intent import android.content.res.ColorStateList Loading @@ -41,18 +41,16 @@ import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger import com.android.systemui.mediaprojection.SessionCreationSource import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialogDelegate import com.android.systemui.qs.tiles.RecordIssueTile import com.android.systemui.recordissue.IssueRecordingState.Companion.ALL_ISSUE_TYPES import com.android.systemui.recordissue.IssueRecordingState.Companion.ISSUE_TYPE_NOT_SET import com.android.systemui.recordissue.IssueRecordingState.Companion.KEY_ISSUE_TYPE_RES import com.android.systemui.res.R import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.traceur.MessageConstants.INTENT_EXTRA_TRACE_TYPE import com.android.traceur.TraceUtils.PresetTraceType import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import java.util.concurrent.Executor import java.util.function.Consumer class RecordIssueDialogDelegate @AssistedInject Loading @@ -64,31 +62,20 @@ constructor( @Main private val mainExecutor: Executor, private val devicePolicyResolver: dagger.Lazy<ScreenCaptureDevicePolicyResolver>, private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger, private val userFileManager: UserFileManager, private val screenCaptureDisabledDialogDelegate: ScreenCaptureDisabledDialogDelegate, private val issueRecordingState: IssueRecordingState, private val state: IssueRecordingState, private val traceurMessageSender: TraceurMessageSender, @Assisted private val onStarted: Consumer<IssueRecordingConfig>, @Assisted private val onStarted: Runnable, ) : SystemUIDialog.Delegate { private val issueTypeOptions: Map<Int, PresetTraceType> = hashMapOf( Pair(R.string.performance, PresetTraceType.PERFORMANCE), Pair(R.string.user_interface, PresetTraceType.UI), Pair(R.string.battery, PresetTraceType.BATTERY), Pair(R.string.thermal, PresetTraceType.THERMAL) ) private var selectedIssueType: PresetTraceType? = null /** To inject dependencies and allow for easier testing */ @AssistedFactory interface Factory { /** Create a dialog object */ fun create(onStarted: Consumer<IssueRecordingConfig>): RecordIssueDialogDelegate fun create(onStarted: Runnable): RecordIssueDialogDelegate } @SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var screenRecordSwitch: Switch @SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var bugReportSwitch: Switch private lateinit var issueTypeButton: Button @MainThread Loading @@ -97,21 +84,8 @@ constructor( setView(LayoutInflater.from(context).inflate(R.layout.record_issue_dialog, null)) setTitle(context.getString(R.string.qs_record_issue_label)) setIcon(R.drawable.qs_record_issue_icon_off) setNegativeButton(R.string.cancel) { _, _ -> dismiss() } setPositiveButton( R.string.qs_record_issue_start, { _, _ -> issueRecordingState.takeBugReport = bugReportSwitch.isChecked onStarted.accept( IssueRecordingConfig( screenRecordSwitch.isChecked, selectedIssueType ?: PresetTraceType.UNSET ) ) dismiss() }, false ) setNegativeButton(R.string.cancel) { _, _ -> } setPositiveButton(R.string.qs_record_issue_start) { _, _ -> onStarted.run() } } bgExecutor.execute { traceurMessageSender.bindToTraceur(dialog.context) } } Loading @@ -121,23 +95,40 @@ constructor( @MainThread override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { dialog.apply { window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS) window?.setGravity(Gravity.CENTER) window?.apply { addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS) setGravity(Gravity.CENTER) } screenRecordSwitch = requireViewById(R.id.screenrecord_switch) screenRecordSwitch.setOnCheckedChangeListener { _, isEnabled -> if (isEnabled) { screenRecordSwitch = requireViewById<Switch>(R.id.screenrecord_switch).apply { isChecked = state.recordScreen setOnCheckedChangeListener { _, isChecked -> state.recordScreen = isChecked if (isChecked) { bgExecutor.execute { onScreenRecordSwitchClicked() } } } bugReportSwitch = requireViewById(R.id.bugreport_switch) val startButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE) issueTypeButton = requireViewById(R.id.issue_type_button) issueTypeButton.setOnClickListener { onIssueTypeClicked(context) { startButton.isEnabled = true } } requireViewById<Switch>(R.id.bugreport_switch).apply { isChecked = state.takeBugreport setOnCheckedChangeListener { _, isChecked -> state.takeBugreport = isChecked } } issueTypeButton = requireViewById<Button>(R.id.issue_type_button).apply { val startButton = dialog.getButton(BUTTON_POSITIVE) if (state.issueTypeRes != ISSUE_TYPE_NOT_SET) { setText(state.issueTypeRes) } else { startButton.isEnabled = false } setOnClickListener { onIssueTypeClicked(context) { startButton.isEnabled = true } } } } } @WorkerThread Loading @@ -160,45 +151,38 @@ constructor( SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER ) if (flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)) { val prefs = userFileManager.getSharedPreferences( RecordIssueTile.TILE_SPEC, Context.MODE_PRIVATE, userTracker.userId ) if (!prefs.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false)) { if ( flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING) && !state.hasUserApprovedScreenRecording ) { mainExecutor.execute { ScreenCapturePermissionDialogDelegate(factory, prefs).createDialog().apply { ScreenCapturePermissionDialogDelegate(factory, state).createDialog().apply { setOnCancelListener { screenRecordSwitch.isChecked = false } show() } } } } } @MainThread private fun onIssueTypeClicked(context: Context, onIssueTypeSelected: Runnable) { val popupMenu = PopupMenu(context, issueTypeButton) issueTypeOptions.keys.forEach { ALL_ISSUE_TYPES.keys.forEach { popupMenu.menu.add(it).apply { setIcon(R.drawable.arrow_pointing_down) if (issueTypeOptions[it] != selectedIssueType) { if (it != state.issueTypeRes) { iconTintList = ColorStateList.valueOf(Color.TRANSPARENT) } intent = Intent().putExtra(INTENT_EXTRA_TRACE_TYPE, issueTypeOptions[it]) intent = Intent().putExtra(KEY_ISSUE_TYPE_RES, it) } } popupMenu.apply { setOnMenuItemClickListener { issueTypeButton.text = it.title selectedIssueType = it.intent?.getSerializableExtra( INTENT_EXTRA_TRACE_TYPE, PresetTraceType::class.java ) state.issueTypeRes = it.intent?.getIntExtra(KEY_ISSUE_TYPE_RES, ISSUE_TYPE_NOT_SET) ?: ISSUE_TYPE_NOT_SET onIssueTypeSelected.run() true } Loading