Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 219bb9ce authored by Stefan Andonian's avatar Stefan Andonian Committed by Android (Google) Code Review
Browse files

Merge "Persist User Selections in Record Issue Tile Dialog" into main

parents a0d0b9eb 4cbc7f31
Loading
Loading
Loading
Loading
+4 −10
Original line number Diff line number Diff line
@@ -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

@@ -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())
@@ -157,7 +152,7 @@ constructor(
        val dialog: AlertDialog =
            delegateFactory
                .create {
                    startIssueRecordingService(it.screenRecord, it.traceType)
                    startIssueRecordingService()
                    dialogTransitionAnimator.disableAllCurrentDialogsExitAnimations()
                    panelInteractor.collapsePanels()
                }
@@ -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()
                }
+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)
+13 −20
Original line number Diff line number Diff line
@@ -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

@@ -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)
@@ -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)
@@ -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.
@@ -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)
}
+58 −3
Original line number Diff line number Diff line
@@ -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) {
@@ -33,6 +68,10 @@ class IssueRecordingState @Inject constructor() {
            listeners.forEach(Runnable::run)
        }

    fun markUserApprovalForScreenRecording() {
        hasUserApprovedScreenRecording = true
    }

    fun addListener(listener: Runnable) {
        listeners.add(listener)
    }
@@ -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)
            )
    }
}
+53 −69
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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
@@ -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) }
    }
@@ -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
@@ -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