Snap for 7662841 from f7db8d7ee4 to sc-v2-release

Change-Id: I953fca9d20a18f81fe6eeef64226574f8b306e92
This commit is contained in:
Android Build Coastguard Worker
2021-08-21 01:08:04 +00:00
15 changed files with 573 additions and 104 deletions

View File

@@ -1694,11 +1694,6 @@
android:value="true" /> android:value="true" />
</activity> </activity>
<activity
android:name=".SettingsTutorialDialogWrapperActivity"
android:theme="@style/Theme.AlertDialog"
android:exported="false"/>
<activity <activity
android:name="Settings$TextToSpeechSettingsActivity" android:name="Settings$TextToSpeechSettingsActivity"
android:exported="true" android:exported="true"

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="412dp"
android:height="300dp"
android:viewportWidth="412"
android:viewportHeight="300">
<path
android:fillColor="#FF000000"
android:pathData="M384.2,300H27.8C12.5,300 0,287.2 0,271.5v-243C0,12.8 12.5,0 27.8,0h356.5C399.5,0 412,12.8 412,28.5v243.2C412,287.2 399.5,300 384.2,300z"/>
<path
android:pathData="M274.9,97.7v-9.5c0,-1.3 -1.1,-2.4 -2.4,-2.4V35.9c0,-6.6 -5.3,-11.9 -11.9,-11.9H151.3c-6.6,0 -11.9,5.3 -11.9,11.9v228.2c0,6.6 5.3,11.9 11.9,11.9h109.4c6.6,0 11.9,-5.3 11.9,-11.9V147.6c1.3,0 2.4,-1.1 2.4,-2.4v-23.8c0,-1.3 -1.1,-2.4 -2.4,-2.4v-19C273.9,100.1 274.9,99 274.9,97.7zM270.2,264.1c0,5.2 -4.3,9.5 -9.5,9.5H151.3c-5.2,0 -9.5,-4.3 -9.5,-9.5V35.9c0,-5.2 4.3,-9.5 9.5,-9.5h109.4c5.2,0 9.5,4.3 9.5,9.5V264.1z"
android:fillColor="#80868B"/>
<path
android:pathData="M176.7,209.6c-3.7,0 -6.6,3 -6.6,6.6v24.6h13.3v-24.6C183.4,212.6 180.4,209.6 176.7,209.6z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="240.8954"
android:startX="176.75"
android:endY="209.6335"
android:endX="176.75"
android:type="linear">
<item android:offset="0" android:color="#00669DF6"/>
<item android:offset="0.695" android:color="#FF669DF6"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M206,209.6c-3.7,0 -6.6,3 -6.6,6.6v24.6h13.3v-24.6C212.6,212.6 209.7,209.6 206,209.6z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="240.8954"
android:startX="206.05"
android:endY="209.6335"
android:endX="206.05"
android:type="linear">
<item android:offset="0" android:color="#00669DF6"/>
<item android:offset="0.695" android:color="#FF669DF6"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M235.3,209.6c-3.7,0 -6.6,3 -6.6,6.6v24.6H242v-24.6C241.9,212.6 239,209.6 235.3,209.6z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="240.8954"
android:startX="235.35"
android:endY="209.6335"
android:endX="235.35"
android:type="linear">
<item android:offset="0" android:color="#00669DF6"/>
<item android:offset="0.695" android:color="#FF669DF6"/>
</gradient>
</aapt:attr>
</path>
</vector>

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="412dp"
android:height="300dp"
android:viewportWidth="412"
android:viewportHeight="300">
<path
android:fillColor="#FF000000"
android:pathData="M384.2,300H27.8C12.5,300 0,287.2 0,271.5v-243C0,12.8 12.5,0 27.8,0h356.5C399.5,0 412,12.8 412,28.5v243.2C412,287.2 399.5,300 384.2,300z"/>
<path
android:pathData="M274.9,97.7v-9.5c0,-1.3 -1.1,-2.4 -2.4,-2.4V35.9c0,-6.6 -5.3,-11.9 -11.9,-11.9H151.3c-6.6,0 -11.9,5.3 -11.9,11.9v228.2c0,6.6 5.3,11.9 11.9,11.9h109.4c6.6,0 11.9,-5.3 11.9,-11.9V147.6c1.3,0 2.4,-1.1 2.4,-2.4v-23.8c0,-1.3 -1.1,-2.4 -2.4,-2.4v-19C273.9,100.1 274.9,99 274.9,97.7zM270.2,264.1c0,5.2 -4.3,9.5 -9.5,9.5H151.3c-5.2,0 -9.5,-4.3 -9.5,-9.5V35.9c0,-5.2 4.3,-9.5 9.5,-9.5h109.4c5.2,0 9.5,4.3 9.5,9.5V264.1z"
android:fillColor="#80868B"/>
<path
android:pathData="M188.6,209.6c-3.7,0 -6.6,3 -6.6,6.6v24.6h13.3v-24.6C195.3,212.6 192.3,209.6 188.6,209.6z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="240.8954"
android:startX="188.65"
android:endY="209.6335"
android:endX="188.65"
android:type="linear">
<item android:offset="0" android:color="#00669DF6"/>
<item android:offset="0.695" android:color="#FF669DF6"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M223.4,209.6c-3.7,0 -6.6,3 -6.6,6.6v24.6H230v-24.6C230,212.6 227.1,209.6 223.4,209.6z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="240.8954"
android:startX="223.4"
android:endY="209.6335"
android:endX="223.4"
android:type="linear">
<item android:offset="0" android:color="#00669DF6"/>
<item android:offset="0.695" android:color="#FF669DF6"/>
</gradient>
</aapt:attr>
</path>
</vector>

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="412dp"
android:height="300dp"
android:viewportWidth="412"
android:viewportHeight="300">
<path
android:pathData="M384.2,300H27.8C12.5,300 0,287.2 0,271.5v-243C0,12.8 12.5,0 27.8,0h356.5C399.5,0 412,12.8 412,28.5v243.2C412,287.2 399.5,300 384.2,300z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M274.9,97.7v-9.5c0,-1.3 -1.1,-2.4 -2.4,-2.4V35.9c0,-6.6 -5.3,-11.9 -11.9,-11.9H151.3c-6.6,0 -11.9,5.3 -11.9,11.9v228.2c0,6.6 5.3,11.9 11.9,11.9h109.4c6.6,0 11.9,-5.3 11.9,-11.9V147.6c1.3,0 2.4,-1.1 2.4,-2.4v-23.8c0,-1.3 -1.1,-2.4 -2.4,-2.4v-19C273.9,100.1 274.9,99 274.9,97.7zM270.2,264.1c0,5.2 -4.3,9.5 -9.5,9.5H151.3c-5.2,0 -9.5,-4.3 -9.5,-9.5V35.9c0,-5.2 4.3,-9.5 9.5,-9.5h109.4c5.2,0 9.5,4.3 9.5,9.5V264.1z"
android:fillColor="#DADCE0"/>
<path
android:pathData="M176.7,209.6c-3.7,0 -6.6,3 -6.6,6.6v24.6h13.3v-24.6C183.4,212.6 180.4,209.6 176.7,209.6z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="240.8954"
android:startX="176.7163"
android:endY="209.6335"
android:endX="176.7163"
android:type="linear">
<item android:offset="0" android:color="#00669DF6"/>
<item android:offset="0.695" android:color="#FF669DF6"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M206,209.6c-3.7,0 -6.6,3 -6.6,6.6v24.6h13.3v-24.6C212.6,212.6 209.7,209.6 206,209.6z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="240.8954"
android:startX="206"
android:endY="209.6335"
android:endX="206"
android:type="linear">
<item android:offset="0" android:color="#00669DF6"/>
<item android:offset="0.695" android:color="#FF669DF6"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M235.3,209.6c-3.7,0 -6.6,3 -6.6,6.6v24.6h13.3v-24.6C241.9,212.6 239,209.6 235.3,209.6z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="240.8954"
android:startX="235.2837"
android:endY="209.6335"
android:endX="235.2837"
android:type="linear">
<item android:offset="0" android:color="#00669DF6"/>
<item android:offset="0.695" android:color="#FF669DF6"/>
</gradient>
</aapt:attr>
</path>
</vector>

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="412dp"
android:height="300dp"
android:viewportWidth="412"
android:viewportHeight="300">
<path
android:pathData="M384.2,300H27.8C12.5,300 0,287.2 0,271.5v-243C0,12.8 12.5,0 27.8,0h356.5C399.5,0 412,12.8 412,28.5v243.2C412,287.2 399.5,300 384.2,300z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M274.9,97.7v-9.5c0,-1.3 -1.1,-2.4 -2.4,-2.4V35.9c0,-6.6 -5.3,-11.9 -11.9,-11.9H151.3c-6.6,0 -11.9,5.3 -11.9,11.9v228.2c0,6.6 5.3,11.9 11.9,11.9h109.4c6.6,0 11.9,-5.3 11.9,-11.9V147.6c1.3,0 2.4,-1.1 2.4,-2.4v-23.8c0,-1.3 -1.1,-2.4 -2.4,-2.4v-19C273.9,100.1 274.9,99 274.9,97.7zM270.2,264.1c0,5.2 -4.3,9.5 -9.5,9.5H151.3c-5.2,0 -9.5,-4.3 -9.5,-9.5V35.9c0,-5.2 4.3,-9.5 9.5,-9.5h109.4c5.2,0 9.5,4.3 9.5,9.5V264.1z"
android:fillColor="#DADCE0"/>
<path
android:pathData="M188.6,209.6c-3.7,0 -6.6,3 -6.6,6.6v24.6h13.3v-24.6C195.3,212.6 192.3,209.6 188.6,209.6z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="240.8954"
android:startX="188.65"
android:endY="209.6335"
android:endX="188.65"
android:type="linear">
<item android:offset="0" android:color="#00669DF6"/>
<item android:offset="0.695" android:color="#FF669DF6"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M223.4,209.6c-3.7,0 -6.6,3 -6.6,6.6v24.6H230v-24.6C230,212.6 227.1,209.6 223.4,209.6z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="240.8954"
android:startX="223.4"
android:endY="209.6335"
android:endX="223.4"
android:type="linear">
<item android:offset="0" android:color="#00669DF6"/>
<item android:offset="0.695" android:color="#FF669DF6"/>
</gradient>
</aapt:attr>
</path>
</vector>

