Merge "Added Background install control UI code."
diff --git a/res/drawable/ic_settings_trackpad.xml b/res/drawable/ic_settings_trackpad.xml
new file mode 100644
index 0000000..9580e02
--- /dev/null
+++ b/res/drawable/ic_settings_trackpad.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2022 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="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M5,40Q3.75,40 2.875,39.125Q2,38.25 2,37V11Q2,9.75 2.875,8.875Q3.75,8 5,8H43Q44.25,8 45.125,8.875Q46,9.75 46,11V37Q46,38.25 45.125,39.125Q44.25,40 43,40ZM6.5,11H5Q5,11 5,11Q5,11 5,11V37Q5,37 5,37Q5,37 5,37H6.5ZM9.5,37H38.5V11H9.5ZM41.5,11V37H43Q43,37 43,37Q43,37 43,37V11Q43,11 43,11Q43,11 43,11ZM41.5,11H43Q43,11 43,11Q43,11 43,11Q43,11 43,11Q43,11 43,11H41.5ZM6.5,11H5Q5,11 5,11Q5,11 5,11Q5,11 5,11Q5,11 5,11Z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_trackpad_gesture_back.xml b/res/drawable/ic_trackpad_gesture_back.xml
new file mode 100644
index 0000000..b6a80cf
--- /dev/null
+++ b/res/drawable/ic_trackpad_gesture_back.xml
@@ -0,0 +1,26 @@
+<!--
+ 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"
+ android:autoMirrored="true"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,11H7.83l5.59-5.59L12,4l-8,8l8,8l1.41-1.41L7.83,13H20V11z" />
+</vector>
diff --git a/res/drawable/ic_trackpad_gesture_home.xml b/res/drawable/ic_trackpad_gesture_home.xml
new file mode 100644
index 0000000..f0e7232
--- /dev/null
+++ b/res/drawable/ic_trackpad_gesture_home.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2022 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="48"
+ android:viewportHeight="48"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M11,39H18.5V26.5H29.5V39H37V19.5L24,9.75L11,19.5ZM8,42V18L24,6L40,18V42H26.5V29.5H21.5V42ZM24,24.35Z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_trackpad_gesture_notifications.xml b/res/drawable/ic_trackpad_gesture_notifications.xml
new file mode 100644
index 0000000..37d77d8
--- /dev/null
+++ b/res/drawable/ic_trackpad_gesture_notifications.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2017 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="?android:attr/colorControlNormal">
+ <path
+ android:pathData="M18,17V11C18,7.93 16.37,5.36 13.5,4.68V4C13.5,3.17 12.83,2.5 12,2.5C11.17,2.5 10.5,3.17 10.5,4V4.68C7.64,5.36 6,7.92 6,11V17H4V19H14H14.38H20V17H18ZM16,17H8V11C8,8.52 9.51,6.5 12,6.5C14.49,6.5 16,8.52 16,11V17ZM14,20C14,21.1 13.1,22 12,22C10.9,22 10,21.1 10,20H14Z"
+ android:fillType="evenOdd"
+ android:fillColor="?android:attr/colorPrimary"/>
+</vector>
diff --git a/res/drawable/ic_trackpad_gesture_recent_apps.xml b/res/drawable/ic_trackpad_gesture_recent_apps.xml
new file mode 100644
index 0000000..76ba829
--- /dev/null
+++ b/res/drawable/ic_trackpad_gesture_recent_apps.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2017 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="?android:attr/colorControlNormal">
+ <path
+ android:pathData="M16,6C16,7.1 16.9,8 18,8C19.1,8 20,7.1 20,6C20,4.9 19.1,4 18,4C16.9,4 16,4.9 16,6ZM6,8C7.1,8 8,7.1 8,6C8,4.9 7.1,4 6,4C4.9,4 4,4.9 4,6C4,7.1 4.9,8 6,8ZM12.001,20C13.101,20 14.001,19.1 14.001,18C14.001,16.9 13.101,16 12.001,16C10.901,16 10.001,16.9 10.001,18C10.001,19.1 10.901,20 12.001,20ZM8.001,18C8.001,19.1 7.101,20 6.001,20C4.901,20 4.001,19.1 4.001,18C4.001,16.9 4.901,16 6.001,16C7.101,16 8.001,16.9 8.001,18ZM6.001,14C7.101,14 8.001,13.1 8.001,12C8.001,10.9 7.101,10 6.001,10C4.901,10 4.001,10.9 4.001,12C4.001,13.1 4.901,14 6.001,14ZM14.001,12C14.001,13.1 13.101,14 12.001,14C10.901,14 10.001,13.1 10.001,12C10.001,10.9 10.901,10 12.001,10C13.101,10 14.001,10.9 14.001,12ZM14.001,6C14.001,7.1 13.101,8 12.001,8C10.901,8 10.001,7.1 10.001,6C10.001,4.9 10.901,4 12.001,4C13.101,4 14.001,4.9 14.001,6ZM18,14C19.1,14 20,13.1 20,12C20,10.9 19.1,10 18,10C16.9,10 16,10.9 16,12C16,13.1 16.9,14 18,14ZM20,18C20,19.1 19.1,20 18,20C16.9,20 16,19.1 16,18C16,16.9 16.9,16 18,16C19.1,16 20,16.9 20,18Z"
+ android:fillType="evenOdd"
+ android:fillColor="?android:attr/colorPrimary"/>
+</vector>
diff --git a/res/drawable/ic_trackpad_gesture_switch_apps.xml b/res/drawable/ic_trackpad_gesture_switch_apps.xml
new file mode 100644
index 0000000..3d18d1c
--- /dev/null
+++ b/res/drawable/ic_trackpad_gesture_switch_apps.xml
@@ -0,0 +1,26 @@
+<!--
+Copyright (C) 2022 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="48"
+ android:viewportHeight="48"
+ android:tint="?android:attr/colorControlNormal"
+ android:autoMirrored="true">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M15.85,40 L13.75,37.9 19.8,31.85H4V28.85H19.8L13.75,22.8L15.85,20.7L25.5,30.35ZM32.15,27.3 L22.5,17.65 32.15,8 34.25,10.1 28.2,16.15H44V19.15H28.2L34.25,25.2Z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_trackpad_pointer_speed.xml b/res/drawable/ic_trackpad_pointer_speed.xml
new file mode 100644
index 0000000..4e9abeb
--- /dev/null
+++ b/res/drawable/ic_trackpad_pointer_speed.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2022 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="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M6.05,24.35V21.35H12.35V24.35ZM11.75,35.9 L9.75,33.9 14.15,29.5 16.15,31.5ZM14.15,16.05 L9.75,11.65 11.75,9.65 16.15,14.05ZM37,40.05 L27.6,30.65 25.4,37.45 20.2,19.45 37.8,25 30.9,27.45 40.25,36.8ZM21.85,12.3V6H24.85V12.3ZM32.55,16.05 L30.55,14.05 34.95,9.65 36.95,11.65Z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_trackpad_reverse_scrolling.xml b/res/drawable/ic_trackpad_reverse_scrolling.xml
new file mode 100644
index 0000000..a62f904
--- /dev/null
+++ b/res/drawable/ic_trackpad_reverse_scrolling.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2022 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="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M16.1,25.5V9.7L10.1,15.7L8,13.6L17.65,3.95L27.3,13.6L25.2,15.7L19.1,9.65V25.5ZM30.35,43.95 L20.7,34.25 22.8,32.2 28.8,38.2V22.4H31.8V38.25L37.9,32.2L40,34.3Z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_trackpad_tap_to_click.xml b/res/drawable/ic_trackpad_tap_to_click.xml
new file mode 100644
index 0000000..7db0454
--- /dev/null
+++ b/res/drawable/ic_trackpad_tap_to_click.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2022 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="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M23.25,2Q27.8,2 31.025,5.175Q34.25,8.35 34.25,12.9Q34.25,15.5 33.125,17.8Q32,20.1 29.95,21.7H28.25V19.2Q29.7,18.05 30.475,16.4Q31.25,14.75 31.25,12.9Q31.25,9.6 28.9,7.3Q26.55,5 23.25,5Q19.95,5 17.6,7.3Q15.25,9.6 15.25,12.9Q15.25,14.75 16.025,16.4Q16.8,18.05 18.25,19.2V22.8Q15.45,21.35 13.85,18.7Q12.25,16.05 12.25,12.9Q12.25,8.35 15.475,5.175Q18.7,2 23.25,2ZM21.35,44Q20.5,44 19.75,43.675Q19,43.35 18.45,42.8L8.15,32.5L10.95,29.6Q11.65,28.9 12.525,28.525Q13.4,28.15 14.35,28.4L18.25,29.3V13Q18.25,10.9 19.7,9.45Q21.15,8 23.25,8Q25.35,8 26.8,9.45Q28.25,10.9 28.25,13V21.6H29.55Q29.8,21.6 30,21.7Q30.2,21.8 30.45,21.9L37.85,25.5Q39.05,26.05 39.625,27.275Q40.2,28.5 39.95,29.8L38.15,40.7Q37.9,42.15 36.75,43.075Q35.6,44 34.15,44ZM20.95,41H35L37.15,28.55Q37.15,28.55 37.15,28.55Q37.15,28.55 37.15,28.55L28,24H25.25V13Q25.25,12.1 24.7,11.55Q24.15,11 23.25,11Q22.35,11 21.8,11.55Q21.25,12.1 21.25,13V32.95L13.55,31.3L12.4,32.45ZM35,41H20.95H21.25Q21.25,41 21.8,41Q22.35,41 23.25,41Q24.15,41 24.7,41Q25.25,41 25.25,41H28H35Q35,41 35,41Q35,41 35,41Z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_trackpad_touch_gestures_inverse.xml b/res/drawable/ic_trackpad_touch_gestures_inverse.xml
new file mode 100644
index 0000000..d663572
--- /dev/null
+++ b/res/drawable/ic_trackpad_touch_gestures_inverse.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2022 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="48"
+ android:viewportHeight="48"
+ android:tint="?android:attr/textColorPrimaryInverse">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M40.15,47 L38.2,43.1 34.3,41.15 38.2,39.3 40.15,35.3 42,39.3 46,41.15 42,43.1ZM28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4ZM23.05,48H30.4L29.75,45H23.05Q22,45 20.975,44.8Q19.95,44.6 19.25,43.9L6.5,30.65L7.85,29.4L19.75,35.85V9.15Q19.75,8.6 20.1,8.25Q20.45,7.9 21,7.9Q21.55,7.9 21.9,8.25Q22.25,8.6 22.25,9.15V24H26V4.25Q26,3.7 26.35,3.35Q26.7,3 27.25,3Q27.8,3 28.15,3.35Q28.5,3.7 28.5,4.25V24H32.25V6.1Q32.25,5.55 32.6,5.2Q32.95,4.85 33.5,4.85Q34.05,4.85 34.4,5.2Q34.75,5.55 34.75,6.1V24H38.5V12.45Q38.5,11.9 38.85,11.55Q39.2,11.2 39.75,11.2Q40.3,11.2 40.65,11.55Q41,11.9 41,12.45V31.4H44V12.45Q44,10.7 42.75,9.45Q41.5,8.2 39.75,8.2Q39.2,8.2 38.675,8.3Q38.15,8.4 37.75,8.75V6.45Q37.75,4.55 36.525,3.2Q35.3,1.85 33.5,1.85Q32.85,1.85 32.25,2.025Q31.65,2.2 31.1,2.6Q30.65,1.4 29.625,0.7Q28.6,0 27.3,0Q25.55,0 24.275,1.25Q23,2.5 23,4.25V5.55Q22.6,5.2 22.075,5.05Q21.55,4.9 21,4.9Q19.25,4.9 18,6.15Q16.75,7.4 16.75,9.15V30.95L8.85,26.5Q8,26.05 7.1,26.325Q6.2,26.6 5.5,27.25L2,30.3L16.85,45.7Q18.05,46.95 19.675,47.475Q21.3,48 23.05,48ZM6.85,14.8 L5.15,11.15 1.5,9.45 5.15,7.75 6.85,4.1 8.55,7.7 12.2,9.6 8.6,11.15Z"/>
+</vector>
diff --git a/res/drawable/ic_trackpad_touch_gestures_normal.xml b/res/drawable/ic_trackpad_touch_gestures_normal.xml
new file mode 100644
index 0000000..34619c8
--- /dev/null
+++ b/res/drawable/ic_trackpad_touch_gestures_normal.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2022 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="48"
+ android:viewportHeight="48"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M40.15,47 L38.2,43.1 34.3,41.15 38.2,39.3 40.15,35.3 42,39.3 46,41.15 42,43.1ZM28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4Q28.5,31.4 28.5,31.4ZM23.05,48H30.4L29.75,45H23.05Q22,45 20.975,44.8Q19.95,44.6 19.25,43.9L6.5,30.65L7.85,29.4L19.75,35.85V9.15Q19.75,8.6 20.1,8.25Q20.45,7.9 21,7.9Q21.55,7.9 21.9,8.25Q22.25,8.6 22.25,9.15V24H26V4.25Q26,3.7 26.35,3.35Q26.7,3 27.25,3Q27.8,3 28.15,3.35Q28.5,3.7 28.5,4.25V24H32.25V6.1Q32.25,5.55 32.6,5.2Q32.95,4.85 33.5,4.85Q34.05,4.85 34.4,5.2Q34.75,5.55 34.75,6.1V24H38.5V12.45Q38.5,11.9 38.85,11.55Q39.2,11.2 39.75,11.2Q40.3,11.2 40.65,11.55Q41,11.9 41,12.45V31.4H44V12.45Q44,10.7 42.75,9.45Q41.5,8.2 39.75,8.2Q39.2,8.2 38.675,8.3Q38.15,8.4 37.75,8.75V6.45Q37.75,4.55 36.525,3.2Q35.3,1.85 33.5,1.85Q32.85,1.85 32.25,2.025Q31.65,2.2 31.1,2.6Q30.65,1.4 29.625,0.7Q28.6,0 27.3,0Q25.55,0 24.275,1.25Q23,2.5 23,4.25V5.55Q22.6,5.2 22.075,5.05Q21.55,4.9 21,4.9Q19.25,4.9 18,6.15Q16.75,7.4 16.75,9.15V30.95L8.85,26.5Q8,26.05 7.1,26.325Q6.2,26.6 5.5,27.25L2,30.3L16.85,45.7Q18.05,46.95 19.675,47.475Q21.3,48 23.05,48ZM6.85,14.8 L5.15,11.15 1.5,9.45 5.15,7.75 6.85,4.1 8.55,7.7 12.2,9.6 8.6,11.15Z"/>
+</vector>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index af830c6..45c9f88 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3662,6 +3662,53 @@
<!-- Title for the 'Virtual keyboards for work' preference. [CHAR LIMIT=45] -->
<string name="virtual_keyboards_for_work_title">On-screen keyboard for work</string>
+ <!-- Title for the button to trigger the 'trackpad settings' page if only connect with a touchpad. [CHAR LIMIT=35] -->
+ <string name="trackpad_settings">Touchpad</string>
+ <!-- Title for the button to trigger the 'trackpad settings' page if connect with a touchpad and a mouse. [CHAR LIMIT=35] -->
+ <string name="trackpad_mouse_settings">Touchpad & mouse</string>
+ <!-- Summary text for the 'trackpad settings' page. [CHAR LIMIT=100] -->
+ <string name="trackpad_settings_summary">Pointer speed, gestures</string>
+
+ <!-- Title text for 'Tap to click'. [CHAR LIMIT=35] -->
+ <string name="trackpad_tap_to_click">Tap to click</string>
+ <!-- Title text for 'Touchpad gestures' [CHAR LIMIT=35] -->
+ <string name="trackpad_touchpad_gesture_title">Touchpad gestures</string>
+ <!-- Summary text for 'Touchpad gestures' [CHAR LIMIT=60] -->
+ <string name="trackpad_touchpad_gesture_summary">Customize individual touchpad navigation gestures</string>
+ <!-- Title text for 'Reverse scrolling' [CHAR LIMIT=35] -->
+ <string name="trackpad_reverse_scrolling_title">Reverse scrolling</string>
+ <!-- Summary text for 'Reverse scrolling' [CHAR LIMIT=60] -->
+ <string name="trackpad_reverse_scrolling_summary">Content moves up when you scroll down</string>
+ <!-- Title text for 'Bottom-right tap' [CHAR LIMIT=35] -->
+ <string name="trackpad_bottom_right_tap_title">Bottom-right tap</string>
+ <!-- Summary text for 'Bottom-right tap' [CHAR LIMIT=60] -->
+ <string name="trackpad_bottom_right_tap_summary">Tap the bottom right corner of the touchpad for more options</string>
+ <!-- Title text for 'Pointer speed'. [CHAR LIMIT=35] -->
+ <string name="trackpad_pointer_speed">Pointer speed</string>
+ <!-- Title for the button to trigger the 'touch gesture' education. [CHAR LIMIT=35] -->
+ <string name="trackpad_touch_gesture">Learn touchpad gestures</string>
+
+ <!-- Title text for 'Go back' [CHAR LIMIT=35] -->
+ <string name="trackpad_go_back_title">Go back</string>
+ <!-- Summary text for 'Go back' [CHAR LIMIT=60] -->
+ <string name="trackpad_go_back_summary">Swipe left or right with three fingers</string>
+ <!-- Title text for 'Go home' [CHAR LIMIT=35] -->
+ <string name="trackpad_go_home_title">Go home</string>
+ <!-- Summary text for 'Go home' [CHAR LIMIT=60] -->
+ <string name="trackpad_go_home_summary">Swipe up with three fingers</string>
+ <!-- Title text for 'Recent apps' [CHAR LIMIT=35] -->
+ <string name="trackpad_recent_apps_title">Recent apps</string>
+ <!-- Summary text for 'Recent apps' [CHAR LIMIT=60] -->
+ <string name="trackpad_recent_apps_summary">Swipe up with three fingers, then hold</string>
+ <!-- Title text for 'Notifications' [CHAR LIMIT=35] -->
+ <string name="trackpad_notifications_title">Notifications</string>
+ <!-- Summary text for 'Notifications' [CHAR LIMIT=60] -->
+ <string name="trackpad_notifications_summary">Swipe down with three fingers</string>
+ <!-- Title text for 'Switch apps' [CHAR LIMIT=35] -->
+ <string name="trackpad_switch_apps_title">Switch apps</string>
+ <!-- Summary text for 'Switch apps' [CHAR LIMIT=60] -->
+ <string name="trackpad_switch_apps_summary">Swipe left or right with four fingers</string>
+
<!-- Summary text for keyboards when no layout has been selected. [CHAR LIMIT=35] -->
<string name="default_keyboard_layout">Default</string>
diff --git a/res/xml/system_dashboard_fragment.xml b/res/xml/system_dashboard_fragment.xml
index 3b24203..1f5559f 100644
--- a/res/xml/system_dashboard_fragment.xml
+++ b/res/xml/system_dashboard_fragment.xml
@@ -46,6 +46,15 @@
settings:controller="com.android.settings.inputmethod.KeyboardPreferenceController"/>
<Preference
+ android:key="trackpad_settings"
+ android:title="@string/trackpad_settings"
+ android:summary="@string/trackpad_settings_summary"
+ android:icon="@drawable/ic_settings_trackpad"
+ android:order="-254"
+ android:fragment="com.android.settings.inputmethod.TrackpadSettings"
+ settings:controller="com.android.settings.inputmethod.TrackpadSettingsController"/>
+
+ <Preference
android:key="gesture_settings"
android:title="@string/gesture_preference_title"
android:icon="@drawable/ic_settings_gestures"
diff --git a/res/xml/trackpad_gesture_settings.xml b/res/xml/trackpad_gesture_settings.xml
new file mode 100644
index 0000000..dfc4199
--- /dev/null
+++ b/res/xml/trackpad_gesture_settings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 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.
+ -->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:title="@string/trackpad_touchpad_gesture_title">
+ <SwitchPreference
+ android:key="gesture_go_back"
+ android:title="@string/trackpad_go_back_title"
+ android:summary="@string/trackpad_go_back_summary"
+ android:icon="@drawable/ic_trackpad_gesture_back"
+ android:order="10"/>
+
+ <SwitchPreference
+ android:key="gesture_go_home"
+ android:title="@string/trackpad_go_home_title"
+ android:summary="@string/trackpad_go_home_summary"
+ android:icon="@drawable/ic_trackpad_gesture_home"
+ android:order="20"/>
+
+ <SwitchPreference
+ android:key="gesture_recent_apps"
+ android:title="@string/trackpad_recent_apps_title"
+ android:summary="@string/trackpad_recent_apps_summary"
+ android:icon="@drawable/ic_trackpad_gesture_recent_apps"
+ android:order="30"/>
+
+ <SwitchPreference
+ android:key="gesture_notifications"
+ android:title="@string/trackpad_notifications_title"
+ android:summary="@string/trackpad_notifications_summary"
+ android:icon="@drawable/ic_trackpad_gesture_notifications"
+ android:order="40"/>
+
+ <SwitchPreference
+ android:key="gesture_switch_apps"
+ android:title="@string/trackpad_switch_apps_title"
+ android:summary="@string/trackpad_switch_apps_summary"
+ android:icon="@drawable/ic_trackpad_gesture_switch_apps"
+ android:order="50"/>
+
+ <com.android.settingslib.widget.ButtonPreference
+ android:key="trackpad_touch_gesture_developer_mode"
+ android:title="@string/trackpad_touch_gesture"
+ android:icon="@drawable/ic_trackpad_touch_gestures_inverse"
+ settings:controller="com.android.settings.inputmethod.TouchGesturesButtonPreferenceController"/>
+</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/trackpad_settings.xml b/res/xml/trackpad_settings.xml
new file mode 100644
index 0000000..6401fb8
--- /dev/null
+++ b/res/xml/trackpad_settings.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 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.
+ -->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:title="@string/trackpad_settings">
+ <Preference
+ android:key="trackpad_gesture_settings"
+ android:title="@string/trackpad_touchpad_gesture_title"
+ android:summary="@string/trackpad_touchpad_gesture_summary"
+ android:icon="@drawable/ic_trackpad_touch_gestures_normal"
+ android:order="-10"
+ android:fragment="com.android.settings.inputmethod.TrackpadTouchGestureSettings"
+ settings:controller="com.android.settings.inputmethod.TrackpadTouchGestureSettingsController"/>
+
+ <SwitchPreference
+ android:key="trackpad_tap_to_click"
+ android:title="@string/trackpad_tap_to_click"
+ android:icon="@drawable/ic_trackpad_tap_to_click"
+ android:order="10"/>
+
+ <SwitchPreference
+ android:key="trackpad_reverse_scrolling"
+ android:title="@string/trackpad_reverse_scrolling_title"
+ android:summary="@string/trackpad_reverse_scrolling_summary"
+ android:icon="@drawable/ic_trackpad_reverse_scrolling"
+ android:order="20"/>
+
+ <SwitchPreference
+ android:key="trackpad_bottom_right_tap"
+ android:title="@string/trackpad_bottom_right_tap_title"
+ android:summary="@string/trackpad_bottom_right_tap_summary"
+ android:icon="@drawable/ic_trackpad_reverse_scrolling"
+ android:order="30"/>
+
+ <com.android.settings.widget.SeekBarPreference
+ android:key="trackpad_pointer_speed"
+ android:title="@string/trackpad_pointer_speed"
+ android:icon="@drawable/ic_trackpad_pointer_speed"
+ android:order="40"
+ android:selectable="false"
+ android:max="100"
+ android:min="0"
+ android:defaultValue="50"/>
+
+ <com.android.settingslib.widget.ButtonPreference
+ android:key="trackpad_touch_gesture"
+ android:title="@string/trackpad_touch_gesture"
+ android:icon="@drawable/ic_trackpad_touch_gestures_inverse"
+ settings:controller="com.android.settings.inputmethod.TouchGesturesButtonPreferenceController"/>
+</PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java b/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
index 51a775e..851797e 100644
--- a/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
+++ b/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
@@ -48,7 +48,9 @@
final ProgressInteractionListener interactionListener =
mInteractionListener.get();
- interactionListener.notifyPreferenceChanged();
+ // Avoid timing issues to update the corresponding preview fail when clicking
+ // the increase/decrease button.
+ seekBar.post(interactionListener::notifyPreferenceChanged);
if (!mSeekByTouch) {
interactionListener.onProgressChanged();
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index da81f6e..fa72fba 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -123,6 +123,7 @@
import com.android.settings.inputmethod.NewKeyboardLayoutEnabledLocalesFragment;
import com.android.settings.inputmethod.PhysicalKeyboardFragment;
import com.android.settings.inputmethod.SpellCheckersSettings;
+import com.android.settings.inputmethod.TrackpadSettings;
import com.android.settings.inputmethod.UserDictionaryList;
import com.android.settings.inputmethod.UserDictionarySettings;
import com.android.settings.language.LanguageAndInputSettings;
@@ -216,6 +217,7 @@
LanguageSettings.class.getName(),
KeyboardSettings.class.getName(),
NewKeyboardLayoutEnabledLocalesFragment.class.getName(),
+ TrackpadSettings.class.getName(),
SpellCheckersSettings.class.getName(),
UserDictionaryList.class.getName(),
UserDictionarySettings.class.getName(),
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
index c2eab57..b048dbc 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
@@ -209,7 +209,7 @@
// Ensure the battery chart group is visible for users.
animateBatteryChartViewGroup();
final BatteryLevelData batteryLevelData =
- DataProcessor.getBatteryLevelData(mContext, mHandler, batteryHistoryMap,
+ DataProcessManager.getBatteryLevelData(mContext, mHandler, batteryHistoryMap,
batteryUsageMap -> {
mBatteryUsageMap = batteryUsageMap;
refreshUi();
diff --git a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
index 2e977fd..c1b7818 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
@@ -17,7 +17,9 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents.Event;
+import android.app.usage.UsageStatsManager;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -25,7 +27,9 @@
import android.os.BatteryUsageStats;
import android.os.Build;
import android.os.LocaleList;
+import android.os.RemoteException;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.Base64;
import android.util.Log;
@@ -167,8 +171,10 @@
/** Converts to {@link AppUsageEvent} from {@link Event} */
@Nullable
public static AppUsageEvent convertToAppUsageEvent(
- Context context, final Event event, final long userId) {
- if (event.getPackageName() == null) {
+ Context context, final IUsageStatsManager usageStatsManager, final Event event,
+ final long userId) {
+ final String packageName = event.getPackageName();
+ if (packageName == null) {
// See b/190609174: Event package names should never be null, but sometimes they are.
// Note that system events like device shutting down should still come with the android
// package name.
@@ -182,13 +188,20 @@
appUsageEventBuilder
.setTimestamp(event.getTimeStamp())
.setType(getAppUsageEventType(event.getEventType()))
- .setPackageName(event.getPackageName())
+ .setPackageName(packageName)
.setUserId(userId);
+ final String taskRootPackageName = getTaskRootPackageName(event);
+ if (taskRootPackageName != null) {
+ appUsageEventBuilder.setTaskRootPackageName(taskRootPackageName);
+ }
+
+ final String effectivePackageName =
+ getEffectivePackageName(usageStatsManager, packageName, taskRootPackageName);
try {
final long uid = context
.getPackageManager()
- .getPackageUidAsUser(event.getPackageName(), (int) userId);
+ .getPackageUidAsUser(effectivePackageName, (int) userId);
appUsageEventBuilder.setUid(uid);
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, String.format(
@@ -201,10 +214,6 @@
} catch (NoClassDefFoundError | NoSuchMethodError e) {
Log.w(TAG, "UsageEvent instance ID API error");
}
- String taskRootPackageName = getTaskRootPackageName(event);
- if (taskRootPackageName != null) {
- appUsageEventBuilder.setTaskRootPackageName(taskRootPackageName);
- }
return appUsageEventBuilder.build();
}
@@ -268,6 +277,35 @@
}
/**
+ * Returns the package name the app usage should be attributed to.
+ *
+ * <ul>
+ * <li>If {@link UsageStatsManager#getUsageSource()} returns {@link
+ * UsageStatsManager#USAGE_SOURCE_CURRENT_ACTIVITY}, this method will return packageName.
+ * <li>If {@link UsageStatsManager#getUsageSource()} returns {@link
+ * UsageStatsManager#USAGE_SOURCE_TASK_ROOT_ACTIVITY}, this method will return
+ * taskRootPackageName if it exists, or packageName otherwise.
+ * </ul>
+ */
+ @VisibleForTesting
+ static String getEffectivePackageName(
+ final IUsageStatsManager usageStatsManager, final String packageName,
+ final String taskRootPackageName) {
+ int usageSource = getUsageSource(usageStatsManager);
+ switch (usageSource) {
+ case UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY:
+ return !TextUtils.isEmpty(taskRootPackageName)
+ ? taskRootPackageName
+ : packageName;
+ case UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY:
+ return packageName;
+ default:
+ Log.e(TAG, "Unexpected usage source: " + usageSource);
+ return packageName;
+ }
+ }
+
+ /**
* Returns the package name of the task root when this event was reported when {@code event} is
* one of:
*
@@ -298,6 +336,20 @@
}
}
+ /**
+ * Returns what App Usage Observers will consider the source of usage for an activity.
+ *
+ * @see UsageStatsManager#getUsageSource()
+ */
+ private static int getUsageSource(final IUsageStatsManager usageStatsManager) {
+ try {
+ return usageStatsManager.getUsageSource();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to getUsageSource", e);
+ return UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY;
+ }
+ }
+
private static AppUsageEventType getAppUsageEventType(final int eventType) {
switch (eventType) {
case Event.ACTIVITY_RESUMED:
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
index 784f64f..dd6b9d9 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
@@ -20,11 +20,13 @@
import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;
+import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.Utils;
@@ -39,7 +41,7 @@
/**
* Manages the async tasks to process battery and app usage data.
*
- * For now, there exist 3 async tasks in this manager:
+ * For now, there exist 4 async tasks in this manager:
* <ul>
* <li>loadCurrentBatteryHistoryMap: load the latest battery history data from battery stats
* service.</li>
@@ -47,9 +49,11 @@
* from usage stats service.</li>
* <li>loadDatabaseAppUsageList: load the necessary app usage data (after last full charge) from
* database</li>
+ * <li>loadAndApplyBatteryMapFromServiceOnly: load all the battery history data (should be after
+ * last full charge) from battery stats service and apply the callback function directly</li>
* </ul>
*
- * The 3 async tasks will be started at the same time.
+ * If there is battery level data, the first 3 async tasks will be started at the same time.
* <ul>
* <li>After loadCurrentAppUsageList and loadDatabaseAppUsageList complete, which means all app
* usage data has been loaded, the intermediate usage result will be generated.</li>
@@ -59,6 +63,9 @@
* <li>If current user is locked, which means we couldn't get the latest app usage data,
* screen-on time will not be shown in the UI and empty screen-on time data will be returned.</li>
* </ul>
+ *
+ * If there is no battery level data, the 4th async task will be started only and the usage map
+ * callback function will be applied directly to show the app list on the UI.
*/
public class DataProcessManager {
private static final String TAG = "DataProcessManager";
@@ -74,23 +81,25 @@
// The start timestamp of battery level data. As we don't know when is the full charge cycle
// start time when loading app usage data, this value is used as the start time of querying app
// usage data.
- private long mStartTimestampOfLevelData = 0;
+ private long mStartTimestampOfLevelData;
private boolean mIsCurrentBatteryHistoryLoaded = false;
private boolean mIsCurrentAppUsageLoaded = false;
private boolean mIsDatabaseAppUsageLoaded = false;
// Used to identify whether screen-on time data should be shown in the UI.
private boolean mShowScreenOnTime = true;
+ // Used to identify whether battery level data should be shown in the UI.
+ private boolean mShowBatteryLevel = true;
private List<AppUsageEvent> mAppUsageEventList = new ArrayList<>();
/**
- * Constructor when this exists battery level data.
+ * Constructor when there exists battery level data.
*/
DataProcessManager(
Context context,
Handler handler,
- final DataProcessor.UsageMapAsyncResponse callbackFunction,
+ @NonNull final DataProcessor.UsageMapAsyncResponse callbackFunction,
@NonNull final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
@NonNull final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
mContext = context.getApplicationContext();
@@ -103,15 +112,39 @@
}
/**
+ * Constructor when there is no battery level data.
+ */
+ DataProcessManager(
+ Context context,
+ Handler handler,
+ @NonNull final DataProcessor.UsageMapAsyncResponse callbackFunction) {
+ mContext = context.getApplicationContext();
+ mHandler = handler;
+ mUserManager = mContext.getSystemService(UserManager.class);
+ mCallbackFunction = callbackFunction;
+ // When there is no battery level data, don't show screen-on time and battery level chart on
+ // the UI.
+ mShowScreenOnTime = false;
+ mShowBatteryLevel = false;
+ }
+
+ /**
* Starts the async tasks to load battery history data and app usage data.
*/
public void start() {
- // Load the latest battery history data from the service.
- loadCurrentBatteryHistoryMap();
- // Load app usage list from database.
- loadDatabaseAppUsageList();
- // Load the latest app usage list from the service.
- loadCurrentAppUsageList();
+ // If we have battery level data, load the battery history map and app usage simultaneously.
+ if (mShowBatteryLevel) {
+ // Loads the latest battery history data from the service.
+ loadCurrentBatteryHistoryMap();
+ // Loads app usage list from database.
+ loadDatabaseAppUsageList();
+ // Loads the latest app usage list from the service.
+ loadCurrentAppUsageList();
+ } else {
+ // If there is no battery level data, only load the battery history data from service
+ // and show it as the app list directly.
+ loadAndApplyBatteryMapFromServiceOnly();
+ }
}
@VisibleForTesting
@@ -154,6 +187,11 @@
return mShowScreenOnTime;
}
+ @VisibleForTesting
+ boolean getShowBatteryLevel() {
+ return mShowBatteryLevel;
+ }
+
private void loadCurrentBatteryHistoryMap() {
new AsyncTask<Void, Void, Map<String, BatteryHistEntry>>() {
@Override
@@ -279,6 +317,35 @@
}.execute();
}
+ private void loadAndApplyBatteryMapFromServiceOnly() {
+ new AsyncTask<Void, Void, Map<Integer, Map<Integer, BatteryDiffData>>>() {
+ @Override
+ protected Map<Integer, Map<Integer, BatteryDiffData>> doInBackground(Void... voids) {
+ final long startTime = System.currentTimeMillis();
+ final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap =
+ DataProcessor.getBatteryUsageMapFromStatsService(mContext);
+ DataProcessor.loadLabelAndIcon(batteryUsageMap);
+ Log.d(TAG, String.format(
+ "execute loadAndApplyBatteryMapFromServiceOnly size=%d in %d/ms",
+ batteryUsageMap.size(), (System.currentTimeMillis() - startTime)));
+ return batteryUsageMap;
+ }
+
+ @Override
+ protected void onPostExecute(
+ final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap) {
+ // Set the unused variables to null.
+ mContext = null;
+ // Post results back to main thread to refresh UI.
+ if (mHandler != null && mCallbackFunction != null) {
+ mHandler.post(() -> {
+ mCallbackFunction.onBatteryUsageMapLoaded(batteryUsageMap);
+ });
+ }
+ }
+ }.execute();
+ }
+
private void tryToProcessAppUsageData() {
// Only when all app usage events has been loaded, start processing app usage data to an
// intermediate result for further use.
@@ -313,6 +380,10 @@
private void generateFinalDataAndApplyCallback() {
// TODO: generate the final data including battery usage map and device screen-on time and
// then apply the callback function.
+ // Set the unused variables to null.
+ mContext = null;
+ mHourlyBatteryLevelsPerDay = null;
+ mBatteryHistoryMap = null;
}
// Whether we should load app usage data from service or database.
@@ -350,4 +421,47 @@
Utils.getManagedProfile(mUserManager);
return userHandle != null ? userHandle.getIdentifier() : Integer.MIN_VALUE;
}
+
+ /**
+ * @return Returns battery level data and start async task to compute battery diff usage data
+ * and load app labels + icons.
+ * Returns null if the input is invalid or not having at least 2 hours data.
+ */
+ @Nullable
+ public static BatteryLevelData getBatteryLevelData(
+ Context context,
+ @Nullable Handler handler,
+ @Nullable final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap,
+ final DataProcessor.UsageMapAsyncResponse asyncResponseDelegate) {
+ if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
+ Log.d(TAG, "batteryHistoryMap is null in getBatteryLevelData()");
+ new DataProcessManager(context, handler, asyncResponseDelegate).start();
+ return null;
+ }
+ handler = handler != null ? handler : new Handler(Looper.getMainLooper());
+ // Process raw history map data into hourly timestamps.
+ final Map<Long, Map<String, BatteryHistEntry>> processedBatteryHistoryMap =
+ DataProcessor.getHistoryMapWithExpectedTimestamps(context, batteryHistoryMap);
+ // Wrap and processed history map into easy-to-use format for UI rendering.
+ final BatteryLevelData batteryLevelData =
+ DataProcessor.getLevelDataThroughProcessedHistoryMap(
+ context, processedBatteryHistoryMap);
+ if (batteryLevelData == null) {
+ new DataProcessManager(context, handler, asyncResponseDelegate).start();
+ Log.d(TAG, "getBatteryLevelData() returns null");
+ return null;
+ }
+
+ // TODO: replace the task below with new DataProcessManager(...).start() after
+ // DataProcessManager is completed;
+ // Start the async task to compute diff usage data and load labels and icons.
+ new DataProcessor.ComputeUsageMapAndLoadItemsTask(
+ context,
+ handler,
+ asyncResponseDelegate,
+ batteryLevelData.getHourlyBatteryLevelsPerDay(),
+ processedBatteryHistoryMap).execute();
+
+ return batteryLevelData;
+ }
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
index bfedeab..c33e0a3 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
@@ -32,7 +32,6 @@
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Handler;
-import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -114,48 +113,6 @@
}
/**
- * @return Returns battery level data and start async task to compute battery diff usage data
- * and load app labels + icons.
- * Returns null if the input is invalid or not having at least 2 hours data.
- */
- @Nullable
- public static BatteryLevelData getBatteryLevelData(
- Context context,
- @Nullable Handler handler,
- @Nullable final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap,
- final UsageMapAsyncResponse asyncResponseDelegate) {
- if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
- Log.d(TAG, "batteryHistoryMap is null in getBatteryLevelData()");
- loadBatteryUsageDataFromBatteryStatsService(
- context, handler, asyncResponseDelegate);
- return null;
- }
- handler = handler != null ? handler : new Handler(Looper.getMainLooper());
- // Process raw history map data into hourly timestamps.
- final Map<Long, Map<String, BatteryHistEntry>> processedBatteryHistoryMap =
- getHistoryMapWithExpectedTimestamps(context, batteryHistoryMap);
- // Wrap and processed history map into easy-to-use format for UI rendering.
- final BatteryLevelData batteryLevelData =
- getLevelDataThroughProcessedHistoryMap(context, processedBatteryHistoryMap);
- if (batteryLevelData == null) {
- loadBatteryUsageDataFromBatteryStatsService(
- context, handler, asyncResponseDelegate);
- Log.d(TAG, "getBatteryLevelData() returns null");
- return null;
- }
-
- // Start the async task to compute diff usage data and load labels and icons.
- new ComputeUsageMapAndLoadItemsTask(
- context,
- handler,
- asyncResponseDelegate,
- batteryLevelData.getHourlyBatteryLevelsPerDay(),
- processedBatteryHistoryMap).execute();
-
- return batteryLevelData;
- }
-
- /**
* @return Returns battery usage data of different entries.
* Returns null if the input is invalid or there is no enough data.
*/
@@ -299,7 +256,8 @@
break;
}
final AppUsageEvent appUsageEvent =
- ConvertUtils.convertToAppUsageEvent(context, event, userId);
+ ConvertUtils.convertToAppUsageEvent(
+ context, sUsageStatsManager, event, userId);
if (appUsageEvent != null) {
numEventsFetched++;
appUsageEventList.add(appUsageEvent);
@@ -361,7 +319,6 @@
* The keys of processed history map should contain every hour between the start and end
* timestamp. If there's no data in some key, the value will be the empty hashmap.
*/
- @VisibleForTesting
static Map<Long, Map<String, BatteryHistEntry>> getHistoryMapWithExpectedTimestamps(
Context context,
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
@@ -385,7 +342,6 @@
return resultMap;
}
- @VisibleForTesting
@Nullable
static BatteryLevelData getLevelDataThroughProcessedHistoryMap(
Context context,
@@ -624,16 +580,37 @@
}
/**
- * Starts the async task to load battery diff usage data and load app labels + icons.
+ * @return Returns the overall battery usage data from battery stats service directly.
+ *
+ * The returned value should be always a 2d map and composed by only 1 part:
+ * - [SELECTED_INDEX_ALL][SELECTED_INDEX_ALL]
*/
- private static void loadBatteryUsageDataFromBatteryStatsService(
- Context context,
- @Nullable Handler handler,
- final UsageMapAsyncResponse asyncResponseDelegate) {
- new LoadUsageMapFromBatteryStatsServiceTask(
- context,
- handler,
- asyncResponseDelegate).execute();
+ static Map<Integer, Map<Integer, BatteryDiffData>> getBatteryUsageMapFromStatsService(
+ final Context context) {
+ final Map<Integer, Map<Integer, BatteryDiffData>> resultMap = new HashMap<>();
+ final Map<Integer, BatteryDiffData> allUsageMap = new HashMap<>();
+ // Always construct the map whether the value is null or not.
+ allUsageMap.put(SELECTED_INDEX_ALL,
+ generateBatteryDiffData(context, getBatteryHistListFromFromStatsService(context)));
+ resultMap.put(SELECTED_INDEX_ALL, allUsageMap);
+ processBatteryDiffData(context, resultMap);
+ return resultMap;
+ }
+
+ static void loadLabelAndIcon(
+ @Nullable final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap) {
+ if (batteryUsageMap == null) {
+ return;
+ }
+ // Pre-loads each BatteryDiffEntry relative icon and label for all slots.
+ final BatteryDiffData batteryUsageMapForAll =
+ batteryUsageMap.get(SELECTED_INDEX_ALL).get(SELECTED_INDEX_ALL);
+ if (batteryUsageMapForAll != null) {
+ batteryUsageMapForAll.getAppDiffEntryList().forEach(
+ entry -> entry.loadLabelAndIcon());
+ batteryUsageMapForAll.getSystemDiffEntryList().forEach(
+ entry -> entry.loadLabelAndIcon());
+ }
}
@Nullable
@@ -672,24 +649,6 @@
return events;
}
- /**
- * @return Returns the overall battery usage data from battery stats service directly.
- *
- * The returned value should be always a 2d map and composed by only 1 part:
- * - [SELECTED_INDEX_ALL][SELECTED_INDEX_ALL]
- */
- private static Map<Integer, Map<Integer, BatteryDiffData>> getBatteryUsageMapFromStatsService(
- final Context context) {
- final Map<Integer, Map<Integer, BatteryDiffData>> resultMap = new HashMap<>();
- final Map<Integer, BatteryDiffData> allUsageMap = new HashMap<>();
- // Always construct the map whether the value is null or not.
- allUsageMap.put(SELECTED_INDEX_ALL,
- generateBatteryDiffData(context, getBatteryHistListFromFromStatsService(context)));
- resultMap.put(SELECTED_INDEX_ALL, allUsageMap);
- processBatteryDiffData(context, resultMap);
- return resultMap;
- }
-
@Nullable
private static List<BatteryHistEntry> getBatteryHistListFromFromStatsService(
final Context context) {
@@ -1469,22 +1428,6 @@
return true;
}
- private static void loadLabelAndIcon(
- @Nullable final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap) {
- if (batteryUsageMap == null) {
- return;
- }
- // Pre-loads each BatteryDiffEntry relative icon and label for all slots.
- final BatteryDiffData batteryUsageMapForAll =
- batteryUsageMap.get(SELECTED_INDEX_ALL).get(SELECTED_INDEX_ALL);
- if (batteryUsageMapForAll != null) {
- batteryUsageMapForAll.getAppDiffEntryList().forEach(
- entry -> entry.loadLabelAndIcon());
- batteryUsageMapForAll.getSystemDiffEntryList().forEach(
- entry -> entry.loadLabelAndIcon());
- }
- }
-
private static long getTimestampWithDayDiff(final long timestamp, final int dayDiff) {
final Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(timestamp);
@@ -1527,7 +1470,7 @@
}
// Compute diff map and loads all items (icon and label) in the background.
- private static class ComputeUsageMapAndLoadItemsTask
+ static class ComputeUsageMapAndLoadItemsTask
extends AsyncTask<Void, Void, Map<Integer, Map<Integer, BatteryDiffData>>> {
Context mApplicationContext;
@@ -1536,7 +1479,7 @@
private List<BatteryLevelData.PeriodBatteryLevelData> mHourlyBatteryLevelsPerDay;
private Map<Long, Map<String, BatteryHistEntry>> mBatteryHistoryMap;
- private ComputeUsageMapAndLoadItemsTask(
+ ComputeUsageMapAndLoadItemsTask(
Context context,
Handler handler,
final UsageMapAsyncResponse asyncResponseDelegate,
@@ -1594,35 +1537,4 @@
}
}
}
-
- // Loads battery usage data from battery stats service directly and loads all items (icon and
- // label) in the background.
- private static final class LoadUsageMapFromBatteryStatsServiceTask
- extends ComputeUsageMapAndLoadItemsTask {
-
- private LoadUsageMapFromBatteryStatsServiceTask(
- Context context,
- Handler handler,
- final UsageMapAsyncResponse asyncResponseDelegate) {
- super(context, handler, asyncResponseDelegate, /*hourlyBatteryLevelsPerDay=*/ null,
- /*batteryHistoryMap=*/ null);
- }
-
- @Override
- protected Map<Integer, Map<Integer, BatteryDiffData>> doInBackground(Void... voids) {
- if (mApplicationContext == null
- || mHandler == null
- || mAsyncResponseDelegate == null) {
- Log.e(TAG, "invalid input for ComputeUsageMapAndLoadItemsTask()");
- return null;
- }
- final long startTime = System.currentTimeMillis();
- final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap =
- getBatteryUsageMapFromStatsService(mApplicationContext);
- loadLabelAndIcon(batteryUsageMap);
- Log.d(TAG, String.format("execute LoadUsageMapFromBatteryStatsServiceTask in %d/ms",
- (System.currentTimeMillis() - startTime)));
- return batteryUsageMap;
- }
- }
}
diff --git a/src/com/android/settings/inputmethod/TouchGesturesButtonPreferenceController.java b/src/com/android/settings/inputmethod/TouchGesturesButtonPreferenceController.java
new file mode 100644
index 0000000..6e54689
--- /dev/null
+++ b/src/com/android/settings/inputmethod/TouchGesturesButtonPreferenceController.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 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.inputmethod;
+
+import android.content.Context;
+import android.util.FeatureFlagUtils;
+
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.widget.ButtonPreference;
+
+public class TouchGesturesButtonPreferenceController extends BasePreferenceController {
+
+ private static final int ORDER_TOP = 0;
+ private static final int ORDER_BOTTOM = 100;
+ private static final String PREFERENCE_KEY = "trackpad_touch_gesture";
+
+ public TouchGesturesButtonPreferenceController(Context context, String key) {
+ super(context, key);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ ButtonPreference buttonPreference =
+ (ButtonPreference) screen.findPreference(getPreferenceKey());
+ boolean touchGestureDeveloperMode = FeatureFlagUtils
+ .isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_TRACKPAD_GESTURE);
+ if (getPreferenceKey().equals(PREFERENCE_KEY)) {
+ if (touchGestureDeveloperMode) {
+ buttonPreference.setOrder(ORDER_TOP);
+ } else {
+ buttonPreference.setOrder(ORDER_BOTTOM);
+ }
+ }
+ buttonPreference.setOnClickListener(v -> {
+ showTouchpadGestureEducation();
+ });
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ private void showTouchpadGestureEducation() {
+ // TODO: Waiting for the education UX design.
+ /* For example:
+ FragmentManager fragmentManager = mParent.getActivity().getSupportFragmentManager();
+ FragmentTransaction transaction = fragmentManager.beginTransaction();
+ TrackpadGestureDialogFragment fragment = new TrackpadGestureDialogFragment();
+ fragment.show(transaction, GESTURE_DIALOG_TAG);
+ */
+ }
+}
diff --git a/src/com/android/settings/inputmethod/TrackpadSettings.java b/src/com/android/settings/inputmethod/TrackpadSettings.java
new file mode 100644
index 0000000..436e3e6
--- /dev/null
+++ b/src/com/android/settings/inputmethod/TrackpadSettings.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 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.inputmethod;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.util.FeatureFlagUtils;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+
+public class TrackpadSettings extends DashboardFragment {
+
+ private static final String TAG = "TrackpadSettings";
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.SETTINGS_KEYBOARDS_TOUCHPAD;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.trackpad_settings;
+ }
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.trackpad_settings) {
+ @Override
+ protected boolean isPageSearchEnabled(Context context) {
+ return FeatureFlagUtils
+ .isEnabled(context, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_TRACKPAD);
+ }
+ };
+}
diff --git a/src/com/android/settings/inputmethod/TrackpadSettingsController.java b/src/com/android/settings/inputmethod/TrackpadSettingsController.java
new file mode 100644
index 0000000..41be395
--- /dev/null
+++ b/src/com/android/settings/inputmethod/TrackpadSettingsController.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 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.inputmethod;
+
+import android.content.Context;
+import android.util.FeatureFlagUtils;
+
+import com.android.settings.core.BasePreferenceController;
+
+public class TrackpadSettingsController extends BasePreferenceController {
+
+ public TrackpadSettingsController(Context context, String key) {
+ super(context, key);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ // TODO: Need to detect if trackpad is connected with device.
+ boolean isFeatureOn = FeatureFlagUtils
+ .isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_TRACKPAD);
+ return isFeatureOn ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ }
+}
diff --git a/src/com/android/settings/inputmethod/TrackpadTouchGestureSettings.java b/src/com/android/settings/inputmethod/TrackpadTouchGestureSettings.java
new file mode 100644
index 0000000..9884862
--- /dev/null
+++ b/src/com/android/settings/inputmethod/TrackpadTouchGestureSettings.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 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.inputmethod;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.util.FeatureFlagUtils;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+
+public class TrackpadTouchGestureSettings extends DashboardFragment {
+
+ private static final String TAG = "TrackpadTouchGestureSettings";
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.SETTINGS_KEYBOARDS_TOUCHPAD_GESTURE;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.trackpad_gesture_settings;
+ }
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.trackpad_settings) {
+ @Override
+ protected boolean isPageSearchEnabled(Context context) {
+ return FeatureFlagUtils
+ .isEnabled(
+ context,
+ FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_TRACKPAD_GESTURE);
+ }
+ };
+}
diff --git a/src/com/android/settings/inputmethod/TrackpadTouchGestureSettingsController.java b/src/com/android/settings/inputmethod/TrackpadTouchGestureSettingsController.java
new file mode 100644
index 0000000..8f04aee
--- /dev/null
+++ b/src/com/android/settings/inputmethod/TrackpadTouchGestureSettingsController.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 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.inputmethod;
+
+import android.content.Context;
+import android.util.FeatureFlagUtils;
+
+import com.android.settings.core.BasePreferenceController;
+
+public class TrackpadTouchGestureSettingsController extends BasePreferenceController {
+
+ public TrackpadTouchGestureSettingsController(Context context, String key) {
+ super(context, key);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ boolean isFeatureOn = FeatureFlagUtils
+ .isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_TRACKPAD_GESTURE);
+ return isFeatureOn ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
index 31e0782..52ccb37 100644
--- a/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
@@ -24,6 +24,7 @@
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.widget.SeekBar;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
@@ -111,4 +112,15 @@
verify(mInteractionListener).onProgressChanged();
}
+
+ @Test
+ public void onProgressChanged_verifyNotifyPreferenceChanged() {
+ mSeekBarController.displayPreference(mPreferenceScreen);
+
+ mSeekBarPreference.setProgress(mSeekBarPreference.getMax());
+ mSeekBarPreference.onProgressChanged(new SeekBar(mContext), /* progress= */
+ 0, /* fromUser= */ false);
+
+ verify(mInteractionListener).notifyPreferenceChanged();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java
index 57963aa..839cae2 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java
@@ -371,8 +371,8 @@
@Test
public void getTotalHours_getExpectedResult() {
Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = createBatteryHistoryMap(60);
- BatteryLevelData batteryLevelData = DataProcessor.getBatteryLevelData(mContext, null,
- batteryHistoryMap, null);
+ BatteryLevelData batteryLevelData =
+ DataProcessManager.getBatteryLevelData(mContext, null, batteryHistoryMap, null);
final int totalHour = BatteryChartPreferenceController.getTotalHours(batteryLevelData);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
index aee8398..fe1bff6 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
@@ -15,6 +15,9 @@
*/
package com.android.settings.fuelgauge.batteryusage;
+import static android.app.usage.UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY;
+import static android.app.usage.UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -22,6 +25,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
+import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageEvents.Event;
import android.content.ContentValues;
@@ -31,6 +35,7 @@
import android.os.BatteryManager;
import android.os.BatteryUsageStats;
import android.os.LocaleList;
+import android.os.RemoteException;
import android.os.UserHandle;
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
@@ -56,6 +61,8 @@
@Mock
private BatteryUsageStats mBatteryUsageStats;
@Mock
+ private IUsageStatsManager mUsageStatsManager;
+ @Mock
private BatteryEntry mMockBatteryEntry;
@Before
@@ -278,7 +285,7 @@
final long userId = 2;
final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(
- mContext, event, userId);
+ mContext, mUsageStatsManager, event, userId);
assertThat(appUsageEvent.getTimestamp()).isEqualTo(101L);
assertThat(appUsageEvent.getType()).isEqualTo(AppUsageEventType.ACTIVITY_RESUMED);
assertThat(appUsageEvent.getPackageName()).isEqualTo("com.android.settings1");
@@ -299,7 +306,7 @@
final long userId = 1;
final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(
- mContext, event, userId);
+ mContext, mUsageStatsManager, event, userId);
assertThat(appUsageEvent.getTimestamp()).isEqualTo(101L);
assertThat(appUsageEvent.getType()).isEqualTo(AppUsageEventType.DEVICE_SHUTDOWN);
assertThat(appUsageEvent.getPackageName()).isEqualTo("com.android.settings1");
@@ -315,7 +322,7 @@
event.mPackage = null;
final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(
- mContext, event, /*userId=*/ 0);
+ mContext, mUsageStatsManager, event, /*userId=*/ 0);
assertThat(appUsageEvent).isNull();
}
@@ -331,7 +338,7 @@
final long userId = 1;
final AppUsageEvent appUsageEvent = ConvertUtils.convertToAppUsageEvent(
- mContext, event, userId);
+ mContext, mUsageStatsManager, event, userId);
assertThat(appUsageEvent).isNull();
}
@@ -415,4 +422,53 @@
mContext.getResources().getConfiguration().setLocales(new LocaleList());
assertThat(ConvertUtils.getLocale(mContext)).isEqualTo(Locale.getDefault());
}
+
+ @Test
+ public void getEffectivePackageName_currentActivity_returnPackageName() throws RemoteException {
+ when(mUsageStatsManager.getUsageSource()).thenReturn(USAGE_SOURCE_CURRENT_ACTIVITY);
+ final String packageName = "com.android.settings1";
+ final String taskRootPackageName = "com.android.settings2";
+
+ assertThat(ConvertUtils.getEffectivePackageName(
+ mUsageStatsManager, packageName, taskRootPackageName))
+ .isEqualTo(packageName);
+ }
+
+ @Test
+ public void getEffectivePackageName_usageSourceThrowException_returnPackageName()
+ throws RemoteException {
+ when(mUsageStatsManager.getUsageSource()).thenThrow(new RemoteException());
+ final String packageName = "com.android.settings1";
+ final String taskRootPackageName = "com.android.settings2";
+
+ assertThat(ConvertUtils.getEffectivePackageName(
+ mUsageStatsManager, packageName, taskRootPackageName))
+ .isEqualTo(packageName);
+ }
+
+ @Test
+ public void getEffectivePackageName_rootActivity_returnTaskRootPackageName()
+ throws RemoteException {
+ when(mUsageStatsManager.getUsageSource()).thenReturn(USAGE_SOURCE_TASK_ROOT_ACTIVITY);
+ final String packageName = "com.android.settings1";
+ final String taskRootPackageName = "com.android.settings2";
+
+ assertThat(ConvertUtils.getEffectivePackageName(
+ mUsageStatsManager, packageName, taskRootPackageName))
+ .isEqualTo(taskRootPackageName);
+ }
+
+ @Test
+ public void getEffectivePackageName_nullOrEmptyTaskRoot_returnPackageName()
+ throws RemoteException {
+ when(mUsageStatsManager.getUsageSource()).thenReturn(USAGE_SOURCE_TASK_ROOT_ACTIVITY);
+ final String packageName = "com.android.settings1";
+
+ assertThat(ConvertUtils.getEffectivePackageName(
+ mUsageStatsManager, packageName, /*taskRootPackageName=*/ null))
+ .isEqualTo(packageName);
+ assertThat(ConvertUtils.getEffectivePackageName(
+ mUsageStatsManager, packageName, /*taskRootPackageName=*/ ""))
+ .isEqualTo(packageName);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
index 1bfff07..ee37469 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
@@ -21,16 +21,21 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
+import android.content.ContentValues;
import android.content.Context;
+import android.content.Intent;
import android.database.MatrixCursor;
+import android.os.BatteryManager;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.UserManager;
+import android.text.format.DateUtils;
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
@@ -43,10 +48,14 @@
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
@RunWith(RobolectricTestRunner.class)
public final class DataProcessManagerTest {
+ private static final String FAKE_ENTRY_KEY = "fake_entry_key";
+
private Context mContext;
private DataProcessManager mDataProcessManager;
@@ -54,6 +63,8 @@
private IUsageStatsManager mUsageStatsManager;
@Mock
private UserManager mUserManager;
+ @Mock
+ private Intent mIntent;
@Before
public void setUp() {
@@ -65,6 +76,9 @@
doReturn(mUserManager)
.when(mContext)
.getSystemService(UserManager.class);
+ doReturn(mIntent).when(mContext).registerReceiver(any(), any());
+ doReturn(100).when(mIntent).getIntExtra(eq(BatteryManager.EXTRA_SCALE), anyInt());
+ doReturn(66).when(mIntent).getIntExtra(eq(BatteryManager.EXTRA_LEVEL), anyInt());
mDataProcessManager = new DataProcessManager(
mContext, /*handler=*/ null, /*callbackFunction=*/ null,
@@ -72,6 +86,14 @@
}
@Test
+ public void constructor_noLevelData() {
+ final DataProcessManager dataProcessManager =
+ new DataProcessManager(mContext, /*handler=*/ null, /*callbackFunction=*/ null);
+ assertThat(dataProcessManager.getShowScreenOnTime()).isFalse();
+ assertThat(dataProcessManager.getShowBatteryLevel()).isFalse();
+ }
+
+ @Test
public void start_loadEmptyDatabaseAppUsageData() {
final MatrixCursor cursor = new MatrixCursor(
new String[]{
@@ -204,6 +226,66 @@
assertThat(dataProcessManager.getStartTimestampOfBatteryLevelData()).isEqualTo(0);
}
+ @Test
+ public void getBatteryLevelData_emptyHistoryMap_returnNull() {
+ assertThat(DataProcessManager.getBatteryLevelData(
+ mContext,
+ /*handler=*/ null,
+ /*batteryHistoryMap=*/ null,
+ /*asyncResponseDelegate=*/ null))
+ .isNull();
+ assertThat(DataProcessManager.getBatteryLevelData(
+ mContext, /*handler=*/ null, new HashMap<>(), /*asyncResponseDelegate=*/ null))
+ .isNull();
+ }
+
+ @Test
+ public void getBatteryLevelData_notEnoughData_returnNull() {
+ // The timestamps and the current time are within half hour before an even hour.
+ final long[] timestamps = {
+ DateUtils.HOUR_IN_MILLIS * 2 - 300L,
+ DateUtils.HOUR_IN_MILLIS * 2 - 200L,
+ DateUtils.HOUR_IN_MILLIS * 2 - 100L};
+ final int[] levels = {100, 99, 98};
+ final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
+ createHistoryMap(timestamps, levels);
+ DataProcessor.sFakeCurrentTimeMillis = timestamps[timestamps.length - 1];
+
+ assertThat(DataProcessManager.getBatteryLevelData(
+ mContext, /*handler=*/ null, batteryHistoryMap, /*asyncResponseDelegate=*/ null))
+ .isNull();
+ }
+
+ @Test
+ public void getBatteryLevelData_returnExpectedResult() {
+ // Timezone GMT+8: 2022-01-01 00:00:00, 2022-01-01 01:00:00
+ final long[] timestamps = {1640966400000L, 1640970000000L};
+ final int[] levels = {100, 99};
+ final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
+ createHistoryMap(timestamps, levels);
+ DataProcessor.sFakeCurrentTimeMillis = timestamps[timestamps.length - 1];
+
+ final BatteryLevelData resultData =
+ DataProcessManager.getBatteryLevelData(
+ mContext,
+ /*handler=*/ null,
+ batteryHistoryMap,
+ /*asyncResponseDelegate=*/ null);
+
+ final List<Long> expectedDailyTimestamps = List.of(
+ 1640966400000L, // 2022-01-01 00:00:00
+ 1640973600000L); // 2022-01-01 02:00:00
+ final List<Integer> expectedDailyLevels = List.of(100, 66);
+ final List<List<Long>> expectedHourlyTimestamps = List.of(expectedDailyTimestamps);
+ final List<List<Integer>> expectedHourlyLevels = List.of(expectedDailyLevels);
+ verifyExpectedBatteryLevelData(
+ resultData,
+ expectedDailyTimestamps,
+ expectedDailyLevels,
+ expectedHourlyTimestamps,
+ expectedHourlyLevels);
+ }
+
private UsageEvents getUsageEvents(final List<UsageEvents.Event> events) {
UsageEvents usageEvents = new UsageEvents(events, new String[] {"package"});
Parcel parcel = Parcel.obtain();
@@ -222,9 +304,77 @@
return event;
}
+ private static Map<Long, Map<String, BatteryHistEntry>> createHistoryMap(
+ final long[] timestamps, final int[] levels) {
+ final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
+ for (int index = 0; index < timestamps.length; index++) {
+ final Map<String, BatteryHistEntry> entryMap = new HashMap<>();
+ final ContentValues values = getContentValuesWithBatteryLevel(levels[index]);
+ final BatteryHistEntry entry = new BatteryHistEntry(values);
+ entryMap.put(FAKE_ENTRY_KEY, entry);
+ batteryHistoryMap.put(timestamps[index], entryMap);
+ }
+ return batteryHistoryMap;
+ }
+
+ private static ContentValues getContentValuesWithBatteryLevel(final int level) {
+ final ContentValues values = new ContentValues();
+ final DeviceBatteryState deviceBatteryState =
+ DeviceBatteryState
+ .newBuilder()
+ .setBatteryLevel(level)
+ .build();
+ final BatteryInformation batteryInformation =
+ BatteryInformation
+ .newBuilder()
+ .setDeviceBatteryState(deviceBatteryState)
+ .build();
+ values.put(BatteryHistEntry.KEY_BATTERY_INFORMATION,
+ ConvertUtils.convertBatteryInformationToString(batteryInformation));
+ return values;
+ }
+
private void assertAppUsageEvent(
final AppUsageEvent event, final AppUsageEventType eventType, final long timestamp) {
assertThat(event.getType()).isEqualTo(eventType);
assertThat(event.getTimestamp()).isEqualTo(timestamp);
}
+
+ private static void verifyExpectedBatteryLevelData(
+ final BatteryLevelData resultData,
+ final List<Long> expectedDailyTimestamps,
+ final List<Integer> expectedDailyLevels,
+ final List<List<Long>> expectedHourlyTimestamps,
+ final List<List<Integer>> expectedHourlyLevels) {
+ final BatteryLevelData.PeriodBatteryLevelData dailyResultData =
+ resultData.getDailyBatteryLevels();
+ final List<BatteryLevelData.PeriodBatteryLevelData> hourlyResultData =
+ resultData.getHourlyBatteryLevelsPerDay();
+ verifyExpectedDailyBatteryLevelData(
+ dailyResultData, expectedDailyTimestamps, expectedDailyLevels);
+ verifyExpectedHourlyBatteryLevelData(
+ hourlyResultData, expectedHourlyTimestamps, expectedHourlyLevels);
+ }
+
+ private static void verifyExpectedDailyBatteryLevelData(
+ final BatteryLevelData.PeriodBatteryLevelData dailyResultData,
+ final List<Long> expectedDailyTimestamps,
+ final List<Integer> expectedDailyLevels) {
+ assertThat(dailyResultData.getTimestamps()).isEqualTo(expectedDailyTimestamps);
+ assertThat(dailyResultData.getLevels()).isEqualTo(expectedDailyLevels);
+ }
+
+ private static void verifyExpectedHourlyBatteryLevelData(
+ final List<BatteryLevelData.PeriodBatteryLevelData> hourlyResultData,
+ final List<List<Long>> expectedHourlyTimestamps,
+ final List<List<Integer>> expectedHourlyLevels) {
+ final int expectedHourlySize = expectedHourlyTimestamps.size();
+ assertThat(hourlyResultData).hasSize(expectedHourlySize);
+ for (int dailyIndex = 0; dailyIndex < expectedHourlySize; dailyIndex++) {
+ assertThat(hourlyResultData.get(dailyIndex).getTimestamps())
+ .isEqualTo(expectedHourlyTimestamps.get(dailyIndex));
+ assertThat(hourlyResultData.get(dailyIndex).getLevels())
+ .isEqualTo(expectedHourlyLevels.get(dailyIndex));
+ }
+ }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
index 0f95754..aab3cb3 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
@@ -99,66 +99,6 @@
}
@Test
- public void getBatteryLevelData_emptyHistoryMap_returnNull() {
- assertThat(DataProcessor.getBatteryLevelData(
- mContext,
- /*handler=*/ null,
- /*batteryHistoryMap=*/ null,
- /*asyncResponseDelegate=*/ null))
- .isNull();
- assertThat(DataProcessor.getBatteryLevelData(
- mContext, /*handler=*/ null, new HashMap<>(), /*asyncResponseDelegate=*/ null))
- .isNull();
- }
-
- @Test
- public void getBatteryLevelData_notEnoughData_returnNull() {
- // The timestamps and the current time are within half hour before an even hour.
- final long[] timestamps = {
- DateUtils.HOUR_IN_MILLIS * 2 - 300L,
- DateUtils.HOUR_IN_MILLIS * 2 - 200L,
- DateUtils.HOUR_IN_MILLIS * 2 - 100L};
- final int[] levels = {100, 99, 98};
- final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
- createHistoryMap(timestamps, levels);
- DataProcessor.sFakeCurrentTimeMillis = timestamps[timestamps.length - 1];
-
- assertThat(DataProcessor.getBatteryLevelData(
- mContext, /*handler=*/ null, batteryHistoryMap, /*asyncResponseDelegate=*/ null))
- .isNull();
- }
-
- @Test
- public void getBatteryLevelData_returnExpectedResult() {
- // Timezone GMT+8: 2022-01-01 00:00:00, 2022-01-01 01:00:00
- final long[] timestamps = {1640966400000L, 1640970000000L};
- final int[] levels = {100, 99};
- final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
- createHistoryMap(timestamps, levels);
- DataProcessor.sFakeCurrentTimeMillis = timestamps[timestamps.length - 1];
-
- final BatteryLevelData resultData =
- DataProcessor.getBatteryLevelData(
- mContext,
- /*handler=*/ null,
- batteryHistoryMap,
- /*asyncResponseDelegate=*/ null);
-
- final List<Long> expectedDailyTimestamps = List.of(
- 1640966400000L, // 2022-01-01 00:00:00
- 1640973600000L); // 2022-01-01 02:00:00
- final List<Integer> expectedDailyLevels = List.of(100, 66);
- final List<List<Long>> expectedHourlyTimestamps = List.of(expectedDailyTimestamps);
- final List<List<Integer>> expectedHourlyLevels = List.of(expectedDailyLevels);
- verifyExpectedBatteryLevelData(
- resultData,
- expectedDailyTimestamps,
- expectedDailyLevels,
- expectedHourlyTimestamps,
- expectedHourlyLevels);
- }
-
- @Test
public void getAppUsageEvents_returnExpectedResult() throws RemoteException {
UserInfo userInfo = new UserInfo(/*id=*/ 0, "user_0", /*flags=*/ 0);
final List<UserInfo> userInfoList = new ArrayList<>();