Skip to content

Commit

Permalink
Take connection operations off the UI thread (project-chip#122) (proj…
Browse files Browse the repository at this point in the history
…ect-chip#25580)

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.
  • Loading branch information
cliffamzn authored and mwswartwout committed Mar 27, 2023
1 parent 78b72ce commit bfb2f39
Showing 1 changed file with 112 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -28,6 +29,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;
Expand All @@ -52,97 +56,123 @@ public void onCreate(Bundle savedInstanceState) {
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Callback callback = (ConnectionFragment.Callback) this.getActivity();

SuccessCallback<VideoPlayer> onConnectionSuccess =
new SuccessCallback<VideoPlayer>() {
@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<ContentApp> onNewOrUpdatedEndpoints =
new SuccessCallback<ContentApp>() {
@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 {
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<VideoPlayer> onConnectionSuccess =
new SuccessCallback<VideoPlayer>() {
@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<ContentApp> onNewOrUpdatedEndpoints =
new SuccessCallback<ContentApp>() {
@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<VideoPlayer> onConnectionSuccess,
FailureCallback onConnectionFailure,
SuccessCallback<ContentApp> 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. */
Expand Down

0 comments on commit bfb2f39

Please sign in to comment.