refactor the Android TV-App prompt message (#33469)

* refactor the class MatterCommissioningPrompter.java,don't need Activity no more to avoid memory leak;

* Restyled by google-java-format

---------

Co-authored-by: Restyled.io <commits@restyled.io>
diff --git a/examples/tv-app/android/App/platform-app/build.gradle b/examples/tv-app/android/App/platform-app/build.gradle
index 967d697..0845105 100644
--- a/examples/tv-app/android/App/platform-app/build.gradle
+++ b/examples/tv-app/android/App/platform-app/build.gradle
@@ -7,7 +7,7 @@
 
     defaultConfig {
         applicationId "com.matter.tv.server"
-        minSdk 24
+        minSdk 26
         targetSdk 30
         versionCode 1
         versionName "1.0"
@@ -57,6 +57,9 @@
 //            ]
         }
     }
+    buildFeatures {
+        viewBinding = true
+    }
 }
 
 dependencies {
diff --git a/examples/tv-app/android/App/platform-app/src/main/AndroidManifest.xml b/examples/tv-app/android/App/platform-app/src/main/AndroidManifest.xml
index 04b30f2..b6e00cd 100644
--- a/examples/tv-app/android/App/platform-app/src/main/AndroidManifest.xml
+++ b/examples/tv-app/android/App/platform-app/src/main/AndroidManifest.xml
@@ -24,6 +24,7 @@
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
         tools:ignore="QueryAllPackagesPermission" />
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
 
     <application
         android:allowBackup="true"
@@ -36,6 +37,7 @@
         android:theme="@style/Theme.MatterTVSlave">
 
         <activity android:name="com.matter.tv.server.MainActivity"
+            android:exported="true"
             android:launchMode="singleTask">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MainActivity.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MainActivity.java
index 2fb375d..df612cf 100644
--- a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MainActivity.java
+++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MainActivity.java
@@ -1,13 +1,16 @@
 package com.matter.tv.server;
 
+import android.content.Intent;
+import android.net.Uri;
 import android.os.Bundle;
+import android.provider.Settings;
+import androidx.appcompat.app.AlertDialog;
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.fragment.app.Fragment;
 import com.google.android.material.bottomnavigation.BottomNavigationView;
 import com.matter.tv.server.fragments.ContentAppFragment;
 import com.matter.tv.server.fragments.QrCodeFragment;
 import com.matter.tv.server.fragments.TerminalFragment;
-import com.matter.tv.server.service.MatterServant;
 import java.util.LinkedHashMap;
 
 public class MainActivity extends AppCompatActivity {
@@ -41,10 +44,6 @@
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);
 
-    // MainActivity is needed to launch dialog prompt
-    // in UserPrompter
-    MatterServant.get().setActivity(this);
-
     BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_navigation);
     bottomNavigationView.setOnItemSelectedListener(navListener);
 
@@ -52,5 +51,25 @@
         .beginTransaction()
         .replace(R.id.fragment_container_view, new QrCodeFragment())
         .commit();
+    checkOverlayPermission();
+  }
+
+  private void checkOverlayPermission() {
+    if (!Settings.canDrawOverlays(this)) {
+      AlertDialog.Builder builder = new AlertDialog.Builder(this);
+      builder
+          .setMessage("Allow permission to display over other apps")
+          .setTitle("Request overlay permission")
+          .setPositiveButton(
+              "Ok",
+              (dialog, which) -> {
+                dialog.dismiss();
+                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
+                intent.setData(Uri.parse("package:" + getPackageName()));
+                startActivity(intent);
+              })
+          .create()
+          .show();
+    }
   }
 }
diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MatterCommissioningPrompter.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MatterCommissioningPrompter.java
old mode 100644
new mode 100755
index 8d2a650..743db56
--- a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MatterCommissioningPrompter.java
+++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MatterCommissioningPrompter.java
@@ -2,21 +2,31 @@
 
 import static androidx.core.content.ContextCompat.getSystemService;
 
-import android.app.Activity;
+import android.annotation.SuppressLint;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
 import android.provider.Settings;
 import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
 import android.widget.EditText;
+import androidx.annotation.NonNull;
 import androidx.appcompat.app.AlertDialog;
 import androidx.core.app.NotificationCompat;
-import com.matter.tv.server.service.MatterServant;
+import com.matter.tv.server.databinding.LayoutDialogBinding;
+import com.matter.tv.server.databinding.LayoutInputDialogBinding;
+import com.matter.tv.server.model.PromptCommissionerPasscode;
 import com.matter.tv.server.tvapp.Message;
 import com.matter.tv.server.tvapp.UserPrompter;
 import com.matter.tv.server.tvapp.UserPrompterResolver;
+import com.matter.tv.server.utils.PxConvert;
+import java.lang.ref.WeakReference;
 
 public class MatterCommissioningPrompter extends UserPrompterResolver implements UserPrompter {
 
@@ -26,16 +36,14 @@
   private final int SUCCESS_ID = 0;
   private final int FAIL_ID = 1;
 
+  private final MsgHandler mHandler = new MsgHandler(this);
+
   public MatterCommissioningPrompter(Context context) {
     this.context = context;
     this.createNotificationChannel();
     setUserPrompter(this);
   }
 
-  private Activity getActivity() {
-    return MatterServant.get().getActivity();
-  }
-
   public void promptForCommissionOkPermission(
       int vendorId, int productId, String commissioneeName) {
     Log.d(
@@ -46,7 +54,6 @@
             + productId
             + ". Commissionee: "
             + commissioneeName);
-
     ContentResolver contentResolver = context.getContentResolver();
     boolean authorisationDialogDisabled =
         Settings.Secure.getInt(contentResolver, "matter_show_authorisation_dialog", 0) == 0;
@@ -57,29 +64,10 @@
       OnPromptAccepted();
       return;
     }
-
-    getActivity()
-        .runOnUiThread(
-            () -> {
-              AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-              builder
-                  .setMessage(
-                      commissioneeName
-                          + " is requesting permission to cast to this device, approve?")
-                  .setTitle("Allow access to " + commissioneeName)
-                  .setPositiveButton(
-                      "Ok",
-                      (dialog, which) -> {
-                        OnPromptAccepted();
-                      })
-                  .setNegativeButton(
-                      "Cancel",
-                      (dialog, which) -> {
-                        OnPromptDeclined();
-                      })
-                  .create()
-                  .show();
-            });
+    android.os.Message obtained = android.os.Message.obtain();
+    obtained.what = MsgHandler.MSG_CommissionOkPermission;
+    obtained.obj = commissioneeName;
+    mHandler.sendMessage(obtained);
   }
 
   @Override
