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

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

Merge changes Ibbfdee10,I2a47a2f7 into main

* changes:
  Save Custom Trace Settings in Persistent Storage.
  Create Custom Trace Settings UI for Record Issue Tile.
parents f55ee221 58f96544
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -573,6 +573,7 @@ android_library {
        "lottie_compose",
        "lottie_compose",
        "LowLightDreamLib",
        "LowLightDreamLib",
        "TraceurCommon",
        "TraceurCommon",
        "Traceur-res",
        "//frameworks/libs/systemui:motion_tool_lib",
        "//frameworks/libs/systemui:motion_tool_lib",
        "notification_flags_lib",
        "notification_flags_lib",
        "PlatformComposeCore",
        "PlatformComposeCore",
@@ -749,6 +750,7 @@ android_library {
        "androidx.compose.animation_animation-graphics",
        "androidx.compose.animation_animation-graphics",
        "androidx.lifecycle_lifecycle-viewmodel-compose",
        "androidx.lifecycle_lifecycle-viewmodel-compose",
        "TraceurCommon",
        "TraceurCommon",
        "Traceur-res",
    ],
    ],
}
}


+167 −0
Original line number Original line Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ 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.
  -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/categories"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="@dimen/qqs_layout_margin_top"
        android:textAppearance="@style/TextAppearance.Dialog.Body.Message" />

    <TextView
        android:id="@+id/cpu_buffer_size"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="@dimen/qqs_layout_margin_top"
        android:textAppearance="@style/TextAppearance.Dialog.Body.Message" />

    <!-- Attach to Bugreport Switch -->
    <LinearLayout
        android:id="@+id/attach_to_bugreport_switch_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/qqs_layout_margin_top"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/attach_to_bugreport_switch_label"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:minHeight="@dimen/screenrecord_option_icon_size"
            android:layout_weight="1"
            android:layout_gravity="fill_vertical"
            android:gravity="start"
            android:textAppearance="@style/TextAppearance.Dialog.Body.Message" />

        <Switch
            android:id="@+id/attach_to_bugreport_switch"
            android:layout_width="wrap_content"
            android:minHeight="@dimen/screenrecord_option_icon_size"
            android:layout_height="wrap_content"
            android:gravity="end"
            android:layout_gravity="fill_vertical"
            android:layout_weight="0" />
    </LinearLayout>

    <!-- Winscope Switch -->
    <LinearLayout
        android:id="@+id/winscope_switch_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/qqs_layout_margin_top"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/winscope_switch_label"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:minHeight="@dimen/screenrecord_option_icon_size"
            android:layout_weight="1"
            android:layout_gravity="fill_vertical"
            android:gravity="start"
            android:textAppearance="@style/TextAppearance.Dialog.Body.Message"/>

        <Switch
            android:id="@+id/winscope_switch"
            android:layout_width="wrap_content"
            android:minHeight="@dimen/screenrecord_option_icon_size"
            android:layout_height="wrap_content"
            android:gravity="end"
            android:layout_gravity="fill_vertical"
            android:layout_weight="0" />
    </LinearLayout>

    <!-- Trace Debuggable Apps Switch -->
    <LinearLayout
        android:id="@+id/trace_debuggable_apps_switch_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/qqs_layout_margin_top"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/debuggable_apps_switch_label"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:minHeight="@dimen/screenrecord_option_icon_size"
            android:layout_weight="1"
            android:layout_gravity="fill_vertical"
            android:gravity="start"
            android:textAppearance="@style/TextAppearance.Dialog.Body.Message" />

        <Switch
            android:id="@+id/trace_debuggable_apps_switch"
            android:layout_width="wrap_content"
            android:minHeight="@dimen/screenrecord_option_icon_size"
            android:layout_height="wrap_content"
            android:gravity="end"
            android:layout_gravity="fill_vertical"
            android:layout_weight="0" />
    </LinearLayout>

    <!-- Long Traces Switch -->
    <LinearLayout
        android:id="@+id/long_traces_switch_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/qqs_layout_margin_top"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/long_traces_switch_label"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:minHeight="@dimen/screenrecord_option_icon_size"
            android:layout_weight="1"
            android:layout_gravity="fill_vertical"
            android:gravity="start"
            android:textAppearance="@style/TextAppearance.Dialog.Body.Message" />

        <Switch
            android:id="@+id/long_traces_switch"
            android:layout_width="wrap_content"
            android:minHeight="@dimen/screenrecord_option_icon_size"
            android:layout_height="wrap_content"
            android:gravity="end"
            android:layout_gravity="fill_vertical"
            android:layout_weight="0" />
    </LinearLayout>

    <TextView
        android:id="@+id/long_trace_size"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="@dimen/qqs_layout_margin_top"
        android:textAppearance="@style/TextAppearance.Dialog.Body.Message" />

    <TextView
        android:id="@+id/long_trace_duration"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="@dimen/qqs_layout_margin_top"
        android:textAppearance="@style/TextAppearance.Dialog.Body.Message" />
