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 fbeed33502a439..2fb375dbed5c93 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
@@ -7,6 +7,7 @@
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 {
@@ -18,13 +19,13 @@ public class MainActivity extends AppCompatActivity {
Fragment selectedFragment = null;
switch (item.getItemId()) {
case R.id.content_app:
- selectedFragment = new ContentAppFragment();
+ selectedFragment = ContentAppFragment.newInstance();
break;
case R.id.qr_code:
- selectedFragment = new QrCodeFragment();
+ selectedFragment = QrCodeFragment.newInstance();
break;
case R.id.terminal:
- selectedFragment = new TerminalFragment();
+ selectedFragment = TerminalFragment.newInstance();
break;
}
@@ -40,6 +41,10 @@ protected void onCreate(Bundle savedInstanceState) {
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);
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
new file mode 100644
index 00000000000000..4be7f24903f1cd
--- /dev/null
+++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MatterCommissioningPrompter.java
@@ -0,0 +1,148 @@
+package com.matter.tv.server;
+
+import static androidx.core.content.ContextCompat.getSystemService;
+
+import android.app.Activity;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.os.Build;
+import android.util.Log;
+import android.widget.EditText;
+import androidx.appcompat.app.AlertDialog;
+import androidx.core.app.NotificationCompat;
+import com.tcl.chip.tvapp.UserPrompter;
+import com.tcl.chip.tvapp.UserPrompterResolver;
+
+public class MatterCommissioningPrompter extends UserPrompterResolver implements UserPrompter {
+
+ private Activity activity;
+ private NotificationManager notificationManager;
+ private final String CHANNEL_ID = "MatterCommissioningPrompter.CHANNEL";
+ private final int SUCCESS_ID = 0;
+ private final int FAIL_ID = 1;
+
+ public MatterCommissioningPrompter(Activity activity) {
+ this.activity = activity;
+ this.createNotificationChannel();
+ }
+
+ public void promptForCommissionOkPermission(
+ int vendorId, int productId, String commissioneeName) {
+ // TODO: find app by vendorId and productId
+ Log.d(
+ TAG,
+ "Received prompt for OK permission vendor id:"
+ + vendorId
+ + " productId:"
+ + productId
+ + ". Commissionee: "
+ + commissioneeName);
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+
+ 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();
+ }
+
+ @Override
+ public void promptForCommissionPinCode(int vendorId, int productId, String commissioneeName) {
+ // TODO: find app by vendorId and productId
+ Log.d(
+ TAG,
+ "Received prompt for PIN code vendor id:"
+ + vendorId
+ + " productId:"
+ + productId
+ + ". Commissionee: "
+ + commissioneeName);
+ EditText editText = new EditText(activity);
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+
+ 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();
+ }
+
+ public void promptCommissioningSucceeded(int vendorId, int productId, String commissioneeName) {
+ Log.d(
+ TAG,
+ "Received prompt for success vendor id:"
+ + vendorId
+ + " productId:"
+ + productId
+ + ". Commissionee: "
+ + commissioneeName);
+ NotificationCompat.Builder builder =
+ new NotificationCompat.Builder(activity, 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());
+ }
+
+ public void promptCommissioningFailed(String commissioneeName, String error) {
+ Log.d(
+ TAG,
+ "Received prompt for failure vendor id:"
+ + vendorId
+ + " productId:"
+ + productId
+ + ". Commissionee: "
+ + commissioneeName);
+ NotificationCompat.Builder builder =
+ new NotificationCompat.Builder(activity, 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 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) {
+ 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(activity, NotificationManager.class);
+ notificationManager.createNotificationChannel(channel);
+ }
+ }
+}
diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/fragments/ContentAppFragment.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/fragments/ContentAppFragment.java
index 89b8804af3677d..c99045fc3ddc28 100644
--- a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/fragments/ContentAppFragment.java
+++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/fragments/ContentAppFragment.java
@@ -40,12 +40,9 @@ public ContentAppFragment() {
* Use this factory method to create a new instance of this fragment using the provided
* parameters.
*
- * @param param1 Parameter 1.
- * @param param2 Parameter 2.
* @return A new instance of fragment SettingsFragment.
*/
- // TODO: Rename and change types and number of parameters
- public static ContentAppFragment newInstance(String param1, String param2) {
+ public static ContentAppFragment newInstance() {
ContentAppFragment fragment = new ContentAppFragment();
Bundle args = new Bundle();
fragment.setArguments(args);
diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/fragments/QrCodeFragment.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/fragments/QrCodeFragment.java
index 8ab6e03a0fa542..04b17bd322e955 100644
--- a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/fragments/QrCodeFragment.java
+++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/fragments/QrCodeFragment.java
@@ -35,12 +35,9 @@ public QrCodeFragment() {
* Use this factory method to create a new instance of this fragment using the provided
* parameters.
*
- * @param param1 Parameter 1.
- * @param param2 Parameter 2.
* @return A new instance of fragment QrCodeFragment.
*/
- // TODO: Rename and change types and number of parameters
- public static QrCodeFragment newInstance(String param1, String param2) {
+ public static QrCodeFragment newInstance() {
QrCodeFragment fragment = new QrCodeFragment();
Bundle args = new Bundle();
fragment.setArguments(args);
diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/fragments/TerminalFragment.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/fragments/TerminalFragment.java
index 2962e5ce04c890..24045ce6f8c46f 100644
--- a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/fragments/TerminalFragment.java
+++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/fragments/TerminalFragment.java
@@ -25,12 +25,9 @@ public TerminalFragment() {
* Use this factory method to create a new instance of this fragment using the provided
* parameters.
*
- * @param param1 Parameter 1.
- * @param param2 Parameter 2.
* @return A new instance of fragment CommssionerFragment.
*/
- // TODO: Rename and change types and number of parameters
- public static TerminalFragment newInstance(String param1, String param2) {
+ public static TerminalFragment newInstance() {
TerminalFragment fragment = new TerminalFragment();
Bundle args = new Bundle();
fragment.setArguments(args);
@@ -63,10 +60,7 @@ public void onResume() {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- builder.setMessage(message).setTitle("Command response:");
-
- AlertDialog dialog = builder.create();
- dialog.show();
+ builder.setMessage(message).setTitle("Response").create().show();
});
}
}
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 f6994264086033..06fa8fdd06fc82 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,6 +17,7 @@
*/
package com.matter.tv.server.service;
+import android.app.Activity;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -28,6 +29,7 @@
import chip.platform.NsdManagerServiceResolver;
import chip.platform.PreferencesConfigurationManager;
import chip.platform.PreferencesKeyValueStoreManager;
+import com.matter.tv.server.MatterCommissioningPrompter;
import com.matter.tv.server.handlers.ContentAppEndpointManagerImpl;
import com.matter.tv.server.model.ContentApp;
import com.tcl.chip.tvapp.ChannelManagerStub;
@@ -66,6 +68,7 @@ public static MatterServant get() {
}
private Context context;
+ private Activity activity;
public void init(@NonNull Context context) {
@@ -113,6 +116,7 @@ public void init(@NonNull Context context) {
}
});
mTvApp.setDACProvider(new DACProviderStub());
+ mTvApp.setUserPrompter(new MatterCommissioningPrompter(activity));
mTvApp.setChipDeviceEventProvider(
new DeviceEventProvider() {
@@ -152,6 +156,10 @@ public void toggleOnOff() {
mIsOn = !mIsOn;
}
+ public void setActivity(Activity activity) {
+ this.activity = 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/res/drawable/ic_baseline_check_24.xml b/examples/tv-app/android/App/platform-app/src/main/res/drawable/ic_baseline_check_24.xml
new file mode 100644
index 00000000000000..0432fa69bb7908
--- /dev/null
+++ b/examples/tv-app/android/App/platform-app/src/main/res/drawable/ic_baseline_check_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/examples/tv-app/android/App/platform-app/src/main/res/drawable/ic_baseline_clear_24.xml b/examples/tv-app/android/App/platform-app/src/main/res/drawable/ic_baseline_clear_24.xml
new file mode 100644
index 00000000000000..16d6d37dd9f765
--- /dev/null
+++ b/examples/tv-app/android/App/platform-app/src/main/res/drawable/ic_baseline_clear_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/examples/tv-app/android/BUILD.gn b/examples/tv-app/android/BUILD.gn
index 0790280116dde3..b227f97fe47101 100644
--- a/examples/tv-app/android/BUILD.gn
+++ b/examples/tv-app/android/BUILD.gn
@@ -62,6 +62,10 @@ shared_library("jni") {
"java/MediaInputManager.h",
"java/MediaPlaybackManager.cpp",
"java/MediaPlaybackManager.h",
+ "java/MyUserPrompter-JNI.cpp",
+ "java/MyUserPrompter-JNI.h",
+ "java/MyUserPrompterResolver-JNI.cpp",
+ "java/MyUserPrompterResolver-JNI.h",
"java/OnOffManager.cpp",
"java/OnOffManager.h",
"java/TVApp-JNI.cpp",
@@ -129,6 +133,8 @@ android_library("java") {
"java/src/com/tcl/chip/tvapp/OnOffManagerStub.java",
"java/src/com/tcl/chip/tvapp/TvApp.java",
"java/src/com/tcl/chip/tvapp/TvAppCallback.java",
+ "java/src/com/tcl/chip/tvapp/UserPrompter.java",
+ "java/src/com/tcl/chip/tvapp/UserPrompterResolver.java",
"java/src/com/tcl/chip/tvapp/WakeOnLanManager.java",
"java/src/com/tcl/chip/tvapp/WakeOnLanManagerStub.java",
]
diff --git a/examples/tv-app/android/java/AppImpl.cpp b/examples/tv-app/android/java/AppImpl.cpp
index e1bed97c35296c..f215c62c024b2b 100644
--- a/examples/tv-app/android/java/AppImpl.cpp
+++ b/examples/tv-app/android/java/AppImpl.cpp
@@ -46,60 +46,6 @@
using namespace chip;
using namespace chip::AppPlatform;
-#if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
-class MyUserPrompter : public UserPrompter
-{
- // tv should override this with a dialog prompt
- inline void PromptForCommissionOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override
- {
- /*
- * Called to prompt the user for consent to allow the given commissioneeName/vendorId/productId to be commissioned.
- * For example "[commissioneeName] is requesting permission to cast to this TV, approve?"
- *
- * If user responds with OK then implementor should call CommissionerRespondOk();
- * If user responds with Cancel then implementor should call CommissionerRespondCancel();
- *
- */
- GetCommissionerDiscoveryController()->Ok();
-
- /**
- * For Demo: Launch Prime Video App
- */
- return;
- }
-
- // tv should override this with a dialog prompt
- inline void PromptForCommissionPincode(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override
- {
- /*
- * Called to prompt the user to enter the setup pincode displayed by the given commissioneeName/vendorId/productId to be
- * commissioned. For example "Please enter pin displayed in casting app."
- *
- * If user enters with pin then implementor should call CommissionerRespondPincode(uint32_t pincode);
- * If user responds with Cancel then implementor should call CommissionerRespondCancel();
- */
-
- GetCommissionerDiscoveryController()->CommissionWithPincode(20202021); // dummy pin code
-
- /**
- * For Demo: Launch Prime Video App
- */
- return;
- }
-
- // tv should override this with a dialog prompt
- inline void PromptCommissioningSucceeded(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override
- {
- return;
- }
-
- // tv should override this with a dialog prompt
- inline void PromptCommissioningFailed(const char * commissioneeName, CHIP_ERROR error) override { return; }
-};
-
-MyUserPrompter gMyUserPrompter;
-#endif // CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
-
#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
class MyPincodeService : public PincodeService
{
@@ -456,7 +402,7 @@ void ContentAppFactoryImpl::SendTestMessage(EndpointId epId, const char * messag
#endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
-CHIP_ERROR InitVideoPlayerPlatform()
+CHIP_ERROR InitVideoPlayerPlatform(JNIMyUserPrompter * userPrompter)
{
#if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
ContentAppPlatform::GetInstance().SetupAppPlatform();
@@ -465,10 +411,10 @@ CHIP_ERROR InitVideoPlayerPlatform()
#if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
CommissionerDiscoveryController * cdc = GetCommissionerDiscoveryController();
- if (cdc != nullptr)
+ if (cdc != nullptr && userPrompter != nullptr)
{
cdc->SetPincodeService(&gMyPincodeService);
- cdc->SetUserPrompter(&gMyUserPrompter);
+ cdc->SetUserPrompter(userPrompter);
cdc->SetPostCommissioningListener(&gMyPostCommissioningListener);
}
diff --git a/examples/tv-app/android/java/AppImpl.h b/examples/tv-app/android/java/AppImpl.h
index c1631bd80673fa..bcd31ccd061fb9 100644
--- a/examples/tv-app/android/java/AppImpl.h
+++ b/examples/tv-app/android/java/AppImpl.h
@@ -42,6 +42,7 @@
#include "ContentAppCommandDelegate.h"
#include "KeypadInputManager.h"
#include "MediaPlaybackManager.h"
+#include "MyUserPrompter-JNI.h"
#include
#include
#include
@@ -51,7 +52,7 @@
#include
#include
-CHIP_ERROR InitVideoPlayerPlatform();
+CHIP_ERROR InitVideoPlayerPlatform(JNIMyUserPrompter * userPrompter);
CHIP_ERROR PreServerInit();
EndpointId AddContentApp(const char * szVendorName, uint16_t vendorId, const char * szApplicationName, uint16_t productId,
const char * szApplicationVersion, jobject manager);
diff --git a/examples/tv-app/android/java/MyUserPrompter-JNI.cpp b/examples/tv-app/android/java/MyUserPrompter-JNI.cpp
new file mode 100644
index 00000000000000..29fff989fdff57
--- /dev/null
+++ b/examples/tv-app/android/java/MyUserPrompter-JNI.cpp
@@ -0,0 +1,223 @@
+/*
+ *
+ * Copyright (c) 2022 Project CHIP Authors
+ *
+ * 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.
+ */
+
+#include "MyUserPrompter-JNI.h"
+#include
+#include
+#include
+#include
+#include
+
+using namespace chip;
+
+JNIMyUserPrompter::JNIMyUserPrompter(jobject provider)
+{
+ JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
+ VerifyOrReturn(env != nullptr, ChipLogError(Zcl, "Failed to GetEnvForCurrentThread for JNIMyUserPrompter"));
+
+ mJNIMyUserPrompterObject = env->NewGlobalRef(provider);
+ VerifyOrReturn(mJNIMyUserPrompterObject != nullptr, ChipLogError(Zcl, "Failed to NewGlobalRef JNIMyUserPrompter"));
+
+ jclass JNIMyUserPrompterClass = env->GetObjectClass(provider);
+ VerifyOrReturn(JNIMyUserPrompterClass != nullptr, ChipLogError(Zcl, "Failed to get JNIMyUserPrompter Java class"));
+
+ mPromptForCommissionOKPermissionMethod =
+ env->GetMethodID(JNIMyUserPrompterClass, "promptForCommissionOkPermission", "(IILjava/lang/String;)V");
+ if (mPromptForCommissionOKPermissionMethod == nullptr)
+ {
+ ChipLogError(Zcl, "Failed to access JNIMyUserPrompter 'promptForCommissionOkPermission' method");
+ env->ExceptionClear();
+ }
+
+ mPromptForCommissionPincodeMethod =
+ env->GetMethodID(JNIMyUserPrompterClass, "promptForCommissionPinCode", "(IILjava/lang/String;)V");
+ if (mPromptForCommissionPincodeMethod == nullptr)
+ {
+ ChipLogError(Zcl, "Failed to access JNIMyUserPrompter 'promptForCommissionPinCode' method");
+ env->ExceptionClear();
+ }
+
+ mPromptCommissioningSucceededMethod =
+ env->GetMethodID(JNIMyUserPrompterClass, "promptCommissioningSucceeded", "(IILjava/lang/String;)V");
+ if (mPromptCommissioningSucceededMethod == nullptr)
+ {
+ ChipLogError(Zcl, "Failed to access JNIMyUserPrompter 'promptCommissioningSucceeded' method");
+ env->ExceptionClear();
+ }
+
+ mPromptCommissioningFailedMethod =
+ env->GetMethodID(JNIMyUserPrompterClass, "promptCommissioningFailed", "(Ljava/lang/String;Ljava/lang/String;)V");
+ if (mPromptCommissioningFailedMethod == nullptr)
+ {
+ ChipLogError(Zcl, "Failed to access JNIMyUserPrompter 'promptCommissioningFailed' method");
+ env->ExceptionClear();
+ }
+}
+
+/*
+ * Called to prompt the user for consent to allow the given commissioneeName/vendorId/productId to be commissioned.
+ * For example "[commissioneeName] is requesting permission to cast to this TV, approve?"
+ *
+ * If user responds with OK then implementor calls UserPrompterResolver.OnPromptAccepted;
+ * If user responds with Cancel then implementor calls calls UserPrompterResolver.OnPromptDeclined();
+ *
+ */
+void JNIMyUserPrompter::PromptForCommissionOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
+ std::string stringCommissioneeName(commissioneeName);
+
+ VerifyOrExit(mJNIMyUserPrompterObject != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
+ VerifyOrExit(mPromptForCommissionOKPermissionMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
+ VerifyOrExit(env != nullptr, err = CHIP_JNI_ERROR_NO_ENV);
+
+ {
+ UtfString jniCommissioneeName(env, stringCommissioneeName.data());
+ env->ExceptionClear();
+ env->CallVoidMethod(mJNIMyUserPrompterObject, mPromptForCommissionOKPermissionMethod, static_cast(vendorId),
+ static_cast(productId), jniCommissioneeName.jniValue());
+ if (env->ExceptionCheck())
+ {
+ ChipLogError(DeviceLayer, "Java exception in PromptForCommissionOKPermission");
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ err = CHIP_ERROR_INCORRECT_STATE;
+ goto exit;
+ }
+ }
+
+exit:
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(Zcl, "PromptForCommissionOKPermission error: %s", err.AsString());
+ }
+}
+
+/*
+ * Called to prompt the user to enter the setup pincode displayed by the given commissioneeName/vendorId/productId to be
+ * commissioned. For example "Please enter pin displayed in casting app."
+ *
+ * If user responds with OK then implementor calls UserPrompterResolver.OnPinCodeEntered();
+ * If user responds with Cancel then implementor calls UserPrompterResolver.OnPinCodeDeclined();
+ *
+ */
+void JNIMyUserPrompter::PromptForCommissionPincode(uint16_t vendorId, uint16_t productId, const char * commissioneeName)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
+ std::string stringCommissioneeName(commissioneeName);
+
+ VerifyOrExit(mJNIMyUserPrompterObject != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
+ VerifyOrExit(mPromptForCommissionPincodeMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
+ VerifyOrExit(env != nullptr, err = CHIP_JNI_ERROR_NO_ENV);
+
+ {
+ UtfString jniCommissioneeName(env, stringCommissioneeName.data());
+ env->ExceptionClear();
+ env->CallVoidMethod(mJNIMyUserPrompterObject, mPromptForCommissionPincodeMethod, static_cast(vendorId),
+ static_cast(productId), jniCommissioneeName.jniValue());
+ if (env->ExceptionCheck())
+ {
+ ChipLogError(Zcl, "Java exception in PromptForCommissionPincode");
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ err = CHIP_ERROR_INCORRECT_STATE;
+ goto exit;
+ }
+ }
+
+exit:
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(Zcl, "PromptForCommissionPincode error: %s", err.AsString());
+ }
+}
+
+/*
+ * Called to notify the user that commissioning succeeded. It can be in form of UI Notification.
+ */
+void JNIMyUserPrompter::PromptCommissioningSucceeded(uint16_t vendorId, uint16_t productId, const char * commissioneeName)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
+ std::string stringCommissioneeName(commissioneeName);
+
+ VerifyOrExit(mJNIMyUserPrompterObject != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
+ VerifyOrExit(mPromptCommissioningSucceededMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
+ VerifyOrExit(env != nullptr, err = CHIP_JNI_ERROR_NO_ENV);
+
+ {
+ UtfString jniCommissioneeName(env, stringCommissioneeName.data());
+ env->ExceptionClear();
+ env->CallVoidMethod(mJNIMyUserPrompterObject, mPromptCommissioningSucceededMethod, static_cast(vendorId),
+ static_cast(productId), jniCommissioneeName.jniValue());
+
+ if (env->ExceptionCheck())
+ {
+ ChipLogError(Zcl, "Java exception in PromptCommissioningSucceeded");
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ err = CHIP_ERROR_INCORRECT_STATE;
+ goto exit;
+ }
+ }
+
+exit:
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(Zcl, "PromptCommissioningSucceeded error: %s", err.AsString());
+ }
+}
+
+/*
+ * Called to notify the user that commissioning failed. It can be in form of UI Notification.
+ */
+void JNIMyUserPrompter::PromptCommissioningFailed(const char * commissioneeName, CHIP_ERROR error)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
+ std::string stringCommissioneeName(commissioneeName);
+
+ VerifyOrExit(mJNIMyUserPrompterObject != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
+ VerifyOrExit(mPromptCommissioningFailedMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
+ VerifyOrExit(env != nullptr, err = CHIP_JNI_ERROR_NO_ENV);
+
+ {
+ std::string stringError(error.AsString());
+ UtfString jniCommissioneeName(env, stringCommissioneeName.data());
+ UtfString jniCommissioneeError(env, stringError.data());
+ env->ExceptionClear();
+ env->CallVoidMethod(mJNIMyUserPrompterObject, mPromptCommissioningFailedMethod, jniCommissioneeName.jniValue(),
+ jniCommissioneeError.jniValue());
+
+ if (env->ExceptionCheck())
+ {
+ ChipLogError(Zcl, "Java exception in PromptCommissioningFailed");
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ err = CHIP_ERROR_INCORRECT_STATE;
+ goto exit;
+ }
+ }
+
+exit:
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(Zcl, "PromptCommissioningFailed error: %s", err.AsString());
+ }
+}
diff --git a/examples/tv-app/android/java/MyUserPrompter-JNI.h b/examples/tv-app/android/java/MyUserPrompter-JNI.h
new file mode 100644
index 00000000000000..cecdcb305643d2
--- /dev/null
+++ b/examples/tv-app/android/java/MyUserPrompter-JNI.h
@@ -0,0 +1,38 @@
+/*
+ *
+ * Copyright (c) 2022 Project CHIP Authors
+ *
+ * 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.
+ */
+#pragma once
+
+#include "CommissionerMain.h"
+#include "lib/support/logging/CHIPLogging.h"
+#include
+
+class JNIMyUserPrompter : public UserPrompter
+{
+public:
+ JNIMyUserPrompter(jobject prompter);
+ void PromptForCommissionOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override;
+ void PromptForCommissionPincode(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override;
+ void PromptCommissioningSucceeded(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override;
+ void PromptCommissioningFailed(const char * commissioneeName, CHIP_ERROR error) override;
+
+private:
+ jobject mJNIMyUserPrompterObject = nullptr;
+ jmethodID mPromptForCommissionOKPermissionMethod = nullptr;
+ jmethodID mPromptForCommissionPincodeMethod = nullptr;
+ jmethodID mPromptCommissioningSucceededMethod = nullptr;
+ jmethodID mPromptCommissioningFailedMethod = nullptr;
+};
diff --git a/examples/tv-app/android/java/MyUserPrompterResolver-JNI.cpp b/examples/tv-app/android/java/MyUserPrompterResolver-JNI.cpp
new file mode 100644
index 00000000000000..d48566813576f7
--- /dev/null
+++ b/examples/tv-app/android/java/MyUserPrompterResolver-JNI.cpp
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright (c) 2022 Project CHIP Authors
+ *
+ * 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.
+ */
+
+#include "MyUserPrompterResolver-JNI.h"
+#include "TvApp-JNI.h"
+#include
+#include
+#include
+#include
+#include
+
+using namespace chip;
+
+#define JNI_METHOD(RETURN, METHOD_NAME) \
+ extern "C" JNIEXPORT RETURN JNICALL Java_com_tcl_chip_tvapp_UserPrompterResolver_##METHOD_NAME
+
+JNI_METHOD(void, OnPinCodeEntered)(JNIEnv *, jobject, jint jPinCode)
+{
+ uint32_t pinCode = (uint32_t) jPinCode;
+ ChipLogProgress(Zcl, "OnPinCodeEntered %d", pinCode);
+ GetCommissionerDiscoveryController()->CommissionWithPincode(pinCode);
+}
+
+JNI_METHOD(void, OnPinCodeDeclined)(JNIEnv *, jobject)
+{
+ ChipLogProgress(Zcl, "OnPinCodeDeclined");
+ GetCommissionerDiscoveryController()->Cancel();
+}
+
+JNI_METHOD(void, OnPromptAccepted)(JNIEnv *, jobject)
+{
+ ChipLogProgress(Zcl, "OnPromptAccepted");
+ GetCommissionerDiscoveryController()->Ok();
+}
+
+JNI_METHOD(void, OnPromptDeclined)(JNIEnv *, jobject)
+{
+ ChipLogProgress(Zcl, "OnPromptDeclined");
+ GetCommissionerDiscoveryController()->Cancel();
+}
diff --git a/examples/tv-app/android/java/MyUserPrompterResolver-JNI.h b/examples/tv-app/android/java/MyUserPrompterResolver-JNI.h
new file mode 100644
index 00000000000000..2b14ca35873683
--- /dev/null
+++ b/examples/tv-app/android/java/MyUserPrompterResolver-JNI.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * Copyright (c) 2022 Project CHIP Authors
+ *
+ * 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.
+ */
+#pragma once
+
+#include "CommissionerMain.h"
+#include "lib/support/logging/CHIPLogging.h"
+#include
+
+class JNIMyUserPrompterResolver
+{
+};
diff --git a/examples/tv-app/android/java/TVApp-JNI.cpp b/examples/tv-app/android/java/TVApp-JNI.cpp
index fc4b69d702041c..9043a66864ff47 100644
--- a/examples/tv-app/android/java/TVApp-JNI.cpp
+++ b/examples/tv-app/android/java/TVApp-JNI.cpp
@@ -27,6 +27,7 @@
#include "LowPowerManager.h"
#include "MediaInputManager.h"
#include "MediaPlaybackManager.h"
+#include "MyUserPrompter-JNI.h"
#include "OnOffManager.h"
#include "WakeOnLanManager.h"
#include "credentials/DeviceAttestationCredsProvider.h"
@@ -48,6 +49,7 @@ using namespace chip::Credentials;
#define JNI_METHOD(RETURN, METHOD_NAME) extern "C" JNIEXPORT RETURN JNICALL Java_com_tcl_chip_tvapp_TvApp_##METHOD_NAME
TvAppJNI TvAppJNI::sInstance;
+JNIMyUserPrompter * userPrompter = nullptr;
void TvAppJNI::InitializeWithObjects(jobject app)
{
@@ -133,6 +135,11 @@ JNI_METHOD(void, setChannelManager)(JNIEnv *, jobject, jint endpoint, jobject ma
ChannelManager::NewManager(endpoint, manager);
}
+JNI_METHOD(void, setUserPrompter)(JNIEnv *, jobject, jobject prompter)
+{
+ userPrompter = new JNIMyUserPrompter(prompter);
+}
+
JNI_METHOD(void, setDACProvider)(JNIEnv *, jobject, jobject provider)
{
if (!chip::Credentials::IsDeviceAttestationCredentialsProviderSet())
@@ -155,7 +162,7 @@ JNI_METHOD(void, postServerInit)(JNIEnv *, jobject app)
chip::DeviceLayer::StackLock lock;
ChipLogProgress(Zcl, "TvAppJNI::postServerInit");
- InitVideoPlayerPlatform();
+ InitVideoPlayerPlatform(userPrompter);
}
JNI_METHOD(void, setOnOffManager)(JNIEnv *, jobject, jint endpoint, jobject manager)
diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvApp.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvApp.java
index f1b3d1a15d7a5e..d79e760f18c05d 100644
--- a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvApp.java
+++ b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvApp.java
@@ -79,6 +79,8 @@ public native int addContentApp(
public native void sendTestMessage(int endpoint, String message);
+ public native void setUserPrompter(UserPrompter userPrompter);
+
static {
System.loadLibrary("TvApp");
}
diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/UserPrompter.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/UserPrompter.java
new file mode 100644
index 00000000000000..2eef058de45886
--- /dev/null
+++ b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/UserPrompter.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2022 Project CHIP Authors
+ * All rights reserved.
+ *
+ * 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.tcl.chip.tvapp;
+
+public interface UserPrompter {
+
+ /*
+ * Called to prompt the user for consent to allow the given commissioneeName/vendorId/productId to be commissioned.
+ * For example "[commissioneeName] is requesting permission to cast to this TV, approve?"
+ *
+ * If user responds with OK then implementor calls UserPrompterResolver.OnPromptAccepted;
+ * If user responds with Cancel then implementor calls calls UserPrompterResolver.OnPromptDeclined();
+ *
+ */
+ void promptForCommissionOkPermission(int vendorId, int productId, String commissioneeName);
+
+ /*
+ * Called to prompt the user to enter the setup pincode displayed by the given commissioneeName/vendorId/productId to be
+ * commissioned. For example "Please enter pin displayed in casting app."
+ *
+ * If user responds with OK then implementor calls UserPrompterResolver.OnPinCodeEntered();
+ * If user responds with Cancel then implementor calls UserPrompterResolver.OnPinCodeDeclined();
+ *
+ */
+ void promptForCommissionPinCode(int vendorId, int productId, String commissioneeName);
+
+ /*
+ * Called to notify the user that commissioning succeeded. It can be in form of UI Notification.
+ */
+ void promptCommissioningSucceeded(int vendorId, int productId, String commissioneeName);
+
+ /*
+ * Called to notify the user that commissioning succeeded. It can be in form of UI Notification.
+ */
+ void promptCommissioningFailed(String commissioneeName, String error);
+}
diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/UserPrompterResolver.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/UserPrompterResolver.java
new file mode 100644
index 00000000000000..18c0bd181de770
--- /dev/null
+++ b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/UserPrompterResolver.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2022 Project CHIP Authors
+ * All rights reserved.
+ *
+ * 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.tcl.chip.tvapp;
+
+public class UserPrompterResolver {
+
+ private static final String TAG = "UserPrompterResolver";
+
+ public native void OnPinCodeEntered(int pinCode);
+
+ public native void OnPinCodeDeclined();
+
+ public native void OnPromptAccepted();
+
+ public native void OnPromptDeclined();
+
+ static {
+ System.loadLibrary("TvApp");
+ }
+}