@@ -92,31 +80,10 @@
             + productId
             + ". Commissionee: "
             + commissioneeName);
-
-    getActivity()
-        .runOnUiThread(
-            () -> {
-              EditText editText = new EditText(getActivity());
-              AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-
-              builder
-                  .setMessage("Please enter PIN displayed in casting app.")
-                  .setTitle("Allow access to " + commissioneeName)
-                  .setView(editText)
-                  .setPositiveButton(
-                      "Ok",
-                      (dialog, which) -> {
-                        String pinCode = editText.getText().toString();
-                        OnPinCodeEntered(Integer.parseInt(pinCode));
-                      })
-                  .setNegativeButton(
-                      "Cancel",
-                      (dialog, which) -> {
-                        OnPinCodeDeclined();
-                      })
-                  .create()
-                  .show();
-            });
+    android.os.Message obtained = android.os.Message.obtain();
+    obtained.what = MsgHandler.MSG_CommissionPinCode;
+    obtained.obj = commissioneeName;
+    mHandler.sendMessage(obtained);
   }
 
   public void hidePromptsOnCancel(int vendorId, int productId, String commissioneeName) {
@@ -128,26 +95,10 @@
             + productId
             + ". Commissionee: "
             + commissioneeName);
-
-    getActivity()
-        .runOnUiThread(
-            () -> {
-              AlertDialog.Builder abuilder = new AlertDialog.Builder(getActivity());
-              abuilder
-                  .setMessage("Cancelled connection to " + commissioneeName)
-                  .setTitle("Connection Cancelled")
-                  .create()
-                  .show();
-
-              NotificationCompat.Builder builder =
-                  new NotificationCompat.Builder(getActivity(), CHANNEL_ID)
-                      .setSmallIcon(R.drawable.ic_baseline_check_24)
-                      .setContentTitle("Connection Cancelled")
-                      .setContentText("Cancelled connection to " + commissioneeName)
-                      .setPriority(NotificationCompat.PRIORITY_DEFAULT);
-
-              notificationManager.notify(SUCCESS_ID, builder.build());
-            });
+    android.os.Message obtained = android.os.Message.obtain();
+    obtained.what = MsgHandler.MSG_HidePromptsOnCancel;
+    obtained.obj = commissioneeName;
+    mHandler.sendMessage(obtained);
   }
 
   public void promptWithCommissionerPasscode(
@@ -167,38 +118,15 @@
             + productId
             + ". Commissionee: "
             + commissioneeName);
-
-    getActivity()
-        .runOnUiThread(
-            () -> {
-              EditText editText = new EditText(getActivity());
-              AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-
-              builder
-                  .setMessage(
-                      "Please enter "
-                          + passcode
-                          + " in "
-                          + commissioneeName
-                          + " app. "
-                          + pairingInstruction
-                          + " ["
-                          + pairingHint
-                          + "]")
-                  .setTitle("Passcode" + passcode)
-                  .setPositiveButton(
-                      "Ok",
-                      (dialog, which) -> {
-                        OnCommissionerPasscodeOK();
-                      })
-                  .setNegativeButton(
-                      "Cancel",
-                      (dialog, which) -> {
-                        OnCommissionerPasscodeCancel();
-                      })
-                  .create()
-                  .show();
-            });
+    Bundle bundle = new Bundle();
+    PromptCommissionerPasscode promptCommissionerPasscode =
+        new PromptCommissionerPasscode(
+            vendorId, productId, commissioneeName, passcode, pairingHint, pairingInstruction);
+    bundle.putParcelable(MsgHandler.KEY_PROMPT_COMMISSIONER_PASSCODE, promptCommissionerPasscode);
+    android.os.Message obtained = android.os.Message.obtain();
+    obtained.what = MsgHandler.MSG_CommissionerPasscode;
+    obtained.setData(bundle);
+    mHandler.sendMessage(obtained);
   }
 
   public void promptCommissioningStarted(int vendorId, int productId, String commissioneeName) {
@@ -210,25 +138,10 @@
             + productId
             + ". Commissionee: "
             + commissioneeName);
-    getActivity()
-        .runOnUiThread(
-            () -> {
-              AlertDialog.Builder abuilder = new AlertDialog.Builder(getActivity());
-              abuilder
-                  .setMessage("Starting connection to " + commissioneeName)
-                  .setTitle("Connection Starting")
-                  .create()
-                  .show();
-
-              NotificationCompat.Builder builder =
-                  new NotificationCompat.Builder(getActivity(), CHANNEL_ID)
-                      .setSmallIcon(R.drawable.ic_baseline_check_24)
-                      .setContentTitle("Connection Starting")
-                      .setContentText("Starting connection to " + commissioneeName)
-                      .setPriority(NotificationCompat.PRIORITY_DEFAULT);
-
-              notificationManager.notify(SUCCESS_ID, builder.build());
-            });
+    android.os.Message obtained = android.os.Message.obtain();
+    obtained.what = MsgHandler.MSG_CommissioningStarted;
+    obtained.obj = commissioneeName;
+    mHandler.sendMessage(obtained);
   }
 
   public void promptCommissioningSucceeded(int vendorId, int productId, String commissioneeName) {
@@ -240,116 +153,360 @@
             + productId
             + ". Commissionee: "
             + commissioneeName);