View File

@@ -29,11 +29,15 @@
android:orientation="vertical" android:orientation="vertical"
android:paddingTop="24dp"> android:paddingTop="24dp">
<TextureView <ImageView
android:id="@+id/gesture_tutorial_video" android:id="@+id/image"
android:layout_width="200dp" android:layout_width="wrap_content"
android:layout_height="200dp" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"/> android:maxHeight="@dimen/accessibility_imageview_size"
android:layout_gravity="center_horizontal"
android:adjustViewBounds="true"
android:paddingLeft="24dp"
android:paddingRight="24dp" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -29,11 +29,15 @@
android:orientation="vertical" android:orientation="vertical"
android:paddingTop="24dp"> android:paddingTop="24dp">
<TextureView <ImageView
android:id="@+id/gesture_tutorial_video" android:id="@+id/image"
android:layout_width="200dp" android:layout_width="wrap_content"
android:layout_height="200dp" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"/> android:maxHeight="@dimen/accessibility_imageview_size"
android:layout_gravity="center_horizontal"
android:adjustViewBounds="true"
android:paddingLeft="24dp"
android:paddingRight="24dp" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -1,42 +0,0 @@
/*
* Copyright (C) 2019 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.settings;
import android.app.Activity;
import android.os.Bundle;
import com.android.settings.accessibility.AccessibilityGestureNavigationTutorial;
import com.android.settings.R;
/**
* This activity is to create the tutorial dialog in gesture navigation settings since we couldn't
* use the dialog utils because SystemNavigationGestureSettings extends RadioButtonPickerFragment,
* not SettingsPreferenceFragment.
*/
public class SettingsTutorialDialogWrapperActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showDialog();
}
private void showDialog() {
AccessibilityGestureNavigationTutorial
.showGestureNavigationSettingsTutorialDialog(this, dialog -> finish());
}
}

