summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> 2025-01-03 13:31:25 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2025-01-03 13:31:25 -0800
commit78d1bc52c8874ca6d4e877f81232669fa3eb847f (patch)
treeef44e92fd9c5b0767209a6bb5bcba3707a6f1c19
parent2928afc4098e1f21a5bc46548ae3dce997beef22 (diff)
parent05f3b231ba56fa057d3e362d82dd6a53f2541378 (diff)
Merge "[CDM] Refactor CDM association discovery timeout mechanism." into main
-rw-r--r--packages/CompanionDeviceManager/res/anim/progress_indeterminate_horizontal_rect1.xml30
-rw-r--r--packages/CompanionDeviceManager/res/anim/progress_indeterminate_horizontal_rect2.xml30
-rw-r--r--packages/CompanionDeviceManager/res/drawable/indeterminate_progress_drawable.xml25
-rw-r--r--packages/CompanionDeviceManager/res/drawable/indeterminate_progress_vector.xml49
-rw-r--r--packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect1_scalex.xml18
-rw-r--r--packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect1_translatex.xml18
-rw-r--r--packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect2_scalex.xml18
-rw-r--r--packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect2_translatex.xml18
-rw-r--r--packages/CompanionDeviceManager/res/layout/activity_confirmation.xml32
-rw-r--r--packages/CompanionDeviceManager/res/values/colors.xml21
-rw-r--r--packages/CompanionDeviceManager/res/values/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values/styles.xml53
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java222
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java50
14 files changed, 454 insertions, 150 deletions
diff --git a/packages/CompanionDeviceManager/res/anim/progress_indeterminate_horizontal_rect1.xml b/packages/CompanionDeviceManager/res/anim/progress_indeterminate_horizontal_rect1.xml
new file mode 100644
index 000000000000..a9fd55f77a9c
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/anim/progress_indeterminate_horizontal_rect1.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="2000"
+ android:propertyXName="translateX"
+ android:pathData="M -522.59998,0 c 48.89972,0 166.02656,0 301.21729,0 c 197.58128,0 420.9827,0 420.9827,0 "
+ android:interpolator="@interpolator/progress_indeterminate_horizontal_rect1_translatex"
+ android:repeatCount="infinite" />
+ <objectAnimator
+ android:duration="2000"
+ android:propertyYName="scaleX"
+ android:pathData="M 0 0.1 L 1 0.826849212646 L 2 0.1"
+ android:interpolator="@interpolator/progress_indeterminate_horizontal_rect1_scalex"
+ android:repeatCount="infinite" />
+</set> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/anim/progress_indeterminate_horizontal_rect2.xml b/packages/CompanionDeviceManager/res/anim/progress_indeterminate_horizontal_rect2.xml
new file mode 100644
index 000000000000..7b5b6a93473d
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/anim/progress_indeterminate_horizontal_rect2.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="2000"
+ android:propertyXName="translateX"
+ android:pathData="M -197.60001,0 c 14.28182,0 85.07782,0 135.54689,0 c 54.26191,0 90.42461,0 168.24331,0 c 144.72154,0 316.40982,0 316.40982,0 "
+ android:interpolator="@interpolator/progress_indeterminate_horizontal_rect2_translatex"
+ android:repeatCount="infinite" />
+ <objectAnimator
+ android:duration="2000"
+ android:propertyYName="scaleX"
+ android:pathData="M 0.0,0.1 L 1.0,0.571379510698 L 2.0,0.909950256348 L 3.0,0.1"
+ android:interpolator="@interpolator/progress_indeterminate_horizontal_rect2_scalex"
+ android:repeatCount="infinite" />
+</set> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable/indeterminate_progress_drawable.xml b/packages/CompanionDeviceManager/res/drawable/indeterminate_progress_drawable.xml
new file mode 100644
index 000000000000..1029590796c1
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable/indeterminate_progress_drawable.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/indeterminate_progress_vector" >
+ <target
+ android:name="rect2_grp"
+ android:animation="@anim/progress_indeterminate_horizontal_rect2" />
+ <target
+ android:name="rect1_grp"
+ android:animation="@anim/progress_indeterminate_horizontal_rect1" />
+</animated-vector> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable/indeterminate_progress_vector.xml b/packages/CompanionDeviceManager/res/drawable/indeterminate_progress_vector.xml
new file mode 100644
index 000000000000..4db3356176e7
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable/indeterminate_progress_vector.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="10dp"
+ android:width="360dp"
+ android:viewportHeight="10"
+ android:viewportWidth="360" >
+ <group
+ android:name="progress_group"
+ android:translateX="180"
+ android:translateY="5" >
+ <path
+ android:name="background_track"
+ android:pathData="M -180.0,-5.0 l 360.0,0 l 0,10.0 l -360.0,0 Z"
+ android:fillColor="@color/progress_bg" />
+ <group
+ android:name="rect2_grp"
+ android:translateX="-197.60001"
+ android:scaleX="0.1" >
+ <path
+ android:name="rect2"
+ android:pathData="M -144.0,-5.0 l 288.0,0 l 0,10.0 l -288.0,0 Z"
+ android:fillColor="@color/progress_fg" />
+ </group>
+ <group
+ android:name="rect1_grp"
+ android:translateX="-522.59998"
+ android:scaleX="0.1" >
+ <path
+ android:name="rect1"
+ android:pathData="M -144.0,-5.0 l 288.0,0 l 0,10.0 l -288.0,0 Z"
+ android:fillColor="@color/progress_fg" />
+ </group>
+ </group>
+</vector> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect1_scalex.xml b/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect1_scalex.xml
new file mode 100644
index 000000000000..453e0928a2b6
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect1_scalex.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="M 0 0 L 0.3665 0 C 0.47252618112021,0.062409910275 0.61541608570164,0.5 0.68325,0.5 C 0.75475061236836,0.5 0.75725829093844,0.814510098964 1.0,1.0" />
diff --git a/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect1_translatex.xml b/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect1_translatex.xml
new file mode 100644
index 000000000000..a6da0eb77fc5
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect1_translatex.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="M 0.0,0.0 L 0.2 0 C 0.3958333333336,0.0 0.474845090492,0.206797621729 0.5916666666664,0.417082932942 C 0.7151610251224,0.639379624869 0.81625,0.974556908664 1.0,1.0 " />
diff --git a/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect2_scalex.xml b/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect2_scalex.xml
new file mode 100644
index 000000000000..785d7abfe51d
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect2_scalex.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="M 0,0 C 0.06834272400867,0.01992566661414 0.19220331656133,0.15855429260523 0.33333333333333,0.34926160892842 C 0.38410433133433,0.41477913453861 0.54945792615267,0.68136029463551 0.66666666666667,0.68279962777002 C 0.752586273196,0.68179620963216 0.737253971954,0.878896194318 1,1" />
diff --git a/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect2_translatex.xml b/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect2_translatex.xml
new file mode 100644
index 000000000000..931dff1c3236
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect2_translatex.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="M 0.0,0.0 C 0.0375,0.0 0.128764607715,0.0895380946618 0.25,0.218553507947 C 0.322410320025,0.295610602487 0.436666666667,0.417591408114 0.483333333333,0.489826169306 C 0.69,0.80972296795 0.793333333333,0.950016125212 1.0,1.0 " />
diff --git a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
index 5805332418d0..afece5fac0fb 100644
--- a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
@@ -71,6 +71,13 @@
android:layout_height="wrap_content"
android:visibility="gone">
+ <TextView
+ android:id="@+id/timeout_message"
+ android:layout_width="match_parent"
+ android:layout_height="100dp"
+ android:visibility="gone"
+ style="@style/TimeoutMessage" />
+
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/device_list"
android:layout_width="match_parent"
@@ -87,9 +94,9 @@
app:layout_constraintHeight_max="220dp"
android:visibility="gone" />
- <View
- android:id="@+id/border_top"
- style="@style/DeviceListBorder" />
+ <ProgressBar
+ android:id="@+id/progress_bar"
+ style="@style/HorizontalProgressBar" />
<View
android:id="@+id/border_bottom"
@@ -98,10 +105,6 @@
</androidx.constraintlayout.widget.ConstraintLayout>
- <ProgressBar
- android:id="@+id/spinner_multiple_device"
- android:visibility="gone"
- style="@style/Spinner" />
</RelativeLayout>
@@ -135,7 +138,8 @@
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp">
- <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. -->
+ <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests.
+ Legacy name before the change that added single-device dialog.-->
<LinearLayout
android:id="@+id/negative_multiple_devices_layout"
android:layout_width="wrap_content"
@@ -159,18 +163,6 @@
</LinearLayout>
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:importantForAccessibility="noHideDescendants">
-
- <ProgressBar
- android:id="@+id/spinner_single_device"
- android:visibility="gone"
- style="@style/Spinner" />
- </RelativeLayout>>
-
</LinearLayout>
</ScrollView> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/values/colors.xml b/packages/CompanionDeviceManager/res/values/colors.xml
new file mode 100644
index 000000000000..8782250f3bba
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<resources>
+ <color name="border">@android:color/system_neutral1_200</color>
+ <color name="progress_bg">@android:color/system_neutral1_600</color>
+ <color name="progress_fg">@android:color/system_neutral1_0</color>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index 2a6d68d1ee35..20ede5ff91d3 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -22,6 +22,21 @@
<!-- Title of the device association confirmation dialog. -->
<string name="confirmation_title">Allow the app &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%1$s</xliff:g>&lt;/strong&gt; to access &lt;strong&gt;<xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g>&lt;/strong&gt;?</string>
+ <!-- Description of soft discovery timeout. [CHAR LIMIT= NONE] -->
+ <string name="message_discovery_soft_timeout">Make sure this <xliff:g id="device_type" example="phone">%1$s</xliff:g> has <xliff:g id="discovery_method" example="Bluetooth">%2$s</xliff:g> turned on, and keep your <xliff:g id="profile_name" example="watch">%3$s</xliff:g> nearby.</string>
+
+ <!-- Description of hard discovery timeout. [CHAR LIMIT= NONE] -->
+ <string name="message_discovery_hard_timeout">No devices found. Please try again later.</string>
+
+ <!-- The discovery method name for Bluetooth [CHAR LIMIT=30] -->
+ <string name="discovery_bluetooth">Bluetooth</string>
+
+ <!-- The discovery method name for Wi-Fi [CHAR LIMIT=30] -->
+ <string name="discovery_wifi">Wi-Fi</string>
+
+ <!-- The discovery method name for both Bluetooth and Wi-Fi [CHAR LIMIT=50] -->
+ <string name="discovery_mixed">Bluetooth and Wi-Fi</string>
+
<!-- ================= DEVICE_PROFILE_WATCH and null profile ================= -->
<!-- The name of the "watch" device type [CHAR LIMIT=30] -->
@@ -30,9 +45,12 @@
<!-- Title of the device selection dialog. -->
<string name="chooser_title_non_profile">Choose a device to be managed by &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%1$s</xliff:g>&lt;/strong&gt;</string>
- <!-- Tile of the multiple devices' dialog. -->
+ <!-- Title of the multiple devices' dialog. -->
<string name="chooser_title">Choose a <xliff:g id="profile_name" example="watch">%1$s</xliff:g> to set up</string>
+ <!-- Title of the single device scan dialog. -->
+ <string name="single_device_title">Looking for a <xliff:g id="profile_name" example="watch">%1$s</xliff:g></string>
+
<!-- Description of the privileges the application will get if associated with the companion device of WATCH profile [CHAR LIMIT=NONE] -->
<string name="summary_watch">This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="device_type" example="phone">%1$s</xliff:g></string>
diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml
index a161a505a0ac..30813baa6e4a 100644
--- a/packages/CompanionDeviceManager/res/values/styles.xml
+++ b/packages/CompanionDeviceManager/res/values/styles.xml
@@ -59,6 +59,43 @@
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
+ <style name="HorizontalProgressBar"
+ parent="@android:style/Widget.Material.ProgressBar.Horizontal">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">1dp</item>
+ <item name="android:layout_marginStart">32dp</item>
+ <item name="android:layout_marginEnd">32dp</item>
+ <item name="android:progress">100</item>
+ <item name="android:indeterminate">true</item>
+ <item name="android:indeterminateOnly">false</item>
+ <item name="android:progressTint">@color/border</item>
+ <item name="android:indeterminateDrawable">@drawable/indeterminate_progress_drawable</item>
+ </style>
+
+ <style name="DeviceListBorder">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">1dp</item>
+ <item name="android:layout_marginStart">32dp</item>
+ <item name="android:layout_marginEnd">32dp</item>
+ <item name="android:background">@color/border</item>
+ </style>
+
+ <style name="TimeoutMessage"
+ parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_marginStart">32dp</item>
+ <item name="android:layout_marginEnd">32dp</item>
+ <item name="android:paddingEnd">8dp</item>
+ <item name="android:paddingStart">8dp</item>
+ <item name="android:paddingTop">18dp</item>
+ <item name="android:paddingBottom">18dp</item>
+ <item name="android:textDirection">locale</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:lineSpacingExtra">2dp</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ </style>
+
<style name="VendorHelperBackButton"
parent="@android:style/Widget.Material.Button.Borderless.Colored">
<item name="android:layout_width">wrap_content</item>
@@ -111,22 +148,6 @@
<item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
</style>
- <style name="DeviceListBorder">
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">1dp</item>
- <item name="android:layout_marginStart">32dp</item>
- <item name="android:layout_marginEnd">32dp</item>
- <item name="android:background">@android:color/system_neutral1_200</item>
- </style>
-
- <style name="Spinner"
- parent="@android:style/Widget.Material.Light.ProgressBar.Large">
- <item name="android:layout_width">56dp</item>
- <item name="android:layout_height">56dp</item>
- <item name="android:indeterminate">true</item>
- <item name="android:layout_centerInParent">true</item>
- </style>
-
<style name="ScrollViewStyle">
<item name="android:scrollbars">none</item>
<item name="android:fillViewport">true</item>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
index 50419f7368be..ea40e13fdcc9 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
@@ -17,14 +17,12 @@
package com.android.companiondevicemanager;
import static android.companion.CompanionDeviceManager.RESULT_CANCELED;
-import static android.companion.CompanionDeviceManager.RESULT_DISCOVERY_TIMEOUT;
import static android.companion.CompanionDeviceManager.RESULT_INTERNAL_ERROR;
import static android.companion.CompanionDeviceManager.RESULT_SECURITY_ERROR;
import static android.companion.CompanionDeviceManager.RESULT_USER_REJECTED;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState;
-import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState.FINISHED_TIMEOUT;
import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.LOCK;
import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.sDiscoveryStarted;
import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_ICONS;
@@ -48,11 +46,13 @@ import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.annotation.SuppressLint;
import android.companion.AssociatedDevice;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
+import android.companion.DeviceFilter;
import android.companion.Flags;
import android.companion.IAssociationRequestCallback;
import android.content.Intent;
@@ -139,22 +139,19 @@ public class CompanionAssociationActivity extends FragmentActivity implements
private TextView mVendorHeaderName;
private ImageButton mVendorHeaderButton;
- // Progress indicator is only shown while we are looking for the first suitable device for a
- // multiple device association.
- private ProgressBar mMultipleDeviceSpinner;
- // Progress indicator is only shown while we are looking for the first suitable device for a
- // single device association.
- private ProgressBar mSingleDeviceSpinner;
+ // Message to be displayed when device hasn't been discovered for a certain duration
+ private TextView mTimeoutMessage;
+
+ // Horizontal progress indicator is always shown as long as the scanner is searching for devices
+ private ProgressBar mProgressBar;
// Present for self-managed association requests and "single-device" regular association
// regular.
private Button mButtonAllow;
private Button mButtonNotAllow;
- // Present for multiple devices' association requests only.
- private Button mButtonNotAllowMultipleDevices;
+ private Button mButtonCancelScan;
- // Present for top and bottom borders for permissions list and device list.
- private View mBorderTop;
+ // Bottom border for permissions list and device list. The progress bar acts as the top border.
private View mBorderBottom;
private LinearLayout mAssociationConfirmationDialog;
@@ -162,9 +159,9 @@ public class CompanionAssociationActivity extends FragmentActivity implements
private ConstraintLayout mConstraintList;
// Only present for self-managed association requests.
private RelativeLayout mVendorHeader;
- // A linearLayout for mButtonNotAllowMultipleDevices, user will press this layout instead
+ // A linearLayout for mButtonCancelScan, user will press this layout instead
// of the button for accessibility.
- private LinearLayout mNotAllowMultipleDevicesLayout;
+ private LinearLayout mCancelScanLayout;
// The recycler view is only shown for multiple-device regular association request, after
// at least one matching device is found.
@@ -297,7 +294,6 @@ public class CompanionAssociationActivity extends FragmentActivity implements
mAssociationConfirmationDialog = findViewById(R.id.association_confirmation);
mVendorHeader = findViewById(R.id.vendor_header);
- mBorderTop = findViewById(R.id.border_top);
mBorderBottom = findViewById(R.id.border_bottom);
mTitle = findViewById(R.id.title);
@@ -311,43 +307,90 @@ public class CompanionAssociationActivity extends FragmentActivity implements
mDeviceIcon = findViewById(R.id.device_icon);
+ mTimeoutMessage = findViewById(R.id.timeout_message);
mDeviceListRecyclerView = findViewById(R.id.device_list);
- mMultipleDeviceSpinner = findViewById(R.id.spinner_multiple_device);
- mSingleDeviceSpinner = findViewById(R.id.spinner_single_device);
+ mProgressBar = findViewById(R.id.progress_bar);
+ mProgressBar.getIndeterminateDrawable().clearColorFilter();
mPermissionListRecyclerView = findViewById(R.id.permission_list);
mPermissionListAdapter = new PermissionListAdapter(this);
mButtonAllow = findViewById(R.id.btn_positive);
mButtonNotAllow = findViewById(R.id.btn_negative);
- mButtonNotAllowMultipleDevices = findViewById(R.id.btn_negative_multiple_devices);
- mNotAllowMultipleDevicesLayout = findViewById(R.id.negative_multiple_devices_layout);
+ mButtonCancelScan = findViewById(R.id.btn_negative_multiple_devices);
+ mCancelScanLayout = findViewById(R.id.negative_multiple_devices_layout);
mButtonAllow.setOnClickListener(this::onPositiveButtonClick);
mButtonNotAllow.setOnClickListener(this::onNegativeButtonClick);
- mNotAllowMultipleDevicesLayout.setOnClickListener(this::onNegativeButtonClick);
+ mCancelScanLayout.setOnClickListener(this::onNegativeButtonClick);
mVendorHeaderButton.setOnClickListener(this::onShowHelperDialog);
if (mRequest.isSelfManaged()) {
initUiForSelfManagedAssociation();
- } else if (mRequest.isSingleDevice()) {
- initUiForSingleDevice();
} else {
- initUiForMultipleDevices();
+ initUiForDeviceDiscovery();
}
}
private void onDiscoveryStateChanged(DiscoveryState newState) {
- if (newState == FINISHED_TIMEOUT
- && CompanionDeviceDiscoveryService.getScanResult().getValue().isEmpty()) {
- synchronized (LOCK) {
- if (sDiscoveryStarted) {
- cancel(RESULT_DISCOVERY_TIMEOUT, null);
+ switch (newState) {
+ case IN_PROGRESS: {
+ mTimeoutMessage.setText(null);
+ mProgressBar.setIndeterminate(true);
+ break;
+ }
+ case IN_PROGRESS_EXTENDED: {
+ final String deviceType = getString(R.string.device_type);
+ final String discoveryType = getString(getDiscoveryMethod());
+ final String profile = getString(PROFILE_NAMES.get(mRequest.getDeviceProfile()));
+ final Spanned message = getHtmlFromResources(this,
+ R.string.message_discovery_soft_timeout,
+ deviceType, discoveryType, profile);
+ mTimeoutMessage.setText(message);
+ break;
+ }
+ case FINISHED_STOPPED: {
+ if (CompanionDeviceDiscoveryService.getScanResult().getValue().isEmpty()) {
+ // If the scan times out, do NOT close the activity automatically and let the
+ // user manually cancel the flow.
+ synchronized (LOCK) {
+ if (sDiscoveryStarted) {
+ stopDiscovery();
+ }
+ }
+ mTimeoutMessage.setText(getString(R.string.message_discovery_hard_timeout));
}
+ mProgressBar.setIndeterminate(false);
+ break;
+ }
+ }
+ }
+
+ @StringRes
+ private int getDiscoveryMethod() {
+ // If no filter was given or at least one bluetooth filter was provided, then
+ // display message for Bluetooth.
+ // If filter is _only_ for Wi-Fi devices, then display message for Wi-Fi.
+ // e.g. "Make sure Bluetooth is on" vs "Make sure Wi-Fi is on"
+ boolean hasBluetooth = false;
+ boolean hasWifi = false;
+ for (DeviceFilter<?> filter : mRequest.getDeviceFilters()) {
+ if (filter.getMediumType() == DeviceFilter.MEDIUM_TYPE_BLUETOOTH
+ || filter.getMediumType() == DeviceFilter.MEDIUM_TYPE_BLUETOOTH_LE) {
+ hasBluetooth = true;
+ } else if (filter.getMediumType() == DeviceFilter.MEDIUM_TYPE_WIFI) {
+ hasWifi = true;
}
}
+ if (hasBluetooth == hasWifi) {
+ return R.string.discovery_mixed;
+ } else if (hasBluetooth) {
+ return R.string.discovery_bluetooth;
+ } else {
+ return R.string.discovery_wifi;
+ }
}
private void onUserSelectedDevice(@NonNull DeviceFilterPair<?> selectedDevice) {
@@ -392,9 +435,7 @@ public class CompanionAssociationActivity extends FragmentActivity implements
mCancelled = true;
// Stop discovery service if it was used.
- if (!mRequest.isSelfManaged()) {
- CompanionDeviceDiscoveryService.stop(this);
- }
+ stopDiscovery();
// First send callback to the app directly...
try {
@@ -408,6 +449,12 @@ public class CompanionAssociationActivity extends FragmentActivity implements
setResultAndFinish(null, errorCode);
}
+ private void stopDiscovery() {
+ if (!mRequest.isSelfManaged()) {
+ CompanionDeviceDiscoveryService.stop(this);
+ }
+ }
+
private void setResultAndFinish(@Nullable AssociationInfo association, int resultCode) {
Slog.i(TAG, "setResultAndFinish(), association="
+ (association == null ? "null" : association)
@@ -479,41 +526,14 @@ public class CompanionAssociationActivity extends FragmentActivity implements
mVendorHeader.setVisibility(View.VISIBLE);
mProfileIcon.setVisibility(View.GONE);
mDeviceListRecyclerView.setVisibility(View.GONE);
- // Top and bottom borders should be gone for selfManaged dialog.
- mBorderTop.setVisibility(View.GONE);
+ mProgressBar.setVisibility(View.GONE);
mBorderBottom.setVisibility(View.GONE);
}
- private void initUiForSingleDevice() {
- Slog.d(TAG, "initUiForSingleDevice()");
-
- final String deviceProfile = mRequest.getDeviceProfile();
-
- if (!SUPPORTED_PROFILES.contains(deviceProfile)) {
- throw new RuntimeException("Unsupported profile " + deviceProfile);
- }
-
- final Drawable profileIcon = getIcon(this, PROFILE_ICONS.get(deviceProfile));
- mProfileIcon.setImageDrawable(profileIcon);
-
- CompanionDeviceDiscoveryService.getScanResult().observe(this, deviceFilterPairs -> {
- if (deviceFilterPairs.isEmpty()) {
- return;
- }
- mSelectedDevice = requireNonNull(deviceFilterPairs.get(0));
- updateSingleDeviceUi();
- });
-
- mSingleDeviceSpinner.setVisibility(View.VISIBLE);
- // Hide permission list and confirmation dialog first before the
- // first matched device is found.
- mPermissionListRecyclerView.setVisibility(View.GONE);
- mDeviceListRecyclerView.setVisibility(View.GONE);
- mAssociationConfirmationDialog.setVisibility(View.GONE);
- }
-
- private void initUiForMultipleDevices() {
- Slog.d(TAG, "initUiForMultipleDevices()");
+ private void initUiForDeviceDiscovery() {
+ Slog.d(TAG, "initUiForDeviceDiscovery() "
+ + "single-device=" + mRequest.isSingleDevice()
+ + ", profile=" + mRequest.getDeviceProfile());
final Drawable profileIcon;
final Spanned title;
@@ -525,41 +545,59 @@ public class CompanionAssociationActivity extends FragmentActivity implements
profileIcon = getIcon(this, PROFILE_ICONS.get(deviceProfile));
- if (deviceProfile == null) {
+ if (mRequest.isSingleDevice()) {
+ title = getHtmlFromResources(this,
+ R.string.single_device_title, getString(PROFILE_NAMES.get(deviceProfile)));
+ } else if (deviceProfile == null) {
title = getHtmlFromResources(this, R.string.chooser_title_non_profile, mAppLabel);
- mButtonNotAllowMultipleDevices.setText(R.string.consent_no);
} else {
title = getHtmlFromResources(this,
R.string.chooser_title, getString(PROFILE_NAMES.get(deviceProfile)));
}
- mDeviceAdapter = new DeviceListAdapter(this, this::onDeviceClicked);
-
mTitle.setText(title);
mProfileIcon.setImageDrawable(profileIcon);
- mDeviceListRecyclerView.setAdapter(mDeviceAdapter);
- mDeviceListRecyclerView.setLayoutManager(new LinearLayoutManager(this));
-
- CompanionDeviceDiscoveryService.getScanResult().observe(this,
- deviceFilterPairs -> {
- // Dismiss the progress bar once there's one device found for multiple devices.
- if (deviceFilterPairs.size() >= 1) {
- mMultipleDeviceSpinner.setVisibility(View.GONE);
+ if (mRequest.isSingleDevice()) {
+ mBorderBottom.setVisibility(View.GONE);
+ CompanionDeviceDiscoveryService.getScanResult().observe(this, deviceFilterPairs -> {
+ if (deviceFilterPairs.isEmpty()) {
+ return;
+ }
+ mSelectedDevice = requireNonNull(deviceFilterPairs.get(0));
+ updateUiForAssociationConsent();
+ });
+ } else {
+ mDeviceAdapter = new DeviceListAdapter(this, this::onDeviceClicked);
+ mDeviceListRecyclerView.setAdapter(mDeviceAdapter);
+ mDeviceListRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+
+ CompanionDeviceDiscoveryService.getScanResult().observe(this, deviceFilterPairs -> {
+ if (deviceFilterPairs.size() >= 1) {
+ // Dismiss the timeout message once there's at least one device found.
+ mTimeoutMessage.setText(null);
+
+ // Update profile-less cancel scan button to read "Don't allow" to indicate
+ // that selecting a device implies user consent.
+ if (deviceProfile == null) {
+ mButtonCancelScan.setText(R.string.consent_no);
}
+ }
- mDeviceAdapter.setDevices(deviceFilterPairs);
- });
+ mDeviceAdapter.setDevices(deviceFilterPairs);
+ });
+
+ mDeviceListRecyclerView.setVisibility(View.VISIBLE);
+ }
mSummary.setVisibility(View.GONE);
- // "Remove" consent button: users would need to click on the list item.
mButtonAllow.setVisibility(View.GONE);
mButtonNotAllow.setVisibility(View.GONE);
- mDeviceListRecyclerView.setVisibility(View.VISIBLE);
- mButtonNotAllowMultipleDevices.setVisibility(View.VISIBLE);
- mNotAllowMultipleDevicesLayout.setVisibility(View.VISIBLE);
+
+ mTimeoutMessage.setVisibility(View.VISIBLE);
+ mButtonCancelScan.setVisibility(View.VISIBLE);
+ mCancelScanLayout.setVisibility(View.VISIBLE);
mConstraintList.setVisibility(View.VISIBLE);
- mMultipleDeviceSpinner.setVisibility(View.VISIBLE);
}
private void onDeviceClicked(int position) {
@@ -586,15 +624,10 @@ public class CompanionAssociationActivity extends FragmentActivity implements
}
// The permission consent dialog should be displayed for the multiple device
// dialog if a device profile exists.
- updateSingleDeviceUi();
- mSummary.setVisibility(View.VISIBLE);
- mButtonAllow.setVisibility(View.VISIBLE);
- mButtonNotAllow.setVisibility(View.VISIBLE);
- mDeviceListRecyclerView.setVisibility(View.GONE);
- mNotAllowMultipleDevicesLayout.setVisibility(View.GONE);
+ updateUiForAssociationConsent();
}
- private void updateSingleDeviceUi() {
+ private void updateUiForAssociationConsent() {
// No need to show permission consent dialog if it is a isSkipPrompt(true)
// AssociationRequest. See AssociationRequestsProcessor#mayAssociateWithoutPrompt.
if (mRequest.isSkipPrompt()) {
@@ -603,9 +636,14 @@ public class CompanionAssociationActivity extends FragmentActivity implements
return;
}
- mSingleDeviceSpinner.setVisibility(View.GONE);
mAssociationConfirmationDialog.setVisibility(View.VISIBLE);
+ mProgressBar.setIndeterminate(false); // Keep as border but remove animation
+ mBorderBottom.setVisibility(View.VISIBLE);
+ mTimeoutMessage.setVisibility(View.GONE);
+ mDeviceListRecyclerView.setVisibility(View.GONE);
+ mCancelScanLayout.setVisibility(View.GONE);
+
final String deviceProfile = mRequest.getDeviceProfile();
final int summaryResourceId = PROFILE_SUMMARIES.get(deviceProfile);
final String remoteDeviceName = mSelectedDevice.getDisplayName();
@@ -624,6 +662,10 @@ public class CompanionAssociationActivity extends FragmentActivity implements
mTitle.setText(title);
mSummary.setText(summary);
+
+ mSummary.setVisibility(View.VISIBLE);
+ mButtonAllow.setVisibility(View.VISIBLE);
+ mButtonNotAllow.setVisibility(View.VISIBLE);
}
private void onPositiveButtonClick(View v) {
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
index f586e3dedf9a..50a01b3bc7c9 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
@@ -76,7 +76,8 @@ public class CompanionDeviceDiscoveryService extends Service {
private static final String TAG = "CDM_CompanionDeviceDiscoveryService";
private static final String SYS_PROP_DEBUG_TIMEOUT = "debug.cdm.discovery_timeout";
- private static final long TIMEOUT_DEFAULT = 20_000L; // 20 seconds
+ private static final long TIMEOUT_SOFT = 20_000L; // 20 seconds
+ private static final long TIMEOUT_HARD = 180_000L; // 3 minutes
private static final long TIMEOUT_MIN = 1_000L; // 1 sec
private static final long TIMEOUT_MAX = 60_000L; // 1 min
@@ -102,7 +103,8 @@ public class CompanionDeviceDiscoveryService extends Service {
private final List<DeviceFilterPair<?>> mDevicesFound = new ArrayList<>();
- private final Runnable mTimeoutRunnable = this::timeout;
+ private final Runnable mSoftTimeoutRunnable = this::softTimeout;
+ private final Runnable mHardTimeoutRunnable = this::stopDiscoveryAndFinish;
private boolean mStopAfterFirstMatch;
@@ -116,8 +118,8 @@ public class CompanionDeviceDiscoveryService extends Service {
enum DiscoveryState {
NOT_STARTED,
IN_PROGRESS,
+ IN_PROGRESS_EXTENDED,
FINISHED_STOPPED,
- FINISHED_TIMEOUT
}
static boolean startForRequest(
@@ -175,7 +177,7 @@ public class CompanionDeviceDiscoveryService extends Service {
break;
case ACTION_STOP_DISCOVERY:
- stopDiscoveryAndFinish(/* timeout */ false);
+ stopDiscoveryAndFinish();
break;
}
return START_NOT_STICKY;
@@ -223,9 +225,17 @@ public class CompanionDeviceDiscoveryService extends Service {
}
@MainThread
- private void stopDiscoveryAndFinish(boolean timeout) {
- Slog.d(TAG, "stopDiscoveryAndFinish(" + timeout + ")");
+ private void softTimeout() {
+ // If no device is found at this point, display a message and continue discovery
+ if (mDevicesFound.isEmpty()) {
+ sStateLiveData.setValue(DiscoveryState.IN_PROGRESS_EXTENDED);
+ } else {
+ stopDiscoveryAndFinish();
+ }
+ }
+ @MainThread
+ private void stopDiscoveryAndFinish() {
synchronized (LOCK) {
if (!sDiscoveryStarted) {
stopSelf();
@@ -260,13 +270,10 @@ public class CompanionDeviceDiscoveryService extends Service {
mBleScanner.stopScan(mBleScanCallback);
}
- Handler.getMain().removeCallbacks(mTimeoutRunnable);
+ Handler.getMain().removeCallbacks(mSoftTimeoutRunnable);
+ Handler.getMain().removeCallbacks(mHardTimeoutRunnable);
- if (timeout) {
- sStateLiveData.setValue(DiscoveryState.FINISHED_TIMEOUT);
- } else {
- sStateLiveData.setValue(DiscoveryState.FINISHED_STOPPED);
- }
+ sStateLiveData.setValue(DiscoveryState.FINISHED_STOPPED);
synchronized (LOCK) {
sDiscoveryStarted = false;
@@ -379,7 +386,7 @@ public class CompanionDeviceDiscoveryService extends Service {
sScanResultsLiveData.setValue(mDevicesFound);
// Stop discovery when there's one device found for singleDevice.
if (mStopAfterFirstMatch) {
- stopDiscoveryAndFinish(/* timeout */ false);
+ stopDiscoveryAndFinish();
}
});
}
@@ -396,20 +403,17 @@ public class CompanionDeviceDiscoveryService extends Service {
}
private void scheduleTimeout() {
- long timeout = SystemProperties.getLong(SYS_PROP_DEBUG_TIMEOUT, -1);
- if (timeout <= 0) {
+ long softTimeout = SystemProperties.getLong(SYS_PROP_DEBUG_TIMEOUT, -1);
+ if (softTimeout <= 0) {
// 0 or negative values indicate that the sysprop was never set or should be ignored.
- timeout = TIMEOUT_DEFAULT;
+ softTimeout = TIMEOUT_SOFT;
} else {
- timeout = min(timeout, TIMEOUT_MAX); // should be <= 1 min (TIMEOUT_MAX)
- timeout = max(timeout, TIMEOUT_MIN); // should be >= 1 sec (TIMEOUT_MIN)
+ softTimeout = min(softTimeout, TIMEOUT_MAX); // should be <= 1 min (TIMEOUT_MAX)
+ softTimeout = max(softTimeout, TIMEOUT_MIN); // should be >= 1 sec (TIMEOUT_MIN)
}
- Handler.getMain().postDelayed(mTimeoutRunnable, timeout);
- }
-
- private void timeout() {
- stopDiscoveryAndFinish(/* timeout */ true);
+ Handler.getMain().postDelayed(mSoftTimeoutRunnable, softTimeout);
+ Handler.getMain().postDelayed(mHardTimeoutRunnable, TIMEOUT_HARD);
}
@Override