-    getActivity()
-        .runOnUiThread(
-            () -> {
-              AlertDialog.Builder abuilder = new AlertDialog.Builder(getActivity());
-              abuilder
-                  .setMessage(
-                      "Success. "
-                          + commissioneeName
-                          + " can now cast to this device. Visit settings to manage access control for casting.")
-                  .setTitle("Connection Complete")
-                  .create()
-                  .show();
-
-              NotificationCompat.Builder builder =
-                  new NotificationCompat.Builder(getActivity(), CHANNEL_ID)
-                      .setSmallIcon(R.drawable.ic_baseline_check_24)
-                      .setContentTitle("Connection Complete")
-                      .setContentText(
-                          "Success. "
-                              + commissioneeName
-                              + " can now cast to this device. Visit settings to manage access control for casting.")
-                      .setPriority(NotificationCompat.PRIORITY_DEFAULT);
-
-              notificationManager.notify(SUCCESS_ID, builder.build());
-            });
+    android.os.Message obtained = android.os.Message.obtain();
+    obtained.what = MsgHandler.MSG_CommissioningSucceeded;
+    obtained.obj = commissioneeName;
+    mHandler.sendMessage(obtained);
   }
 
   public void promptCommissioningFailed(String commissioneeName, String error) {
     Log.d(TAG, "Received prompt for failure Commissionee: " + commissioneeName);
-    getActivity()
-        .runOnUiThread(
-            () -> {
-              AlertDialog.Builder abuilder = new AlertDialog.Builder(getActivity());
-              abuilder
-                  .setMessage("Failed. " + commissioneeName + " experienced error: " + error + ".")
-                  .setTitle("Connection Failed")
-                  .create()
-                  .show();
 
-              NotificationCompat.Builder builder =
-                  new NotificationCompat.Builder(getActivity(), CHANNEL_ID)
-                      .setSmallIcon(R.drawable.ic_baseline_clear_24)
-                      .setContentTitle("Connection Failed")
-                      .setContentText(
-                          "Failed. " + commissioneeName + " experienced error: " + error + ".")
-                      .setPriority(NotificationCompat.PRIORITY_DEFAULT);
-
-              notificationManager.notify(FAIL_ID, builder.build());
-            });
+    android.os.Message obtained = android.os.Message.obtain();
+    obtained.what = MsgHandler.MSG_CommissioningFailed;
+    obtained.obj = commissioneeName;
+    mHandler.sendMessage(obtained);
   }
 
   public void promptWithMessage(Message message) {
     Log.d(TAG, "Received message prompt for " + message.messageText);
-    getActivity()
-        .runOnUiThread(
-            () -> {
-              if (message.responseOptions.length != 2) {
-                AlertDialog.Builder abuilder = new AlertDialog.Builder(getActivity());
-                abuilder
-                    .setMessage("" + message.messageId + ":" + message.messageText)
-                    .setTitle("New Message from Test")
-                    .setPositiveButton(
-                        "Ok",
-                        (dialog, which) -> {
-                          OnMessageResponse(message.messageId, 0); // ack
-                        })
-                    .setNegativeButton(
-                        "Ignore",
-                        (dialog, which) -> {
-                          OnMessageResponse(message.messageId, -1); // ignore
-                        })
-                    .create()
-                    .show();
-              } else {
-                AlertDialog.Builder abuilder = new AlertDialog.Builder(getActivity());
-                abuilder
-                    .setMessage("" + message.messageId + ":" + message.messageText)
-                    .setTitle("New Message from Test")
-                    .setPositiveButton(
-                        message.responseOptions[0].label,
-                        (dialog, which) -> {
-                          OnMessageResponse(message.messageId, message.responseOptions[0].id);
-                        })
-                    .setNegativeButton(
-                        message.responseOptions[1].label,
-                        (dialog, which) -> {
-                          OnMessageResponse(message.messageId, message.responseOptions[1].id);
-                        })
-                    .create()
-                    .show();
-              }
-            });
+
+    android.os.Message obtained = android.os.Message.obtain();
+    obtained.what = MsgHandler.MSG_PromptWithMessage;
+    obtained.obj = message;
+    mHandler.sendMessage(obtained);
+  }
+
+  @SuppressLint("ClickableViewAccessibility")
+  private void handleCommissionOkPermission(android.os.Message msg) {
+    String commissioneeName = (String) msg.obj;
+
+    PromptDialogManager promptDialogManager =
+        new PromptDialogManager.Builder()
+            .setCancelable(false)
+            .setWidth(PxConvert.dp2px(MatterTvServerApplication.getApplication(), 350))
+            .setStyle(PromptDialogManager.DIALOG_STYLE_1)
+            .build();
+
+    LayoutDialogBinding udcDialogBinding =
+        LayoutDialogBinding.inflate(
+            LayoutInflater.from(
+                MatterTvServerApplication.getApplication().getApplicationContext()));
+    udcDialogBinding.tvContent.setText(
+        commissioneeName + " is requesting permission to cast to this device, approve?");
+    udcDialogBinding.tvTitle.setText("Allow access to " + commissioneeName);
+    udcDialogBinding.tvLeft.setOnClickListener(
+        v -> {
+          OnPromptDeclined();
+          promptDialogManager.removeViewImmediate(udcDialogBinding.getRoot());
+        });
+    udcDialogBinding.tvRight.setOnClickListener(
+        v -> {
+          OnPromptAccepted();
+          promptDialogManager.removeViewImmediate(udcDialogBinding.getRoot());
+        });
+    promptDialogManager.addView(udcDialogBinding.getRoot(), this::OnPromptDeclined);
+  }
+
+  private void handleCommissionPinCode(android.os.Message msg) {
+    String commissioneeName = (String) msg.obj;
+
+    PromptDialogManager promptDialogManager =
+        new PromptDialogManager.Builder()
+            .setCancelable(false)
+            .setWidth(PxConvert.dp2px(MatterTvServerApplication.getApplication(), 350))
+            .setStyle(PromptDialogManager.DIALOG_STYLE_2)
+            .build();
+
+    LayoutInputDialogBinding inputDialogBinding =
+        LayoutInputDialogBinding.inflate(
+            LayoutInflater.from(
+                MatterTvServerApplication.getApplication().getApplicationContext()));
+    EditText etPin = inputDialogBinding.etPin;
+    inputDialogBinding.tvTitle.setText("Allow access to " + commissioneeName);
+    inputDialogBinding.tvContent.setText("Please enter PIN displayed in casting app.");
+    inputDialogBinding.tvLeft.setOnClickListener(
+        view -> {
+          OnPinCodeDeclined();
+          promptDialogManager.removeViewImmediate(inputDialogBinding.getRoot());
+        });
+    inputDialogBinding.tvRight.setOnClickListener(
+        view -> {
+          String pinCode = etPin.getText().toString();
+          OnPinCodeEntered(Integer.parseInt(pinCode));
+          promptDialogManager.removeViewImmediate(inputDialogBinding.getRoot());
+        });
+    promptDialogManager.addView(inputDialogBinding.getRoot(), this::OnPinCodeDeclined);
+  }
+
+  private void handleHidePromptsOnCancel(android.os.Message msg) {
+    String commissioneeName = (String) msg.obj;
+
+    PromptDialogManager promptDialogManager =
+        new PromptDialogManager.Builder()
+            .setCancelable(true)
+            .setWidth(PxConvert.dp2px(MatterTvServerApplication.getApplication(), 300))
+            .setStyle(PromptDialogManager.DIALOG_STYLE_3)
+            .build();
+
+    LayoutDialogBinding dialogBinding =
+        LayoutDialogBinding.inflate(
+            LayoutInflater.from(
+                MatterTvServerApplication.getApplication().getApplicationContext()));
+    dialogBinding.tvContent.setText("Cancelled connection to " + commissioneeName);
+    dialogBinding.tvTitle.setText("Connection Cancelled");
+    dialogBinding.llAction.setVisibility(View.GONE);
+    promptDialogManager.addView(dialogBinding.getRoot(), null);
+
+    NotificationCompat.Builder builder =
+        new NotificationCompat.Builder(getContext(), CHANNEL_ID)
+            .setSmallIcon(R.drawable.ic_baseline_check_24)
+            .setContentTitle("Connection Cancelled")
+            .setContentText("Cancelled connection to " + commissioneeName)
+            .setPriority(NotificationCompat.PRIORITY_DEFAULT);
+
+    notificationManager.notify(SUCCESS_ID, builder.build());
+  }
+
+  private void handleCommissionerPasscode(android.os.Message msg) {
+    Bundle bundle = msg.getData();
+    PromptCommissionerPasscode parcelable =
+        bundle.getParcelable(MsgHandler.KEY_PROMPT_COMMISSIONER_PASSCODE);
+
+    PromptDialogManager promptDialogManager =
+        new PromptDialogManager.Builder()
+            .setCancelable(false)
+            .setWidth(PxConvert.dp2px(MatterTvServerApplication.getApplication(), 350))
+            .setStyle(PromptDialogManager.DIALOG_STYLE_1)
+            .build();
+
+    LayoutDialogBinding udcDialogBinding =
+        LayoutDialogBinding.inflate(
+            LayoutInflater.from(
+                MatterTvServerApplication.getApplication().getApplicationContext()));
+    udcDialogBinding.tvContent.setText(
+        "Please enter "
+            + parcelable.getPasscode()
+            + " in "
+            + parcelable.getCommissioneeName()
+            + " app. "
+            + parcelable.getPairingInstruction()
+            + " ["
+            + parcelable.getPairingHint()
+            + "]");
+    udcDialogBinding.tvTitle.setText("Passcode" + parcelable.getPasscode());
+    udcDialogBinding.tvLeft.setOnClickListener(
+        v -> {
+          OnCommissionerPasscodeCancel();
+          promptDialogManager.removeViewImmediate(udcDialogBinding.getRoot());
+        });
+    udcDialogBinding.tvRight.setOnClickListener(
+        v -> {
+          OnCommissionerPasscodeOK();
+          promptDialogManager.removeViewImmediate(udcDialogBinding.getRoot());
+        });
+    promptDialogManager.addView(udcDialogBinding.getRoot(), this::OnCommissionerPasscodeCancel);
+  }
+
+  private void handleCommissioningStarted(android.os.Message msg) {
+    String commissioneeName = (String) msg.obj;
+
+    PromptDialogManager promptDialogManager =
+        new PromptDialogManager.Builder()
+            .setCancelable(true)
+            .setWidth(PxConvert.dp2px(MatterTvServerApplication.getApplication(), 300))
+            .setStyle(PromptDialogManager.DIALOG_STYLE_3)
+            .build();
+
+    LayoutDialogBinding dialogBinding =
+        LayoutDialogBinding.inflate(
+            LayoutInflater.from(
+                MatterTvServerApplication.getApplication().getApplicationContext()));
+    dialogBinding.tvContent.setText("Starting connection to " + commissioneeName);
+    dialogBinding.tvTitle.setText("Connection Starting");
+    dialogBinding.llAction.setVisibility(View.GONE);
+    promptDialogManager.addView(dialogBinding.getRoot(), null);
+
+    NotificationCompat.Builder builder =
+        new NotificationCompat.Builder(getContext(), CHANNEL_ID)
+            .setSmallIcon(R.drawable.ic_baseline_check_24)
+            .setContentTitle("Connection Starting")
+            .setContentText("Starting connection to " + commissioneeName)
+            .setPriority(NotificationCompat.PRIORITY_DEFAULT);
+
+    notificationManager.notify(SUCCESS_ID, builder.build());
+  }
+
+  private void handleCommissioningSucceeded(android.os.Message msg) {
+    String commissioneeName = (String) msg.obj;
+    PromptDialogManager promptDialogManager =
+        new PromptDialogManager.Builder()
+            .setCancelable(true)
+            .setWidth(PxConvert.dp2px(MatterTvServerApplication.getApplication(), 300))
+            .setStyle(PromptDialogManager.DIALOG_STYLE_3)
+            .build();
+
+    LayoutDialogBinding dialogBinding =
+        LayoutDialogBinding.inflate(
+            LayoutInflater.from(
+                MatterTvServerApplication.getApplication().getApplicationContext()));
+    dialogBinding.tvContent.setText(
+        "Success. "
+            + commissioneeName
+            + " can now cast to this device. Visit settings to manage access control for casting.");
+    dialogBinding.tvTitle.setText("Connection Complete");
+    dialogBinding.llAction.setVisibility(View.GONE);
+    promptDialogManager.addView(dialogBinding.getRoot(), null);
+
+    NotificationCompat.Builder builder =
+        new NotificationCompat.Builder(getContext(), CHANNEL_ID)
+            .setSmallIcon(R.drawable.ic_baseline_check_24)
+            .setContentTitle("Connection Complete")
+            .setContentText(
+                "Success. "
+                    + commissioneeName
+                    + " can now cast to this device. Visit settings to manage access control for casting.")
+            .setPriority(NotificationCompat.PRIORITY_DEFAULT);
+
+    notificationManager.notify(SUCCESS_ID, builder.build());
+  }
+
+  private void handleCommissioningFailed(android.os.Message msg) {
+    String commissioneeName = (String) msg.obj;
+    String error = (String) msg.obj;
+    AlertDialog.Builder abuilder = new AlertDialog.Builder(getContext());
+    AlertDialog alertDialog =
+        abuilder
+            .setMessage("Failed. " + commissioneeName + " experienced error: " + error + ".")
+            .setTitle("Connection Failed")
+            .create();
+    alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_PANEL);
+    alertDialog.show();
+
+    NotificationCompat.Builder builder =
+        new NotificationCompat.Builder(getContext(), CHANNEL_ID)
+            .setSmallIcon(R.drawable.ic_baseline_clear_24)
+            .setContentTitle("Connection Failed")
+            .setContentText("Failed. " + commissioneeName + " experienced error: " + error + ".")
+            .setPriority(NotificationCompat.PRIORITY_DEFAULT);
+
+    notificationManager.notify(FAIL_ID, builder.build());
+  }
+
+  private void handlePromptWithMessage(android.os.Message msg) {
+    Message message = (Message) msg.obj;
+
+    PromptDialogManager promptDialogManager =
+        new PromptDialogManager.Builder()
+            .setCancelable(false)
+            .setWidth(PxConvert.dp2px(MatterTvServerApplication.getApplication(), 380))
+            .setStyle(PromptDialogManager.DIALOG_STYLE_1)
+            .build();
+
+    LayoutDialogBinding dialogBinding =
+        LayoutDialogBinding.inflate(
+            LayoutInflater.from(
+                MatterTvServerApplication.getApplication().getApplicationContext()));
+    dialogBinding.tvContent.setText("" + message.messageId + ":" + message.messageText);
+    dialogBinding.tvTitle.setText("New Message from Test");
+    if (message.responseOptions.length != 2) {
+      dialogBinding.tvLeft.setText("Ignore");
+      dialogBinding.tvRight.setText("Ok");
+      dialogBinding.tvLeft.setOnClickListener(
+          v -> {
+            OnMessageResponse(message.messageId, -1);
+            promptDialogManager.removeViewImmediate(dialogBinding.getRoot());
+          });
+      dialogBinding.tvRight.setOnClickListener(
+          v -> {
+            OnMessageResponse(message.messageId, 0);
+            promptDialogManager.removeViewImmediate(dialogBinding.getRoot());
+          });
+    } else {
+      dialogBinding.tvLeft.setText(message.responseOptions[1].label);
+      dialogBinding.tvRight.setText(message.responseOptions[0].label);
+      dialogBinding.tvLeft.setOnClickListener(
+          v -> {
+            OnMessageResponse(message.messageId, message.responseOptions[1].id);
+            promptDialogManager.removeViewImmediate(dialogBinding.getRoot());
+          });
+      dialogBinding.tvRight.setOnClickListener(
+          v -> {
+            OnMessageResponse(message.messageId, message.responseOptions[0].id);
+            promptDialogManager.removeViewImmediate(dialogBinding.getRoot());
+          });
+    }
+    promptDialogManager.addView(
+        dialogBinding.getRoot(),
+        () -> {
+          if (message.responseOptions.length != 2) {
+            OnMessageResponse(message.messageId, -1);
+            promptDialogManager.removeViewImmediate(dialogBinding.getRoot());
+          } else {
+            OnMessageResponse(message.messageId, message.responseOptions[1].id);
+            promptDialogManager.removeViewImmediate(dialogBinding.getRoot());
+          }
+        });
   }
 
   private void createNotificationChannel() {
-    // Create the NotificationChannel, but only on API 26+ because
-    // the NotificationChannel class is new and not in the support library
-    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-      Log.d(TAG, "   -------------            createNotificationChannel");
-      CharSequence name = "MatterPromptNotificationChannel";
-      String description = "Matter Channel for sending notifications";
-      int importance = NotificationManager.IMPORTANCE_DEFAULT;
-      NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
-      channel.setDescription(description);
-      // Register the channel with the system; you can't change the importance
-      // or other notification behaviors after this
-      this.notificationManager = getSystemService(context, NotificationManager.class);
-      notificationManager.createNotificationChannel(channel);
-    } else {
-      Log.d(TAG, "   -------------            NOT createNotificationChannel");
+    Log.d(TAG, "   -------------            createNotificationChannel");
+    CharSequence name = "MatterPromptNotificationChannel";
+    String description = "Matter Channel for sending notifications";
+    int importance = NotificationManager.IMPORTANCE_DEFAULT;
+    NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
+    channel.setDescription(description);
+    // Register the channel with the system; you can't change the importance
+    // or other notification behaviors after this
+    this.notificationManager = getSystemService(context, NotificationManager.class);
+    notificationManager.createNotificationChannel(channel);
+  }
+
+  private Context getContext() {
+    return MatterTvServerApplication.getApplication();
+  }
+
+  static class MsgHandler extends Handler {
+    public static final int MSG_CommissionOkPermission = 101;
+    public static final int MSG_CommissionPinCode = 102;
+    public static final int MSG_HidePromptsOnCancel = 103;
+    public static final int MSG_CommissionerPasscode = 104;
+    public static final int MSG_CommissioningStarted = 105;
+    public static final int MSG_CommissioningSucceeded = 106;
+    public static final int MSG_CommissioningFailed = 107;
+    public static final int MSG_PromptWithMessage = 108;
+    public static final String KEY_PROMPT_COMMISSIONER_PASSCODE =
+        "KEY_PROMPT_COMMISSIONER_PASSCODE";
+    private final WeakReference<MatterCommissioningPrompter> mPrompterWeakReference;
+
+    public MsgHandler(MatterCommissioningPrompter commissioningPrompter) {
+      super(Looper.getMainLooper());
+      mPrompterWeakReference = new WeakReference<>(commissioningPrompter);
+    }
+
+    @Override
+    public void handleMessage(@NonNull android.os.Message msg) {
+      MatterCommissioningPrompter commissioningPrompter = mPrompterWeakReference.get();
+      if (commissioningPrompter == null) {
+        return;
+      }
+      if (msg.what == MSG_CommissionOkPermission) {
+        commissioningPrompter.handleCommissionOkPermission(msg);
+      } else if (msg.what == MSG_CommissionPinCode) {
+        commissioningPrompter.handleCommissionPinCode(msg);
+      } else if (msg.what == MSG_HidePromptsOnCancel) {
+        commissioningPrompter.handleHidePromptsOnCancel(msg);
+      } else if (msg.what == MSG_CommissionerPasscode) {
+        commissioningPrompter.handleCommissionerPasscode(msg);
+      } else if (msg.what == MSG_CommissioningStarted) {
+        commissioningPrompter.handleCommissioningStarted(msg);
+      } else if (msg.what == MSG_CommissioningSucceeded) {
+        commissioningPrompter.handleCommissioningSucceeded(msg);
+      } else if (msg.what == MSG_CommissioningFailed) {
+        commissioningPrompter.handleCommissioningFailed(msg);
+      } else if (msg.what == MSG_PromptWithMessage) {
+        commissioningPrompter.handlePromptWithMessage(msg);
+      }
     }
   }
 }
diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MatterTvServerApplication.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MatterTvServerApplication.java
index c2f1579..9da6f3f 100644
--- a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MatterTvServerApplication.java
+++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MatterTvServerApplication.java
@@ -6,9 +6,16 @@
 import com.matter.tv.server.service.MatterServantService;
 
 public class MatterTvServerApplication extends Application {
+  private static Application mApp;
+
+  public static Application getApplication() {
+    return mApp;
+  }
+
   @Override
   public void onCreate() {
     super.onCreate();
+    mApp = this;
     startMatterServantService();
   }
 
diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/PromptDialogManager.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/PromptDialogManager.java
new file mode 100644
index 0000000..3548de3
--- /dev/null
+++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/PromptDialogManager.java
@@ -0,0 +1,170 @@
+package com.matter.tv.server;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowManager;
+import androidx.annotation.Nullable;
+
+public class PromptDialogManager {
+  private static final String TAG = "PromptDialogManager";
+  /** normal type,like a standard AlertDialog */
+  public static final int DIALOG_STYLE_1 = 1;
+  /** input type,include an EditText */
+  public static final int DIALOG_STYLE_2 = 2;
+  /** normal type,no action area */
+  public static final int DIALOG_STYLE_3 = 3;
+
+  public static final int DIALOG_STYLE_4 = 4;
+  private final int width;
+  private final int height;
+  public final float dimAmount;
+  public final int style;
+  private final boolean cancelable;
+
+  private PromptDialogManager(Builder builder) {
+    this.cancelable = builder.cancelable;
+    this.height = builder.height;
+    this.width = builder.width;
+    this.dimAmount = builder.dimAmount;
+    this.style = builder.style;
+  }
+
+  @SuppressLint("ClickableViewAccessibility")
+  public void addView(View view, @Nullable onBackPressListener onBackPressListener) {
+    WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+    params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+    params.format = PixelFormat.TRANSLUCENT;
+    params.gravity = Gravity.CENTER;
+    params.width = this.width;
+    params.height = this.height;
+    params.windowAnimations = android.R.style.Animation_Dialog;
+    params.dimAmount = this.dimAmount;
+    params.flags = convert2Flag(this.style);
+    WindowManager windowManager =
+        (WindowManager)
+            MatterTvServerApplication.getApplication()
+                .getApplicationContext()
+                .getSystemService(Context.WINDOW_SERVICE);
+
+    view.setFocusable(true);
+    view.setFocusableInTouchMode(true);
+    view.requestFocus();
+
+    View.OnKeyListener onKeyListener =
+        (view1, keyCode, keyEvent) -> {
+          Log.d(TAG, "addView: setOnKeyListener keyCode = " + keyCode);
+          if (keyCode == KeyEvent.KEYCODE_BACK && keyEvent.getAction() == KeyEvent.ACTION_UP) {
+            windowManager.removeView(view);
+            if (onBackPressListener != null) {
+              onBackPressListener.onBackPress();
+            }
+            return true;
+          }
+          return false;
+        };
+    view.setOnKeyListener(onKeyListener);
+    view.setOnTouchListener(
+        (v, motionEvent) -> {
+          if (motionEvent.getAction() == MotionEvent.ACTION_OUTSIDE) {
+            Log.d(TAG, "handleCommissionOkPermission: setOnTouchListener ACTION_OUTSIDE");
+            windowManager.removeView(view);
+            return true;
+          }
+          return false;
+        });
+
+    windowManager.addView(view, params);
+  }
+
+  public void removeView(View view) {
+    WindowManager windowManager =
+        (WindowManager)
+            MatterTvServerApplication.getApplication()
+                .getApplicationContext()
+                .getSystemService(Context.WINDOW_SERVICE);
+    try {
+      windowManager.removeView(view);
+    } catch (Exception ex) {
+      Log.w(TAG, "removeView: error = " + ex);
+    }
+  }
+
+  public void removeViewImmediate(View view) {
+    WindowManager windowManager =
+        (WindowManager)
+            MatterTvServerApplication.getApplication()
+                .getApplicationContext()
+                .getSystemService(Context.WINDOW_SERVICE);
+    try {
+      windowManager.removeViewImmediate(view);
+    } catch (Exception ex) {
+      Log.w(TAG, "removeViewImmediate: error = " + ex);
+    }
+  }
+
+  private static int convert2Flag(int style) {
+    int flags = WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+    if (style == DIALOG_STYLE_1) {
+      return flags
+          | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+          | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+    } else if (style == DIALOG_STYLE_2) {
+      return flags;
+    } else if (style == DIALOG_STYLE_3) {
+      return flags
+          | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+          | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+    } else {
+      return flags;
+    }
+  }
+
+  public static class Builder {
+
+    private int width = WindowManager.LayoutParams.WRAP_CONTENT;
+    private int height = WindowManager.LayoutParams.WRAP_CONTENT;
+    private float dimAmount = 0.6f;
+
+    private int style = DIALOG_STYLE_1;
+    private boolean cancelable = false;
+
+    public Builder setWidth(int width) {
+      this.width = width;
+      return this;
+    }
+
+    public Builder setHeight(int height) {
+      this.height = height;
+      return this;
+    }
+
+    public Builder setCancelable(boolean mCancelable) {
+      this.cancelable = mCancelable;
+      return this;
+    }
+
+    public Builder setDimAmount(float dimAmount) {
+      this.dimAmount = dimAmount;
+      return this;
+    }
+
+    public Builder setStyle(int style) {
+      this.style = style;
+      return this;
+    }
+
+    public PromptDialogManager build() {
+      return new PromptDialogManager(this);
+    }
+  }
+
+  public interface onBackPressListener {
+    void onBackPress();
+  }
+}
diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/model/PromptCommissionerPasscode.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/model/PromptCommissionerPasscode.java
new file mode 100644
index 0000000..1a5657f
--- /dev/null
+++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/model/PromptCommissionerPasscode.java
@@ -0,0 +1,133 @@
+package com.matter.tv.server.model;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class PromptCommissionerPasscode implements Parcelable {
+  private int vendorId;
+  private int productId;
+  private String commissioneeName;
+  private long passcode;
+  private int pairingHint;
+  private String pairingInstruction;
+
+  public PromptCommissionerPasscode(
+      int vendorId,
+      int productId,
+      String commissioneeName,
+      long passcode,
+      int pairingHint,
+      String pairingInstruction) {
+    this.vendorId = vendorId;
+    this.productId = productId;
+    this.commissioneeName = commissioneeName;
+    this.passcode = passcode;
+    this.pairingHint = pairingHint;
+    this.pairingInstruction = pairingInstruction;
+  }
+
+  protected PromptCommissionerPasscode(Parcel in) {
+    vendorId = in.readInt();
+    productId = in.readInt();
+    commissioneeName = in.readString();
+    passcode = in.readLong();
+    pairingHint = in.readInt();
+    pairingInstruction = in.readString();
+  }
+
+  public static final Creator<PromptCommissionerPasscode> CREATOR =
+      new Creator<PromptCommissionerPasscode>() {
+        @Override
+        public PromptCommissionerPasscode createFromParcel(Parcel in) {
+          return new PromptCommissionerPasscode(in);
+        }
+
+        @Override
+        public PromptCommissionerPasscode[] newArray(int size) {
+          return new PromptCommissionerPasscode[size];
+        }
+      };
+
+  public int getVendorId() {
+    return vendorId;
+  }
+
+  public void setVendorId(int vendorId) {
+    this.vendorId = vendorId;
+  }
+
+  public int getProductId() {
+    return productId;
+  }
+
+  public void setProductId(int productId) {
+    this.productId = productId;
+  }
+
+  public String getCommissioneeName() {
+    return commissioneeName;
+  }
+
+  public void setCommissioneeName(String commissioneeName) {
+    this.commissioneeName = commissioneeName;
+  }
+
+  public long getPasscode() {
+    return passcode;
+  }
+
+  public void setPasscode(long passcode) {
+    this.passcode = passcode;
+  }
+
+  public int getPairingHint() {
+    return pairingHint;
+  }
+
+  public void setPairingHint(int pairingHint) {
+    this.pairingHint = pairingHint;
+  }
+
+  public String getPairingInstruction() {
+    return pairingInstruction;
+  }
+
+  public void setPairingInstruction(String pairingInstruction) {
+    this.pairingInstruction = pairingInstruction;
+  }
+
+  @Override
+  public String toString() {
+    return "PromptCommissionerPasscode{"
+        + "vendorId="
+        + vendorId
+        + ", productId="
+        + productId
+        + ", commissioneeName='"
+        + commissioneeName
+        + '\''
+        + ", passcode="
+        + passcode
+        + ", pairingHint="
+        + pairingHint
+        + ", pairingInstruction='"
+        + pairingInstruction
+        + '\''
+        + '}';
+  }
+
+  @Override
+  public int describeContents() {
+    return 0;
+  }
+
+  @Override
+  public void writeToParcel(Parcel parcel, int i) {
+    parcel.writeInt(vendorId);
+    parcel.writeInt(productId);
+    parcel.writeString(commissioneeName);
+    parcel.writeLong(passcode);
+    parcel.writeInt(pairingHint);
+    parcel.writeString(pairingInstruction);
+  }
+}
diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/MatterServant.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/MatterServant.java
index 540798a..c1d479e 100644
--- a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/MatterServant.java
+++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/MatterServant.java
@@ -17,7 +17,6 @@
  */
 package com.matter.tv.server.service;
 
-import android.app.Activity;
 import android.content.Context;
 import android.util.Log;
 import androidx.annotation.NonNull;
@@ -68,7 +67,6 @@
   }
 
   private Context context;