</LinearLayout>
+4 −0
Original line number Original line Diff line number Diff line
@@ -948,6 +948,10 @@
    <string name="performance">Performance</string>
    <string name="performance">Performance</string>
    <string name="user_interface">User Interface</string>
    <string name="user_interface">User Interface</string>
    <string name="thermal">Thermal</string>
    <string name="thermal">Thermal</string>
    <string name="custom">Custom</string>

    <string name="custom_trace_settings_dialog_title">Custom Trace Settings</string>
    <string name="restore_default">Restore Default</string>


    <!-- QuickSettings: Label for the toggle that controls whether One-handed mode is enabled. [CHAR LIMIT=NONE] -->
    <!-- QuickSettings: Label for the toggle that controls whether One-handed mode is enabled. [CHAR LIMIT=NONE] -->
    <string name="quick_settings_onehanded_label">One-handed mode</string>
    <string name="quick_settings_onehanded_label">One-handed mode</string>
+236 −0
Original line number Original line 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 android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.widget.Switch
import android.widget.TextView
import com.android.systemui.recordissue.IssueRecordingState.Companion.TAG_TITLE_DELIMITER
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.traceur.PresetTraceConfigs
import com.android.traceur.TraceConfig
import com.android.traceur.res.R as T
import java.util.function.Consumer

