Add half sheet apk to nearby
Test: start activity show avaiable device to pairing click connect to
start pairing process pair success
Bug: 202335820
Change-Id: I454527f11a396ecf8acd55649dc740ed2d09c6fc
diff --git a/nearby/framework/java/android/nearby/IFastPairHalfSheetCallback.aidl b/nearby/framework/java/android/nearby/IFastPairHalfSheetCallback.aidl
index 43d31d1..2e6fc87 100644
--- a/nearby/framework/java/android/nearby/IFastPairHalfSheetCallback.aidl
+++ b/nearby/framework/java/android/nearby/IFastPairHalfSheetCallback.aidl
@@ -14,11 +14,12 @@
package android.nearby;
+import android.content.Intent;
/**
* Provides callback interface for halfsheet to send FastPair call back.
*
* {@hide}
*/
interface IFastPairHalfSheetCallback {
- void onHalfSheetConnectionConfirm();
+ void onHalfSheetConnectionConfirm(in Intent intent);
}
\ No newline at end of file
diff --git a/nearby/halfsheet/Android.bp b/nearby/halfsheet/Android.bp
index a1a90fb..781f308 100644
--- a/nearby/halfsheet/Android.bp
+++ b/nearby/halfsheet/Android.bp
@@ -30,7 +30,11 @@
"nearby-service-string",
],
static_libs: [
- "androidx.annotation_annotation"
+ "androidx.annotation_annotation",
+ "androidx.fragment_fragment",
+ "androidx-constraintlayout_constraintlayout",
+ "androidx.localbroadcastmanager_localbroadcastmanager",
+ "fast-pair-lite-protos",
],
lint: { strict_updatability_linting: true }
}
@@ -41,6 +45,9 @@
resource_dirs: ["res"],
certificate: ":com.android.nearby.halfsheetcertificate",
updatable: true,
+ static_libs: [
+ "com.google.android.material_material",
+ ],
apex_available: ["com.android.nearby",],
}
diff --git a/nearby/halfsheet/res/anim/fast_pair_bottom_sheet_enter.xml b/nearby/halfsheet/res/anim/fast_pair_bottom_sheet_enter.xml
new file mode 100644
index 0000000..098dccb
--- /dev/null
+++ b/nearby/halfsheet/res/anim/fast_pair_bottom_sheet_enter.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:interpolator/decelerate_quint">
+ <translate android:fromYDelta="100%"
+ android:toYDelta="0"
+ android:duration="900"/>
+</set>
diff --git a/nearby/halfsheet/res/anim/fast_pair_bottom_sheet_exit.xml b/nearby/halfsheet/res/anim/fast_pair_bottom_sheet_exit.xml
new file mode 100644
index 0000000..1cf7401
--- /dev/null
+++ b/nearby/halfsheet/res/anim/fast_pair_bottom_sheet_exit.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:interpolator/decelerate_quint">
+ <translate android:fromYDelta="0"
+ android:toYDelta="100%"
+ android:duration="500"/>
+</set>
diff --git a/nearby/halfsheet/res/anim/fast_pair_half_sheet_slide_in.xml b/nearby/halfsheet/res/anim/fast_pair_half_sheet_slide_in.xml
new file mode 100644
index 0000000..9a51ddb
--- /dev/null
+++ b/nearby/halfsheet/res/anim/fast_pair_half_sheet_slide_in.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:targetApi="23"
+ android:duration="@integer/half_sheet_slide_in_duration"
+ android:interpolator="@android:interpolator/fast_out_slow_in">
+ <translate
+ android:fromYDelta="100%p"
+ android:toYDelta="0%p"/>
+
+ <alpha
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"/>
+</set>
diff --git a/nearby/halfsheet/res/anim/fast_pair_half_sheet_slide_out.xml b/nearby/halfsheet/res/anim/fast_pair_half_sheet_slide_out.xml
new file mode 100644
index 0000000..c589482
--- /dev/null
+++ b/nearby/halfsheet/res/anim/fast_pair_half_sheet_slide_out.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:duration="@integer/half_sheet_fade_out_duration"
+ android:interpolator="@android:interpolator/fast_out_slow_in">
+
+ <translate
+ android:fromYDelta="0%p"
+ android:toYDelta="100%p"/>
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"/>
+
+</set>
diff --git a/nearby/halfsheet/res/drawable/fastpair_outline.xml b/nearby/halfsheet/res/drawable/fastpair_outline.xml
new file mode 100644
index 0000000..6765e11
--- /dev/null
+++ b/nearby/halfsheet/res/drawable/fastpair_outline.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <stroke
+ android:width="1dp"
+ android:color="@color/fast_pair_notification_image_outline"/>
+</shape>
diff --git a/nearby/halfsheet/res/drawable/half_sheet_bg.xml b/nearby/halfsheet/res/drawable/half_sheet_bg.xml
new file mode 100644
index 0000000..7e7d8dd
--- /dev/null
+++ b/nearby/halfsheet/res/drawable/half_sheet_bg.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:targetApi="23">
+ <solid android:color="@color/fast_pair_half_sheet_background" />
+ <corners
+ android:topLeftRadius="16dp"
+ android:topRightRadius="16dp"
+ android:padding="8dp"/>
+</shape>
diff --git a/nearby/halfsheet/res/layout/fast_pair_app_launch_fragment.xml b/nearby/halfsheet/res/layout/fast_pair_app_launch_fragment.xml
new file mode 100644
index 0000000..ad321b2
--- /dev/null
+++ b/nearby/halfsheet/res/layout/fast_pair_app_launch_fragment.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v7.widget.LinearLayoutCompat
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:orientation="vertical"
+ android:layout_width="match_parent" android:layout_height="match_parent">
+
+ <android.support.constraint.ConstraintLayout
+ android:id="@+id/image_view"
+ android:layout_width="match_parent"
+ android:layout_height="340dp"
+ android:paddingStart="12dp"
+ android:paddingEnd="12dp"
+ android:paddingTop="12dp">
+ <TextView
+ android:id="@+id/header_subtitle"
+ android:textColor="@color/fast_pair_half_sheet_subtitle_color"
+ android:fontFamily="google-sans"
+ android:textSize="14sp"
+ android:gravity="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent" />
+
+ <ImageView
+ android:id="@+id/pairing_pic"
+ android:layout_width="@dimen/fast_pair_half_sheet_image_size"
+ android:layout_height="@dimen/fast_pair_half_sheet_image_size"
+ android:paddingTop="18dp"
+ android:paddingBottom="18dp"
+ android:importantForAccessibility="no"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/header_subtitle" />
+
+ <Button
+ android:id="@+id/connect_btn"
+ android:text="@string/fast_pair_app_launch_button"
+ android:layout_height="@dimen/fast_pair_connect_button_height"
+ android:layout_width="@dimen/fast_pair_half_sheet_image_size"
+ android:background="@color/fast_pair_half_sheet_button_color"
+ android:paddingTop="6dp"
+ android:paddingBottom="6dp"
+ app:layout_constraintTop_toBottomOf="@+id/pairing_pic"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ style="@style/HalfSheetButton" />
+
+ </android.support.constraint.ConstraintLayout>
+
+</android.support.v7.widget.LinearLayoutCompat>
diff --git a/nearby/halfsheet/res/layout/fast_pair_consent_fragment.xml b/nearby/halfsheet/res/layout/fast_pair_consent_fragment.xml
new file mode 100644
index 0000000..aba9a32
--- /dev/null
+++ b/nearby/halfsheet/res/layout/fast_pair_consent_fragment.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v7.widget.LinearLayoutCompat
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:orientation="vertical"
+ android:layout_width="match_parent" android:layout_height="match_parent">
+
+ <android.support.constraint.ConstraintLayout
+ android:id="@+id/image_view"
+ android:layout_width="match_parent"
+ android:layout_height="340dp"
+ android:paddingStart="12dp"
+ android:paddingEnd="12dp"
+ android:paddingTop="12dp">
+ <TextView
+ android:id="@+id/header_subtitle"
+ android:textColor="@color/fast_pair_half_sheet_subtitle_color"
+ android:fontFamily="google-sans"
+ android:textSize="14sp"
+ android:gravity="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent" />
+
+ <ImageView
+ android:id="@+id/pairing_pic"
+ android:layout_width="@dimen/fast_pair_half_sheet_image_size"
+ android:layout_height="@dimen/fast_pair_half_sheet_image_size"
+ android:paddingTop="18dp"
+ android:paddingBottom="18dp"
+ android:importantForAccessibility="no"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/header_subtitle" />
+
+ <ProgressBar
+ android:id="@+id/connect_progressbar"
+ android:layout_width="@dimen/fast_pair_half_sheet_image_size"
+ android:layout_height="2dp"
+ android:indeterminate="true"
+ android:indeterminateTint="@color/fast_pair_progress_color"
+ android:indeterminateTintMode="src_in"
+ style="?android:attr/progressBarStyleHorizontal"
+ app:layout_constraintTop_toBottomOf="@+id/pairing_pic"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"/>
+
+ <Button
+ android:id="@+id/connect_btn"
+ android:text="@string/fast_pair_app_launch_button"
+ android:layout_height="@dimen/fast_pair_connect_button_height"
+ android:layout_width="@dimen/fast_pair_half_sheet_image_size"
+ android:background="@color/fast_pair_half_sheet_button_color"
+ android:paddingTop="6dp"
+ android:paddingBottom="6dp"
+ app:layout_constraintTop_toBottomOf="@+id/pairing_pic"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ style="@style/HalfSheetButton" />
+
+ <Button
+ android:id="@+id/result_action_btn"
+ android:text="@string/common_done"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingTop="6dp"
+ android:paddingBottom="6dp"
+ app:layout_constraintTop_toBottomOf="@+id/pairing_pic"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ style="@style/HalfSheetButtonBorderless" />
+
+ </android.support.constraint.ConstraintLayout>
+
+</android.support.v7.widget.LinearLayoutCompat>
diff --git a/nearby/halfsheet/res/layout/fast_pair_device_pairing_fragment.xml b/nearby/halfsheet/res/layout/fast_pair_device_pairing_fragment.xml
new file mode 100644
index 0000000..24fcd83
--- /dev/null
+++ b/nearby/halfsheet/res/layout/fast_pair_device_pairing_fragment.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:orientation="vertical"
+ tools:ignore="RtlCompat"
+ android:layout_width="match_parent" android:layout_height="match_parent">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/image_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="340dp"
+ android:paddingStart="12dp"
+ android:paddingEnd="12dp"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp">
+ <TextView
+ android:id="@+id/header_subtitle"
+ android:textColor="@color/fast_pair_half_sheet_subtitle_color"
+ android:fontFamily="google-sans"
+ android:textSize="14sp"
+ android:maxLines="3"
+ android:gravity="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent" />
+
+ <ImageView
+ android:id="@+id/pairing_pic"
+ android:layout_width="@dimen/fast_pair_half_sheet_image_size"
+ android:layout_height="@dimen/fast_pair_half_sheet_image_size"
+ android:paddingTop="18dp"
+ android:paddingBottom="18dp"
+ android:importantForAccessibility="no"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/header_subtitle" />
+
+ <TextView
+ android:id="@+id/pin_code"
+ android:textColor="@color/fast_pair_half_sheet_subtitle_color"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/fast_pair_half_sheet_image_size"
+ android:paddingTop="18dp"
+ android:paddingBottom="18dp"
+ android:visibility="invisible"
+ android:textSize="50sp"
+ android:letterSpacing="0.2"
+ android:fontFamily="google-sans-medium"
+ android:gravity="center"
+ android:importantForAccessibility="yes"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/header_subtitle" />
+
+ <ProgressBar
+ android:id="@+id/connect_progressbar"
+ android:layout_width="@dimen/fast_pair_half_sheet_image_size"
+ android:layout_height="2dp"
+ android:indeterminate="true"
+ android:indeterminateTint="@color/fast_pair_progress_color"
+ android:indeterminateTintMode="src_in"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_marginBottom="6dp"
+ app:layout_constraintTop_toBottomOf="@+id/pairing_pic"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"/>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ app:layout_constraintTop_toBottomOf="@+id/connect_progressbar"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent">
+ <ImageView
+ android:id="@+id/info_icon"
+ android:layout_centerInParent="true"
+ android:contentDescription="@null"
+ android:layout_marginEnd="10dp"
+ android:layout_toStartOf="@id/connect_btn"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <Button
+ android:id="@+id/connect_btn"
+ android:text="@string/common_connect"
+ android:layout_height="wrap_content"
+ android:layout_width="@dimen/fast_pair_half_sheet_image_size"
+ android:layout_centerInParent="true"
+ android:background="@color/fast_pair_half_sheet_button_color"
+ style="@style/HalfSheetButton" />
+ </RelativeLayout>
+
+ <Button
+ android:id="@+id/cancel_btn"
+ android:text="@string/common_done"
+ android:visibility="gone"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:gravity="start|center_vertical"
+ android:layout_marginTop="6dp"
+ android:layout_marginBottom="16dp"
+ style="@style/HalfSheetButtonBorderless"/>
+
+ <Button
+ android:id="@+id/setup_btn"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:layout_marginTop="6dp"
+ android:layout_marginBottom="16dp"
+ android:background="@color/fast_pair_half_sheet_button_color"
+ android:visibility="gone"
+ android:layout_height="@dimen/fast_pair_half_sheet_bottom_button_height"
+ android:layout_width="wrap_content"
+ style="@style/HalfSheetButton" />
+ </androidx.constraintlayout.widget.ConstraintLayout>
+
+</LinearLayout>
diff --git a/nearby/halfsheet/res/layout/fast_pair_half_sheet.xml b/nearby/halfsheet/res/layout/fast_pair_half_sheet.xml
new file mode 100644
index 0000000..705aa1b
--- /dev/null
+++ b/nearby/halfsheet/res/layout/fast_pair_half_sheet.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:ignore="RtlCompat, UselessParent, MergeRootFrame"
+ android:id="@+id/background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/card"
+ android:orientation="vertical"
+ android:transitionName="card"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_gravity= "center|bottom"
+ android:paddingLeft="12dp"
+ android:paddingRight="12dp"
+ android:background="@drawable/half_sheet_bg"
+ android:accessibilityLiveRegion="polite"
+ android:gravity="bottom">
+
+ <RelativeLayout
+ android:id="@+id/toolbar_wrapper"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="20dp"
+ android:paddingRight="20dp">
+
+ <ImageView
+ android:layout_marginTop="9dp"
+ android:layout_marginBottom="9dp"
+ android:id="@+id/toolbar_image"
+ android:layout_width="42dp"
+ android:layout_height="42dp"
+ android:contentDescription="@null"
+ android:layout_toStartOf="@id/toolbar_title"
+ android:layout_centerHorizontal="true"
+ android:visibility="invisible"/>
+
+ <TextView
+ android:layout_marginTop="18dp"
+ android:layout_marginBottom="18dp"
+ android:layout_centerHorizontal="true"
+ android:id="@+id/toolbar_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="google-sans-medium"
+ android:textSize="24sp"
+ android:textColor="@color/fast_pair_half_sheet_text_color"
+ style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title" />
+ </RelativeLayout>
+
+ <FrameLayout
+ android:id="@+id/fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ </LinearLayout>
+
+</FrameLayout>
+
diff --git a/nearby/halfsheet/res/layout/fast_pair_heads_up_notification.xml b/nearby/halfsheet/res/layout/fast_pair_heads_up_notification.xml
new file mode 100644
index 0000000..11b8343
--- /dev/null
+++ b/nearby/halfsheet/res/layout/fast_pair_heads_up_notification.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:baselineAligned="false"
+ android:background="@color/fast_pair_notification_background"
+ tools:ignore="ContentDescription,UnusedAttribute,RtlCompat,Overdraw">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginTop="@dimen/fast_pair_notification_padding"
+ android:layout_marginStart="@dimen/fast_pair_notification_padding"
+ android:layout_marginEnd="@dimen/fast_pair_notification_padding">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="sans-serif-medium"
+ android:textSize="@dimen/fast_pair_notification_text_size"
+ android:textColor="@color/fast_pair_primary_text"
+ android:layout_marginBottom="2dp"
+ android:lines="1"/>
+
+ <TextView
+ android:id="@android:id/text2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/fast_pair_notification_text_size_small"
+ android:textColor="@color/fast_pair_primary_text"
+ android:layout_marginBottom="2dp"
+ android:layout_marginStart="4dp"
+ android:lines="1"/>
+ </LinearLayout>
+
+ <TextView
+ android:id="@android:id/text1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/fast_pair_notification_text_size"
+ android:textColor="@color/fast_pair_primary_text"
+ android:maxLines="2"
+ android:ellipsize="end"
+ android:breakStrategy="simple" />
+
+ <FrameLayout
+ android:id="@android:id/secondaryProgress"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginEnd="32dp"
+ android:orientation="horizontal"
+ android:visibility="gone">
+
+ <ProgressBar
+ android:id="@android:id/progress"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:indeterminateTint="@color/discovery_activity_accent"/>
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@android:id/icon1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+</LinearLayout>
diff --git a/nearby/halfsheet/res/layout/fast_pair_heads_up_notification_large_image.xml b/nearby/halfsheet/res/layout/fast_pair_heads_up_notification_large_image.xml
new file mode 100644
index 0000000..dd28947
--- /dev/null
+++ b/nearby/halfsheet/res/layout/fast_pair_heads_up_notification_large_image.xml
@@ -0,0 +1,7 @@
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/fast_pair_notification_large_image_size"
+ android:layout_height="@dimen/fast_pair_notification_large_image_size"
+ android:scaleType="fitStart"
+ tools:ignore="ContentDescription"/>
diff --git a/nearby/halfsheet/res/layout/fast_pair_heads_up_notification_small_image.xml b/nearby/halfsheet/res/layout/fast_pair_heads_up_notification_small_image.xml
new file mode 100644
index 0000000..ee1d89f
--- /dev/null
+++ b/nearby/halfsheet/res/layout/fast_pair_heads_up_notification_small_image.xml
@@ -0,0 +1,11 @@
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/fast_pair_notification_small_image_size"
+ android:layout_height="@dimen/fast_pair_notification_small_image_size"
+ android:layout_marginTop="@dimen/fast_pair_notification_padding"
+ android:layout_marginBottom="@dimen/fast_pair_notification_padding"
+ android:layout_marginStart="@dimen/fast_pair_notification_padding"
+ android:layout_marginEnd="@dimen/fast_pair_notification_padding"
+ android:scaleType="fitStart"
+ tools:ignore="ContentDescription,RtlCompat"/>
diff --git a/nearby/halfsheet/res/values/colors.xml b/nearby/halfsheet/res/values/colors.xml
new file mode 100644
index 0000000..2a2ed41
--- /dev/null
+++ b/nearby/halfsheet/res/values/colors.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Use original background color -->
+ <color name="fast_pair_notification_background">#00000000</color>
+ <color name="fast_pair_half_sheet_button_color">@android:color/system_accent1_100</color>
+ <color name="fast_pair_half_sheet_button_text">@android:color/system_neutral1_900</color>
+ <color name="fast_pair_half_sheet_button_accent_text">@android:color/system_neutral1_900</color>
+ <color name="fast_pair_progress_color">@android:color/system_accent1_600</color>
+ <color name="fast_pair_half_sheet_subtitle_color">@android:color/system_neutral2_700</color>
+ <color name="fast_pair_half_sheet_text_color">@android:color/system_neutral1_900</color>
+
+ <!-- Nearby Discoverer -->
+ <color name="discovery_activity_accent">#4285F4</color>
+
+ <!-- Fast Pair -->
+ <color name="fast_pair_primary_text">#DE000000</color>
+ <color name="fast_pair_notification_image_outline">#24000000</color>
+ <color name="fast_pair_battery_level_low">#D93025</color>
+ <color name="fast_pair_battery_level_normal">#80868B</color>
+ <color name="fast_pair_half_sheet_background">#FFFFFF</color>
+ <color name="fast_pair_half_sheet_color_accent">#1A73E8</color>
+ <color name="fast_pair_fail_progress_color">#F44336</color>
+ <color name="fast_pair_progress_back_ground">#24000000</color>
+</resources>
diff --git a/nearby/halfsheet/res/values/dimens.xml b/nearby/halfsheet/res/values/dimens.xml
new file mode 100644
index 0000000..f843042
--- /dev/null
+++ b/nearby/halfsheet/res/values/dimens.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Fast Pair notification values -->
+ <dimen name="fast_pair_halfsheet_mid_image_size">160dp</dimen>
+ <dimen name="fast_pair_notification_text_size">14sp</dimen>
+ <dimen name="fast_pair_notification_text_size_small">11sp</dimen>
+ <dimen name="fast_pair_battery_notification_empty_view_height">4dp</dimen>
+ <dimen name="fast_pair_battery_notification_margin_top">8dp</dimen>
+ <dimen name="fast_pair_battery_notification_margin_bottom">8dp</dimen>
+ <dimen name="fast_pair_battery_notification_content_height">40dp</dimen>
+ <dimen name="fast_pair_battery_notification_content_height_v2">64dp</dimen>
+ <dimen name="fast_pair_battery_notification_image_size">32dp</dimen>
+ <dimen name="fast_pair_battery_notification_image_padding">3dp</dimen>
+ <dimen name="fast_pair_half_sheet_min_height">350dp</dimen>
+ <dimen name="fast_pair_half_sheet_image_size">215dp</dimen>
+ <dimen name="fast_pair_half_sheet_land_image_size">136dp</dimen>
+ <dimen name="fast_pair_connect_button_height">36dp</dimen>
+ <dimen name="accessibility_required_min_touch_target_size">48dp</dimen>
+ <dimen name="fast_pair_half_sheet_battery_case_image_size">152dp</dimen>
+ <dimen name="fast_pair_half_sheet_battery_bud_image_size">100dp</dimen>
+ <integer name="half_sheet_battery_case_width_dp">156</integer>
+ <integer name="half_sheet_battery_case_height_dp">182</integer>
+
+ <!-- Maximum height for SliceView, override on slices/view/src/main/res/values/dimens.xml -->
+ <dimen name="abc_slice_large_height">360dp</dimen>
+
+ <dimen name="action_dialog_content_margin_left">16dp</dimen>
+ <dimen name="action_dialog_content_margin_top">70dp</dimen>
+ <dimen name="action_button_focused_elevation">4dp</dimen>
+ <!-- Subsequent Notification -->
+ <dimen name="fast_pair_notification_padding">4dp</dimen>
+ <dimen name="fast_pair_notification_large_image_size">32dp</dimen>
+ <dimen name="fast_pair_notification_small_image_size">32dp</dimen>
+ <!-- Battery Notification -->
+ <dimen name="fast_pair_battery_notification_main_view_padding">0dp</dimen>
+ <dimen name="fast_pair_battery_notification_title_image_margin_start">0dp</dimen>
+ <dimen name="fast_pair_battery_notification_title_text_margin_start">0dp</dimen>
+ <dimen name="fast_pair_battery_notification_title_text_margin_start_v2">0dp</dimen>
+ <dimen name="fast_pair_battery_notification_image_margin_start">0dp</dimen>
+
+ <dimen name="fast_pair_half_sheet_bottom_button_height">48dp</dimen>
+</resources>
diff --git a/nearby/halfsheet/res/values/ints.xml b/nearby/halfsheet/res/values/ints.xml
new file mode 100644
index 0000000..07bf9d2
--- /dev/null
+++ b/nearby/halfsheet/res/values/ints.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <integer name="half_sheet_slide_in_duration">250</integer>
+ <integer name="half_sheet_fade_out_duration">250</integer>
+</resources>
diff --git a/nearby/halfsheet/res/values/strings.xml b/nearby/halfsheet/res/values/strings.xml
index 22f78d7..a3a2b00 100644
--- a/nearby/halfsheet/res/values/strings.xml
+++ b/nearby/halfsheet/res/values/strings.xml
@@ -16,4 +16,9 @@
<resources>
<string name="app_name">Nearby HalfSheet Dialog</string>
+ <string name="common_done" description="After pairing process finish button text to dismiss halfsheet">Done</string>
+ <string name="common_save">Save</string>
+ <string name="common_connect" description="Button text to start connecting process">Connect</string>
+ <string name="fast_pair_app_launch_button" description="String on app launch half sheet button.">Set up</string>
+
</resources>
\ No newline at end of file
diff --git a/nearby/halfsheet/res/values/styles.xml b/nearby/halfsheet/res/values/styles.xml
new file mode 100644
index 0000000..b48da70
--- /dev/null
+++ b/nearby/halfsheet/res/values/styles.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <style name="HalfSheetStyle" parent="Theme.MaterialComponents.DayNight.NoActionBar">
+ <item name="android:windowFrame">@null</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowEnterAnimation">@anim/fast_pair_half_sheet_slide_in</item>
+ <item name="android:windowExitAnimation">@anim/fast_pair_half_sheet_slide_out</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:backgroundDimEnabled">true</item>
+ <item name="android:statusBarColor">@android:color/transparent</item>
+ <item name="android:fitsSystemWindows">true</item>
+ <item name="android:windowTranslucentNavigation">true</item>
+ </style>
+
+ <style name="HalfSheetButton" parent="@style/Widget.MaterialComponents.Button.TextButton">
+ <item name="android:textColor">@color/fast_pair_half_sheet_button_accent_text</item>
+ <item name="android:backgroundTint">@color/fast_pair_half_sheet_button_color</item>
+ <item name="android:textSize">@dimen/fast_pair_notification_text_size</item>
+ <item name="android:fontFamily">google-sans-medium</item>
+ <item name="android:textAlignment">center</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
+
+ <style name="HalfSheetButtonBorderless"
+ parent="@style/Widget.MaterialComponents.Button.OutlinedButton">
+ <item name="android:textColor">@color/fast_pair_half_sheet_button_text</item>
+ <item name="android:strokeColor">@color/fast_pair_half_sheet_button_color</item>
+ <item name="android:textAllCaps">false</item>
+ <item name="android:textSize">@dimen/fast_pair_notification_text_size</item>
+ <item name="android:fontFamily">google-sans-medium</item>
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:textAlignment">center</item>
+ <item name="android:minHeight">@dimen/accessibility_required_min_touch_target_size</item>
+ </style>
+
+</resources>
diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/HalfSheetActivity.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/HalfSheetActivity.java
index 4a74c9b..c495c35 100644
--- a/nearby/halfsheet/src/com/android/nearby/halfsheet/HalfSheetActivity.java
+++ b/nearby/halfsheet/src/com/android/nearby/halfsheet/HalfSheetActivity.java
@@ -16,33 +16,295 @@
package com.android.nearby.halfsheet;
-import static com.android.server.nearby.fastpair.Constant.EXTRA_BINDER;
-import static com.android.server.nearby.fastpair.Constant.EXTRA_BUNDLE;
+import static com.android.nearby.halfsheet.fragment.DevicePairingFragment.APP_LAUNCH_FRAGMENT_TYPE;
+import static com.android.server.nearby.common.bluetooth.fastpair.FastPairConstants.EXTRA_MODEL_ID;
+import static com.android.server.nearby.common.fastpair.service.UserActionHandlerBase.EXTRA_MAC_ADDRESS;
+import static com.android.server.nearby.fastpair.Constant.ACTION_FAST_PAIR_HALF_SHEET_BAN_STATE_RESET;
+import static com.android.server.nearby.fastpair.Constant.ACTION_FAST_PAIR_HALF_SHEET_CANCEL;
+import static com.android.server.nearby.fastpair.Constant.DEVICE_PAIRING_FRAGMENT_TYPE;
+import static com.android.server.nearby.fastpair.Constant.EXTRA_HALF_SHEET_INFO;
+import static com.android.server.nearby.fastpair.Constant.EXTRA_HALF_SHEET_TYPE;
-import android.app.Activity;
-import android.nearby.IFastPairHalfSheetCallback;
+import android.content.Context;
+import android.content.Intent;
import android.os.Bundle;
-import android.os.RemoteException;
+import android.util.DisplayMetrics;
import android.util.Log;
+import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.nearby.halfsheet.fragment.DevicePairingFragment;
+import com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment;
+import com.android.nearby.halfsheet.utils.BroadcastUtils;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import java.util.Locale;
+
+import service.proto.Cache;
/**
* Half sheet activity to show pairing ux.
*/
-public class HalfSheetActivity extends Activity {
+public class HalfSheetActivity extends FragmentActivity {
+
+ public static final String EXTRA_HALF_SHEET_PENDING_INTENT_CALL_BACK =
+ "com.android.nearby.halfsheet.EXTRA_HALF_SHEET_PENDING_INTENT_CALL_BACK";
+ public static final String EXTRA_HALF_SHEET_PACKAGE_NAME =
+ "com.android.nearby.halfsheet.EXTRA_HALF_SHEET_PACKAGE_NAME";
+ public static final String EXTRA_HALF_SHEET_CONTENT =
+ "com.android.nearby.halfsheet.HALF_SHEET_CONTENT";
+ public static final String EXTRA_TITLE =
+ "com.android.nearby.halfsheet.HALF_SHEET_TITLE";
+ public static final String EXTRA_DESCRIPTION =
+ "com.android.nearby.halfsheet.HALF_SHEET_DESCRIPTION";
+ public static final String EXTRA_HALF_SHEET_ID =
+ "com.android.nearby.halfsheet.HALF_SHEET_ID";
+ public static final String EXTRA_HALF_SHEET_IS_RETROACTIVE =
+ "com.android.nearby.halfsheet.HALF_SHEET_IS_RETROACTIVE";
+ public static final String EXTRA_HALF_SHEET_IS_SUBSEQUENT_PAIR =
+ "com.android.nearby.halfsheet.HALF_SHEET_IS_SUBSEQUENT_PAIR";
+ public static final String EXTRA_HALF_SHEET_PAIRING_RESURFACE =
+ "com.android.nearby.halfsheet.EXTRA_HALF_SHEET_PAIRING_RESURFACE";
+ public static final String ACTION_HALF_SHEET_FOREGROUND_STATE =
+ "com.android.nearby.halfsheet.ACTION_HALF_SHEET_FOREGROUND_STATE";
+ public static final String ACTION_HALF_SHEET_BAN_ALL_ITEM =
+ "com.android.nearby.halfsheet.ACTION_HALF_SHEET_BAN_ALL_ITEM";
+ public static final String ACTION_HALF_SHEET_APP_LAUNCH_CLICKED =
+ "com.android.nearby.halfsheet.ACTION_HALF_SHEET_APP_LAUNCH_CLICKED";
+ public static final String ACTION_HALF_SHEET_WEAR_OS_CLICKED =
+ "com.android.nearby.halfsheet.ACTION_HALF_SHEET_WEAR_OS_CLICKED";
+ public static final String ACTION_HALF_SHEET_USER_COMPLETE_CONFIRMATION =
+ "com.android.nearby.halfsheet.ACTION_HALF_SHEET_USER_COMPLETE_CONFIRMATION";
+ public static final String ACTION_FAST_PAIR_HANDLE_CHIP_DEVICE =
+ "com.android.nearby.halfsheet.ACTION_FAST_PAIR_HANDLE_CHIP_DEVICE";
+ // Intent extra contains another intent that will trigger DiscoveryChimeraService to upload
+ // device
+ // information to the cloud.
+ public static final String EXTRA_HALF_SHEET_CLOUD_SYNC_INTENT =
+ "com.android.nearby.halfsheet.HALF_SHEET_CLOUD_SYNC_INTENT";
+ // Intent extra contains the user gmail name eg. testaccount@gmail.com.
+ public static final String EXTRA_HALF_SHEET_ACCOUNT_NAME =
+ "com.android.nearby.halfsheet.HALF_SHEET_ACCOUNT_NAME";
+ public static final String EXTRA_HALF_SHEET_FOREGROUND =
+ "com.android.nearby.halfsheet.EXTRA_HALF_SHEET_FOREGROUND";
+ public static final String EXTRA_USER_CONSENT_SYNC_CONTACTS =
+ "com.android.nearby.halfsheet.EXTRA_USER_CONSENT_SYNC_CONTACTS";
+ public static final String EXTRA_USER_CONSENT_SYNC_SMS =
+ "com.android.nearby.halfsheet.EXTRA_USER_CONSENT_SYNC_SMS";
+ public static final String EXTRA_USER_CONFIRM_PASSKEY =
+ "com.android.nearby.halfsheet.EXTRA_USER_CONFIRM_PASSKEY";
+ public static final String CLASS_NAME =
+ "com.android.nearby.halfsheet.HalfSheetActivity";
+ public static final String ACTION_HALF_SHEET_STATUS_CHANGE =
+ "com.android.nearby.halfsheet.ACTION_HALF_SHEET_STATUS_CHANGE";
+ public static final String FINISHED_STATE = "FINISHED_STATE";
+ public static final String EXTRA_CLASSIC_MAC_ADDRESS = "EXTRA_CLASSIC_MAC_ADDRESS";
+ public static final String ARG_FRAGMENT_STATE = "ARG_FRAGMENT_STATE";
+ @Nullable
+ private HalfSheetModuleFragment mHalfSheetModuleFragment;
+ @Nullable
+ private Cache.ScanFastPairStoreItem mScanFastPairStoreItem;
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_half_sheet);
- Bundle bundle = getIntent().getBundleExtra(EXTRA_BUNDLE);
- // Any app that has the component name can start the activity so the binder can't be
- // trusted.
- try {
- IFastPairHalfSheetCallback.Stub.asInterface(bundle.getBinder(EXTRA_BINDER))
- .onHalfSheetConnectionConfirm();
- } catch (RemoteException e) {
- Log.d("FastPairHalfSheet", "invoke callback fall");
+ byte[] infoArray = getIntent().getByteArrayExtra(EXTRA_HALF_SHEET_INFO);
+ String fragmentType = getIntent().getStringExtra(EXTRA_HALF_SHEET_TYPE);
+ if (infoArray == null || fragmentType == null) {
+ Log.d(
+ "HalfSheetActivity",
+ "exit flag off or do not have enough half sheet information.");
+ finish();
+ return;
}
+
+ switch (fragmentType) {
+ case DEVICE_PAIRING_FRAGMENT_TYPE:
+ mHalfSheetModuleFragment = DevicePairingFragment.newInstance(getIntent(),
+ savedInstanceState);
+ if (mHalfSheetModuleFragment == null) {
+ Log.d("HalfSheetActivity", "device pairing fragment has error.");
+ finish();
+ return;
+ }
+ break;
+ case APP_LAUNCH_FRAGMENT_TYPE:
+ // currentFragment = AppLaunchFragment.newInstance(getIntent());
+ if (mHalfSheetModuleFragment == null) {
+ Log.v("HalfSheetActivity", "app launch fragment has error.");
+ finish();
+ return;
+ }
+ break;
+ default:
+ Log.w("HalfSheetActivity", "there is no valid type for half sheet");
+ finish();
+ return;
+ }
+ if (mHalfSheetModuleFragment != null) {
+ getSupportFragmentManager()
+ .beginTransaction()
+ .replace(R.id.fragment_container, mHalfSheetModuleFragment)
+ .commit();
+ }
+ setContentView(R.layout.fast_pair_half_sheet);
+
+ // If the user taps on the background, then close the activity.
+ // Unless they tap on the card itself, then ignore the tap.
+ findViewById(R.id.background).setOnClickListener(v -> onCancelClicked());
+ findViewById(R.id.card)
+ .setOnClickListener(
+ v -> Log.v("HalfSheetActivity", "card view is clicked noop"));
+ try {
+ mScanFastPairStoreItem =
+ Cache.ScanFastPairStoreItem.parseFrom(infoArray);
+ } catch (InvalidProtocolBufferException e) {
+ Log.w(
+ "HalfSheetActivity", "error happens when pass info to half sheet");
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ BroadcastUtils.sendBroadcast(
+ this,
+ new Intent(ACTION_HALF_SHEET_FOREGROUND_STATE)
+ .putExtra(EXTRA_HALF_SHEET_FOREGROUND, true));
+ }
+
+ @Override
+ protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
+ super.onSaveInstanceState(savedInstanceState);
+ if (mHalfSheetModuleFragment != null) {
+ mHalfSheetModuleFragment.onSaveInstanceState(savedInstanceState);
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ super.onBackPressed();
+ sendHalfSheetCancelBroadcast();
+ }
+
+ @Override
+ protected void onUserLeaveHint() {
+ super.onUserLeaveHint();
+ sendHalfSheetCancelBroadcast();
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ String fragmentType = getIntent().getStringExtra(EXTRA_HALF_SHEET_TYPE);
+ if (fragmentType == null) {
+ return;
+ }
+ if (fragmentType.equals(DEVICE_PAIRING_FRAGMENT_TYPE)
+ && intent.getExtras() != null
+ && intent.getByteArrayExtra(EXTRA_HALF_SHEET_INFO) != null) {
+ try {
+ Cache.ScanFastPairStoreItem testScanFastPairStoreItem =
+ Cache.ScanFastPairStoreItem.parseFrom(
+ intent.getByteArrayExtra(EXTRA_HALF_SHEET_INFO));
+ if (mScanFastPairStoreItem != null
+ && !testScanFastPairStoreItem.getAddress().equals(
+ mScanFastPairStoreItem.getAddress())
+ && testScanFastPairStoreItem.getModelId().equals(
+ mScanFastPairStoreItem.getModelId())) {
+ Log.d("HalfSheetActivity", "possible factory reset happens");
+ halfSheetStateChange();
+ }
+ } catch (InvalidProtocolBufferException | NullPointerException e) {
+ Log.w("HalfSheetActivity", "error happens when pass info to half sheet");
+ }
+ }
+ }
+
+ /** This function should be called when user click empty area and cancel button. */
+ public void onCancelClicked() {
+ sendHalfSheetCancelBroadcast();
+ finish();
+ }
+
+ /** Changes the half sheet foreground state to false. */
+ public void halfSheetStateChange() {
+ BroadcastUtils.sendBroadcast(
+ this,
+ new Intent(ACTION_HALF_SHEET_FOREGROUND_STATE)
+ .putExtra(EXTRA_HALF_SHEET_FOREGROUND, false));
+ finish();
+ }
+
+ /**
+ * Change the half sheet ban state to active sometimes users leave half sheet to go to fast pair
+ * info page we do not want the behavior to be counted as dismiss.
+ */
+ public void sendBanStateResetBroadcast() {
+ if (mScanFastPairStoreItem != null) {
+ BroadcastUtils.sendBroadcast(
+ this,
+ new Intent(ACTION_FAST_PAIR_HALF_SHEET_BAN_STATE_RESET)
+ .putExtra(EXTRA_MODEL_ID,
+ mScanFastPairStoreItem.getModelId().toLowerCase(Locale.ROOT)));
+ }
+ }
+
+ private void sendHalfSheetCancelBroadcast() {
+ BroadcastUtils.sendBroadcast(
+ this,
+ new Intent(ACTION_HALF_SHEET_FOREGROUND_STATE)
+ .putExtra(EXTRA_HALF_SHEET_FOREGROUND, false));
+ if (mScanFastPairStoreItem != null) {
+ BroadcastUtils.sendBroadcast(
+ this,
+ new Intent(ACTION_FAST_PAIR_HALF_SHEET_CANCEL)
+ .putExtra(EXTRA_MODEL_ID,
+ mScanFastPairStoreItem.getModelId().toLowerCase(Locale.ROOT))
+ .putExtra(EXTRA_HALF_SHEET_TYPE,
+ getIntent().getStringExtra(EXTRA_HALF_SHEET_TYPE))
+ .putExtra(
+ EXTRA_HALF_SHEET_IS_SUBSEQUENT_PAIR,
+ getIntent().getBooleanExtra(EXTRA_HALF_SHEET_IS_SUBSEQUENT_PAIR,
+ false))
+ .putExtra(
+ EXTRA_HALF_SHEET_IS_RETROACTIVE,
+ getIntent().getBooleanExtra(EXTRA_HALF_SHEET_IS_RETROACTIVE,
+ false))
+ .putExtra(EXTRA_MAC_ADDRESS, mScanFastPairStoreItem.getAddress()));
+ }
+ }
+
+ @Nullable
+ @VisibleForTesting
+ public HalfSheetModuleFragment getFragmentModel() {
+ return mHalfSheetModuleFragment;
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ super.setTitle(title);
+ TextView toolbarTitle = findViewById(R.id.toolbar_title);
+ toolbarTitle.setText(title);
+ }
+
+
+ /**
+ * This method converts dp unit to equivalent pixels, depending on device density.
+ *
+ * @param dp A value in dp (density independent pixels) unit, which we need to convert into
+ * pixels
+ * @param context Context to get resources and device specific display metrics
+ * @return A float value to represent px equivalent to dp depending on device density
+ */
+ private float convertDpToPixel(float dp, Context context) {
+ return dp
+ * ((float) context.getResources().getDisplayMetrics().densityDpi
+ / DisplayMetrics.DENSITY_DEFAULT);
}
}
diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/DevicePairingFragment.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/DevicePairingFragment.java
new file mode 100644
index 0000000..c99c130
--- /dev/null
+++ b/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/DevicePairingFragment.java
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.nearby.halfsheet.fragment;
+
+import static android.text.TextUtils.isEmpty;
+
+import static com.android.nearby.halfsheet.HalfSheetActivity.ACTION_HALF_SHEET_STATUS_CHANGE;
+import static com.android.nearby.halfsheet.HalfSheetActivity.ARG_FRAGMENT_STATE;
+import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_CLASSIC_MAC_ADDRESS;
+import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_DESCRIPTION;
+import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_HALF_SHEET_ACCOUNT_NAME;
+import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_HALF_SHEET_CONTENT;
+import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_HALF_SHEET_ID;
+import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_HALF_SHEET_IS_RETROACTIVE;
+import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_HALF_SHEET_IS_SUBSEQUENT_PAIR;
+import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_HALF_SHEET_PAIRING_RESURFACE;
+import static com.android.nearby.halfsheet.HalfSheetActivity.EXTRA_TITLE;
+import static com.android.nearby.halfsheet.HalfSheetActivity.FINISHED_STATE;
+import static com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment.HalfSheetFragmentState.NOT_STARTED;
+import static com.android.nearby.halfsheet.fragment.HalfSheetModuleFragment.HalfSheetFragmentState.RESULT_FAILURE;
+import static com.android.server.nearby.common.bluetooth.fastpair.FastPairConstants.EXTRA_MODEL_ID;
+import static com.android.server.nearby.fastpair.Constant.DISMISS;
+import static com.android.server.nearby.fastpair.Constant.EXTRA_BINDER;
+import static com.android.server.nearby.fastpair.Constant.EXTRA_BUNDLE;
+import static com.android.server.nearby.fastpair.Constant.EXTRA_HALF_SHEET_INFO;
+import static com.android.server.nearby.fastpair.Constant.FAIL_STATE;
+import static com.android.server.nearby.fastpair.Constant.SUCCESS_STATE;
+import static com.android.server.nearby.fastpair.UserActionHandler.ACTION_FAST_PAIR;
+import static com.android.server.nearby.fastpair.UserActionHandler.EXTRA_PRIVATE_BLE_ADDRESS;
+
+import android.animation.AnimatorSet;
+import android.app.Activity;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.nearby.IFastPairHalfSheetCallback;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.nearby.halfsheet.HalfSheetActivity;
+import com.android.nearby.halfsheet.R;
+import com.android.nearby.halfsheet.utils.BroadcastUtils;
+import com.android.nearby.halfsheet.utils.FastPairUtils;
+import com.android.server.nearby.fastpair.UserActionHandler;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import service.proto.Cache.ScanFastPairStoreItem;
+
+/**
+ * Modularize half sheet for fast pair this fragment will show when half sheet does device pairing.
+ *
+ * <p>This fragment will handle initial pairing subsequent pairing and retroactive pairing.
+ */
+@SuppressWarnings("nullness")
+public class DevicePairingFragment extends HalfSheetModuleFragment {
+ private Button mConnectButton;
+ private Button mSetupButton;
+ private Button mCancelButton;
+ private ImageView mInfoIconButton;
+ private ProgressBar mConnectProgressBar;
+ private View mRootView;
+ private TextView mSubTitle;
+ private TextView mTitle;
+ private ImageView mImage;
+ private ScanFastPairStoreItem mScanFastPairStoreItem;
+ // This open companion app intent will be triggered after user finish Fast Pair.
+ private Intent mOpenCompanionAppIntent;
+ // Indicates that the setup button is clicked before.
+ private boolean mSetupButtonClicked = false;
+ private boolean mIsSubsequentPair = false;
+ private boolean mIsPairingResurface = false;
+ private String mBluetoothMacAddress = "";
+ private HalfSheetFragmentState mFragmentState = NOT_STARTED;
+ private AnimatorSet mAnimatorSet = new AnimatorSet();
+ // True means pairing was successful and false means failed.
+ private Boolean mPairingResult = false;
+ private Bundle mBundle;
+
+ public static final String APP_LAUNCH_FRAGMENT_TYPE = "APP_LAUNCH";
+ public static final String FAST_PAIR_CONSENT_FRAGMENT_TYPE = "FAST_PAIR_CONSENT";
+ private static final String ARG_SETUP_BUTTON_CLICKED = "SETUP_BUTTON_CLICKED";
+ public static final String RESULT_FAIL = "RESULT_FAIL";
+ private static final String ARG_PAIRING_RESULT = "PAIRING_RESULT";
+
+
+ /**
+ * Create certain fragment according to the intent.
+ */
+ @Nullable
+ public static HalfSheetModuleFragment newInstance(
+ Intent intent, @Nullable Bundle saveInstanceStates) {
+ Bundle args = new Bundle();
+ byte[] infoArray = intent.getByteArrayExtra(EXTRA_HALF_SHEET_INFO);
+ boolean isRetroactive = intent.getBooleanExtra(EXTRA_HALF_SHEET_IS_RETROACTIVE, false);
+ boolean isSubsequentPair = intent.getBooleanExtra(EXTRA_HALF_SHEET_IS_SUBSEQUENT_PAIR,
+ false);
+ boolean isPairingResurface = intent.getBooleanExtra(EXTRA_HALF_SHEET_PAIRING_RESURFACE,
+ false);
+ Bundle bundle = intent.getBundleExtra(EXTRA_BUNDLE);
+ String title = intent.getStringExtra(EXTRA_TITLE);
+ String description = intent.getStringExtra(EXTRA_DESCRIPTION);
+ String accountName = intent.getStringExtra(EXTRA_HALF_SHEET_ACCOUNT_NAME);
+ String result = intent.getStringExtra(EXTRA_HALF_SHEET_CONTENT);
+ String publicAddress = intent.getStringExtra(EXTRA_CLASSIC_MAC_ADDRESS);
+ int halfSheetId = intent.getIntExtra(EXTRA_HALF_SHEET_ID, 0);
+
+ args.putByteArray(EXTRA_HALF_SHEET_INFO, infoArray);
+ args.putBoolean(EXTRA_HALF_SHEET_IS_RETROACTIVE, isRetroactive);
+ args.putBoolean(EXTRA_HALF_SHEET_IS_SUBSEQUENT_PAIR, isSubsequentPair);
+ args.putBoolean(EXTRA_HALF_SHEET_PAIRING_RESURFACE, isPairingResurface);
+ args.putString(EXTRA_HALF_SHEET_ACCOUNT_NAME, accountName);
+ args.putString(EXTRA_TITLE, title);
+ args.putString(EXTRA_DESCRIPTION, description);
+ args.putInt(EXTRA_HALF_SHEET_ID, halfSheetId);
+ args.putString(EXTRA_HALF_SHEET_CONTENT, result == null ? "" : result);
+ args.putBundle(EXTRA_BUNDLE, bundle);
+ if (saveInstanceStates != null) {
+ if (saveInstanceStates.containsKey(ARG_FRAGMENT_STATE)) {
+ args.putSerializable(
+ ARG_FRAGMENT_STATE, saveInstanceStates.getSerializable(ARG_FRAGMENT_STATE));
+ }
+ if (saveInstanceStates.containsKey(BluetoothDevice.EXTRA_DEVICE)) {
+ args.putParcelable(
+ BluetoothDevice.EXTRA_DEVICE,
+ saveInstanceStates.getParcelable(BluetoothDevice.EXTRA_DEVICE));
+ }
+ if (saveInstanceStates.containsKey(BluetoothDevice.EXTRA_PAIRING_KEY)) {
+ args.putInt(
+ BluetoothDevice.EXTRA_PAIRING_KEY,
+ saveInstanceStates.getInt(BluetoothDevice.EXTRA_PAIRING_KEY));
+ }
+ if (saveInstanceStates.containsKey(ARG_SETUP_BUTTON_CLICKED)) {
+ args.putBoolean(
+ ARG_SETUP_BUTTON_CLICKED,
+ saveInstanceStates.getBoolean(ARG_SETUP_BUTTON_CLICKED));
+ }
+ if (saveInstanceStates.containsKey(ARG_PAIRING_RESULT)) {
+ args.putBoolean(ARG_PAIRING_RESULT,
+ saveInstanceStates.getBoolean(ARG_PAIRING_RESULT));
+ }
+ }
+ DevicePairingFragment fragment = new DevicePairingFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ mRootView =
+ inflater.inflate(
+ R.layout.fast_pair_device_pairing_fragment, container, /* attachToRoot= */
+ false);
+ if (getContext() == null) {
+ Log.d("DevicePairingFragment", "can't find the attached activity");
+ return mRootView;
+ }
+ Bundle args = getArguments();
+ byte[] storeFastPairItemBytesArray = args.getByteArray(EXTRA_HALF_SHEET_INFO);
+ boolean isRetroactive = args.getBoolean(EXTRA_HALF_SHEET_IS_RETROACTIVE);
+ mIsSubsequentPair = args.getBoolean(EXTRA_HALF_SHEET_IS_SUBSEQUENT_PAIR);
+ mIsPairingResurface = args.getBoolean(EXTRA_HALF_SHEET_PAIRING_RESURFACE);
+ String accountName = args.getString(EXTRA_HALF_SHEET_ACCOUNT_NAME);
+ mBundle = args.getBundle(EXTRA_BUNDLE);
+ if (args.containsKey(ARG_FRAGMENT_STATE)) {
+ mFragmentState = (HalfSheetFragmentState) args.getSerializable(ARG_FRAGMENT_STATE);
+ }
+ if (args.containsKey(ARG_SETUP_BUTTON_CLICKED)) {
+ mSetupButtonClicked = args.getBoolean(ARG_SETUP_BUTTON_CLICKED);
+ }
+ if (args.containsKey(ARG_PAIRING_RESULT)) {
+ mPairingResult = args.getBoolean(ARG_PAIRING_RESULT);
+ } else {
+ mPairingResult = false;
+ }
+
+ // title = ((FragmentActivity) getContext()).findViewById(R.id.toolbar_title);
+ mConnectButton = mRootView.findViewById(R.id.connect_btn);
+ mImage = mRootView.findViewById(R.id.pairing_pic);
+
+ mConnectProgressBar = mRootView.findViewById(R.id.connect_progressbar);
+ mConnectProgressBar.setVisibility(View.INVISIBLE);
+
+ DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
+ if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ mRootView.getLayoutParams().height = displayMetrics.heightPixels * 4 / 5;
+ mRootView.getLayoutParams().width = displayMetrics.heightPixels * 4 / 5;
+ mImage.getLayoutParams().height = displayMetrics.heightPixels / 2;
+ mImage.getLayoutParams().width = displayMetrics.heightPixels / 2;
+ mConnectProgressBar.getLayoutParams().width = displayMetrics.heightPixels / 2;
+ mConnectButton.getLayoutParams().width = displayMetrics.heightPixels / 2;
+ }
+
+ mCancelButton = mRootView.findViewById(R.id.cancel_btn);
+ mSetupButton = mRootView.findViewById(R.id.setup_btn);
+ mInfoIconButton = mRootView.findViewById(R.id.info_icon);
+ mSubTitle = mRootView.findViewById(R.id.header_subtitle);
+ mSetupButton.setVisibility(View.GONE);
+ mInfoIconButton.setVisibility(View.GONE);
+
+ try {
+ if (storeFastPairItemBytesArray != null) {
+ mScanFastPairStoreItem =
+ ScanFastPairStoreItem.parseFrom(storeFastPairItemBytesArray);
+ }
+
+ // If the fragmentState is not NOT_STARTED, it is because the fragment was just
+ // resumed from
+ // configuration change (e.g. rotating the screen or half-sheet resurface). Let's
+ // recover the
+ // UI directly.
+ if (mFragmentState != NOT_STARTED) {
+ switch (mFragmentState) {
+ case PAIRING:
+ Log.d("DevicePairingFragment", "redraw for PAIRING state.");
+ return mRootView;
+ case RESULT_SUCCESS:
+ Log.d("DevicePairingFragment", "redraw for RESULT_SUCCESS state.");
+ return mRootView;
+ case RESULT_FAILURE:
+ Log.d("DevicePairingFragment", "redraw for RESULT_FAILURE state.");
+ return mRootView;
+ default:
+ // fall-out
+ Log.d("DevicePairingFragment",
+ "DevicePairingFragment: not supported state");
+ }
+ }
+ if (mIsPairingResurface) {
+ // Since the Settings contextual card has sent the pairing intent, we don't send the
+ // pairing intent here.
+ onConnectClick(/* sendPairingIntent= */ false);
+ } else {
+ mSubTitle.setText(this.getArguments().getString(EXTRA_DESCRIPTION));
+ mSubTitle.setText("");
+ mConnectButton.setOnClickListener(
+ v -> onConnectClick(/* sendPairingIntent= */ true));
+ // Pairing fail half sheet resurface
+ if (this.getArguments().getString(EXTRA_HALF_SHEET_CONTENT).equals(RESULT_FAIL)) {
+ mFragmentState = RESULT_FAILURE;
+ showFailInfo();
+ } else {
+ mConnectButton.setOnClickListener(
+ v -> onConnectClick(/* sendPairingIntent= */ true));
+ }
+ }
+ } catch (InvalidProtocolBufferException e) {
+ Log.w("DevicePairingFragment",
+ "DevicePairingFragment: error happens when pass info to half sheet");
+ }
+ return mRootView;
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // Get access to the activity's menu
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (getContext() != null) {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ACTION_HALF_SHEET_STATUS_CHANGE);
+ BroadcastUtils.registerReceiver(getContext(), mHalfSheetChangeReceiver, intentFilter);
+ }
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (getContext() != null) {
+ BroadcastUtils.unregisterReceiver(getContext(), mHalfSheetChangeReceiver);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle savedInstanceState) {
+ super.onSaveInstanceState(savedInstanceState);
+
+ savedInstanceState.putSerializable(ARG_FRAGMENT_STATE, mFragmentState);
+ savedInstanceState.putBoolean(ARG_SETUP_BUTTON_CLICKED, mSetupButtonClicked);
+ savedInstanceState.putBoolean(ARG_PAIRING_RESULT, mPairingResult);
+
+
+ }
+
+ @Nullable
+ private Intent createCompletionIntent(@Nullable String companionApp, @Nullable String address) {
+ if (isEmpty(companionApp)) {
+ return null;
+ } else if (FastPairUtils.isAppInstalled(companionApp, getContext())
+ && isLaunchable(companionApp)) {
+ mOpenCompanionAppIntent = createCompanionAppIntent(companionApp, address);
+ return mOpenCompanionAppIntent;
+ } else {
+ return null;
+ }
+ }
+
+ @Nullable
+ private Intent createCompanionAppIntent(String packageName, @Nullable String address) {
+ return createCompanionAppIntent(getContext(), packageName, address);
+ }
+
+ @Nullable
+ private static Intent createCompanionAppIntent(
+ Context context, String packageName, @Nullable String address) {
+ Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName);
+ BluetoothManager manager = context.getSystemService(BluetoothManager.class);
+ if (address != null && manager != null) {
+ BluetoothAdapter adapter = manager.getAdapter();
+ if (intent != null && adapter != null) {
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, adapter.getRemoteDevice(address));
+ }
+ }
+ return intent;
+ }
+
+ private void onConnectClick(boolean sendPairingIntent) {
+ if (mScanFastPairStoreItem == null) {
+ Log.w("DevicePairingFragment", "No pairing related information in half sheet");
+ return;
+ }
+
+ Log.d("FastPairHalfSheet", "on connect click");
+ // Allow user to setup device before connection setup.
+ // showPairingLastPhase();
+ ((Activity) getContext())
+ .findViewById(R.id.background)
+ .setOnClickListener(
+ v ->
+ Log.d("DevicePairingFragment",
+ "DevicePairingFragment: tap empty area do not dismiss "
+ + "half sheet when pairing."));
+ if (sendPairingIntent) {
+ try {
+ Log.d("FastPairHalfSheet", "on connect click");
+ Intent intent =
+ new Intent(ACTION_FAST_PAIR)
+ // Using the DiscoveryChimeraService notification id for
+ // backwards compat
+ .putExtra(
+ UserActionHandler.EXTRA_DISCOVERY_ITEM,
+ FastPairUtils.convertFrom(
+ mScanFastPairStoreItem).toByteArray())
+ .putExtra(EXTRA_MODEL_ID, mScanFastPairStoreItem.getModelId())
+ .putExtra(EXTRA_PRIVATE_BLE_ADDRESS,
+ mScanFastPairStoreItem.getAddress());
+ IFastPairHalfSheetCallback.Stub.asInterface(mBundle.getBinder(EXTRA_BINDER))
+ .onHalfSheetConnectionConfirm(intent);
+ } catch (RemoteException e) {
+ Log.d("FastPairHalfSheet", "invoke callback fall");
+ }
+ }
+ }
+
+ private void onFailConnectClick() {
+ startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
+ }
+
+ private final BroadcastReceiver mHalfSheetChangeReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!ACTION_HALF_SHEET_STATUS_CHANGE.equals(intent.getAction())) {
+ return;
+ }
+ if (SUCCESS_STATE.equals(intent.getStringExtra(FINISHED_STATE))) {
+ mBluetoothMacAddress = intent.getStringExtra(EXTRA_CLASSIC_MAC_ADDRESS);
+ showSuccessInfo();
+ if (mOpenCompanionAppIntent != null) {
+ //((HalfSheetActivity) getContext()).halfSheetStateChange();
+ String companionApp =
+ FastPairUtils.getCompanionAppFromActionUrl(
+ mScanFastPairStoreItem.getActionUrl());
+ // Redirect user to companion app if user choose to setup the app.
+ // Recreate the intent
+ // since the correct mac address just populated.
+ startActivity(
+ createCompletionIntent(companionApp, mBluetoothMacAddress));
+ }
+ } else if (FAIL_STATE.equals(intent.getStringExtra(FINISHED_STATE))) {
+ showFailInfo();
+ } else if (DISMISS.equals(intent.getStringExtra(FINISHED_STATE))) {
+ if (getContext() != null) {
+ HalfSheetActivity activity = (HalfSheetActivity) getContext();
+ activity.finish();
+ }
+ }
+ }
+ };
+
+ private boolean isLaunchable(String companionApp) {
+ return createCompanionAppIntent(companionApp, null) != null;
+ }
+}
diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/HalfSheetModuleFragment.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/HalfSheetModuleFragment.java
new file mode 100644
index 0000000..88caf95
--- /dev/null
+++ b/nearby/halfsheet/src/com/android/nearby/halfsheet/fragment/HalfSheetModuleFragment.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.nearby.halfsheet.fragment;
+
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+
+/** Base class for all of the half sheet fragment. */
+// TODO(b/177675274): Resolve nullness suppression.
+@SuppressWarnings("nullness")
+public abstract class HalfSheetModuleFragment extends Fragment {
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ /** UI states of the half-sheet fragment. */
+ public enum HalfSheetFragmentState {
+ NOT_STARTED,
+ SYNC_CONTACTS,
+ SYNC_SMS,
+ PROGRESSING,
+ CONFIRM_PASSKEY,
+ WRONG_PASSKEY,
+ PAIRING,
+ ADDITIONAL_SETUP_PROGRESS,
+ ADDITIONAL_SETUP_FINAL,
+ RESULT_SUCCESS,
+ RESULT_FAILURE,
+ FINISHED
+ }
+
+ /** Only used in {@link DevicePairingFragment} show pairing success info in half sheet. */
+ public void showSuccessInfo() {
+ }
+
+ /** Only used in {@link DevicePairingFragment} show pairing fail info in half sheet. */
+ public void showFailInfo() {
+ }
+
+ /**
+ * Returns the {@link HalfSheetFragmentState} to the parent activity.
+ *
+ * <p>Overrides this method if the fragment's state needs to be preserved in the parent
+ * activity.
+ */
+ public HalfSheetFragmentState getFragmentState() {
+ return HalfSheetFragmentState.NOT_STARTED;
+ }
+}
diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/BroadcastUtils.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/BroadcastUtils.java
new file mode 100644
index 0000000..cae2d8e
--- /dev/null
+++ b/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/BroadcastUtils.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.nearby.halfsheet.utils;
+
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+/**
+ * Broadcast util class
+ */
+public class BroadcastUtils {
+ /**
+ * Registers a list of broadcast receiver.
+ */
+ public static void registerReceiver(
+ Context context, BroadcastReceiver receiver, IntentFilter intentFilter) {
+ context.registerReceiver(receiver, intentFilter);
+ }
+
+ /**
+ * Unregisters the already registered receiver.
+ */
+ public static void unregisterReceiver(Context context, BroadcastReceiver receiver) {
+ context.unregisterReceiver(receiver);
+ }
+
+ /**
+ * Helps send broadcast.
+ */
+ public static void sendBroadcast(Context context, Intent intent) {
+ context.sendBroadcast(intent);
+ }
+
+ private BroadcastUtils() {
+ }
+}
diff --git a/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/FastPairUtils.java b/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/FastPairUtils.java
new file mode 100644
index 0000000..00cd11d
--- /dev/null
+++ b/nearby/halfsheet/src/com/android/nearby/halfsheet/utils/FastPairUtils.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.nearby.halfsheet.utils;
+
+import static com.android.server.nearby.common.fastpair.service.UserActionHandlerBase.EXTRA_COMPANION_APP;
+import static com.android.server.nearby.fastpair.UserActionHandler.ACTION_FAST_PAIR;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import java.net.URISyntaxException;
+
+import service.proto.Cache;
+
+/**
+ * Util class in half sheet apk
+ */
+public class FastPairUtils {
+
+ /** FastPair util method check certain app is install on the device or not. */
+ public static boolean isAppInstalled(String packageName, Context context) {
+ try {
+ context.getPackageManager().getPackageInfo(packageName, 0);
+ return true;
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ /** FastPair util method to properly format the action url extra. */
+ @Nullable
+ public static String getCompanionAppFromActionUrl(String actionUrl) {
+ try {
+ Intent intent = Intent.parseUri(actionUrl, Intent.URI_INTENT_SCHEME);
+ if (!intent.getAction().equals(ACTION_FAST_PAIR)) {
+ Log.e("FastPairUtils", "Companion app launch attempted from malformed action url");
+ return null;
+ }
+ return intent.getStringExtra(EXTRA_COMPANION_APP);
+ } catch (URISyntaxException e) {
+ Log.e("FastPairUtils", "FastPair: fail to get companion app info from discovery item");
+ return null;
+ }
+ }
+
+ /**
+ * Converts {@link service.proto.Cache.StoredDiscoveryItem} from
+ * {@link service.proto.Cache.ScanFastPairStoreItem}
+ */
+ public static Cache.StoredDiscoveryItem convertFrom(Cache.ScanFastPairStoreItem item) {
+ return convertFrom(item, /* isSubsequentPair= */ false);
+ }
+
+ /**
+ * Converts a {@link ScanFastPairStoreItem} to a {@link StoredDiscoveryItem}.
+ *
+ * <p>This is needed to make the new Fast Pair scanning stack compatible with the rest of the
+ * legacy Fast Pair code.
+ */
+ public static Cache.StoredDiscoveryItem convertFrom(
+ Cache.ScanFastPairStoreItem item, boolean isSubsequentPair) {
+ return Cache.StoredDiscoveryItem.newBuilder()
+ .setId(item.getModelId())
+ .setFirstObservationTimestampMillis(item.getFirstObservationTimestampMillis())
+ .setLastObservationTimestampMillis(item.getLastObservationTimestampMillis())
+ .setType(Cache.NearbyType.NEARBY_DEVICE)
+ .setActionUrl(item.getActionUrl())
+ .setActionUrlType(Cache.ResolvedUrlType.APP)
+ .setTitle(
+ isSubsequentPair
+ ? item.getFastPairStrings().getTapToPairWithoutAccount()
+ : item.getDeviceName())
+ .setMacAddress(item.getAddress())
+ .setState(Cache.StoredDiscoveryItem.State.STATE_ENABLED)
+ .setTriggerId(item.getModelId())
+ .setIconPng(item.getIconPng())
+ .setIconFifeUrl(item.getIconFifeUrl())
+ .setDescription(
+ isSubsequentPair
+ ? item.getDeviceName()
+ : item.getFastPairStrings().getTapToPairWithoutAccount())
+ .setAuthenticationPublicKeySecp256R1(item.getAntiSpoofingPublicKey())
+ .setCompanionDetail(item.getCompanionDetail())
+ .setFastPairStrings(item.getFastPairStrings())
+ .setFastPairInformation(
+ Cache.FastPairInformation.newBuilder()
+ .setDataOnlyConnection(item.getDataOnlyConnection())
+ .setTrueWirelessImages(item.getTrueWirelessImages())
+ .setAssistantSupported(item.getAssistantSupported())
+ .setCompanyName(item.getCompanyName()))
+ .build();
+ }
+
+ private FastPairUtils() {
+
+ }
+}
diff --git a/nearby/service/Android.bp b/nearby/service/Android.bp
index 63e2748..b80d677 100644
--- a/nearby/service/Android.bp
+++ b/nearby/service/Android.bp
@@ -27,6 +27,9 @@
name: "nearby-service-string-res",
srcs: [
"java/**/Constant.java",
+ "java/**/UserActionHandlerBase.java",
+ "java/**/UserActionHandler.java",
+ "java/**/FastPairConstants.java",
],
}
@@ -55,6 +58,7 @@
static_libs: [
"androidx.annotation_annotation",
"androidx.core_core",
+ "androidx.localbroadcastmanager_localbroadcastmanager",
"guava",
"libprotobuf-java-lite",
"fast-pair-lite-protos",
diff --git a/nearby/service/java/com/android/server/nearby/common/fastpair/service/UserActionHandlerBase.java b/nearby/service/java/com/android/server/nearby/common/fastpair/service/UserActionHandlerBase.java
index c50e219..67d87e3 100644
--- a/nearby/service/java/com/android/server/nearby/common/fastpair/service/UserActionHandlerBase.java
+++ b/nearby/service/java/com/android/server/nearby/common/fastpair/service/UserActionHandlerBase.java
@@ -23,6 +23,7 @@
public static final String EXTRA_ITEM_ID = PREFIX + "EXTRA_ITEM_ID";
public static final String EXTRA_COMPANION_APP = ACTION_PREFIX + "EXTRA_COMPANION_APP";
+ public static final String EXTRA_MAC_ADDRESS = PREFIX + "EXTRA_MAC_ADDRESS";
}
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/Constant.java b/nearby/service/java/com/android/server/nearby/fastpair/Constant.java
index bf73360..1477d95 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/Constant.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/Constant.java
@@ -22,4 +22,22 @@
public class Constant {
public static final String EXTRA_BINDER = "com.android.server.nearby.fastpair.BINDER";
public static final String EXTRA_BUNDLE = "com.android.server.nearby.fastpair.BUNDLE_EXTRA";
+ public static final String SUCCESS_STATE = "SUCCESS";
+ public static final String FAIL_STATE = "FAIL";
+ public static final String DISMISS = "DISMISS";
+ public static final String NEED_CONFIRM_PASSKEY = "NEED CONFIRM PASSKEY";
+ // device support assistant additional setup
+ public static final String NEED_ADDITIONAL_SETUP = "NEED ADDITIONAL SETUP";
+ public static final String SHOW_PAIRING_WITHOUT_INTERACTION =
+ "SHOW_PAIRING_WITHOUT_INTERACTION";
+ public static final String ACTION_FAST_PAIR_HALF_SHEET_CANCEL =
+ "com.android.nearby.ACTION_FAST_PAIR_HALF_SHEET_CANCEL";
+ public static final String ACTION_FAST_PAIR_HALF_SHEET_BAN_STATE_RESET =
+ "com.android.nearby.ACTION_FAST_PAIR_BAN_STATE_RESET";
+ public static final String EXTRA_HALF_SHEET_INFO =
+ "com.android.nearby.halfsheet.HALF_SHEET";
+ public static final String EXTRA_HALF_SHEET_TYPE =
+ "com.android.nearby.halfsheet.HALF_SHEET_TYPE";
+ public static final String DEVICE_PAIRING_FRAGMENT_TYPE = "DEVICE_PAIRING";
+
}
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java b/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java
index 56650ce..247339e 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/FastPairManager.java
@@ -16,8 +16,12 @@
package com.android.server.nearby.fastpair;
+
+import static com.android.server.nearby.fastpair.Constant.DEVICE_PAIRING_FRAGMENT_TYPE;
import static com.android.server.nearby.fastpair.Constant.EXTRA_BINDER;
import static com.android.server.nearby.fastpair.Constant.EXTRA_BUNDLE;
+import static com.android.server.nearby.fastpair.Constant.EXTRA_HALF_SHEET_INFO;
+import static com.android.server.nearby.fastpair.Constant.EXTRA_HALF_SHEET_TYPE;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
@@ -35,6 +39,7 @@
import android.nearby.ScanCallback;
import android.os.Bundle;
import android.os.UserHandle;
+import android.util.Base64;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -58,6 +63,8 @@
import com.android.server.nearby.fastpair.pairinghandler.PairingProgressHandlerBase;
import com.android.server.nearby.util.Environment;
+import com.google.protobuf.ByteString;
+
import java.security.GeneralSecurityException;
import java.util.List;
import java.util.concurrent.ExecutionException;
@@ -69,6 +76,7 @@
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
+import service.proto.Cache;
import service.proto.Rpcs;
/**
@@ -80,9 +88,12 @@
private static final String ACTION_PREFIX = UserActionHandler.PREFIX;
private static final int WAIT_FOR_UNLOCK_MILLIS = 5000;
private static final String ACTIVITY_INTENT_ACTION = "android.nearby.SHOW_HALFSHEET";
- /** A notification ID which should be dismissed*/
+ /** A notification ID which should be dismissed */
public static final String EXTRA_NOTIFICATION_ID = ACTION_PREFIX + "EXTRA_NOTIFICATION_ID";
public static final String ACTION_RESOURCES_APK = "android.nearby.SHOW_HALFSHEET";
+ // Temp action deleted when the scanner is ready
+ public static final String ACTION_START_PAIRING = "NEARBY_START_PAIRING";
+ public static final String EXTRA_ADDRESS = "ADDRESS";
private static Executor sFastPairExecutor;
@@ -96,6 +107,17 @@
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
Log.d("FastPairService", " screen on");
+ } else if (intent.getAction().equals(ACTION_START_PAIRING)) {
+ String address = intent.getStringExtra(EXTRA_ADDRESS);
+ String testPublicKey =
+ "E4kxROcAj2SPlqrxHgXOm_yF-XIMSS91pFrfmeLcxULWyn0nK8i52PPkoC4r"
+ + "LKRM2kZzNgT8bhDwK5njJAjiOg";
+ showHalfSheet(Cache.ScanFastPairStoreItem.newBuilder().setAddress(address)
+ .setAntiSpoofingPublicKey(ByteString.copyFrom(
+ Base64.decode(testPublicKey, Base64.URL_SAFE
+ | Base64.NO_WRAP | Base64.NO_PADDING)))
+ .build());
+ Log.d("FastPairService", "start pair " + address);
} else {
Log.d("FastPairService", " screen off");
}
@@ -128,12 +150,14 @@
}
};
+
/**
* Function called when nearby service start.
*/
public void initiate() {
mIntentFilter.addAction(Intent.ACTION_SCREEN_ON);
mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ mIntentFilter.addAction("NEARBY_START_PAIRING");
mLocatorContextWrapper.getContext()
.registerReceiver(mScreenBroadcastReceiver, mIntentFilter);
@@ -228,7 +252,9 @@
item.getFastPairInformation().getDataOnlyConnection());
}
FastPairConnection connection = new FastPairDualConnection(
- context, item.getMacAddress(), prefsBuilder.build(), null);
+ context, item.getMacAddress(),
+ // Change to prefsBuilder.build when the api integration complete
+ Preferences.builderFromGmsLog().build(), null);
pairingProgressHandlerBase.onPairingSetupCompleted();
FastPairConnection.SharedSecret sharedSecret;
@@ -239,7 +265,7 @@
: item.getAuthenticationPublicKeySecp256R1());
byte[] key = pairingProgressHandlerBase.getKeyForLocalCache(accountKey,
- connection, sharedSecret);
+ connection, sharedSecret);
// We don't cache initial pairing case here but cache it when upload to footprints.
if (key != null) {
@@ -308,7 +334,8 @@
return manager == null ? null : manager.getAdapter();
}
- /** Gets the package name of HalfSheet.apk
+ /**
+ * Gets the package name of HalfSheet.apk
* getHalfSheetApkPkgName may invoke PackageManager multiple times and it does not have
* race condition check. Since there is no lock for mHalfSheetApkPkgName.
*/
@@ -318,8 +345,8 @@
}
List<ResolveInfo> resolveInfos = mLocatorContextWrapper.getContext()
.getPackageManager().queryIntentActivities(
- new Intent(ACTION_RESOURCES_APK),
- PackageManager.MATCH_SYSTEM_ONLY);
+ new Intent(ACTION_RESOURCES_APK),
+ PackageManager.MATCH_SYSTEM_ONLY);
// remove apps that don't live in the nearby apex
resolveInfos.removeIf(info ->
@@ -352,14 +379,19 @@
* Invokes half sheet in the other apk. This function can only be called in Nearby because other
* app can't get the correct component name.
*/
- void showHalfSheet() {
+ void showHalfSheet(Cache.ScanFastPairStoreItem scanFastPairStoreItem) {
if (mLocatorContextWrapper != null) {
String packageName = getHalfSheetApkPkgName();
HalfSheetCallback callback = new HalfSheetCallback();
+ callback.setmFastPairController(Locator
+ .getFromContextWrapper(mLocatorContextWrapper, FastPairController.class));
Bundle bundle = new Bundle();
bundle.putBinder(EXTRA_BINDER, callback);
mLocatorContextWrapper.getContext()
.startActivityAsUser(new Intent(ACTIVITY_INTENT_ACTION)
+ .putExtra(EXTRA_HALF_SHEET_INFO,
+ scanFastPairStoreItem.toByteArray())
+ .putExtra(EXTRA_HALF_SHEET_TYPE, DEVICE_PAIRING_FRAGMENT_TYPE)
.putExtra(EXTRA_BUNDLE, bundle)
.setComponent(new ComponentName(packageName,
packageName + ".HalfSheetActivity")),
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/FastPairModule.java b/nearby/service/java/com/android/server/nearby/fastpair/FastPairModule.java
index 5444e82..e92bb8a 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/FastPairModule.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/FastPairModule.java
@@ -18,9 +18,12 @@
import android.content.Context;
+import com.android.server.nearby.common.eventloop.EventLoop;
import com.android.server.nearby.common.locator.Locator;
import com.android.server.nearby.common.locator.Module;
import com.android.server.nearby.fastpair.cache.FastPairCacheManager;
+import com.android.server.nearby.fastpair.footprint.FootprintsDeviceManager;
+import com.android.server.nearby.fastpair.halfsheet.FastPairHalfSheetManager;
import java.time.Clock;
import java.time.Instant;
@@ -37,10 +40,16 @@
public void configure(Context context, Class<?> type, Locator locator) {
if (type.equals(FastPairCacheManager.class)) {
locator.bind(FastPairCacheManager.class, new FastPairCacheManager(context));
+ } else if (type.equals(FootprintsDeviceManager.class)) {
+ locator.bind(FootprintsDeviceManager.class, new FootprintsDeviceManager());
+ } else if (type.equals(EventLoop.class)) {
+ locator.bind(EventLoop.class, EventLoop.newInstance("NearbyFastPair"));
} else if (type.equals(FastPairController.class)) {
locator.bind(FastPairController.class, new FastPairController(context));
} else if (type.equals(FastPairCacheManager.class)) {
locator.bind(FastPairCacheManager.class, new FastPairCacheManager(context));
+ } else if (type.equals(FastPairHalfSheetManager.class)) {
+ locator.bind(FastPairHalfSheetManager.class, new FastPairHalfSheetManager());
} else if (type.equals(Clock.class)) {
locator.bind(Clock.class, new Clock() {
@Override
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/UserActionHandler.java b/nearby/service/java/com/android/server/nearby/fastpair/UserActionHandler.java
index f2b0d59..674633d 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/UserActionHandler.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/UserActionHandler.java
@@ -25,4 +25,7 @@
public static final String EXTRA_DISCOVERY_ITEM = PREFIX + "EXTRA_DISCOVERY_ITEM";
public static final String EXTRA_FAST_PAIR_SECRET = PREFIX + "EXTRA_FAST_PAIR_SECRET";
+ public static final String ACTION_FAST_PAIR = ACTION_PREFIX + "ACTION_FAST_PAIR";
+ public static final String EXTRA_PRIVATE_BLE_ADDRESS =
+ ACTION_PREFIX + "EXTRA_PRIVATE_BLE_ADDRESS";
}
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/FastPairHalfSheetManager.java b/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/FastPairHalfSheetManager.java
index d59305b..4c43965 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/FastPairHalfSheetManager.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/FastPairHalfSheetManager.java
@@ -17,6 +17,7 @@
package com.android.server.nearby.fastpair.halfsheet;
import android.bluetooth.BluetoothDevice;
+import android.util.Log;
import com.android.server.nearby.fastpair.cache.DiscoveryItem;
@@ -29,7 +30,7 @@
* Shows pairing fail half sheet.
*/
public void showPairingFailed() {
-
+ Log.d("FastPairHalfSheetManager", "show fail half sheet");
}
/**
@@ -42,30 +43,38 @@
/**
* Show passkey confirmation info on half sheet
*/
- public void showPasskeyConfirmation(BluetoothDevice device, int passkey) {}
+ public void showPasskeyConfirmation(BluetoothDevice device, int passkey) {
+ }
/**
* This function will handle pairing steps for half sheet.
*/
- public void showPairingHalfSheet(DiscoveryItem item) {}
+ public void showPairingHalfSheet(DiscoveryItem item) {
+ Log.d("FastPairHalfSheetManager", "show pairing half sheet");
+ }
/**
* Shows pairing success info.
*/
- public void showPairingSuccessHalfSheet(String address){}
+ public void showPairingSuccessHalfSheet(String address) {
+ Log.d("FastPairHalfSheetManager", "show success half sheet");
+ }
/**
* Removes dismiss runnable.
*/
- public void disableDismissRunnable(){}
+ public void disableDismissRunnable() {
+ }
/**
* Destroys the bluetooth pairing controller.
*/
- public void destroyBluetoothPairController(){}
+ public void destroyBluetoothPairController() {
+ }
/**
* Notify manager the pairing has finished.
*/
- public void notifyPairingProcessDone(boolean success, String address, DiscoveryItem item) {}
+ public void notifyPairingProcessDone(boolean success, String address, DiscoveryItem item) {
+ }
}
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/HalfSheetCallback.java b/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/HalfSheetCallback.java
index fd9e460..2c792ed 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/HalfSheetCallback.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/halfsheet/HalfSheetCallback.java
@@ -16,15 +16,37 @@
package com.android.server.nearby.fastpair.halfsheet;
+import android.content.Intent;
import android.nearby.IFastPairHalfSheetCallback;
import android.util.Log;
+import com.android.server.nearby.fastpair.FastPairController;
+
+
/**
* Callback to send ux action back to nearby service.
*/
-public class HalfSheetCallback extends IFastPairHalfSheetCallback.Stub {
+public class HalfSheetCallback extends IFastPairHalfSheetCallback.Stub {
+ private FastPairController mFastPairController;
+
+ public HalfSheetCallback() {
+ }
+
+ /**
+ * Set function for Fast Pair controller.
+ */
+ public void setmFastPairController(FastPairController fastPairController) {
+ mFastPairController = fastPairController;
+ }
+
+ /**
+ * Half Sheet connection button clicked.
+ */
@Override
- public void onHalfSheetConnectionConfirm() {
+ public void onHalfSheetConnectionConfirm(Intent intent) {
Log.d("FastPairHalfSheet", "Call back receiver");
+ if (mFastPairController != null) {
+ mFastPairController.pair(intent);
+ }
}
}
diff --git a/nearby/service/java/com/android/server/nearby/fastpair/pairinghandler/PairingProgressHandlerBase.java b/nearby/service/java/com/android/server/nearby/fastpair/pairinghandler/PairingProgressHandlerBase.java
index 760b4e0..ccd7e5e 100644
--- a/nearby/service/java/com/android/server/nearby/fastpair/pairinghandler/PairingProgressHandlerBase.java
+++ b/nearby/service/java/com/android/server/nearby/fastpair/pairinghandler/PairingProgressHandlerBase.java
@@ -76,7 +76,8 @@
Log.v("PairingHandler",
- "PairingProgressHandler:Create %s for pairing");
+ "PairingProgressHandler:Create "
+ + item.getMacAddress() + " for pairing");
return pairingProgressHandlerBase;
}
@@ -134,7 +135,7 @@
* <li>1, optIn footprint for initial pairing.
* <li>2, write the device name to provider
* <li>2.1, generate default personalized name for initial pairing or get the personalized name
- * from footprint for subsequent pairing.
+ * from footprint for subsequent pairing.
* <li>2.2, set alias name for the bluetooth device.
* <li>2.3, update the device name for connection to write into provider for initial pair.
* <li>3, suppress battery notifications until oobe finishes.
@@ -180,7 +181,7 @@
*/
public void onPairingSuccess(String address) {
Log.v("PairingHandler", "PairingProgressHandler:onPairingSuccess with address: "
- + maskBluetoothAddress(address));
+ + maskBluetoothAddress(address));
}
private static void optInFootprintsForInitialPairing(
diff --git a/nearby/service/proto/src/fastpair/cache.proto b/nearby/service/proto/src/fastpair/cache.proto
index bf80b58..12731fb 100644
--- a/nearby/service/proto/src/fastpair/cache.proto
+++ b/nearby/service/proto/src/fastpair/cache.proto
@@ -304,3 +304,158 @@
reserved 5;
}
+
+// A locally cached Fast Pair device associating an account key with the
+// bluetooth address of the device.
+message StoredFastPairItem {
+ // The device's public mac address.
+ string mac_address = 1;
+
+ // The account key written to the device.
+ bytes account_key = 2;
+
+ // When user need to update provider name, enable this value to trigger
+ // writing new name to provider.
+ bool need_to_update_provider_name = 3;
+
+ // The retry times to update name into provider.
+ int32 update_name_retries = 4;
+
+ // Latest firmware version from the server.
+ string latest_firmware_version = 5;
+
+ // The firmware version that is on the device.
+ string device_firmware_version = 6;
+
+ // The timestamp from the last time we fetched the firmware version from the
+ // device.
+ int64 last_check_firmware_timestamp_millis = 7;
+
+ // The timestamp from the last time we fetched the firmware version from
+ // server.
+ int64 last_server_query_timestamp_millis = 8;
+
+ // Only allows one bloom filter check process to create gatt connection and
+ // try to read the firmware version value.
+ bool can_read_firmware = 9;
+
+ // Device's model id.
+ string model_id = 10;
+
+ // Features that this Fast Pair device supports.
+ repeated FastPairFeature features = 11;
+
+ // Keeps the stored discovery item in local cache, we can have most
+ // information of fast pair device locally without through footprints, i.e. we
+ // can have most fast pair features locally.
+ StoredDiscoveryItem discovery_item = 12;
+
+ // When true, the latest uploaded event to FMA is connected. We use
+ // it as the previous ACL state when getting the BluetoothAdapter STATE_OFF to
+ // determine if need to upload the disconnect event to FMA.
+ bool fma_state_is_connected = 13;
+
+ // Device's buffer size range.
+ repeated BufferSizeRange buffer_size_range = 18;
+
+ // The additional account key if this device could be associated with multiple
+ // accounts. Notes that for this device, the account_key field is the basic
+ // one which will not be associated with the accounts.
+ repeated bytes additional_account_key = 19;
+
+ // Deprecated fields.
+ reserved 14, 15, 16, 17;
+}
+
+// Contains information about Fast Pair devices stored through our scanner.
+// Next ID: 29
+message ScanFastPairStoreItem {
+ // Device's model id.
+ string model_id = 1;
+
+ // Device's RSSI value
+ int32 rssi = 2;
+
+ // Device's tx power
+ int32 tx_power = 3;
+
+ // Bytes of item icon in PNG format displayed in Discovery item list.
+ bytes icon_png = 4;
+
+ // A FIFE URL of the item icon displayed in Discovery item list.
+ string icon_fife_url = 28;
+
+ // Device name like "Bose QC 35".
+ string device_name = 5;
+
+ // Client timestamp when user last saw Fast Pair device.
+ int64 last_observation_timestamp_millis = 6;
+
+ // Action url after user click the notification.
+ string action_url = 7;
+
+ // Device's bluetooth address.
+ string address = 8;
+
+ // The computed threshold rssi value that would trigger FastPair notifications
+ int32 threshold_rssi = 9;
+
+ // Populated with the contents of the bloom filter in the event that
+ // the scanned device is advertising a bloom filter instead of a model id
+ bytes bloom_filter = 10;
+
+ // Device name from the BLE scan record
+ string ble_device_name = 11;
+
+ // Strings used for the FastPair UI
+ FastPairStrings fast_pair_strings = 12;
+
+ // A key used to authenticate advertising device.
+ // See NearbyItem.authentication_public_key_secp256r1 for more information.
+ bytes anti_spoofing_public_key = 13;
+
+ // When true, Fast Pair will only create a bond with the device and not
+ // attempt to connect any profiles (for example, A2DP or HFP).
+ bool data_only_connection = 14;
+
+ // The type of the manufacturer (first party, third party, etc).
+ int32 manufacturer_type_num = 15;
+
+ // Additional images that are attached specifically for true wireless Fast
+ // Pair devices.
+ TrueWirelessHeadsetImages true_wireless_images = 16;
+
+ // When true, this device can support assistant function.
+ bool assistant_supported = 17;
+
+ // Optional, the name of the company producing this Fast Pair device.
+ string company_name = 18;
+
+ // Features supported by the Fast Pair device.
+ FastPairFeature features = 19;
+
+ // The interaction type that this scan should trigger
+ InteractionType interaction_type = 20;
+
+ // The copy of the advertisement bytes, used to pass along to other
+ // apps that use Fast Pair as the discovery vehicle.
+ bytes full_ble_record = 21;
+
+ // Companion app related information
+ CompanionAppDetails companion_detail = 22;
+
+ // Client timestamp when user first saw Fast Pair device.
+ int64 first_observation_timestamp_millis = 23;
+
+ // The type of the device (wearable, headphones, etc).
+ int32 device_type_num = 24;
+
+ // The type of notification (app launch smart setup, etc).
+ NotificationType notification_type = 25;
+
+ // The customized title.
+ string customized_title = 26;
+
+ // The customized description.
+ string customized_description = 27;
+}
diff --git a/nearby/service/proto/src/fastpair/rpcs.proto b/nearby/service/proto/src/fastpair/rpcs.proto
index 384d471..0399d09 100644
--- a/nearby/service/proto/src/fastpair/rpcs.proto
+++ b/nearby/service/proto/src/fastpair/rpcs.proto
@@ -313,3 +313,17 @@
string fast_pair_tv_connect_device_no_account_description = 24;
}
+// The buffer size range of a Fast Pair devices support dynamic buffer size.
+message BufferSizeRange {
+ // The max buffer size in ms.
+ int32 max_size = 1;
+
+ // The min buffer size in ms.
+ int32 min_size = 2;
+
+ // The default buffer size in ms.
+ int32 default_size = 3;
+
+ // The codec of this buffer size range.
+ int32 codec = 4;
+}