Loading packages/SystemUI/res/drawable/arrow_pointing_down.xml 0 → 100644 +26 −0 Original line number Diff line number Diff line <!-- ~ Copyright (C) 2023 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. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0" android:tint="?attr/colorControlNormal"> <path android:fillColor="@android:color/white" android:pathData="M5.41,7.59L4,9l8,8 8,-8 -1.41,-1.41L12,14.17" /> </vector> No newline at end of file packages/SystemUI/res/layout/record_issue_dialog.xml 0 → 100644 +81 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2023 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" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:orientation="vertical" > <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:textAppearance="@style/TextAppearance.Dialog.Body.Message" android:text="@string/qs_record_issue_dropdown_header" /> <Button android:id="@+id/issue_type_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/qs_record_issue_dropdown_prompt" android:lines="1" android:drawableRight="@drawable/arrow_pointing_down" android:layout_marginTop="@dimen/qqs_layout_margin_top" android:focusable="false" android:clickable="true" /> <!-- Screen Record Switch --> <LinearLayout android:id="@+id/screenrecord_switch_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/qqs_layout_margin_top" android:orientation="horizontal"> <ImageView android:layout_width="@dimen/screenrecord_option_icon_size" android:layout_height="@dimen/screenrecord_option_icon_size" android:layout_weight="0" android:src="@drawable/ic_screenrecord" app:tint="?androidprv:attr/materialColorOnSurface" android:layout_gravity="center" android:layout_marginEnd="@dimen/screenrecord_option_padding" /> <TextView 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="center" android:text="@string/quick_settings_screen_record_label" android:textAppearance="@style/TextAppearance.Dialog.Body.Message" android:importantForAccessibility="no"/> <Switch android:id="@+id/screenrecord_switch" android:layout_width="wrap_content" android:minHeight="@dimen/screenrecord_option_icon_size" android:layout_height="wrap_content" android:gravity="center" android:layout_gravity="fill_vertical" android:layout_weight="0" android:contentDescription="@string/quick_settings_screen_record_label" /> </LinearLayout> </LinearLayout> No newline at end of file packages/SystemUI/res/values/strings.xml +14 −0 Original line number Diff line number Diff line Loading @@ -831,6 +831,20 @@ <!-- QuickSettings: Text to prompt the user to stop an ongoing recording [CHAR LIMIT=20] --> <string name="qs_record_issue_stop">Stop</string> <!-- QuickSettings: Issue Type Drop down options in Record Issue Start Dialog [CHAR LIMIT=50] --> <string name="qs_record_issue_dropdown_header">What part of your device experience was affected?</string> <!-- QuickSettings: Issue Type Drop down prompt in Record Issue Start Dialog [CHAR LIMIT=30] --> <string name="qs_record_issue_dropdown_prompt">Select issue type</string> <!-- QuickSettings: Screen record switch label in Record Issue Start Dialog [CHAR LIMIT=20] --> <string name="qs_record_issue_dropdown_screenrecord">Screen record</string> <!-- QuickSettings: Issue Type Drop down choices list in Record Issue Start Dialog [CHAR LIMIT=30] --> <string-array name="qs_record_issue_types"> <item>Performance</item> <item>User Interface</item> <item>Battery</item> </string-array> <!-- 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> Loading packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt +45 −3 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles import android.app.AlertDialog import android.content.Intent import android.os.Handler import android.os.Looper Loading @@ -24,8 +25,11 @@ import android.text.TextUtils import android.view.View import android.widget.Switch import androidx.annotation.VisibleForTesting import com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN import com.android.internal.logging.MetricsLogger import com.android.systemui.Flags.recordIssueQsTile import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter Loading @@ -36,7 +40,11 @@ import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.recordissue.RecordIssueDialogDelegate import com.android.systemui.res.R import com.android.systemui.statusbar.phone.KeyguardDismissUtil import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.policy.KeyguardStateController import javax.inject.Inject class RecordIssueTile Loading @@ -50,7 +58,11 @@ constructor( metricsLogger: MetricsLogger, statusBarStateController: StatusBarStateController, activityStarter: ActivityStarter, qsLogger: QSLogger qsLogger: QSLogger, private val keyguardDismissUtil: KeyguardDismissUtil, private val keyguardStateController: KeyguardStateController, private val dialogLaunchAnimator: DialogLaunchAnimator, private val sysuiDialogFactory: SystemUIDialog.Factory, ) : QSTileImpl<QSTile.BooleanState>( host, Loading @@ -76,11 +88,41 @@ constructor( handlesLongClick = false } override fun handleClick(view: View?) { isRecording = !isRecording @VisibleForTesting public override fun handleClick(view: View?) { if (isRecording) { isRecording = false } else { mUiHandler.post { showPrompt(view) } } refreshState() } private fun showPrompt(view: View?) { val dialog: AlertDialog = RecordIssueDialogDelegate(sysuiDialogFactory) { isRecording = true refreshState() } .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 (view != null && !keyguardStateController.isShowing) { dialogLaunchAnimator.showFromView( dialog, view, DialogCuj(CUJ_SHADE_DIALOG_OPEN, TILE_SPEC) ) } else { dialog.show() } false } keyguardDismissUtil.executeWhenUnlocked(dismissAction, false, true) } override fun getLongClickIntent(): Intent? = null @VisibleForTesting Loading packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt 0 → 100644 +88 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.content.Context import android.content.res.ColorStateList import android.graphics.Color import android.os.Bundle import android.view.Gravity import android.view.LayoutInflater import android.view.WindowManager import android.widget.Button import android.widget.PopupMenu import android.widget.Switch import com.android.systemui.res.R import com.android.systemui.statusbar.phone.SystemUIDialog class RecordIssueDialogDelegate( private val factory: SystemUIDialog.Factory, private val onStarted: Runnable ) : SystemUIDialog.Delegate { @SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var screenRecordSwitch: Switch private lateinit var issueTypeButton: Button override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { dialog.apply { 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) { _, _ -> onStarted.run() dismiss() } } } override fun createDialog(): SystemUIDialog = factory.create(this) override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { dialog.apply { window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS) window?.setGravity(Gravity.CENTER) screenRecordSwitch = requireViewById(R.id.screenrecord_switch) issueTypeButton = requireViewById(R.id.issue_type_button) issueTypeButton.setOnClickListener { onIssueTypeClicked(context) } } } private fun onIssueTypeClicked(context: Context) { val selectedCategory = issueTypeButton.text.toString() val popupMenu = PopupMenu(context, issueTypeButton) context.resources.getStringArray(R.array.qs_record_issue_types).forEachIndexed { i, cat -> popupMenu.menu.add(0, 0, i, cat).apply { setIcon(R.drawable.arrow_pointing_down) if (selectedCategory != cat) { iconTintList = ColorStateList.valueOf(Color.TRANSPARENT) } } } popupMenu.apply { setOnMenuItemClickListener { issueTypeButton.text = it.title true } setForceShowIcon(true) show() } } } Loading
packages/SystemUI/res/drawable/arrow_pointing_down.xml 0 → 100644 +26 −0 Original line number Diff line number Diff line <!-- ~ Copyright (C) 2023 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. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0" android:tint="?attr/colorControlNormal"> <path android:fillColor="@android:color/white" android:pathData="M5.41,7.59L4,9l8,8 8,-8 -1.41,-1.41L12,14.17" /> </vector> No newline at end of file
packages/SystemUI/res/layout/record_issue_dialog.xml 0 → 100644 +81 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2023 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" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:orientation="vertical" > <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:textAppearance="@style/TextAppearance.Dialog.Body.Message" android:text="@string/qs_record_issue_dropdown_header" /> <Button android:id="@+id/issue_type_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/qs_record_issue_dropdown_prompt" android:lines="1" android:drawableRight="@drawable/arrow_pointing_down" android:layout_marginTop="@dimen/qqs_layout_margin_top" android:focusable="false" android:clickable="true" /> <!-- Screen Record Switch --> <LinearLayout android:id="@+id/screenrecord_switch_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/qqs_layout_margin_top" android:orientation="horizontal"> <ImageView android:layout_width="@dimen/screenrecord_option_icon_size" android:layout_height="@dimen/screenrecord_option_icon_size" android:layout_weight="0" android:src="@drawable/ic_screenrecord" app:tint="?androidprv:attr/materialColorOnSurface" android:layout_gravity="center" android:layout_marginEnd="@dimen/screenrecord_option_padding" /> <TextView 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="center" android:text="@string/quick_settings_screen_record_label" android:textAppearance="@style/TextAppearance.Dialog.Body.Message" android:importantForAccessibility="no"/> <Switch android:id="@+id/screenrecord_switch" android:layout_width="wrap_content" android:minHeight="@dimen/screenrecord_option_icon_size" android:layout_height="wrap_content" android:gravity="center" android:layout_gravity="fill_vertical" android:layout_weight="0" android:contentDescription="@string/quick_settings_screen_record_label" /> </LinearLayout> </LinearLayout> No newline at end of file
packages/SystemUI/res/values/strings.xml +14 −0 Original line number Diff line number Diff line Loading @@ -831,6 +831,20 @@ <!-- QuickSettings: Text to prompt the user to stop an ongoing recording [CHAR LIMIT=20] --> <string name="qs_record_issue_stop">Stop</string> <!-- QuickSettings: Issue Type Drop down options in Record Issue Start Dialog [CHAR LIMIT=50] --> <string name="qs_record_issue_dropdown_header">What part of your device experience was affected?</string> <!-- QuickSettings: Issue Type Drop down prompt in Record Issue Start Dialog [CHAR LIMIT=30] --> <string name="qs_record_issue_dropdown_prompt">Select issue type</string> <!-- QuickSettings: Screen record switch label in Record Issue Start Dialog [CHAR LIMIT=20] --> <string name="qs_record_issue_dropdown_screenrecord">Screen record</string> <!-- QuickSettings: Issue Type Drop down choices list in Record Issue Start Dialog [CHAR LIMIT=30] --> <string-array name="qs_record_issue_types"> <item>Performance</item> <item>User Interface</item> <item>Battery</item> </string-array> <!-- 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> Loading
packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt +45 −3 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles import android.app.AlertDialog import android.content.Intent import android.os.Handler import android.os.Looper Loading @@ -24,8 +25,11 @@ import android.text.TextUtils import android.view.View import android.widget.Switch import androidx.annotation.VisibleForTesting import com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN import com.android.internal.logging.MetricsLogger import com.android.systemui.Flags.recordIssueQsTile import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter Loading @@ -36,7 +40,11 @@ import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.recordissue.RecordIssueDialogDelegate import com.android.systemui.res.R import com.android.systemui.statusbar.phone.KeyguardDismissUtil import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.policy.KeyguardStateController import javax.inject.Inject class RecordIssueTile Loading @@ -50,7 +58,11 @@ constructor( metricsLogger: MetricsLogger, statusBarStateController: StatusBarStateController, activityStarter: ActivityStarter, qsLogger: QSLogger qsLogger: QSLogger, private val keyguardDismissUtil: KeyguardDismissUtil, private val keyguardStateController: KeyguardStateController, private val dialogLaunchAnimator: DialogLaunchAnimator, private val sysuiDialogFactory: SystemUIDialog.Factory, ) : QSTileImpl<QSTile.BooleanState>( host, Loading @@ -76,11 +88,41 @@ constructor( handlesLongClick = false } override fun handleClick(view: View?) { isRecording = !isRecording @VisibleForTesting public override fun handleClick(view: View?) { if (isRecording) { isRecording = false } else { mUiHandler.post { showPrompt(view) } } refreshState() } private fun showPrompt(view: View?) { val dialog: AlertDialog = RecordIssueDialogDelegate(sysuiDialogFactory) { isRecording = true refreshState() } .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 (view != null && !keyguardStateController.isShowing) { dialogLaunchAnimator.showFromView( dialog, view, DialogCuj(CUJ_SHADE_DIALOG_OPEN, TILE_SPEC) ) } else { dialog.show() } false } keyguardDismissUtil.executeWhenUnlocked(dismissAction, false, true) } override fun getLongClickIntent(): Intent? = null @VisibleForTesting Loading
packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt 0 → 100644 +88 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.content.Context import android.content.res.ColorStateList import android.graphics.Color import android.os.Bundle import android.view.Gravity import android.view.LayoutInflater import android.view.WindowManager import android.widget.Button import android.widget.PopupMenu import android.widget.Switch import com.android.systemui.res.R import com.android.systemui.statusbar.phone.SystemUIDialog class RecordIssueDialogDelegate( private val factory: SystemUIDialog.Factory, private val onStarted: Runnable ) : SystemUIDialog.Delegate { @SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var screenRecordSwitch: Switch private lateinit var issueTypeButton: Button override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { dialog.apply { 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) { _, _ -> onStarted.run() dismiss() } } } override fun createDialog(): SystemUIDialog = factory.create(this) override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { dialog.apply { window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS) window?.setGravity(Gravity.CENTER) screenRecordSwitch = requireViewById(R.id.screenrecord_switch) issueTypeButton = requireViewById(R.id.issue_type_button) issueTypeButton.setOnClickListener { onIssueTypeClicked(context) } } } private fun onIssueTypeClicked(context: Context) { val selectedCategory = issueTypeButton.text.toString() val popupMenu = PopupMenu(context, issueTypeButton) context.resources.getStringArray(R.array.qs_record_issue_types).forEachIndexed { i, cat -> popupMenu.menu.add(0, 0, i, cat).apply { setIcon(R.drawable.arrow_pointing_down) if (selectedCategory != cat) { iconTintList = ColorStateList.valueOf(Color.TRANSPARENT) } } } popupMenu.apply { setOnMenuItemClickListener { issueTypeButton.text = it.title true } setForceShowIcon(true) show() } } }