View File

@@ -32,7 +32,6 @@ import android.text.style.ImageSpan;
import android.util.Log; import android.util.Log;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.TextureView;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.Window; import android.view.Window;
@@ -77,13 +76,13 @@ public final class AccessibilityGestureNavigationTutorial {
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({ @IntDef({
DialogType.LAUNCH_SERVICE_BY_ACCESSIBILITY_BUTTON, DialogType.LAUNCH_SERVICE_BY_ACCESSIBILITY_BUTTON,
DialogType.LAUNCH_SERVICE_BY_GESTURE_NAVIGATION, DialogType.LAUNCH_SERVICE_BY_ACCESSIBILITY_GESTURE,
DialogType.GESTURE_NAVIGATION_SETTINGS, DialogType.GESTURE_NAVIGATION_SETTINGS,
}) })
private @interface DialogType { private @interface DialogType {
int LAUNCH_SERVICE_BY_ACCESSIBILITY_BUTTON = 0; int LAUNCH_SERVICE_BY_ACCESSIBILITY_BUTTON = 0;
int LAUNCH_SERVICE_BY_GESTURE_NAVIGATION = 1; int LAUNCH_SERVICE_BY_ACCESSIBILITY_GESTURE = 1;
int GESTURE_NAVIGATION_SETTINGS = 2; int GESTURE_NAVIGATION_SETTINGS = 2;
} }
@@ -92,13 +91,17 @@ public final class AccessibilityGestureNavigationTutorial {
private static final DialogInterface.OnClickListener mOnClickListener = private static final DialogInterface.OnClickListener mOnClickListener =
(DialogInterface dialog, int which) -> dialog.dismiss(); (DialogInterface dialog, int which) -> dialog.dismiss();
public static void showGestureNavigationSettingsTutorialDialog(Context context, /**
DialogInterface.OnDismissListener dismissListener) { * Displays a dialog that guides users to use accessibility features with accessibility
* gestures under system gesture navigation mode.
*/
public static void showGestureNavigationTutorialDialog(Context context,
DialogInterface.OnDismissListener onDismissListener) {
final AlertDialog alertDialog = new AlertDialog.Builder(context) final AlertDialog alertDialog = new AlertDialog.Builder(context)
.setView(createTutorialDialogContentView(context, .setView(createTutorialDialogContentView(context,
DialogType.GESTURE_NAVIGATION_SETTINGS)) DialogType.GESTURE_NAVIGATION_SETTINGS))
.setNegativeButton(R.string.accessibility_tutorial_dialog_button, mOnClickListener) .setNegativeButton(R.string.accessibility_tutorial_dialog_button, mOnClickListener)
.setOnDismissListener(dismissListener) .setOnDismissListener(onDismissListener)
.create(); .create();
alertDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); alertDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
@@ -117,8 +120,8 @@ public final class AccessibilityGestureNavigationTutorial {
return alertDialog; return alertDialog;
} }
static AlertDialog showGestureNavigationTutorialDialog(Context context) { static AlertDialog showAccessibilityGestureTutorialDialog(Context context) {
return createDialog(context, DialogType.LAUNCH_SERVICE_BY_GESTURE_NAVIGATION); return createDialog(context, DialogType.LAUNCH_SERVICE_BY_ACCESSIBILITY_GESTURE);
} }
static AlertDialog createAccessibilityTutorialDialog(Context context, int shortcutTypes) { static AlertDialog createAccessibilityTutorialDialog(Context context, int shortcutTypes) {
@@ -129,7 +132,7 @@ public final class AccessibilityGestureNavigationTutorial {
} }
/** /**
* Get a content View for a dialog to confirm that they want to enable a service. * Gets a content View for a dialog to confirm that they want to enable a service.
* *
* @param context A valid context * @param context A valid context
* @param dialogType The type of tutorial dialog * @param dialogType The type of tutorial dialog
@@ -146,42 +149,36 @@ public final class AccessibilityGestureNavigationTutorial {
content = inflater.inflate( content = inflater.inflate(
R.layout.tutorial_dialog_launch_service_by_accessibility_button, null); R.layout.tutorial_dialog_launch_service_by_accessibility_button, null);
break; break;
case DialogType.LAUNCH_SERVICE_BY_GESTURE_NAVIGATION: case DialogType.LAUNCH_SERVICE_BY_ACCESSIBILITY_GESTURE:
content = inflater.inflate( content = inflater.inflate(
R.layout.tutorial_dialog_launch_service_by_gesture_navigation, null); R.layout.tutorial_dialog_launch_service_by_gesture_navigation, null);
final TextureView gestureTutorialVideo = content.findViewById( setupGestureNavigationTextWithImage(context, content);
R.id.gesture_tutorial_video);
final TextView gestureTutorialMessage = content.findViewById(
R.id.gesture_tutorial_message);
VideoPlayer.create(context, AccessibilityUtil.isTouchExploreEnabled(context)
? R.raw.illustration_accessibility_gesture_three_finger
: R.raw.illustration_accessibility_gesture_two_finger,
gestureTutorialVideo);
gestureTutorialMessage.setText(AccessibilityUtil.isTouchExploreEnabled(context)
? R.string.accessibility_tutorial_dialog_message_gesture_talkback
: R.string.accessibility_tutorial_dialog_message_gesture);
break; break;
case DialogType.GESTURE_NAVIGATION_SETTINGS: case DialogType.GESTURE_NAVIGATION_SETTINGS:
content = inflater.inflate( content = inflater.inflate(
R.layout.tutorial_dialog_launch_by_gesture_navigation_settings, null); R.layout.tutorial_dialog_launch_by_gesture_navigation_settings, null);
final TextureView gestureSettingsTutorialVideo = content.findViewById( setupGestureNavigationTextWithImage(context, content);
R.id.gesture_tutorial_video);
final TextView gestureSettingsTutorialMessage = content.findViewById(
R.id.gesture_tutorial_message);
VideoPlayer.create(context, AccessibilityUtil.isTouchExploreEnabled(context)
? R.raw.illustration_accessibility_gesture_three_finger
: R.raw.illustration_accessibility_gesture_two_finger,
gestureSettingsTutorialVideo);
final int stringResId = AccessibilityUtil.isTouchExploreEnabled(context)
? R.string.accessibility_tutorial_dialog_message_gesture_settings_talkback
: R.string.accessibility_tutorial_dialog_message_gesture_settings;
gestureSettingsTutorialMessage.setText(stringResId);
break; break;
} }
return content; return content;
} }
private static void setupGestureNavigationTextWithImage(Context context, View view) {
final boolean isTouchExploreEnabled = AccessibilityUtil.isTouchExploreEnabled(context);
final ImageView imageView = view.findViewById(R.id.image);
final int gestureSettingsImageResId =
isTouchExploreEnabled ? R.drawable.illustration_accessibility_gesture_three_finger
: R.drawable.illustration_accessibility_gesture_two_finger;
imageView.setImageResource(gestureSettingsImageResId);
final TextView textView = view.findViewById(R.id.gesture_tutorial_message);
textView.setText(isTouchExploreEnabled
? R.string.accessibility_tutorial_dialog_message_gesture_settings_talkback
: R.string.accessibility_tutorial_dialog_message_gesture_settings);
}
private static AlertDialog createDialog(Context context, int dialogType) { private static AlertDialog createDialog(Context context, int dialogType) {
final AlertDialog alertDialog = new AlertDialog.Builder(context) final AlertDialog alertDialog = new AlertDialog.Builder(context)
.setView(createTutorialDialogContentView(context, dialogType)) .setView(createTutorialDialogContentView(context, dialogType))

View File

@@ -141,7 +141,7 @@ public class ToggleScreenMagnificationPreferenceFragment extends
switch (dialogId) { switch (dialogId) {
case DialogEnums.GESTURE_NAVIGATION_TUTORIAL: case DialogEnums.GESTURE_NAVIGATION_TUTORIAL:
return AccessibilityGestureNavigationTutorial return AccessibilityGestureNavigationTutorial
.showGestureNavigationTutorialDialog(getPrefContext()); .showAccessibilityGestureTutorialDialog(getPrefContext());
case DialogEnums.MAGNIFICATION_EDIT_SHORTCUT: case DialogEnums.MAGNIFICATION_EDIT_SHORTCUT:
final CharSequence dialogTitle = getPrefContext().getString( final CharSequence dialogTitle = getPrefContext().getString(
R.string.accessibility_shortcut_title, mPackageName); R.string.accessibility_shortcut_title, mPackageName);

View File

@@ -20,24 +20,47 @@ import android.app.backup.BackupDataInputStream;
import android.app.backup.BackupDataOutput; import android.app.backup.BackupDataOutput;
import android.app.backup.BackupHelper; import android.app.backup.BackupHelper;
import android.content.Context; import android.content.Context;
import android.os.IDeviceIdleController;
import android.os.RemoteException;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Log; import android.util.Log;
import androidx.annotation.VisibleForTesting;
import java.io.IOException;
import java.util.Arrays;
/** An implementation to backup and restore battery configurations. */ /** An implementation to backup and restore battery configurations. */
public final class BatteryBackupHelper implements BackupHelper { public final class BatteryBackupHelper implements BackupHelper {
/** An inditifier for {@link BackupHelper}. */ /** An inditifier for {@link BackupHelper}. */
public static final String TAG = "BatteryBackupHelper"; public static final String TAG = "BatteryBackupHelper";
private static final String DEVICE_IDLE_SERVICE = "deviceidle";
private static final boolean DEBUG = false;
@VisibleForTesting
static final CharSequence DELIMITER = ":";
@VisibleForTesting
static final String KEY_FULL_POWER_LIST = "full_power_list";
@VisibleForTesting
IDeviceIdleController mIDeviceIdleController;
private final Context mContext; private final Context mContext;
public BatteryBackupHelper(Context context) { public BatteryBackupHelper(Context context) {
mContext = context; mContext = context.getApplicationContext();
} }
@Override @Override
public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) { ParcelFileDescriptor newState) {
Log.d(TAG, "performBackup()"); if (!isOwner()) {
Log.w(TAG, "ignore the backup process for non-owner");
return;
}
backupFullPowerList(getIDeviceIdleController(), data);
} }
@Override @Override
@@ -48,4 +71,51 @@ public final class BatteryBackupHelper implements BackupHelper {
@Override @Override
public void writeNewStateDescription(ParcelFileDescriptor newState) { public void writeNewStateDescription(ParcelFileDescriptor newState) {
} }
private void backupFullPowerList(
IDeviceIdleController deviceIdleService, BackupDataOutput data) {
final long timestamp = System.currentTimeMillis();
String[] allowlistedApps;
try {
allowlistedApps = deviceIdleService.getFullPowerWhitelist();
} catch (RemoteException e) {
Log.e(TAG, "backupFullPowerList() failed", e);
return;
}
// Ignores unexpected emptty result case.
if (allowlistedApps == null || allowlistedApps.length == 0) {
Log.w(TAG, "no data found in the getFullPowerList()");
return;
}
debugLog("allowlistedApps:" + Arrays.toString(allowlistedApps));
final String allowedApps = String.join(DELIMITER, allowlistedApps);
final byte[] allowedAppsBytes = allowedApps.getBytes();
try {
data.writeEntityHeader(KEY_FULL_POWER_LIST, allowedAppsBytes.length);
data.writeEntityData(allowedAppsBytes, allowedAppsBytes.length);
} catch (IOException e) {
Log.e(TAG, "backup getFullPowerList() failed", e);
return;
}
Log.d(TAG, String.format("backup getFullPowerList() size=%d in %d/ms",
allowlistedApps.length, (System.currentTimeMillis() - timestamp)));
}
// Provides an opportunity to inject mock IDeviceIdleController for testing.
private IDeviceIdleController getIDeviceIdleController() {
if (mIDeviceIdleController != null) {
return mIDeviceIdleController;
}
mIDeviceIdleController = IDeviceIdleController.Stub.asInterface(
ServiceManager.getService(DEVICE_IDLE_SERVICE));
return mIDeviceIdleController;
}
private void debugLog(String debugContent) {
if (DEBUG) Log.d(TAG, debugContent);
}
private static boolean isOwner() {
return UserHandle.myUserId() == UserHandle.USER_OWNER;
}
} }

View File

@@ -28,17 +28,19 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.om.IOverlayManager; import android.content.om.IOverlayManager;
import android.content.om.OverlayInfo; import android.content.om.OverlayInfo;
import android.os.Bundle;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.provider.Settings; import android.provider.Settings;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsTutorialDialogWrapperActivity; import com.android.settings.accessibility.AccessibilityGestureNavigationTutorial;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;
@@ -57,8 +59,6 @@ import java.util.List;
public class SystemNavigationGestureSettings extends RadioButtonPickerFragment implements public class SystemNavigationGestureSettings extends RadioButtonPickerFragment implements
HelpResourceProvider { HelpResourceProvider {
private static final String TAG = "SystemNavigationGesture";
@VisibleForTesting @VisibleForTesting
static final String KEY_SYSTEM_NAV_3BUTTONS = "system_nav_3buttons"; static final String KEY_SYSTEM_NAV_3BUTTONS = "system_nav_3buttons";
@VisibleForTesting @VisibleForTesting
@@ -69,10 +69,33 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment i
public static final String PREF_KEY_SUGGESTION_COMPLETE = public static final String PREF_KEY_SUGGESTION_COMPLETE =
"pref_system_navigation_suggestion_complete"; "pref_system_navigation_suggestion_complete";
private static final String KEY_SHOW_A11Y_TUTORIAL_DIALOG = "show_a11y_tutorial_dialog_bool";
private boolean mA11yTutorialDialogShown = false;
private IOverlayManager mOverlayManager; private IOverlayManager mOverlayManager;
private IllustrationPreference mVideoPreference; private IllustrationPreference mVideoPreference;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mA11yTutorialDialogShown =
savedInstanceState.getBoolean(KEY_SHOW_A11Y_TUTORIAL_DIALOG, false);
if (mA11yTutorialDialogShown) {
AccessibilityGestureNavigationTutorial.showGestureNavigationTutorialDialog(
getContext(), dialog -> mA11yTutorialDialogShown = false);
}
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putBoolean(KEY_SHOW_A11Y_TUTORIAL_DIALOG, mA11yTutorialDialogShown);
super.onSaveInstanceState(outState);
}
@Override @Override
public void onAttach(Context context) { public void onAttach(Context context) {
super.onAttach(context); super.onAttach(context);
@@ -177,14 +200,7 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment i
protected boolean setDefaultKey(String key) { protected boolean setDefaultKey(String key) {
setCurrentSystemNavigationMode(mOverlayManager, key); setCurrentSystemNavigationMode(mOverlayManager, key);
setIllustrationVideo(mVideoPreference, key); setIllustrationVideo(mVideoPreference, key);
if (TextUtils.equals(KEY_SYSTEM_NAV_GESTURAL, key) setGestureNavigationTutorialDialog(key);
&& !isAccessibilityFloatingMenuEnabled()
&& (isAnyServiceSupportAccessibilityButton() || isNavBarMagnificationEnabled())) {
final Intent intent = new Intent(getActivity(),
SettingsTutorialDialogWrapperActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
return true; return true;
} }
@@ -257,6 +273,18 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment i
} }
} }
private void setGestureNavigationTutorialDialog(String systemNavKey) {
if (TextUtils.equals(KEY_SYSTEM_NAV_GESTURAL, systemNavKey)
&& !isAccessibilityFloatingMenuEnabled()
&& (isAnyServiceSupportAccessibilityButton() || isNavBarMagnificationEnabled())) {
mA11yTutorialDialogShown = true;
AccessibilityGestureNavigationTutorial.showGestureNavigationTutorialDialog(getContext(),
dialog -> mA11yTutorialDialogShown = false);
} else {
mA11yTutorialDialogShown = false;
}
}
private boolean isAnyServiceSupportAccessibilityButton() { private boolean isAnyServiceSupportAccessibilityButton() {
final AccessibilityManager ams = getContext().getSystemService(AccessibilityManager.class); final AccessibilityManager ams = getContext().getSystemService(AccessibilityManager.class);
final List<String> targets = ams.getAccessibilityShortcutTargets( final List<String> targets = ams.getAccessibilityShortcutTargets(

View File

@@ -0,0 +1,157 @@
/*
* Copyright (C) 2021 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.settings.fuelgauge;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.backup.BackupDataOutput;
import android.content.Context;
import android.os.IDeviceIdleController;
import android.os.RemoteException;
import android.os.UserHandle;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {BatteryBackupHelperTest.ShadowUserHandle.class})
public final class BatteryBackupHelperTest {
private Context mContext;
private BatteryBackupHelper mBatteryBackupHelper;
@Mock
private BackupDataOutput mBackupDataOutput;
@Mock
private IDeviceIdleController mDeviceController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mBatteryBackupHelper = new BatteryBackupHelper(mContext);
mBatteryBackupHelper.mIDeviceIdleController = mDeviceController;
}
@After
public void resetShadows() {
ShadowUserHandle.reset();
}
@Test
public void performBackup_nullPowerList_notBackupPowerList() throws Exception {
doReturn(null).when(mDeviceController).getFullPowerWhitelist();
mBatteryBackupHelper.performBackup(null, mBackupDataOutput, null);
verify(mBackupDataOutput, never()).writeEntityHeader(anyString(), anyInt());
}
@Test
public void performBackup_emptyPowerList_notBackupPowerList() throws Exception {
doReturn(new String[0]).when(mDeviceController).getFullPowerWhitelist();
mBatteryBackupHelper.performBackup(null, mBackupDataOutput, null);
verify(mBackupDataOutput, never()).writeEntityHeader(anyString(), anyInt());
}
@Test
public void performBackup_remoteException_notBackupPowerList() throws Exception {
doThrow(new RemoteException()).when(mDeviceController).getFullPowerWhitelist();
mBatteryBackupHelper.performBackup(null, mBackupDataOutput, null);
verify(mBackupDataOutput, never()).writeEntityHeader(anyString(), anyInt());
}
@Test
public void performBackup_oneFullPowerListElement_backupFullPowerListData()
throws Exception {
final String[] fullPowerList = {"com.android.package"};
doReturn(fullPowerList).when(mDeviceController).getFullPowerWhitelist();
mBatteryBackupHelper.performBackup(null, mBackupDataOutput, null);
final byte[] expectedBytes = fullPowerList[0].getBytes();
verify(mBackupDataOutput).writeEntityHeader(
BatteryBackupHelper.KEY_FULL_POWER_LIST, expectedBytes.length);
verify(mBackupDataOutput).writeEntityData(expectedBytes, expectedBytes.length);
}
@Test
public void performBackup_backupFullPowerListData() throws Exception {
final String[] fullPowerList = {"com.android.package1", "com.android.package2"};
doReturn(fullPowerList).when(mDeviceController).getFullPowerWhitelist();
mBatteryBackupHelper.performBackup(null, mBackupDataOutput, null);
final String expectedResult = fullPowerList[0]
+ BatteryBackupHelper.DELIMITER + fullPowerList[1];
final byte[] expectedBytes = expectedResult.getBytes();
verify(mBackupDataOutput).writeEntityHeader(
BatteryBackupHelper.KEY_FULL_POWER_LIST, expectedBytes.length);
verify(mBackupDataOutput).writeEntityData(expectedBytes, expectedBytes.length);
}
@Test
public void performBackup_nonOwner_ignoreAllBackupAction() throws Exception {
ShadowUserHandle.setUid(1);
final String[] fullPowerList = {"com.android.package"};
doReturn(fullPowerList).when(mDeviceController).getFullPowerWhitelist();
mBatteryBackupHelper.performBackup(null, mBackupDataOutput, null);
verify(mBackupDataOutput, never()).writeEntityHeader(anyString(), anyInt());
}
@Implements(UserHandle.class)
public static class ShadowUserHandle {
// Sets the default as thte OWNER role.
private static int sUid = 0;
public static void setUid(int uid) {
sUid = uid;
}
@Implementation
public static int myUserId() {
return sUid;
}
@Resetter
public static void reset() {
sUid = 0;
}
}
}