diff --git a/examples/tv-casting-app/android/App/app/src/main/AndroidManifest.xml b/examples/tv-casting-app/android/App/app/src/main/AndroidManifest.xml index 2a75df6e946d21..e551de818eb5b0 100644 --- a/examples/tv-casting-app/android/App/app/src/main/AndroidManifest.xml +++ b/examples/tv-casting-app/android/App/app/src/main/AndroidManifest.xml @@ -10,7 +10,6 @@ - castingPlayerList = new ArrayList<>(); - // private FailureCallback failureCallback; - private static SuccessCallback discoverySuccessCallback; + private static ArrayAdapter arrayAdapter; // Get a singleton instance of the MatterCastingPlayerDiscovery private static final CastingPlayerDiscovery matterCastingPlayerDiscovery = @@ -77,15 +75,26 @@ public void onAdded(CastingPlayer castingPlayer) { TAG, "onAdded() Discovered CastingPlayer deviceId: " + castingPlayer.getDeviceId()); // Display CastingPlayer info on the screen - if (discoverySuccessCallback != null) { - discoverySuccessCallback.handle(castingPlayer); - } else { - Log.e( - TAG, - "onAdded() Warning: DiscoveryExampleFragment UX discoverySuccessCallback not set"); - } + new Handler(Looper.getMainLooper()) + .post( + () -> { + final Optional playerInList = + castingPlayerList + .stream() + .filter(node -> castingPlayer.equals(node)) + .findFirst(); + if (playerInList.isPresent()) { + Log.d( + TAG, + "onAdded() Replacing existing CastingPlayer entry " + + playerInList.get().getDeviceId() + + " in castingPlayerList list"); + arrayAdapter.remove(playerInList.get()); + } + arrayAdapter.add(castingPlayer); + }); } else { - Log.d(TAG, "onAdded()"); + Log.d(TAG, "onAdded() CastingPlayer is null"); // Attempt to invoke interface method on a null object reference will throw an error Log.d( TAG, @@ -96,20 +105,63 @@ public void onAdded(CastingPlayer castingPlayer) { @Override public void onChanged(CastingPlayer castingPlayer) { - Log.i( - TAG, - "onChanged() Discovered changes to CastingPlayer with deviceId: " - + castingPlayer.getDeviceId()); - // TODO: In following PRs. Consume changes to the provided CastingPlayer. + if (castingPlayer != null) { + Log.i( + TAG, + "onChanged() Discovered changes to CastingPlayer with deviceId: " + + castingPlayer.getDeviceId()); + // Update the CastingPlayer on the screen + new Handler(Looper.getMainLooper()) + .post( + () -> { + final Optional playerInList = + castingPlayerList + .stream() + .filter(node -> castingPlayer.equals(node)) + .findFirst(); + if (playerInList.isPresent()) { + Log.d( + TAG, + "onChanged() Updating existing CastingPlayer entry " + + playerInList.get().getDeviceId() + + " in castingPlayerList list"); + arrayAdapter.remove(playerInList.get()); + } + arrayAdapter.add(castingPlayer); + }); + } else { + Log.d(TAG, "onChanged() CastingPlayer is null"); + } } @Override public void onRemoved(CastingPlayer castingPlayer) { - Log.i( - TAG, - "onRemoved() Removed CastingPlayer with deviceId: " - + castingPlayer.getDeviceId()); - // TODO: In following PRs. Consume CastingPlayer removed or lost from the network. + if (castingPlayer != null) { + Log.i( + TAG, + "onRemoved() Removed CastingPlayer with deviceId: " + + castingPlayer.getDeviceId()); + // Remove CastingPlayer from the screen + new Handler(Looper.getMainLooper()) + .post( + () -> { + final Optional playerInList = + castingPlayerList + .stream() + .filter(node -> castingPlayer.equals(node)) + .findFirst(); + if (playerInList.isPresent()) { + Log.d( + TAG, + "onRemoved() Removing existing CastingPlayer entry " + + playerInList.get().getDeviceId() + + " in castingPlayerList list"); + arrayAdapter.remove(playerInList.get()); + } + }); + } else { + Log.d(TAG, "onRemoved() CastingPlayer is null"); + } } }; @@ -142,49 +194,22 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { matterDiscoveryMessageTextView.setText( getString(R.string.matter_discovery_message_initializing_text)); - ArrayAdapter arrayAdapter = - new CastingPlayerCommissionerAdapter(getActivity(), castingPlayerList); + arrayAdapter = new CastingPlayerArrayAdapter(getActivity(), castingPlayerList); final ListView list = getActivity().findViewById(R.id.castingPlayerList); list.setAdapter(arrayAdapter); Log.d(TAG, "onViewCreated() creating callbacks"); - this.discoverySuccessCallback = - new SuccessCallback() { - @Override - public void handle(CastingPlayer castingPlayer) { - Log.d( - TAG, - "SuccessCallback handle() CastingPlayer deviceId: " + castingPlayer.getDeviceId()); - new Handler(Looper.getMainLooper()) - .post( - () -> { - final Optional playerInList = - castingPlayerList - .stream() - .filter( - node -> castingPlayer.discoveredCastingPlayerHasSameSource(node)) - .findFirst(); - if (playerInList.isPresent()) { - Log.d( - TAG, - "Replacing existing CastingPlayer entry " - + playerInList.get().getDeviceId() - + " in castingPlayerList list"); - arrayAdapter.remove(playerInList.get()); - } - arrayAdapter.add(castingPlayer); - }); - } - }; - + // TODO: In following PRs. Enable startDiscoveryButton and stopDiscoveryButton when + // stopDiscovery is implemented in the core Matter SKD DNS-SD API. Enable in + // fragment_matter_discovery_example.xml Button startDiscoveryButton = getView().findViewById(R.id.startDiscoveryButton); startDiscoveryButton.setOnClickListener( v -> { Log.i( TAG, "onViewCreated() startDiscoveryButton button clicked. Calling startDiscovery()"); - arrayAdapter.clear(); - if (!startDiscovery(15)) { + // arrayAdapter.clear(); + if (!startDiscovery()) { Log.e(TAG, "onViewCreated() startDiscovery() call Failed"); } }); @@ -211,7 +236,7 @@ public void handle(CastingPlayer castingPlayer) { public void onResume() { super.onResume(); Log.i(TAG, "onResume() called. Calling startDiscovery()"); - if (!startDiscovery(15)) { + if (!startDiscovery()) { Log.e(TAG, "onResume() Warning: startDiscovery() call Failed"); } } @@ -220,8 +245,11 @@ public void onResume() { public void onPause() { super.onPause(); Log.i(TAG, "onPause() called"); - stopDiscovery(); - scheduledFutureTask.cancel(true); + // stopDiscovery(); + // Don't crash the app + if (scheduledFutureTask != null) { + scheduledFutureTask.cancel(true); + } } /** Interface for notifying the host. */ @@ -231,31 +259,26 @@ public interface Callback { void handleConnectionButtonClicked(CastingPlayer castingPlayer); } - private boolean startDiscovery(int discoveryDuration) { - Log.i(TAG, "startDiscovery() called, discoveryDuration: " + discoveryDuration + " seconds"); - if (discovering) { - Log.e(TAG, "startDiscovery() Warning: already discovering, stop before starting"); - return false; - } + private boolean startDiscovery() { + Log.i(TAG, "startDiscovery() called"); + // Add the implemented CastingPlayerChangeListener to listen to changes in the discovered // CastingPlayers - MatterError errAdd = + MatterError err = matterCastingPlayerDiscovery.addCastingPlayerChangeListener(castingPlayerChangeListener); - if (errAdd.hasError()) { - Log.e(TAG, "startDiscovery() addCastingPlayerChangeListener() called, errAdd: " + errAdd); + if (err.hasError()) { + Log.e(TAG, "startDiscovery() addCastingPlayerChangeListener() called, err Add: " + err); return false; } // Start discovery Log.i(TAG, "startDiscovery() calling startDiscovery()"); - MatterError errStart = matterCastingPlayerDiscovery.startDiscovery(); - if (errStart.hasError()) { - Log.e(TAG, "startDiscovery() startDiscovery() called, errStart: " + errStart); + err = matterCastingPlayerDiscovery.startDiscovery(DISCOVERY_TARGET_DEVICE_TYPE); + if (err.hasError()) { + Log.e(TAG, "startDiscovery() startDiscovery() called, err Start: " + err); return false; } - discovering = true; - discoveryAttempt++; - Log.i(TAG, "startDiscovery() started discovery attempt #" + discoveryAttempt); + Log.i(TAG, "startDiscovery() started discovery"); matterDiscoveryMessageTextView.setText( getString(R.string.matter_discovery_message_discovering_text)); @@ -264,45 +287,38 @@ private boolean startDiscovery(int discoveryDuration) { "startDiscovery() text set to: " + getString(R.string.matter_discovery_message_discovering_text)); + // TODO: In following PRs. Enable this to auto-stop discovery after stopDiscovery is + // implemented in the core Matter SKD DNS-SD API. // Schedule a service to stop discovery and remove the CastingPlayerChangeListener // Safe to call if discovery is not running - scheduledFutureTask = - executorService.schedule( - () -> { - Log.i( - TAG, - "startDiscovery() executorService " - + discoveryDuration - + " seconds timer expired. Calling stopDiscovery() on attempt #" - + discoveryAttempt); - stopDiscovery(); - }, - discoveryDuration, - TimeUnit.SECONDS); + // scheduledFutureTask = + // executorService.schedule( + // () -> { + // Log.i( + // TAG, + // "startDiscovery() executorService " + // + DISCOVERY_RUNTIME_SEC + // + " seconds timer expired. Auto-calling stopDiscovery()"); + // stopDiscovery(); + // }, + // DISCOVERY_RUNTIME_SEC, + // TimeUnit.SECONDS); return true; } private void stopDiscovery() { - Log.i(TAG, "stopDiscovery() called on attempt #" + discoveryAttempt); - boolean stopDiscoverySuccess = true; - if (!discovering) { - Log.e(TAG, "stopDiscovery() not discovering"); - return; - } + Log.i(TAG, "stopDiscovery() called"); // Stop discovery - MatterError errStop = matterCastingPlayerDiscovery.stopDiscovery(); - if (errStop.hasError()) { + MatterError err = matterCastingPlayerDiscovery.stopDiscovery(); + if (err.hasError()) { Log.e( TAG, - "stopDiscovery() MatterCastingPlayerDiscovery.stopDiscovery() called, errStop: " - + errStop); - stopDiscoverySuccess = false; + "stopDiscovery() MatterCastingPlayerDiscovery.stopDiscovery() called, err Stop: " + err); } else { // TODO: In following PRs. Implement stop discovery in the Android core API. Log.d(TAG, "stopDiscovery() MatterCastingPlayerDiscovery.stopDiscovery() success"); - discovering = false; } matterDiscoveryMessageTextView.setText( @@ -314,31 +330,26 @@ private void stopDiscovery() { // Remove the CastingPlayerChangeListener Log.i(TAG, "stopDiscovery() removing CastingPlayerChangeListener"); - MatterError errRemove = + err = matterCastingPlayerDiscovery.removeCastingPlayerChangeListener(castingPlayerChangeListener); - if (errRemove.hasError()) { + if (err.hasError()) { Log.e( TAG, - "stopDiscovery() matterCastingPlayerDiscovery.removeCastingPlayerChangeListener() called, errRemove: " - + errRemove); - stopDiscoverySuccess = false; - } - - if (!stopDiscoverySuccess) { - Log.e(TAG, "stopDiscovery() Warning: complete with errors! Discovering: " + discovering); + "stopDiscovery() matterCastingPlayerDiscovery.removeCastingPlayerChangeListener() called, err Remove: " + + err); } } } -class CastingPlayerCommissionerAdapter extends ArrayAdapter { +class CastingPlayerArrayAdapter extends ArrayAdapter { private final List playerList; private final Context context; private LayoutInflater inflater; - private static final String TAG = CastingPlayerCommissionerAdapter.class.getSimpleName(); + private static final String TAG = CastingPlayerArrayAdapter.class.getSimpleName(); - public CastingPlayerCommissionerAdapter(Context context, List playerList) { + public CastingPlayerArrayAdapter(Context context, List playerList) { super(context, 0, playerList); - Log.i(TAG, "CastingPlayerCommissionerAdapter() constructor called"); + Log.i(TAG, "CastingPlayerArrayAdapter() constructor called"); this.context = context; this.playerList = playerList; inflater = (LayoutInflater.from(context)); @@ -381,10 +392,9 @@ private String getCastingPlayerButtonText(CastingPlayer player) { player.getDeviceType() > 0 ? (aux.isEmpty() ? "" : ", ") + "Device Type: " + player.getDeviceType() : ""; - aux = aux.isEmpty() ? aux : "\n" + aux; + aux += (aux.isEmpty() ? "" : ", ") + "Resolved IP: " + (player.getNumberIPs() > 0); - // String preCommissioned = commissioner.isPreCommissioned() ? " (Pre-commissioned)" : ""; - // return main + aux + preCommissioned; + aux = aux.isEmpty() ? aux : "\n" + aux; return main + aux; } } diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingPlayer.java b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingPlayer.java index dfa7bf0d988e25..d359330f25ab97 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingPlayer.java +++ b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingPlayer.java @@ -19,6 +19,12 @@ import java.net.InetAddress; import java.util.List; +/** + * The CastingPlayer interface defines a Matter commissioner that is able to play media to a + * physical output or to a display screen which is part of the device (e.g. TV). It is discovered on + * the local network using Matter Commissioner discovery over DNS. It contains all the information + * about the service discovered/resolved. + */ public interface CastingPlayer { boolean isConnected(); @@ -42,7 +48,11 @@ public interface CastingPlayer { int getDeviceType(); - boolean discoveredCastingPlayerHasSameSource(Object o); + String toString(); + + boolean equals(Object o); + + int hashCode(); // TODO: Implement in following PRs. Related to player connection implementation. // List getEndpoints(); diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingPlayerDiscovery.java b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingPlayerDiscovery.java index 9455e3a75ef727..9f2a7f8faff68e 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingPlayerDiscovery.java +++ b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/CastingPlayerDiscovery.java @@ -17,35 +17,94 @@ package com.matter.casting.core; import android.util.Log; +import com.matter.casting.support.MatterError; import java.util.List; +/** + * The CastingPlayerDiscovery interface defines the API to control Matter Casting Player discovery + * over DNS-SD, and to collect discovery results. Discovery is centrally managed by the native C++ + * layer in the Matter SDK. This class exposes native functions to add and remove a + * CastingPlayerChangeListener, which contains the C++ to Java callbacks for when Casting Players + * are discovered, updated, or lost from the network. This class is a singleton. + */ public interface CastingPlayerDiscovery { + /** + * @return a list of Casting Players discovered during the current discovery session. This list is + * cleared when discovery stops. + */ List getCastingPlayers(); - com.matter.casting.support.MatterError startDiscovery(); + /** + * Starts Casting Players discovery or returns an error. + * + * @param discoveryTargetDeviceType the target device type to be discovered using DNS-SD. 35 + * represents device type of Matter Casting Player. + * @return a specific MatterError if the the operation failed or NO_ERROR if succeeded. + */ + MatterError startDiscovery(int discoveryTargetDeviceType); - com.matter.casting.support.MatterError stopDiscovery(); + /** + * Stops Casting Players discovery or returns an error. + * + * @return a specific MatterError if the the operation failed or NO_ERROR if succeeded. + */ + MatterError stopDiscovery(); - com.matter.casting.support.MatterError addCastingPlayerChangeListener( - CastingPlayerChangeListener listener); + /** + * Adds a CastingPlayerChangeListener instance to be used during discovery. The + * CastingPlayerChangeListener contains the C++ to Java callbacks for when Casting Players are + * discovered, updated, or lost from the network. Should be called prior to calling + * MatterCastingPlayerDiscovery.startDiscovery(). + * + * @param listener an instance of the CastingPlayerChangeListener to be implemented by the APIs + * consumer. + * @return a specific MatterError if the the operation failed or NO_ERROR if succeeded. + */ + MatterError addCastingPlayerChangeListener(CastingPlayerChangeListener listener); - com.matter.casting.support.MatterError removeCastingPlayerChangeListener( - CastingPlayerChangeListener listener); + /** + * Removes CastingPlayerChangeListener from the native layer. + * + * @param listener the specific instance of CastingPlayerChangeListener to be removed. + * @return a specific MatterError if the the operation failed or NO_ERROR if succeeded. + */ + MatterError removeCastingPlayerChangeListener(CastingPlayerChangeListener listener); /** - * The Casting Client discovers CastingPlayers using Matter Commissioner discovery over DNS-SD by - * listening for CastingPlayer events as they are discovered, updated, or lost from the network. + * The CastingPlayerChangeListener can discover CastingPlayers by implementing the onAdded, + * onChanged and onRemoved callbacks which are called as CastingPlayers, are discovered, updated, + * or lost from the network. The onAdded(), onChanged() and onRemoved() callbacks must be + * implemented by the APIs client. */ abstract class CastingPlayerChangeListener { static final String TAG = CastingPlayerChangeListener.class.getSimpleName(); + /** + * Called by the native C++ layer when a Casting Player is added to the local network. + * + * @param castingPlayer the Casting Player added. + */ public abstract void onAdded(CastingPlayer castingPlayer); + /** + * Called by the native C++ layer when a Casting Player on the local network is changed. + * + * @param castingPlayer the Casting Player changed. + */ public abstract void onChanged(CastingPlayer castingPlayer); + /** + * Called by the native C++ layer when a Casting Player is removed from the local network. + * + * @param castingPlayer the Casting Player removed. + */ public abstract void onRemoved(CastingPlayer castingPlayer); + /** + * The following methods are used to catch possible exceptions thrown by the methods above, when + * not implemented correctly. + */ protected final void _onAdded(CastingPlayer castingPlayer) { try { onAdded(castingPlayer); diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/MatterCastingPlayer.java b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/MatterCastingPlayer.java index da0ce3a68125a6..fdf8b4e2cc74fd 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/MatterCastingPlayer.java +++ b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/MatterCastingPlayer.java @@ -16,14 +16,17 @@ */ package com.matter.casting.core; -import android.util.Log; import java.net.InetAddress; import java.util.List; import java.util.Objects; -/** The Matter Commissioner (e.g. TV) discovered using Matter Commissioner discovery. */ +/** + * A Matter Casting Player represents a Matter commissioner that is able to play media to a physical + * output or to a display screen which is part of the device (e.g. TV). It is discovered on the + * local network using Matter Commissioner discovery over DNS. It contains all the information about + * the service discovered/resolved. + */ public class MatterCastingPlayer implements CastingPlayer { - private static final String TAG = MatterCastingPlayer.class.getSimpleName(); private boolean connected; private String deviceId; private String deviceName; @@ -48,8 +51,6 @@ public MatterCastingPlayer( int productId, int vendorId, int deviceType) { - Log.d(TAG, "MatterCastingPlayer() constructor, building player with deviceId: " + deviceId); - Log.d(TAG, "MatterCastingPlayer() constructor, building player with deviceName: " + deviceName); this.connected = connected; this.deviceId = deviceId; this.hostName = hostName; @@ -63,11 +64,19 @@ public MatterCastingPlayer( this.deviceType = deviceType; } + /** + * @return a boolean indicating whether a Casting Player instance is connected to the TV Casting + * App. + */ @Override public boolean isConnected() { return this.connected; } + /** + * @return a String representing the Casting Player device ID which is a concatenation of the + * device's IP address and port number. + */ @Override public String getDeviceId() { return this.deviceId; @@ -88,11 +97,13 @@ public String getInstanceName() { return this.instanceName; } + /** @return an int, corresponding to the number of valid IP addresses for this Casting PLayer. */ @Override public int getNumberIPs() { return this.numberIPs; } + /** @return a list of valid IP addresses for this Casting PLayer. */ @Override public List getIpAddresses() { return this.ipAddresses; @@ -119,10 +130,20 @@ public int getDeviceType() { } @Override - public boolean discoveredCastingPlayerHasSameSource(Object o) { + public String toString() { + return this.deviceId; + } + + @Override + public int hashCode() { + return this.deviceId.hashCode(); + } + + @Override + public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MatterCastingPlayer that = (MatterCastingPlayer) o; - return Objects.equals(deviceId, that.deviceId) && vendorId == that.vendorId; + return Objects.equals(this.deviceId, that.deviceId); } } diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/MatterCastingPlayerDiscovery.java b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/MatterCastingPlayerDiscovery.java index 66ce86aa301000..bcd36708e7e77b 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/MatterCastingPlayerDiscovery.java +++ b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/core/MatterCastingPlayerDiscovery.java @@ -16,36 +16,72 @@ */ package com.matter.casting.core; -import android.util.Log; +import com.matter.casting.support.MatterError; import java.util.List; +/** + * MatterCastingPlayerDiscovery provides an API to control Matter Casting Player discovery over + * DNS-SD, and to collect discovery results. Discovery is centrally managed by the native C++ layer + * in the Matter SDK. This class exposes native functions to add and remove a + * CastingPlayerChangeListener, which contains the C++ to Java callbacks for when Casting Players + * are discovered, updated, or lost from the network. This class is a singleton. + */ public final class MatterCastingPlayerDiscovery implements CastingPlayerDiscovery { private static final String TAG = MatterCastingPlayerDiscovery.class.getSimpleName(); private static MatterCastingPlayerDiscovery matterCastingPlayerDiscoveryInstance; // Methods: public static MatterCastingPlayerDiscovery getInstance() { - Log.d(TAG, "MatterCastingPlayerDiscovery.getInstance() called"); if (matterCastingPlayerDiscoveryInstance == null) { matterCastingPlayerDiscoveryInstance = new MatterCastingPlayerDiscovery(); } return matterCastingPlayerDiscoveryInstance; }; + /** + * @return a list of Casting Players discovered during the current discovery session. This list is + * cleared when discovery stops. + */ @Override public native List getCastingPlayers(); + /** + * Starts Casting Players discovery or returns an error. + * + * @param discoveryTargetDeviceType the target device type to be discovered using DNS-SD. 35 + * represents device type of Matter Casting Player. + * @return a specific MatterError if the the operation failed or NO_ERROR if succeeded. + */ @Override - public native com.matter.casting.support.MatterError startDiscovery(); + public native MatterError startDiscovery(int discoveryTargetDeviceType); + /** + * Stops Casting Players discovery or returns an error. + * + * @return a specific MatterError if the the operation failed or NO_ERROR if succeeded. + */ @Override - public native com.matter.casting.support.MatterError stopDiscovery(); + public native MatterError stopDiscovery(); + /** + * Adds a CastingPlayerChangeListener instance to be used during discovery. The + * CastingPlayerChangeListener contains the C++ to Java callbacks for when Casting Players are + * discovered, updated, or lost from the network. Should be called prior to calling + * MatterCastingPlayerDiscovery.startDiscovery(). + * + * @param listener an instance of the CastingPlayerChangeListener to be implemented by the APIs + * consumer. + * @return a specific MatterError if the the operation failed or NO_ERROR if succeeded. + */ @Override - public native com.matter.casting.support.MatterError addCastingPlayerChangeListener( - CastingPlayerChangeListener listener); + public native MatterError addCastingPlayerChangeListener(CastingPlayerChangeListener listener); + /** + * Removes CastingPlayerChangeListener from the native layer. + * + * @param listener the specific instance of CastingPlayerChangeListener to be removed. + * @return a specific MatterError if the the operation failed or NO_ERROR if succeeded. + */ @Override - public native com.matter.casting.support.MatterError removeCastingPlayerChangeListener( - CastingPlayerChangeListener listener); + public native MatterError removeCastingPlayerChangeListener(CastingPlayerChangeListener listener); } diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/CastingPlayerDiscovery-JNI.cpp b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/CastingPlayerDiscovery-JNI.cpp index c3b4e2dd0e3613..d585b3412abd93 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/CastingPlayerDiscovery-JNI.cpp +++ b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/core/CastingPlayerDiscovery-JNI.cpp @@ -41,7 +41,7 @@ namespace casting { namespace core { /** - * @brief React to CastingPlayer discovery results with this sigleton + * @brief React to CastingPlayer discovery results with this singleton */ class DiscoveryDelegateImpl : public DiscoveryDelegate { @@ -59,11 +59,8 @@ class DiscoveryDelegateImpl : public DiscoveryDelegate static DiscoveryDelegateImpl * GetInstance() { - ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI::DiscoveryDelegateImpl::GetInstance() called"); if (DiscoveryDelegateImpl::discoveryDelegateImplSingletonInstance == nullptr) { - ChipLogProgress(AppServer, - "CastingPlayerDiscovery-JNI::DiscoveryDelegateImpl::GetInstance() creating new singleton instance"); DiscoveryDelegateImpl::discoveryDelegateImplSingletonInstance = new DiscoveryDelegateImpl(); } return DiscoveryDelegateImpl::discoveryDelegateImplSingletonInstance; @@ -71,91 +68,80 @@ class DiscoveryDelegateImpl : public DiscoveryDelegate void HandleOnAdded(matter::casting::memory::Strong player) override { - ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI::DiscoveryDelegateImpl::HandleOnAdded() called with CastingPlayer:"); - player->LogDetail(); + ChipLogProgress(AppServer, + "CastingPlayerDiscovery-JNI::DiscoveryDelegateImpl::HandleOnAdded() called with CastingPlayer, ID: %s", + player->GetId()); + + VerifyOrReturn(castingPlayerChangeListenerJavaObject != nullptr, + ChipLogError(AppServer, + "CastingPlayerDiscovery-JNI::DiscoveryDelegateImpl::HandleOnAdded() Warning: Not set, " + "CastingPlayerChangeListener == nullptr")); + VerifyOrReturn(onAddedCallbackJavaMethodID != nullptr, + ChipLogError(AppServer, + "CastingPlayerDiscovery-JNI::DiscoveryDelegateImpl::HandleOnAdded() Warning: Not set, " + "onAddedCallbackJavaMethodID == nullptr")); - if (castingPlayerChangeListenerJavaObject == nullptr || onAddedCallbackJavaMethodID == nullptr) - { - ChipLogError(AppServer, - "CastingPlayerDiscovery-JNI::DiscoveryDelegateImpl::HandleOnAdded() Warning: Not specified, " - "CastingPlayerChangeListener == nullptr"); - } - else - { - JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); - jobject matterCastingPlayerJavaObject = support::createJCastingPlayer(player); - if (matterCastingPlayerJavaObject == nullptr) - { - ChipLogError(AppServer, - "CastingPlayerDiscovery-JNI::DiscoveryDelegateImpl::HandleOnAdded() Warning: Could not create " - "CastingPlayer jobject"); - } - else - { - env->CallVoidMethod(castingPlayerChangeListenerJavaObject, onAddedCallbackJavaMethodID, - matterCastingPlayerJavaObject); - } - } + jobject matterCastingPlayerJavaObject = support::createJCastingPlayer(player); + VerifyOrReturn(matterCastingPlayerJavaObject != nullptr, + ChipLogError(AppServer, + "CastingPlayerDiscovery-JNI::DiscoveryDelegateImpl::HandleOnAdded() Warning: Could not create " + "CastingPlayer jobject")); + + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + env->CallVoidMethod(castingPlayerChangeListenerJavaObject, onAddedCallbackJavaMethodID, matterCastingPlayerJavaObject); } void HandleOnUpdated(matter::casting::memory::Strong player) override { ChipLogProgress(AppServer, - "CastingPlayerDiscovery-JNI DiscoveryDelegateImpl::HandleOnUpdated() called with CastingPlayer:"); - player->LogDetail(); + "CastingPlayerDiscovery-JNI DiscoveryDelegateImpl::HandleOnUpdated() called with CastingPlayer, ID: %s", + player->GetId()); + + VerifyOrReturn(castingPlayerChangeListenerJavaObject != nullptr, + ChipLogError(AppServer, + "CastingPlayerDiscovery-JNI::DiscoveryDelegateImpl::HandleOnUpdated() Warning: Not set, " + "CastingPlayerChangeListener == nullptr")); + VerifyOrReturn(onChangedCallbackJavaMethodID != nullptr, + ChipLogError(AppServer, + "CastingPlayerDiscovery-JNI::DiscoveryDelegateImpl::HandleOnUpdated() Warning: Not set, " + "onChangedCallbackJavaMethodID == nullptr")); - if (castingPlayerChangeListenerJavaObject == nullptr || onChangedCallbackJavaMethodID == nullptr) - { - ChipLogError(AppServer, - "CastingPlayerDiscovery-JNI::DiscoveryDelegateImpl::HandleOnUpdated() Warning: Not specified, " - "CastingPlayerChangeListener == nullptr"); - } - else - { - JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); - jobject matterCastingPlayerJavaObject = support::createJCastingPlayer(player); - if (matterCastingPlayerJavaObject == nullptr) - { - ChipLogError(AppServer, - "CastingPlayerDiscovery-JNI::DiscoveryDelegateImpl::HandleOnUpdated() Warning: Could not create " - "CastingPlayer jobject"); - } - else - { - env->CallVoidMethod(castingPlayerChangeListenerJavaObject, onChangedCallbackJavaMethodID, - matterCastingPlayerJavaObject); - } - } + jobject matterCastingPlayerJavaObject = support::createJCastingPlayer(player); + VerifyOrReturn(matterCastingPlayerJavaObject != nullptr, + ChipLogError(AppServer, + "CastingPlayerDiscovery-JNI::DiscoveryDelegateImpl::HandleOnUpdated() Warning: Could not " + "create CastingPlayer jobject")); + + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + env->CallVoidMethod(castingPlayerChangeListenerJavaObject, onChangedCallbackJavaMethodID, matterCastingPlayerJavaObject); } // TODO: In following PRs. Implement HandleOnRemoved after implemented in tv-casting-commom CastingPlayerDiscovery.h/cpp // void HandleOnRemoved(matter::casting::memory::Strong player) override // { // ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI DiscoveryDelegateImpl::HandleOnRemoved() called with - // CastingPlayer:"); player->LogDetail(); + // CastingPlayer, ID: %s", player->GetId()); // } }; // Initialize the static instance to nullptr DiscoveryDelegateImpl * DiscoveryDelegateImpl::discoveryDelegateImplSingletonInstance = nullptr; -JNI_METHOD(jobject, startDiscovery)(JNIEnv * env, jobject) +JNI_METHOD(jobject, startDiscovery)(JNIEnv * env, jobject, jint discoveryTargetDeviceType) { chip::DeviceLayer::StackLock lock; - ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI::startDiscovery() called"); - DiscoveryDelegateImpl * delegate = DiscoveryDelegateImpl::GetInstance(); - CastingPlayerDiscovery::GetInstance()->SetDelegate(delegate); + ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI::startDiscovery() called with discoveryTargetDeviceType: %lu", + (uint64_t) discoveryTargetDeviceType); + CastingPlayerDiscovery::GetInstance()->SetDelegate(DiscoveryDelegateImpl::GetInstance()); - // Start CastingPlayer discovery - const uint64_t kTargetPlayerDeviceType = 35; // 35 represents device type of Matter Video Player - CHIP_ERROR err = CastingPlayerDiscovery::GetInstance()->StartDiscovery(kTargetPlayerDeviceType); + // Start CastingPlayer discovery, 35 represents device type of Matter Casting Player + CHIP_ERROR err = CastingPlayerDiscovery::GetInstance()->StartDiscovery((uint64_t) discoveryTargetDeviceType); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "CastingPlayerDiscovery-JNI startDiscovery() err: %" CHIP_ERROR_FORMAT, err.Format()); + return support::createJMatterError(err); } - // TODO: Verify error returned? - VerifyOrReturnValue(err == CHIP_NO_ERROR, support::createJMatterError(CHIP_ERROR_INCORRECT_STATE)); return support::createJMatterError(CHIP_NO_ERROR); } @@ -169,9 +155,9 @@ JNI_METHOD(jobject, stopDiscovery)(JNIEnv * env, jobject) if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "CastingPlayerDiscovery-JNI::StopDiscovery() err: %" CHIP_ERROR_FORMAT, err.Format()); + return support::createJMatterError(err); } - VerifyOrReturnValue(err == CHIP_NO_ERROR, support::createJMatterError(CHIP_ERROR_INCORRECT_STATE)); return support::createJMatterError(CHIP_NO_ERROR); } @@ -181,8 +167,7 @@ JNI_METHOD(jobject, addCastingPlayerChangeListener)(JNIEnv * env, jobject, jobje ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI::addCastingPlayerChangeListener() called"); VerifyOrReturnValue(castingPlayerChangeListenerJavaObject != nullptr, support::createJMatterError(CHIP_ERROR_INCORRECT_STATE)); - DiscoveryDelegateImpl * delegate = DiscoveryDelegateImpl::GetInstance(); - if (delegate->castingPlayerChangeListenerJavaObject != nullptr) + if (DiscoveryDelegateImpl::GetInstance()->castingPlayerChangeListenerJavaObject != nullptr) { ChipLogError(AppServer, "CastingPlayerDiscovery-JNI::addCastingPlayerChangeListener() Warning: Call removeCastingPlayerChangeListener " @@ -205,10 +190,11 @@ JNI_METHOD(jobject, addCastingPlayerChangeListener)(JNIEnv * env, jobject, jobje // support::createJMatterError(CHIP_ERROR_INCORRECT_STATE)); // Set Java callbacks in the DiscoveryDelegateImpl Singleton - delegate->castingPlayerChangeListenerJavaObject = env->NewGlobalRef(castingPlayerChangeListenerJavaObject); - delegate->onAddedCallbackJavaMethodID = onAddedJavaMethodID; - delegate->onChangedCallbackJavaMethodID = onChangedJavaMethodID; - // delegate->onRemovedCallbackJavaMethodID = onRemovedJavaMethodID; + DiscoveryDelegateImpl::GetInstance()->castingPlayerChangeListenerJavaObject = + env->NewGlobalRef(castingPlayerChangeListenerJavaObject); + DiscoveryDelegateImpl::GetInstance()->onAddedCallbackJavaMethodID = onAddedJavaMethodID; + DiscoveryDelegateImpl::GetInstance()->onChangedCallbackJavaMethodID = onChangedJavaMethodID; + // DiscoveryDelegateImpl::GetInstance()->onRemovedCallbackJavaMethodID = onRemovedJavaMethodID; return support::createJMatterError(CHIP_NO_ERROR); } @@ -218,24 +204,20 @@ JNI_METHOD(jobject, removeCastingPlayerChangeListener)(JNIEnv * env, jobject, jo chip::DeviceLayer::StackLock lock; ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI::removeCastingPlayerChangeListener() called"); - // Remove the Java callbacks in the DiscoveryDelegateImpl Singleton - DiscoveryDelegateImpl * delegate = DiscoveryDelegateImpl::GetInstance(); - // Check if the passed object is the same as the one added in addCastingPlayerChangeListener() JNI method - jboolean isSameObject = - env->IsSameObject(castingPlayerChangeListenerJavaObject, delegate->castingPlayerChangeListenerJavaObject); + jboolean isSameObject = env->IsSameObject(castingPlayerChangeListenerJavaObject, + DiscoveryDelegateImpl::GetInstance()->castingPlayerChangeListenerJavaObject); if ((bool) isSameObject) { - ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI::removeCastingPlayerChangeListener() removing listener"); - // Delete the global reference to the Java object - env->DeleteGlobalRef(delegate->castingPlayerChangeListenerJavaObject); - delegate->castingPlayerChangeListenerJavaObject = nullptr; + env->DeleteGlobalRef(DiscoveryDelegateImpl::GetInstance()->castingPlayerChangeListenerJavaObject); + // Remove the Java callbacks in the DiscoveryDelegateImpl Singleton + DiscoveryDelegateImpl::GetInstance()->castingPlayerChangeListenerJavaObject = nullptr; // No explicit cleanup required - delegate->onAddedCallbackJavaMethodID = nullptr; - delegate->onChangedCallbackJavaMethodID = nullptr; - // delegate->onRemovedCallbackJavaMethodID = nullptr; + DiscoveryDelegateImpl::GetInstance()->onAddedCallbackJavaMethodID = nullptr; + DiscoveryDelegateImpl::GetInstance()->onChangedCallbackJavaMethodID = nullptr; + // DiscoveryDelegateImpl::GetInstance()->onRemovedCallbackJavaMethodID = nullptr; return support::createJMatterError(CHIP_NO_ERROR); } @@ -267,12 +249,7 @@ JNI_METHOD(jobject, getCastingPlayers)(JNIEnv * env, jobject) if (matterCastingPlayerJavaObject != nullptr) { jboolean added = env->CallBooleanMethod(arrayList, addMethod, matterCastingPlayerJavaObject); - if ((bool) added) - { - ChipLogProgress(AppServer, "CastingPlayerDiscovery-JNI::getCastingPlayers() added CastingPlayer with ID: %s", - player->GetId()); - } - else + if (!((bool) added)) { ChipLogError(AppServer, "CastingPlayerDiscovery-JNI::getCastingPlayers() Warning: Unable to add CastingPlayer with ID: %s", diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/support/CastingPlayerConverter-JNI.cpp b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/support/CastingPlayerConverter-JNI.cpp index 04208bfbc7c97f..86d42a76fabc13 100644 --- a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/support/CastingPlayerConverter-JNI.cpp +++ b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/support/CastingPlayerConverter-JNI.cpp @@ -52,17 +52,7 @@ jobject createJCastingPlayer(matter::casting::memory::StrongIsConnected()) - { - connected = true; - } - jboolean connectedJavaBoolean = static_cast(connected); - jstring idJavaString = env->NewStringUTF(player->GetId()); - jstring deviceNameJavaString = env->NewStringUTF(player->GetDeviceName()); - jstring hostNameJavaString = env->NewStringUTF(player->GetHostName()); - jstring instanceNameJavaString = env->NewStringUTF(player->GetInstanceName()); - jint numIPsJavaInt = (jint) (player->GetNumIPs()); + bool connected = player->IsConnected() ? true : false; jobject ipAddressListJavaObject = nullptr; const chip::Inet::IPAddress * ipAddresses = player->GetIPAddresses(); @@ -84,17 +74,14 @@ jobject createJCastingPlayer(matter::casting::memory::StrongGetPort()); - jint productIdJavaInt = (jint) (player->GetProductId()); - jint vendorIdJavaInt = (jint) (player->GetVendorId()); - jint deviceTypeJavaInt = (jint) (player->GetDeviceType()); - // Create a new instance of the MatterCastingPlayer Java class jobject matterCastingPlayerJavaObject = nullptr; matterCastingPlayerJavaObject = - env->NewObject(matterCastingPlayerJavaClass, constructor, connectedJavaBoolean, idJavaString, hostNameJavaString, - deviceNameJavaString, instanceNameJavaString, numIPsJavaInt, ipAddressListJavaObject, portJavaInt, - productIdJavaInt, vendorIdJavaInt, deviceTypeJavaInt); + env->NewObject(matterCastingPlayerJavaClass, constructor, static_cast(connected), + env->NewStringUTF(player->GetId()), env->NewStringUTF(player->GetHostName()), + env->NewStringUTF(player->GetDeviceName()), env->NewStringUTF(player->GetInstanceName()), + (jint) (player->GetNumIPs()), ipAddressListJavaObject, (jint) (player->GetPort()), + (jint) (player->GetProductId()), (jint) (player->GetVendorId()), (jint) (player->GetDeviceType())); if (matterCastingPlayerJavaObject == nullptr) { ChipLogError(AppServer, diff --git a/examples/tv-casting-app/android/App/app/src/main/res/layout/fragment_matter_discovery_example.xml b/examples/tv-casting-app/android/App/app/src/main/res/layout/fragment_matter_discovery_example.xml index 57b59355ff5e1b..1c3eb13ce6c486 100644 --- a/examples/tv-casting-app/android/App/app/src/main/res/layout/fragment_matter_discovery_example.xml +++ b/examples/tv-casting-app/android/App/app/src/main/res/layout/fragment_matter_discovery_example.xml @@ -15,12 +15,14 @@ android:padding="10sp">