From 37456565f9a164f6066db92e4eba62a95a3477dd Mon Sep 17 00:00:00 2001 From: Cliff Chung <116232729+cliffamzn@users.noreply.github.com> Date: Tue, 7 Mar 2023 15:31:26 -0800 Subject: [PATCH] Take connection operations off the UI thread (#122) Clicking connect in the tv-casting-app freezes the UI for a few seconds because it tries to attempt the connection on the UI thread. This puts it in an async thread so that the UI is not blocked while its trying to establish that connection. Tested by attempting the connection and verifying it wasn't paused and still showed the right messages and connected successfully. --- .../chip/casting/app/ConnectionFragment.java | 195 ++++++++++-------- 1 file changed, 112 insertions(+), 83 deletions(-) diff --git a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/ConnectionFragment.java b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/ConnectionFragment.java index c48b28974ef22e..6bbca778fa1a37 100644 --- a/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/ConnectionFragment.java +++ b/examples/tv-casting-app/android/App/app/src/main/java/com/chip/casting/app/ConnectionFragment.java @@ -17,6 +17,7 @@ import com.chip.casting.TvCastingApp; import com.chip.casting.VideoPlayer; import com.chip.casting.util.GlobalCastingConstants; +import java.util.concurrent.Executors; /** A {@link Fragment} to get the TV Casting App commissioned / connected. */ public class ConnectionFragment extends Fragment { @@ -29,6 +30,9 @@ public class ConnectionFragment extends Fragment { private boolean openCommissioningWindowSuccess; private boolean sendUdcSuccess; + private TextView commissioningWindowStatusView; + private TextView onboardingPayloadView; + public ConnectionFragment(TvCastingApp tvCastingApp, DiscoveredNodeData selectedCommissioner) { this.tvCastingApp = tvCastingApp; this.selectedCommissioner = selectedCommissioner; @@ -53,98 +57,123 @@ public void onCreate(Bundle savedInstanceState) { @Override public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - Callback callback = (ConnectionFragment.Callback) this.getActivity(); - - SuccessCallback onConnectionSuccess = - new SuccessCallback() { - @Override - public void handle(VideoPlayer videoPlayer) { - Log.d(TAG, "handle() called on OnConnectionSuccess with " + videoPlayer); - callback.handleCommissioningComplete(); - } - }; - - FailureCallback onConnectionFailure = - new FailureCallback() { - @Override - public void handle(MatterError matterError) { - Log.d(TAG, "handle() called on OnConnectionFailure with " + matterError); - } - }; - - SuccessCallback onNewOrUpdatedEndpoints = - new SuccessCallback() { - @Override - public void handle(ContentApp contentApp) { - Log.d(TAG, "handle() called on OnNewOrUpdatedEndpoint with " + contentApp); - } - }; - - if (selectedCommissioner != null && selectedCommissioner.isPreCommissioned()) { - VideoPlayer videoPlayer = selectedCommissioner.toConnectableVideoPlayer(); - Log.d(TAG, "Calling verifyOrEstablishConnectionSuccess with VideoPlayer: " + videoPlayer); - this.verifyOrEstablishConnectionSuccess = - tvCastingApp.verifyOrEstablishConnection( - videoPlayer, onConnectionSuccess, onConnectionFailure, onNewOrUpdatedEndpoints); - } else { - Log.d(TAG, "Running commissioning"); - this.openCommissioningWindowSuccess = - tvCastingApp.openBasicCommissioningWindow( - GlobalCastingConstants.CommissioningWindowDurationSecs, - new MatterCallbackHandler() { - @Override - public void handle(MatterError error) { - Log.d(TAG, "handle() called on CommissioningComplete event with " + error); - } - }, - onConnectionSuccess, - onConnectionFailure, - onNewOrUpdatedEndpoints); - - if (this.openCommissioningWindowSuccess) { - if (selectedCommissioner != null && selectedCommissioner.getNumIPs() > 0) { - String ipAddress = selectedCommissioner.getIpAddresses().get(0).getHostAddress(); - Log.d( - TAG, - "ConnectionFragment calling tvCastingApp.sendUserDirectedCommissioningRequest with IP: " - + ipAddress - + " port: " - + selectedCommissioner.getPort()); - - this.sendUdcSuccess = tvCastingApp.sendCommissioningRequest(selectedCommissioner); - } - } - } - return inflater.inflate(R.layout.fragment_connection, container, false); } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - String commissioningWindowStatus = ""; - if (selectedCommissioner != null && selectedCommissioner.isPreCommissioned()) { - commissioningWindowStatus = "Establishing connection with selected Video Player"; - } else { - commissioningWindowStatus = "Failed to open commissioning window"; - if (this.openCommissioningWindowSuccess) { - commissioningWindowStatus = "Commissioning window has been opened. Commission manually."; - if (this.sendUdcSuccess) { - commissioningWindowStatus = - "Commissioning window has been opened. Commissioning requested from " - + selectedCommissioner.getDeviceName(); - } - TextView onboardingPayloadView = getView().findViewById(R.id.onboardingPayload); - onboardingPayloadView.setText( - "Onboarding PIN: " - + GlobalCastingConstants.SetupPasscode - + "\nDiscriminator: " - + GlobalCastingConstants.Discriminator); + onboardingPayloadView = getView().findViewById(R.id.onboardingPayload); + commissioningWindowStatusView = getView().findViewById(R.id.commissioningWindowStatus); + + String commissioningWindowStatus = + (selectedCommissioner != null && selectedCommissioner.isPreCommissioned()) + ? "Establishing CASE session with video player..." + : "Requesting to be commissioned by the video player..."; + commissioningWindowStatusView.setText(commissioningWindowStatus); + + Executors.newSingleThreadExecutor() + .submit( + () -> { + Callback callback = (ConnectionFragment.Callback) this.getActivity(); + + SuccessCallback onConnectionSuccess = + new SuccessCallback() { + @Override + public void handle(VideoPlayer videoPlayer) { + Log.d(TAG, "handle() called on OnConnectionSuccess with " + videoPlayer); + callback.handleCommissioningComplete(); + } + }; + + FailureCallback onConnectionFailure = + new FailureCallback() { + @Override + public void handle(MatterError matterError) { + Log.d(TAG, "handle() called on OnConnectionFailure with " + matterError); + } + }; + + SuccessCallback onNewOrUpdatedEndpoints = + new SuccessCallback() { + @Override + public void handle(ContentApp contentApp) { + Log.d(TAG, "handle() called on OnNewOrUpdatedEndpoint with " + contentApp); + } + }; + + if (selectedCommissioner != null && selectedCommissioner.isPreCommissioned()) { + VideoPlayer videoPlayer = selectedCommissioner.toConnectableVideoPlayer(); + Log.d( + TAG, + "Calling verifyOrEstablishConnectionSuccess with VideoPlayer: " + videoPlayer); + tvCastingApp.verifyOrEstablishConnection( + videoPlayer, onConnectionSuccess, onConnectionFailure, onNewOrUpdatedEndpoints); + } else { + beginCommissioning( + onConnectionSuccess, onConnectionFailure, onNewOrUpdatedEndpoints); + } + }); + } + + private void beginCommissioning( + SuccessCallback onConnectionSuccess, + FailureCallback onConnectionFailure, + SuccessCallback onNewOrUpdatedEndpoints) { + Log.d(TAG, "Running commissioning"); + this.openCommissioningWindowSuccess = + tvCastingApp.openBasicCommissioningWindow( + GlobalCastingConstants.CommissioningWindowDurationSecs, + new MatterCallbackHandler() { + @Override + public void handle(MatterError error) { + Log.d(TAG, "handle() called on CommissioningComplete event with " + error); + } + }, + onConnectionSuccess, + onConnectionFailure, + onNewOrUpdatedEndpoints); + + if (this.openCommissioningWindowSuccess) { + if (selectedCommissioner != null && selectedCommissioner.getNumIPs() > 0) { + String ipAddress = selectedCommissioner.getIpAddresses().get(0).getHostAddress(); + Log.d( + TAG, + "ConnectionFragment calling tvCastingApp.sendUserDirectedCommissioningRequest with IP: " + + ipAddress + + " port: " + + selectedCommissioner.getPort()); + + this.sendUdcSuccess = tvCastingApp.sendCommissioningRequest(selectedCommissioner); + updateUiOnConnectionSuccess(); } + } else { + getActivity() + .runOnUiThread( + () -> { + commissioningWindowStatusView.setText("Failed to open commissioning window"); + }); } + } - TextView commissioningWindowStatusView = getView().findViewById(R.id.commissioningWindowStatus); - commissioningWindowStatusView.setText(commissioningWindowStatus); + private void updateUiOnConnectionSuccess() { + getActivity() + .runOnUiThread( + () -> { + String finalCommissioningWindowStatus = + "Commissioning window has been opened. Commission manually."; + if (this.sendUdcSuccess) { + finalCommissioningWindowStatus = + "Commissioning window has been opened. Commissioning requested from " + + selectedCommissioner.getDeviceName(); + } + onboardingPayloadView.setText( + "Onboarding PIN: " + + GlobalCastingConstants.SetupPasscode + + "\nDiscriminator: " + + GlobalCastingConstants.Discriminator); + commissioningWindowStatusView.setText(finalCommissioningWindowStatus); + }); } /** Interface for notifying the host. */