-  private Activity activity;
 
   public void init(@NonNull Context context) {
 
@@ -150,14 +148,6 @@
     mIsOn = !mIsOn;
   }
 
-  public void setActivity(Activity activity) {
-    this.activity = activity;
-  }
-
-  public Activity getActivity() {
-    return activity;
-  }
-
   public void sendCustomCommand(String customCommand) {
     Log.i(MatterServant.class.getName(), customCommand);
     // TODO: insert logic ot send custom command here
diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/utils/PxConvert.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/utils/PxConvert.java
new file mode 100644
index 0000000..5f00cc3
--- /dev/null
+++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/utils/PxConvert.java
@@ -0,0 +1,13 @@
+package com.matter.tv.server.utils;
+
+import android.content.Context;
+import android.util.TypedValue;
+
+public class PxConvert {
+  public static int dp2px(Context context, float value) {
+    return (int)
+        (TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_DIP, value, context.getResources().getDisplayMetrics())
+            + 0.5f);
+  }
+}
diff --git a/examples/tv-app/android/App/platform-app/src/main/res/drawable/blue_button_background.xml b/examples/tv-app/android/App/platform-app/src/main/res/drawable/blue_button_background.xml
new file mode 100644
index 0000000..0e597b4
--- /dev/null
+++ b/examples/tv-app/android/App/platform-app/src/main/res/drawable/blue_button_background.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true">
+        <shape android:shape="rectangle">
+            <solid android:color="@color/blue_btn_bg_pressed_color" />
+            <corners android:radius="6dp"/>
+        </shape>
+    </item>
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="@color/blue_btn_bg_color" />
+            <corners android:radius="6dp"/>
+        </shape>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/examples/tv-app/android/App/platform-app/src/main/res/drawable/gray_button_background.xml b/examples/tv-app/android/App/platform-app/src/main/res/drawable/gray_button_background.xml
new file mode 100644
index 0000000..04e807d
--- /dev/null
+++ b/examples/tv-app/android/App/platform-app/src/main/res/drawable/gray_button_background.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true">
+        <shape android:shape="rectangle">
+            <solid android:color="@color/gray_btn_bg_pressed_color" />
+            <corners android:radius="6dp"/>
+        </shape>
+    </item>
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="@color/gray_btn_bg_color" />
+            <corners android:radius="6dp"/>
+        </shape>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/examples/tv-app/android/App/platform-app/src/main/res/drawable/shape_dialog_background.xml b/examples/tv-app/android/App/platform-app/src/main/res/drawable/shape_dialog_background.xml
new file mode 100644
index 0000000..0f9508b
--- /dev/null
+++ b/examples/tv-app/android/App/platform-app/src/main/res/drawable/shape_dialog_background.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@color/dialog_bg_color" />
+    <corners android:radius="6dp"/>
+</shape>
\ No newline at end of file
diff --git a/examples/tv-app/android/App/platform-app/src/main/res/layout/layout_dialog.xml b/examples/tv-app/android/App/platform-app/src/main/res/layout/layout_dialog.xml
new file mode 100644
index 0000000..6fb96a1
--- /dev/null
+++ b/examples/tv-app/android/App/platform-app/src/main/res/layout/layout_dialog.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout 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"
+    android:layout_width="350dp"
+    android:layout_height="wrap_content"
+    android:background="@drawable/shape_dialog_background">
+
+    <TextView
+        android:id="@+id/tv_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="10dp"
+        android:textColor="#575757"
+        android:textSize="19sp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:text="Allow access to Test TV casting app" />
+
+    <TextView
+        android:id="@+id/tv_content"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_marginHorizontal="10dp"
+        android:layout_marginTop="10dp"
+        android:gravity="center"
+        android:textAlignment="center"
+        android:textColor="#797979"
+        android:textSize="14sp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/tv_title"
+        tools:text="Test TV casting app is requesting permission to cast to this device, approve?" />
+
+    <View
+        android:id="@+id/v_horizon"
+        android:layout_width="0dp"
+        android:layout_height="1dp"
+        android:layout_marginTop="10dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/tv_content" />
+
+    <LinearLayout
+        android:id="@+id/ll_action"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:orientation="horizontal"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/v_horizon">
+
+        <Button
+            android:id="@+id/tv_left"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="16dp"
+            style="@style/dialog_blue_button"
+            android:layout_weight="1"
+            android:text="Cancel" />
+
+        <Button
+            android:id="@+id/tv_right"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            style="@style/dialog_blue_button"
+            android:layout_marginStart="48dp"
+            android:layout_marginEnd="16dp"
+            android:layout_weight="1"
+            android:text="Ok" />
+    </LinearLayout>
+
+
+    <Space
+        android:layout_width="wrap_content"
+        android:layout_height="8dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/ll_action" />
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/examples/tv-app/android/App/platform-app/src/main/res/layout/layout_input_dialog.xml b/examples/tv-app/android/App/platform-app/src/main/res/layout/layout_input_dialog.xml
new file mode 100644
index 0000000..96326c2
--- /dev/null
+++ b/examples/tv-app/android/App/platform-app/src/main/res/layout/layout_input_dialog.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout 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"
+    android:layout_width="400dp"
+    android:layout_height="wrap_content"
+    android:background="@drawable/shape_dialog_background">
+
+    <TextView
+        android:id="@+id/tv_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="10dp"
+        android:singleLine="true"
+        android:textColor="#575757"
+        android:textSize="19sp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:text="Allow access to Test TV" />
+
+    <TextView
+        android:id="@+id/tv_content"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="15dp"
+        android:layout_marginTop="10dp"
+        android:textSize="14sp"
+        android:textAlignment="center"
+        android:gravity="center"
+        android:textColor="#797979"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/tv_title"
+        tools:text="Please enter PIN displayed in casting app." />
+
+    <EditText
+        android:id="@+id/et_pin"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="10dp"
+        android:inputType="number"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/tv_content" />
+
+    <View
+        android:id="@+id/v_horizon"
+        android:layout_width="0dp"
+        android:layout_height="1dp"
+        android:layout_marginTop="10dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/et_pin" />
+
+    <Button
+        android:id="@+id/tv_left"
+        style="@style/dialog_blue_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="8dp"
+        android:text="Cancel"
+        app:layout_constraintEnd_toStartOf="@+id/tv_right"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintHorizontal_chainStyle="spread"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/v_horizon" />
+
+    <Button
+        android:id="@+id/tv_right"
+        style="@style/dialog_blue_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="10dp"
+        android:layout_marginEnd="8dp"
+        android:text="Ok"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintStart_toEndOf="@+id/tv_left"
+        app:layout_constraintTop_toBottomOf="@id/v_horizon" />
+
+    <Space
+        android:layout_width="wrap_content"
+        android:layout_height="8dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/tv_right" />
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/examples/tv-app/android/App/platform-app/src/main/res/values/colors.xml b/examples/tv-app/android/App/platform-app/src/main/res/values/colors.xml
index f8c6127..112e295 100644
--- a/examples/tv-app/android/App/platform-app/src/main/res/values/colors.xml
+++ b/examples/tv-app/android/App/platform-app/src/main/res/values/colors.xml
@@ -7,4 +7,11 @@
     <color name="teal_700">#FF018786</color>
     <color name="black">#FF000000</color>
     <color name="white">#FFFFFFFF</color>