class CustomTraceSettingsDialogDelegate(
    private val factory: SystemUIDialog.Factory,
    private val customTraceState: CustomTraceState,
    private val tagTitles: Set<String>,
    private val onSave: Runnable,
) : SystemUIDialog.Delegate {

    private val builder = TraceConfig.Builder(customTraceState.traceConfig)

    override fun createDialog(): SystemUIDialog = factory.create(this)

    override fun beforeCreate(dialog: SystemUIDialog?, savedInstanceState: Bundle?) {
        super.beforeCreate(dialog, savedInstanceState)

        dialog?.apply {
            setTitle(R.string.custom_trace_settings_dialog_title)
            setView(
                LayoutInflater.from(context).inflate(R.layout.custom_trace_settings_dialog, null)
            )
            setPositiveButton(R.string.save) { _, _ ->
                onSave.run()
                customTraceState.traceConfig = builder.build()
            }
            setNegativeButton(R.string.cancel) { _, _ -> }
        }
    }

    @SuppressLint("SetTextI18n")
    override fun onCreate(dialog: SystemUIDialog?, savedInstanceState: Bundle?) {
        super.onCreate(dialog, savedInstanceState)

        dialog?.apply {
            requireViewById<TextView>(R.id.categories).apply {
                text =
                    context.getString(T.string.categories) +
                        "\n" +
                        if (
                            builder.tags == null ||
                                builder.tags!! == PresetTraceConfigs.getDefaultConfig().tags
                        ) {
                            context.getString(R.string.notification_alert_title)
                        } else {
                            tagTitles
                                .filter {
                                    builder.tags!!.contains(it.substringBefore(TAG_TITLE_DELIMITER))
                                }
                                .joinToString()
                        }
                setOnClickListener { showCategorySelector(this) }
            }
            requireViewById<Switch>(R.id.attach_to_bugreport_switch).apply {
                isChecked = builder.attachToBugreport
                setOnCheckedChangeListener { _, isChecked -> builder.attachToBugreport = isChecked }
            }
            requireViewById<TextView>(R.id.cpu_buffer_size).setupSingleChoiceText(
                T.array.buffer_size_values,
                T.array.buffer_size_names,
                builder.bufferSizeKb,
                T.string.buffer_size,
            ) {
                builder.bufferSizeKb = it
            }
            val longTraceSizeText: TextView =
                requireViewById<TextView>(R.id.long_trace_size).setupSingleChoiceText(
                    T.array.long_trace_size_values,
                    T.array.long_trace_size_names,
                    builder.maxLongTraceSizeMb,
                    T.string.max_long_trace_size,
                ) {
                    builder.maxLongTraceSizeMb = it
                }
            val longTraceDurationText: TextView =
                requireViewById<TextView>(R.id.long_trace_duration).setupSingleChoiceText(
                    T.array.long_trace_duration_values,
                    T.array.long_trace_duration_names,
                    builder.maxLongTraceDurationMinutes,
                    T.string.max_long_trace_duration,
                ) {
                    builder.maxLongTraceDurationMinutes = it
                }
            requireViewById<Switch>(R.id.long_traces_switch).apply {
                isChecked = builder.longTrace
                val disabledAlpha by lazy { getDisabledAlpha(context) }
                val alpha = if (isChecked) 1f else disabledAlpha
                longTraceDurationText.alpha = alpha
                longTraceSizeText.alpha = alpha

                setOnCheckedChangeListener { _, isChecked ->
                    builder.longTrace = isChecked
                    longTraceDurationText.isEnabled = isChecked
                    longTraceSizeText.isEnabled = isChecked

                    val newAlpha = if (isChecked) 1f else disabledAlpha
                    longTraceDurationText.alpha = newAlpha
                    longTraceSizeText.alpha = newAlpha
                }
            }
            requireViewById<Switch>(R.id.winscope_switch).apply {
                isChecked = builder.winscope
                setOnCheckedChangeListener { _, isChecked -> builder.winscope = isChecked }
            }
            requireViewById<Switch>(R.id.trace_debuggable_apps_switch).apply {
                isChecked = builder.apps
                setOnCheckedChangeListener { _, isChecked -> builder.apps = isChecked }
            }
            requireViewById<TextView>(R.id.long_traces_switch_label).text =
                context.getString(T.string.long_traces)
            requireViewById<TextView>(R.id.debuggable_apps_switch_label).text =
                context.getString(T.string.trace_debuggable_applications)
            requireViewById<TextView>(R.id.winscope_switch_label).text =
                context.getString(T.string.winscope_tracing)
            requireViewById<TextView>(R.id.attach_to_bugreport_switch_label).text =
                context.getString(T.string.attach_to_bug_report)
        }
    }

    @SuppressLint("SetTextI18n")
    private fun showCategorySelector(root: TextView) {
        showDialog(root.context) {
            val tags = builder.tags ?: PresetTraceConfigs.getDefaultConfig().tags
            val titlesToCheckmarks =
                tagTitles.associateBy(
                    { it },
                    { tags.contains(it.substringBefore(TAG_TITLE_DELIMITER)) }
                )
            val titles = titlesToCheckmarks.keys.toTypedArray()
            val checkmarks = titlesToCheckmarks.values.toBooleanArray()
            val checkedTitleSuffixes =
                titlesToCheckmarks.entries
                    .filter { it.value }
                    .map { it.key.substringAfter(TAG_TITLE_DELIMITER) }
                    .toMutableSet()

            val newTags = tags.toMutableSet()
            setMultiChoiceItems(titles, checkmarks) { _, i, isChecked ->
                val tag = titles[i].substringBefore(TAG_TITLE_DELIMITER)
                val titleSuffix = titles[i].substringAfter(TAG_TITLE_DELIMITER)
                if (isChecked) {
                    newTags.add(tag)
                    checkedTitleSuffixes.add(titleSuffix)
                } else {
                    newTags.remove(tag)
                    checkedTitleSuffixes.remove(titleSuffix)
                }
            }
            setPositiveButton(R.string.save) { _, _ ->
                root.text =
                    root.context.resources.getString(T.string.categories) +
                        "\n" +
                        checkedTitleSuffixes.joinToString()
                builder.tags = newTags
            }
            setNeutralButton(R.string.restore_default) { _, _ ->
                root.text =
                    context.getString(T.string.categories) +
                        "\n" +
                        context.getString(R.string.notification_alert_title)
                builder.tags = null
            }
            setNegativeButton(R.string.cancel) { _, _ -> }
        }
    }

    @SuppressLint("SetTextI18n")
    private fun TextView.setupSingleChoiceText(
        resValues: Int,
        resNames: Int,
        startingValue: Int,
        alertTitleRes: Int,
        onChosen: Consumer<Int>,
    ): TextView {
        val values = resources.getStringArray(resValues).map { Integer.parseInt(it) }
        val names = resources.getStringArray(resNames)
        val startingIndex = values.indexOf(startingValue)
        text = resources.getString(alertTitleRes) + "\n${names[startingIndex]}"

        setOnClickListener {
            showDialog(context) {
                setTitle(alertTitleRes)
                setSingleChoiceItems(names, startingIndex) { d, i ->
                    text = resources.getString(alertTitleRes) + "\n${names[i]}"
                    onChosen.accept(values[i])
                    d.dismiss()
                }
            }
        }
        return this
    }

    private fun showDialog(context: Context, onBuilder: AlertDialog.Builder.() -> Unit) =
        AlertDialog.Builder(context, R.style.Theme_SystemUI_Dialog_Alert)
            .apply { onBuilder() }
            .create()
            .also { SystemUIDialog.applyFlags(it) }
            .show()

    private fun getDisabledAlpha(context: Context): Float {
        val ta = context.obtainStyledAttributes(intArrayOf(android.R.attr.disabledAlpha))
        val alpha = ta.getFloat(0, 0f)
        ta.recycle()
        return alpha
    }
}
+74 −0
Original line number Original line 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 android.content.SharedPreferences
import com.android.traceur.PresetTraceConfigs.TraceOptions
import com.android.traceur.PresetTraceConfigs.getDefaultConfig
import com.android.traceur.TraceConfig

