Add tutorial dialog for gesture navigation
Add new tutorial dialog for gesture navigation in order to teach users how to use the gesture Fixes: 133650388 Test: Manual Change-Id: I7cc6a950af49044b27cf7ca41e3bcf67ef40b5fd
This commit is contained in:
@@ -1500,6 +1500,11 @@
|
|||||||
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:label="@string/tts_settings">
|
android:label="@string/tts_settings">
|
||||||
|
25
res/drawable/ic_accessibility_new.xml
Normal file
25
res/drawable/ic_accessibility_new.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#757575"
|
||||||
|
android:pathData="M20.5,6c-2.61,0.7 -5.67,1 -8.5,1s-5.89,-0.3 -8.5,-1L3,8c1.86,0.5 4,0.83 6,1v13h2v-6h2v6h2V9c2,-0.17 4.14,-0.5 6,-1l-0.5,-2zM12,6c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z"/>
|
||||||
|
</vector>
|
30
res/drawable/illustration_accessibility_button.xml
Normal file
30
res/drawable/illustration_accessibility_button.xml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector android:height="260dp" android:viewportHeight="260"
|
||||||
|
android:viewportWidth="260" android:width="260dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#FFFFFF" android:pathData="M55,23.81V197c0,3.31 2.69,6 6,6h138c3.31,0 6,-2.69 6,-6V23.81C183.81,8.81 157.93,0 130,0S76.19,8.81 55,23.81z"/>
|
||||||
|
<path android:fillColor="#F1F3F4" android:pathData="M61,209h138c6.62,0 12,-5.38 12,-12V28.32c-1.96,-1.56 -3.95,-3.07 -6,-4.52V197c0,3.31 -2.69,6 -6,6H61c-3.31,0 -6,-2.69 -6,-6V23.81c-2.05,1.45 -4.04,2.96 -6,4.52V197C49,203.62 54.38,209 61,209z"/>
|
||||||
|
<path android:fillColor="#DADCE0" android:pathData="M61,212h138c8.27,0 15,-6.73 15,-15V30.79c-0.5,-0.42 -1,-0.85 -1.5,-1.26V197c0,7.44 -6.06,13.5 -13.5,13.5H61c-7.44,0 -13.5,-6.06 -13.5,-13.5V29.53c-0.5,0.41 -1,0.84 -1.5,1.26V197C46,205.27 52.73,212 61,212z"/>
|
||||||
|
<path android:fillColor="#DADCE0" android:pathData="M211,197c0,6.62 -5.38,12 -12,12H61c-6.62,0 -12,-5.38 -12,-12V28.32c-0.5,0.4 -1,0.8 -1.5,1.21V197c0,7.44 6.06,13.5 13.5,13.5h138c7.44,0 13.5,-6.06 13.5,-13.5V29.53c-0.5,-0.41 -1,-0.81 -1.5,-1.21V197z"/>
|
||||||
|
<path android:fillColor="#202124" android:pathData="M199,203H61c-3.31,0 -6,-2.69 -6,-6v-21.24h150V197C205,200.31 202.31,203 199,203z"/>
|
||||||
|
<path android:fillAlpha="0.8" android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M81.89,190.03l6.5,3.75c0.5,0.29 1.12,-0.07 1.12,-0.64v-7.51c0,-0.57 -0.62,-0.93 -1.12,-0.64l-6.5,3.75C81.4,189.02 81.4,189.74 81.89,190.03z" android:strokeAlpha="0.8"/>
|
||||||
|
<path android:fillAlpha="0.8" android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M130,189.38m-4.2,0a4.2,4.2 0,1 1,8.4 0a4.2,4.2 0,1 1,-8.4 0" android:strokeAlpha="0.8"/>
|
||||||
|
<path android:fillColor="#FFFFFF" android:pathData="M179.78,184.38c-2.18,0.58 -4.73,0.83 -7.08,0.83c-2.36,0 -4.91,-0.25 -7.08,-0.83l-0.42,1.67c1.55,0.42 3.33,0.69 5,0.83v10.83h1.67v-5h1.67v5h1.67v-10.83c1.67,-0.14 3.45,-0.42 5,-0.83L179.78,184.38zM172.69,184.38c0.92,0 1.67,-0.75 1.67,-1.67c0,-0.92 -0.75,-1.67 -1.67,-1.67c-0.92,0 -1.67,0.75 -1.67,1.67C171.03,183.63 171.78,184.38 172.69,184.38z"/>
|
||||||
|
<path android:fillColor="#E8EAED" android:pathData="M130,2c34.19,0 66.33,13.31 90.51,37.49S258,95.81 258,130s-13.31,66.33 -37.49,90.51S164.19,258 130,258s-66.33,-13.31 -90.51,-37.49S2,164.19 2,130s13.31,-66.33 37.49,-90.51S95.81,2 130,2M130,0C58.2,0 0,58.2 0,130s58.2,130 130,130s130,-58.2 130,-130S201.8,0 130,0L130,0z"/>
|
||||||
|
</vector>
|
@@ -0,0 +1,60 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:textDirection="locale"
|
||||||
|
android:scrollbarStyle="outsideOverlay">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:theme="@style/Theme.AlertDialog"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingTop="24dp">
|
||||||
|
|
||||||
|
<TextureView
|
||||||
|
android:id="@+id/gesture_tutorial_video"
|
||||||
|
android:layout_width="200dp"
|
||||||
|
android:layout_height="200dp"
|
||||||
|
android:layout_gravity="center_horizontal"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:paddingTop="32dp"
|
||||||
|
android:paddingLeft="24dp"
|
||||||
|
android:paddingRight="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/gesture_tutorial_title"
|
||||||
|
android:text="@string/accessibility_tutorial_dialog_title_gesture_settings"
|
||||||
|
style="@style/AccessibilityDialogTitle" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/gesture_tutorial_message"
|
||||||
|
style="@style/AccessibilityDialogDescription" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
@@ -0,0 +1,61 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:textDirection="locale"
|
||||||
|
android:scrollbarStyle="outsideOverlay">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:theme="@style/Theme.AlertDialog"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingTop="24dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/button_tutorial_image"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/illustration_accessibility_button"
|
||||||
|
android:scaleType="fitCenter"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:paddingTop="32dp"
|
||||||
|
android:paddingLeft="24dp"
|
||||||
|
android:paddingRight="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/button_tutorial_title"
|
||||||
|
android:text="@string/accessibility_tutorial_dialog_title_button"
|
||||||
|
style="@style/AccessibilityDialogTitle" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/button_tutorial_message"
|
||||||
|
style="@style/AccessibilityDialogDescription" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
@@ -0,0 +1,60 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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
|
||||||
|
-->
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:textDirection="locale"
|
||||||
|
android:scrollbarStyle="outsideOverlay">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:theme="@style/Theme.AlertDialog"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingTop="24dp">
|
||||||
|
|
||||||
|
<TextureView
|
||||||
|
android:id="@+id/gesture_tutorial_video"
|
||||||
|
android:layout_width="200dp"
|
||||||
|
android:layout_height="200dp"
|
||||||
|
android:layout_gravity="center_horizontal"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:paddingTop="32dp"
|
||||||
|
android:paddingLeft="24dp"
|
||||||
|
android:paddingRight="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/gesture_tutorial_title"
|
||||||
|
android:text="@string/accessibility_tutorial_dialog_title_gesture"
|
||||||
|
style="@style/AccessibilityDialogTitle" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/gesture_tutorial_message"
|
||||||
|
style="@style/AccessibilityDialogDescription" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
BIN
res/raw/illustration_accessibility_gesture_three_finger.mp4
Normal file
BIN
res/raw/illustration_accessibility_gesture_three_finger.mp4
Normal file
Binary file not shown.
BIN
res/raw/illustration_accessibility_gesture_two_finger.mp4
Normal file
BIN
res/raw/illustration_accessibility_gesture_two_finger.mp4
Normal file
Binary file not shown.
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* 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());
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,217 @@
|
|||||||
|
/*
|
||||||
|
* 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.accessibility;
|
||||||
|
|
||||||
|
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.SpannableString;
|
||||||
|
import android.text.style.ImageSpan;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.TextureView;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.view.accessibility.AccessibilityManager;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.ColorInt;
|
||||||
|
import androidx.annotation.IntDef;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for creating the dialog that guides users for gesture navigation for
|
||||||
|
* accessibility services.
|
||||||
|
*/
|
||||||
|
public class AccessibilityGestureNavigationTutorial {
|
||||||
|
|
||||||
|
/** IntDef enum for dialog type. */
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@IntDef({
|
||||||
|
DialogType.LAUNCH_SERVICE_BY_ACCESSIBILITY_BUTTON,
|
||||||
|
DialogType.LAUNCH_SERVICE_BY_GESTURE_NAVIGATION,
|
||||||
|
DialogType.GESTURE_NAVIGATION_SETTINGS,
|
||||||
|
})
|
||||||
|
|
||||||
|
private @interface DialogType {
|
||||||
|
int LAUNCH_SERVICE_BY_ACCESSIBILITY_BUTTON = 0;
|
||||||
|
int LAUNCH_SERVICE_BY_GESTURE_NAVIGATION = 1;
|
||||||
|
int GESTURE_NAVIGATION_SETTINGS = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final DialogInterface.OnClickListener mOnClickListener =
|
||||||
|
(DialogInterface dialog, int which) -> dialog.dismiss();
|
||||||
|
|
||||||
|
public static void showGestureNavigationSettingsTutorialDialog(Context context,
|
||||||
|
DialogInterface.OnDismissListener dismissListener) {
|
||||||
|
final AlertDialog alertDialog = new AlertDialog.Builder(context)
|
||||||
|
.setView(createTutorialDialogContentView(context,
|
||||||
|
DialogType.GESTURE_NAVIGATION_SETTINGS))
|
||||||
|
.setNegativeButton(R.string.accessibility_tutorial_dialog_button, mOnClickListener)
|
||||||
|
.setOnDismissListener(dismissListener)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
alertDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
alertDialog.setCanceledOnTouchOutside(false);
|
||||||
|
alertDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
static AlertDialog showAccessibilityButtonTutorialDialog(Context context) {
|
||||||
|
final AlertDialog alertDialog = createDialog(context,
|
||||||
|
DialogType.LAUNCH_SERVICE_BY_ACCESSIBILITY_BUTTON);
|
||||||
|
|
||||||
|
if (!isGestureNavigateEnabled(context)) {
|
||||||
|
updateMessageWithIcon(context, alertDialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
return alertDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AlertDialog showGestureNavigationTutorialDialog(Context context) {
|
||||||
|
return createDialog(context, DialogType.LAUNCH_SERVICE_BY_GESTURE_NAVIGATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a content View for a dialog to confirm that they want to enable a service.
|
||||||
|
*
|
||||||
|
* @param context A valid context
|
||||||
|
* @param dialogType The type of tutorial dialog
|
||||||
|
* @return A content view suitable for viewing
|
||||||
|
*/
|
||||||
|
private static View createTutorialDialogContentView(Context context, int dialogType) {
|
||||||
|
final LayoutInflater inflater = (LayoutInflater) context.getSystemService(
|
||||||
|
Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
|
||||||
|
View content = null;
|
||||||
|
|
||||||
|
switch (dialogType) {
|
||||||
|
case DialogType.LAUNCH_SERVICE_BY_ACCESSIBILITY_BUTTON:
|
||||||
|
content = inflater.inflate(
|
||||||
|
R.layout.tutorial_dialog_launch_service_by_accessibility_button, null);
|
||||||
|
break;
|
||||||
|
case DialogType.LAUNCH_SERVICE_BY_GESTURE_NAVIGATION:
|
||||||
|
content = inflater.inflate(
|
||||||
|
R.layout.tutorial_dialog_launch_service_by_gesture_navigation, null);
|
||||||
|
final TextureView gestureTutorialVideo = content.findViewById(
|
||||||
|
R.id.gesture_tutorial_video);
|
||||||
|
final TextView gestureTutorialMessage = content.findViewById(
|
||||||
|
R.id.gesture_tutorial_message);
|
||||||
|
VideoPlayer.create(context, isTouchExploreOn(context)
|
||||||
|
? R.raw.illustration_accessibility_gesture_three_finger
|
||||||
|
: R.raw.illustration_accessibility_gesture_two_finger,
|
||||||
|
gestureTutorialVideo);
|
||||||
|
gestureTutorialMessage.setText(isTouchExploreOn(context)
|
||||||
|
? R.string.accessibility_tutorial_dialog_message_gesture_with_talkback
|
||||||
|
: R.string.accessibility_tutorial_dialog_message_gesture_without_talkback);
|
||||||
|
break;
|
||||||
|
case DialogType.GESTURE_NAVIGATION_SETTINGS:
|
||||||
|
content = inflater.inflate(
|
||||||
|
R.layout.tutorial_dialog_launch_by_gesture_navigation_settings, null);
|
||||||
|
final TextureView gestureSettingsTutorialVideo = content.findViewById(
|
||||||
|
R.id.gesture_tutorial_video);
|
||||||
|
final TextView gestureSettingsTutorialMessage = content.findViewById(
|
||||||
|
R.id.gesture_tutorial_message);
|
||||||
|
VideoPlayer.create(context, isTouchExploreOn(context)
|
||||||
|
? R.raw.illustration_accessibility_gesture_three_finger
|
||||||
|
: R.raw.illustration_accessibility_gesture_two_finger,
|
||||||
|
gestureSettingsTutorialVideo);
|
||||||
|
gestureSettingsTutorialMessage.setText(isTouchExploreOn(context)
|
||||||
|
?
|
||||||
|
R.string.accessibility_tutorial_dialog_message_gesture_settings_with_talkback
|
||||||
|
: R.string.accessibility_tutorial_dialog_message_gesture_settings_without_talkback);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AlertDialog createDialog(Context context, int dialogType) {
|
||||||
|
final AlertDialog alertDialog = new AlertDialog.Builder(context)
|
||||||
|
.setView(createTutorialDialogContentView(context, dialogType))
|
||||||
|
.setNegativeButton(R.string.accessibility_tutorial_dialog_button, mOnClickListener)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
alertDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
alertDialog.setCanceledOnTouchOutside(false);
|
||||||
|
alertDialog.show();
|
||||||
|
|
||||||
|
return alertDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateMessageWithIcon(Context context, AlertDialog alertDialog) {
|
||||||
|
final TextView gestureTutorialMessage = alertDialog.findViewById(
|
||||||
|
R.id.button_tutorial_message);
|
||||||
|
|
||||||
|
// Get the textView line height to update [icon] size. Must be called after show()
|
||||||
|
final int lineHeight = gestureTutorialMessage.getLineHeight();
|
||||||
|
gestureTutorialMessage.setText(getMessageStringWithIcon(context, lineHeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SpannableString getMessageStringWithIcon(Context context, int lineHeight) {
|
||||||
|
final String messageString = context
|
||||||
|
.getString(R.string.accessibility_tutorial_dialog_message_button);
|
||||||
|
final SpannableString spannableMessage = SpannableString.valueOf(messageString);
|
||||||
|
|
||||||
|
// Icon
|
||||||
|
final int indexIconStart = messageString.indexOf("%s");
|
||||||
|
final int indexIconEnd = indexIconStart + 2;
|
||||||
|
final Drawable icon = context.getDrawable(R.drawable.ic_accessibility_new);
|
||||||
|
icon.setTint(getThemeAttrColor(context, android.R.attr.textColorPrimary));
|
||||||
|
icon.setBounds(0, 0, lineHeight, lineHeight);
|
||||||
|
spannableMessage.setSpan(
|
||||||
|
new ImageSpan(icon), indexIconStart, indexIconEnd,
|
||||||
|
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
|
||||||
|
return spannableMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the color associated with the specified attribute in the context's theme. */
|
||||||
|
@ColorInt
|
||||||
|
private static int getThemeAttrColor(final Context context, final int attributeColor) {
|
||||||
|
final int colorResId = getAttrResourceId(context, attributeColor);
|
||||||
|
return ContextCompat.getColor(context, colorResId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the identifier of the resolved resource assigned to the given attribute. */
|
||||||
|
private static int getAttrResourceId(final Context context, final int attributeColor) {
|
||||||
|
final int[] attrs = {attributeColor};
|
||||||
|
final TypedArray typedArray = context.obtainStyledAttributes(attrs);
|
||||||
|
final int colorResId = typedArray.getResourceId(0, 0);
|
||||||
|
typedArray.recycle();
|
||||||
|
return colorResId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isGestureNavigateEnabled(Context context) {
|
||||||
|
return context.getResources().getInteger(
|
||||||
|
com.android.internal.R.integer.config_navBarInteractionMode)
|
||||||
|
== NAV_BAR_MODE_GESTURAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isTouchExploreOn(Context context) {
|
||||||
|
return ((AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE))
|
||||||
|
.isTouchExplorationEnabled();
|
||||||
|
}
|
||||||
|
}
|
@@ -16,14 +16,18 @@
|
|||||||
|
|
||||||
package com.android.settings.accessibility;
|
package com.android.settings.accessibility;
|
||||||
|
|
||||||
|
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
|
||||||
|
|
||||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.content.pm.ServiceInfo;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@@ -50,6 +54,7 @@ public class ToggleAccessibilityServicePreferenceFragment
|
|||||||
|
|
||||||
private static final int DIALOG_ID_ENABLE_WARNING = 1;
|
private static final int DIALOG_ID_ENABLE_WARNING = 1;
|
||||||
private static final int DIALOG_ID_DISABLE_WARNING = 2;
|
private static final int DIALOG_ID_DISABLE_WARNING = 2;
|
||||||
|
private static final int DIALOG_ID_LAUNCH_ACCESSIBILITY_TUTORIAL = 3;
|
||||||
|
|
||||||
public static final int ACTIVITY_REQUEST_CONFIRM_CREDENTIAL_FOR_WEAKER_ENCRYPTION = 1;
|
public static final int ACTIVITY_REQUEST_CONFIRM_CREDENTIAL_FOR_WEAKER_ENCRYPTION = 1;
|
||||||
|
|
||||||
@@ -144,6 +149,16 @@ public class ToggleAccessibilityServicePreferenceFragment
|
|||||||
.createDisableDialog(getActivity(), info, this);
|
.createDisableDialog(getActivity(), info, this);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case DIALOG_ID_LAUNCH_ACCESSIBILITY_TUTORIAL: {
|
||||||
|
if (isGestureNavigateEnabled()) {
|
||||||
|
mDialog = AccessibilityGestureNavigationTutorial
|
||||||
|
.showGestureNavigationTutorialDialog(getActivity());
|
||||||
|
} else {
|
||||||
|
mDialog = AccessibilityGestureNavigationTutorial
|
||||||
|
.showAccessibilityButtonTutorialDialog(getActivity());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
@@ -205,6 +220,9 @@ public class ToggleAccessibilityServicePreferenceFragment
|
|||||||
ACTIVITY_REQUEST_CONFIRM_CREDENTIAL_FOR_WEAKER_ENCRYPTION);
|
ACTIVITY_REQUEST_CONFIRM_CREDENTIAL_FOR_WEAKER_ENCRYPTION);
|
||||||
} else {
|
} else {
|
||||||
handleConfirmServiceEnabled(true);
|
handleConfirmServiceEnabled(true);
|
||||||
|
if (isServiceSupportAccessibilityButton()) {
|
||||||
|
showDialog(DIALOG_ID_LAUNCH_ACCESSIBILITY_TUTORIAL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (view.getId() == R.id.permission_enable_deny_button) {
|
} else if (view.getId() == R.id.permission_enable_deny_button) {
|
||||||
handleConfirmServiceEnabled(false);
|
handleConfirmServiceEnabled(false);
|
||||||
@@ -218,6 +236,30 @@ public class ToggleAccessibilityServicePreferenceFragment
|
|||||||
mDialog.dismiss();
|
mDialog.dismiss();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isGestureNavigateEnabled() {
|
||||||
|
return getContext().getResources().getInteger(
|
||||||
|
com.android.internal.R.integer.config_navBarInteractionMode)
|
||||||
|
== NAV_BAR_MODE_GESTURAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isServiceSupportAccessibilityButton() {
|
||||||
|
final AccessibilityManager ams = (AccessibilityManager) getContext().getSystemService(
|
||||||
|
Context.ACCESSIBILITY_SERVICE);
|
||||||
|
final List<AccessibilityServiceInfo> services = ams.getInstalledAccessibilityServiceList();
|
||||||
|
|
||||||
|
for (AccessibilityServiceInfo info : services) {
|
||||||
|
if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) {
|
||||||
|
ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo;
|
||||||
|
if (serviceInfo != null && TextUtils.equals(serviceInfo.name,
|
||||||
|
getAccessibilityServiceInfo().getResolveInfo().serviceInfo.name)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private void handleConfirmServiceEnabled(boolean confirmed) {
|
private void handleConfirmServiceEnabled(boolean confirmed) {
|
||||||
mSwitchBar.setCheckedInternal(confirmed);
|
mSwitchBar.setCheckedInternal(confirmed);
|
||||||
getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, confirmed);
|
getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, confirmed);
|
||||||
@@ -229,11 +271,13 @@ public class ToggleAccessibilityServicePreferenceFragment
|
|||||||
switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(UserHandle.myUserId())) {
|
switch (mLockPatternUtils.getKeyguardStoredPasswordQuality(UserHandle.myUserId())) {
|
||||||
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: {
|
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: {
|
||||||
resId = R.string.enable_service_pattern_reason;
|
resId = R.string.enable_service_pattern_reason;
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
|
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
|
||||||
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX: {
|
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX: {
|
||||||
resId = R.string.enable_service_pin_reason;
|
resId = R.string.enable_service_pin_reason;
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return getString(resId, getAccessibilityServiceInfo().getResolveInfo()
|
return getString(resId, getAccessibilityServiceInfo().getResolveInfo()
|
||||||
.loadLabel(getPackageManager()));
|
.loadLabel(getPackageManager()));
|
||||||
|
@@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
package com.android.settings.accessibility;
|
package com.android.settings.accessibility;
|
||||||
|
|
||||||
|
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -26,6 +29,8 @@ import android.media.MediaPlayer;
|
|||||||
import android.media.MediaPlayer.OnPreparedListener;
|
import android.media.MediaPlayer.OnPreparedListener;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.view.Display;
|
import android.view.Display;
|
||||||
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
@@ -44,6 +49,10 @@ import com.android.settings.widget.SwitchBar;
|
|||||||
public class ToggleScreenMagnificationPreferenceFragment extends
|
public class ToggleScreenMagnificationPreferenceFragment extends
|
||||||
ToggleFeaturePreferenceFragment implements SwitchBar.OnSwitchChangeListener {
|
ToggleFeaturePreferenceFragment implements SwitchBar.OnSwitchChangeListener {
|
||||||
|
|
||||||
|
private static final int DIALOG_ID_GESTURE_NAVIGATION_TUTORIAL = 1;
|
||||||
|
|
||||||
|
private Dialog mDialog;
|
||||||
|
|
||||||
protected class VideoPreference extends Preference {
|
protected class VideoPreference extends Preference {
|
||||||
private ImageView mVideoBackgroundView;
|
private ImageView mVideoBackgroundView;
|
||||||
private OnGlobalLayoutListener mLayoutListener;
|
private OnGlobalLayoutListener mLayoutListener;
|
||||||
@@ -161,12 +170,32 @@ public class ToggleScreenMagnificationPreferenceFragment extends
|
|||||||
updateConfigurationWarningIfNeeded();
|
updateConfigurationWarningIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(int dialogId) {
|
||||||
|
if (dialogId == DIALOG_ID_GESTURE_NAVIGATION_TUTORIAL) {
|
||||||
|
if (isGestureNavigateEnabled()) {
|
||||||
|
mDialog = AccessibilityGestureNavigationTutorial
|
||||||
|
.showGestureNavigationTutorialDialog(getActivity());
|
||||||
|
} else {
|
||||||
|
mDialog = AccessibilityGestureNavigationTutorial
|
||||||
|
.showAccessibilityButtonTutorialDialog(getActivity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mDialog;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMetricsCategory() {
|
public int getMetricsCategory() {
|
||||||
// TODO: Distinguish between magnification modes
|
// TODO: Distinguish between magnification modes
|
||||||
return SettingsEnums.ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFICATION;
|
return SettingsEnums.ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFICATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDialogMetricsCategory(int dialogId) {
|
||||||
|
return SettingsEnums.ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFICATION;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSwitchChanged(Switch switchView, boolean isChecked) {
|
public void onSwitchChanged(Switch switchView, boolean isChecked) {
|
||||||
onPreferenceToggled(mPreferenceKey, isChecked);
|
onPreferenceToggled(mPreferenceKey, isChecked);
|
||||||
@@ -174,6 +203,11 @@ public class ToggleScreenMagnificationPreferenceFragment extends
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
|
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
|
||||||
|
if (enabled && TextUtils.equals(
|
||||||
|
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
|
||||||
|
preferenceKey)) {
|
||||||
|
showDialog(DIALOG_ID_GESTURE_NAVIGATION_TUTORIAL);
|
||||||
|
}
|
||||||
MagnificationPreferenceFragment.setChecked(getContentResolver(), preferenceKey, enabled);
|
MagnificationPreferenceFragment.setChecked(getContentResolver(), preferenceKey, enabled);
|
||||||
updateConfigurationWarningIfNeeded();
|
updateConfigurationWarningIfNeeded();
|
||||||
}
|
}
|
||||||
@@ -224,6 +258,12 @@ public class ToggleScreenMagnificationPreferenceFragment extends
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isGestureNavigateEnabled() {
|
||||||
|
return getContext().getResources().getInteger(
|
||||||
|
com.android.internal.R.integer.config_navBarInteractionMode)
|
||||||
|
== NAV_BAR_MODE_GESTURAL;
|
||||||
|
}
|
||||||
|
|
||||||
private void updateConfigurationWarningIfNeeded() {
|
private void updateConfigurationWarningIfNeeded() {
|
||||||
final CharSequence warningMessage =
|
final CharSequence warningMessage =
|
||||||
MagnificationPreferenceFragment.getConfigurationWarningStringForSecureSettingsKey(
|
MagnificationPreferenceFragment.getConfigurationWarningStringForSecureSettingsKey(
|
||||||
|
149
src/com/android/settings/accessibility/VideoPlayer.java
Normal file
149
src/com/android/settings/accessibility/VideoPlayer.java
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* 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.accessibility;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.SurfaceTexture;
|
||||||
|
import android.media.MediaPlayer;
|
||||||
|
import android.view.Surface;
|
||||||
|
import android.view.TextureView;
|
||||||
|
import android.view.TextureView.SurfaceTextureListener;
|
||||||
|
|
||||||
|
import androidx.annotation.GuardedBy;
|
||||||
|
import androidx.annotation.RawRes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays the video by {@link MediaPlayer} on {@link TextureView}, calls {@link #create(Context, int,
|
||||||
|
* TextureView)} to setup the listener for TextureView and start to play the video. Once this player
|
||||||
|
* is no longer used, call {@link #release()} so that MediaPlayer object can be released.
|
||||||
|
*/
|
||||||
|
public class VideoPlayer implements SurfaceTextureListener {
|
||||||
|
private final Context context;
|
||||||
|
private final Object mediaPlayerLock = new Object();
|
||||||
|
// Media player object can't be used after it has been released, so it will be set to null. But
|
||||||
|
// VideoPlayer is asynchronized, media player object might be paused or resumed again before
|
||||||
|
// released media player is set to null. Therefore, lock mediaPlayer and mediaPlayerState by
|
||||||
|
// mediaPlayerLock keep their states consistent.
|
||||||
|
@GuardedBy("mediaPlayerLock")
|
||||||
|
private MediaPlayer mediaPlayer;
|
||||||
|
@GuardedBy("mediaPlayerLock")
|
||||||
|
private State mediaPlayerState = State.NONE;
|
||||||
|
@RawRes
|
||||||
|
private final int videoRes;
|
||||||
|
private Surface animationSurface;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link MediaPlayer} for a given resource id and starts playback when the surface
|
||||||
|
* for
|
||||||
|
* a given {@link TextureView} is ready.
|
||||||
|
*/
|
||||||
|
public static VideoPlayer create(Context context, @RawRes int videoRes,
|
||||||
|
TextureView textureView) {
|
||||||
|
return new VideoPlayer(context, videoRes, textureView);
|
||||||
|
}
|
||||||
|
|
||||||
|
private VideoPlayer(Context context, @RawRes int videoRes, TextureView textureView) {
|
||||||
|
this.context = context;
|
||||||
|
this.videoRes = videoRes;
|
||||||
|
textureView.setSurfaceTextureListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pause() {
|
||||||
|
synchronized (mediaPlayerLock) {
|
||||||
|
if (mediaPlayerState == State.STARTED) {
|
||||||
|
mediaPlayerState = State.PAUSED;
|
||||||
|
mediaPlayer.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resume() {
|
||||||
|
synchronized (mediaPlayerLock) {
|
||||||
|
if (mediaPlayerState == State.PAUSED) {
|
||||||
|
mediaPlayer.start();
|
||||||
|
mediaPlayerState = State.STARTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Release media player when it's no longer needed. */
|
||||||
|
public void release() {
|
||||||
|
synchronized (mediaPlayerLock) {
|
||||||
|
if (mediaPlayerState != State.NONE && mediaPlayerState != State.END) {
|
||||||
|
mediaPlayerState = State.END;
|
||||||
|
mediaPlayer.release();
|
||||||
|
mediaPlayer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (animationSurface != null) {
|
||||||
|
animationSurface.release();
|
||||||
|
animationSurface = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
|
||||||
|
animationSurface = new Surface(surface);
|
||||||
|
synchronized (mediaPlayerLock) {
|
||||||
|
mediaPlayer = MediaPlayer.create(context, videoRes);
|
||||||
|
mediaPlayerState = State.PREPARED;
|
||||||
|
mediaPlayer.setSurface(animationSurface);
|
||||||
|
mediaPlayer.setLooping(true);
|
||||||
|
mediaPlayer.start();
|
||||||
|
mediaPlayerState = State.STARTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
|
||||||
|
release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The state of MediaPlayer object. Refer to
|
||||||
|
* https://developer.android.com/reference/android/media/MediaPlayer#StateDiagram.
|
||||||
|
*/
|
||||||
|
public enum State {
|
||||||
|
/** MediaPlayer objects has not be created. */
|
||||||
|
NONE,
|
||||||
|
/** MediaPlayer objects is created by create() method. */
|
||||||
|
PREPARED,
|
||||||
|
/** MediaPlayer is started. It can be paused by pause() method. */
|
||||||
|
STARTED,
|
||||||
|
/** MediaPlayer object is paused. Calling start() to resume it. */
|
||||||
|
PAUSED,
|
||||||
|
/**
|
||||||
|
* MediaPlayer object is stopped and cannot be started until calling prepare() or
|
||||||
|
* prepareAsync()
|
||||||
|
* methods.
|
||||||
|
*/
|
||||||
|
STOPPED,
|
||||||
|
/** MediaPlayer object is released. It cannot be used again. */
|
||||||
|
END
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -25,18 +25,24 @@ import static com.android.settings.widget.RadioButtonPreferenceWithExtraWidget.E
|
|||||||
import static com.android.settings.widget.RadioButtonPreferenceWithExtraWidget.EXTRA_WIDGET_VISIBILITY_INFO;
|
import static com.android.settings.widget.RadioButtonPreferenceWithExtraWidget.EXTRA_WIDGET_VISIBILITY_INFO;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
|
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.om.IOverlayManager;
|
import android.content.om.IOverlayManager;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
import android.provider.SearchIndexableResource;
|
import android.provider.SearchIndexableResource;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.accessibility.AccessibilityManager;
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.preference.PreferenceScreen;
|
import androidx.preference.PreferenceScreen;
|
||||||
|
|
||||||
|
import com.android.settings.SettingsTutorialDialogWrapperActivity;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
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;
|
||||||
@@ -165,6 +171,12 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment {
|
|||||||
|
|
||||||
setCurrentSystemNavigationMode(mOverlayManager, key);
|
setCurrentSystemNavigationMode(mOverlayManager, key);
|
||||||
setIllustrationVideo(mVideoPreference, key);
|
setIllustrationVideo(mVideoPreference, key);
|
||||||
|
if (TextUtils.equals(KEY_SYSTEM_NAV_GESTURAL, key) && (
|
||||||
|
isAnyServiceSupportAccessibilityButton() || isNavBarMagnificationEnabled())) {
|
||||||
|
Intent intent = new Intent(getActivity(), SettingsTutorialDialogWrapperActivity.class);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,6 +268,26 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment {
|
|||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isAnyServiceSupportAccessibilityButton() {
|
||||||
|
final AccessibilityManager ams = (AccessibilityManager) getContext().getSystemService(
|
||||||
|
Context.ACCESSIBILITY_SERVICE);
|
||||||
|
final List<AccessibilityServiceInfo> services = ams.getEnabledAccessibilityServiceList(
|
||||||
|
AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
|
||||||
|
|
||||||
|
for (AccessibilityServiceInfo info : services) {
|
||||||
|
if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isNavBarMagnificationEnabled() {
|
||||||
|
return Settings.Secure.getInt(getContext().getContentResolver(),
|
||||||
|
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
static class NavModeCandidateInfo extends CandidateInfo {
|
static class NavModeCandidateInfo extends CandidateInfo {
|
||||||
private final CharSequence mLabel;
|
private final CharSequence mLabel;
|
||||||
private final CharSequence mSummary;
|
private final CharSequence mSummary;
|
||||||
|
Reference in New Issue
Block a user