+
+    <color name="dialog_bg_color">#FFFFFF</color>
+    <color name="button_text_color">#FFFFFF</color>
+    <color name="gray_btn_bg_color">#D0D0D0</color>
+    <color name="gray_btn_bg_pressed_color">#B6B6B6</color>
+    <color name="blue_btn_bg_color">#AEDEF4</color>
+    <color name="blue_btn_bg_pressed_color">#96BFD2</color>
 </resources>
\ No newline at end of file
diff --git a/examples/tv-app/android/App/platform-app/src/main/res/values/themes.xml b/examples/tv-app/android/App/platform-app/src/main/res/values/themes.xml
index 2d74dd9..499da28 100644
--- a/examples/tv-app/android/App/platform-app/src/main/res/values/themes.xml
+++ b/examples/tv-app/android/App/platform-app/src/main/res/values/themes.xml
@@ -13,4 +13,15 @@
         <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
         <!-- Customize your theme here. -->
     </style>
+
+    <!-- AlertDialog theme. -->
+    <style name="dialog_blue_button" parent="android:Widget.Button">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">31dp</item>
+        <item name="android:background">@drawable/blue_button_background</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:paddingLeft">21dp</item>
+        <item name="android:paddingRight">21dp</item>
+        <item name="android:textColor">@color/button_text_color</item>
+    </style>
 </resources>
\ No newline at end of file