class CustomTraceState(private val prefs: SharedPreferences) {

    private var enabledTags: Set<String>?
        get() = prefs.getStringSet(KEY_TAGS, getDefaultConfig().tags) ?: getDefaultConfig().tags
        set(value) = prefs.edit().putStringSet(KEY_TAGS, value).apply()

    var traceConfig: TraceConfig
        get() = TraceConfig(options, enabledTags)
        set(value) {
            enabledTags = value.tags
            options = value.options
        }

    private var options: TraceOptions
        get() =
            TraceOptions(
                prefs.getInt(KEY_CUSTOM_BUFFER_SIZE_KB, getDefaultConfig().bufferSizeKb),
                prefs.getBoolean(KEY_WINSCOPE, getDefaultConfig().winscope),
                prefs.getBoolean(KEY_APPS, getDefaultConfig().apps),
                prefs.getBoolean(KEY_LONG_TRACE, getDefaultConfig().longTrace),
                prefs.getBoolean(KEY_ATTACH_TO_BUGREPORT, getDefaultConfig().attachToBugreport),
                prefs.getInt(KEY_LONG_TRACE_SIZE_MB, getDefaultConfig().maxLongTraceSizeMb),
                prefs.getInt(
                    KEY_LONG_TRACE_DURATION_MINUTES,
                    getDefaultConfig().maxLongTraceDurationMinutes
                ),
            )
        set(value) {
            prefs
                .edit()
                .putInt(KEY_CUSTOM_BUFFER_SIZE_KB, value.bufferSizeKb)
                .putBoolean(KEY_WINSCOPE, value.winscope)
                .putBoolean(KEY_APPS, value.apps)
                .putBoolean(KEY_LONG_TRACE, value.longTrace)
                .putBoolean(KEY_ATTACH_TO_BUGREPORT, value.attachToBugreport)
                .putInt(KEY_LONG_TRACE_SIZE_MB, value.maxLongTraceSizeMb)
                .putInt(KEY_LONG_TRACE_DURATION_MINUTES, value.maxLongTraceDurationMinutes)
                .apply()
        }

    companion object {
        private const val KEY_CUSTOM_BUFFER_SIZE_KB = "key_bufferSizeKb"
        private const val KEY_WINSCOPE = "key_winscope"
        private const val KEY_APPS = "key_apps"
        private const val KEY_LONG_TRACE = "key_longTrace"
        private const val KEY_ATTACH_TO_BUGREPORT = "key_attachToBugReport"
        private const val KEY_LONG_TRACE_SIZE_MB = "key_maxLongTraceSizeMb"
        private const val KEY_LONG_TRACE_DURATION_MINUTES = "key_maxLongTraceDurationInMinutes"
        private const val KEY_TAGS = "key_tags"
    }
}
Loading