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/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() {
+
+    }
+}