Skip to content

Commit

Permalink
Initial/untested: Endpoint, cluster, Command
Browse files Browse the repository at this point in the history
  • Loading branch information
sharadb-amazon committed Feb 13, 2024
1 parent 30a7636 commit 16dcb31
Show file tree
Hide file tree
Showing 45 changed files with 1,772 additions and 62 deletions.
6 changes: 2 additions & 4 deletions examples/tv-casting-app/APIs.md
Original file line number Diff line number Diff line change
Expand Up @@ -1042,10 +1042,8 @@ vendorIDAttribute!.read(nil) { context, before, after, err in
### Subscriptions
_{Complete Attribute subscription examples:
[Linux](linux/simple-app-helper.cpp)}_
_{Complete Attribute Read examples: [Linux](linux/simple-app-helper.cpp) |
_{Complete Attribute subscription examples: [Linux](linux/simple-app-helper.cpp)
|
[iOS](darwin/TvCasting/TvCasting/MCMediaPlaybackSubscribeToCurrentStateExampleViewModel.swift)}_
A Casting Client may subscribe to an attribute on an `Endpoint` of the
Expand Down
11 changes: 11 additions & 0 deletions examples/tv-casting-app/android/App/.idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.chip.casting.TvCastingApp;
import com.chip.casting.util.GlobalCastingConstants;
import com.chip.casting.util.PreferencesConfigurationManager;
import com.matter.casting.ActionSelectorFragment;
import com.matter.casting.ConnectionExampleFragment;
import com.matter.casting.DiscoveryExampleFragment;
import com.matter.casting.InitializationExample;
Expand All @@ -22,7 +23,8 @@ public class MainActivity extends AppCompatActivity
ConnectionFragment.Callback,
SelectClusterFragment.Callback,
DiscoveryExampleFragment.Callback,
ConnectionExampleFragment.Callback {
ConnectionExampleFragment.Callback,
ActionSelectorFragment.Callback {

private static final String TAG = MainActivity.class.getSimpleName();

Expand Down Expand Up @@ -73,9 +75,12 @@ public void handleCommissioningComplete() {
@Override
public void handleConnectionComplete(CastingPlayer castingPlayer) {
Log.i(TAG, "MainActivity.handleConnectionComplete() called ");
showFragment(ActionSelectorFragment.newInstance(castingPlayer));
}

// TODO: Implement in following PRs. Select Cluster Fragment.
// showFragment(SelectClusterFragment.newInstance(tvCastingApp));
@Override
public void handleContentLauncherLaunchURLSelected() {
showFragment(ContentLauncherFragment.newInstance(tvCastingApp));
}

@Override
Expand All @@ -95,7 +100,10 @@ public void handleMediaPlaybackSelected() {

@Override
public void handleDisconnect() {
showFragment(CommissionerDiscoveryFragment.newInstance(tvCastingApp));
showFragment(
GlobalCastingConstants.ChipCastingSimplified
? DiscoveryExampleFragment.newInstance()
: CommissionerDiscoveryFragment.newInstance(tvCastingApp));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ public class GlobalCastingConstants {
public static final int SetupPasscode = 20202021;
public static final int Discriminator = 0xF00;
public static final boolean ChipCastingSimplified =
false; // set this flag to true to demo simplified casting APIs
true; // set this flag to true to demo simplified casting APIs
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright (c) 2024 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.matter.casting;

import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.R;
import com.matter.casting.core.CastingPlayer;

/** An interstitial {@link Fragment} to select one of the supported media actions to perform */
public class ActionSelectorFragment extends Fragment {
private static final String TAG = ActionSelectorFragment.class.getSimpleName();

private final CastingPlayer selectedCastingPlayer;

private View.OnClickListener selectContentLauncherButtonClickListener;
private View.OnClickListener disconnectButtonClickListener;

public ActionSelectorFragment(CastingPlayer selectedCastingPlayer) {
this.selectedCastingPlayer = selectedCastingPlayer;
}

/**
* Use this factory method to create a new instance of this fragment using the provided
* parameters.
*
* @param selectedCastingPlayer CastingPlayer that the casting app connected to
* @return A new instance of fragment SelectActionFragment.
*/
public static ActionSelectorFragment newInstance(CastingPlayer selectedCastingPlayer) {
return new ActionSelectorFragment(selectedCastingPlayer);
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}

@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ActionSelectorFragment.Callback callback = (ActionSelectorFragment.Callback) this.getActivity();
this.selectContentLauncherButtonClickListener =
v -> {
Log.d(TAG, "handle() called on selectContentLauncherButtonClickListener");
callback.handleContentLauncherLaunchURLSelected();
};

this.disconnectButtonClickListener =
v -> {
Log.d(TAG, "Disconnecting from current casting player");
selectedCastingPlayer.disconnect();
callback.handleDisconnect();
};

return inflater.inflate(R.layout.fragment_matter_action_selector, container, false);
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Log.d(TAG, "ActionSelectorFragment.onViewCreated called");
getView()
.findViewById(R.id.selectContentLauncherButton)
.setOnClickListener(selectContentLauncherButtonClickListener);

getView().findViewById(R.id.disconnectButton).setOnClickListener(disconnectButtonClickListener);
}

/** Interface for notifying the host. */
public interface Callback {
/** Notifies listener to trigger transition on selection of Content Launcher cluster */
void handleContentLauncherLaunchURLSelected();

/** Notifies listener to trigger transition on click of the Disconnect button */
void handleDisconnect();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,26 +100,24 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
new EndpointFilter(null, 65521, new ArrayList<DeviceTypeStruct>());
// The desired commissioning window timeout and EndpointFilter are optional.
CompletableFuture<Void> completableFuture =
targetCastingPlayer.VerifyOrEstablishConnection(
targetCastingPlayer.verifyOrEstablishConnection(
MIN_CONNECTION_TIMEOUT_SEC, desiredEndpointFilter);

Log.d(TAG, "onViewCreated() verifyOrEstablishConnection() called");

Log.d(
TAG,
"onViewCreated() verifyOrEstablishConnection() completableFuture == null? "
+ (completableFuture == null));

completableFuture
.thenRun(
() -> {
.thenAccept(
(response) -> {
Log.i(
TAG,
"CompletableFuture.thenRun(), connected to CastingPlayer with deviceId: "
"CompletableFuture.thenAccept(), connected to CastingPlayer with deviceId: "
+ targetCastingPlayer.getDeviceId());
getActivity()
.runOnUiThread(
() -> {
connectionFragmentStatusTextView.setText(
"Connected to Casting Player with device name: "
+ targetCastingPlayer.getDeviceName());
connectionFragmentNextButton.setEnabled(true);
});
connectionFragmentNextButton.setEnabled(true);
})
.exceptionally(
exc -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package com.matter.casting;

import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.R;
import com.chip.casting.ContentApp;
import com.chip.casting.MatterCallbackHandler;
import com.chip.casting.MatterError;
import com.chip.casting.TvCastingApp;
import com.matter.casting.clusters.MatterClusters;
import com.matter.casting.clusters.MatterCommands;
import com.matter.casting.core.CastingPlayer;
import com.matter.casting.core.Endpoint;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

/** A {@link Fragment} to send Content Launcher LaunchURL command from the TV Casting App. */
public class ContentLauncherLaunchURLExampleFragment extends Fragment {
private static final String TAG = ContentLauncherLaunchURLExampleFragment.class.getSimpleName();
private static final Integer SAMPLE_ENDPOINT_VID = 65521;

private final CastingPlayer selectedCastingPlayer;

private View.OnClickListener launchUrlButtonClickListener;

private static final ContentApp kContentApp = new ContentApp((short) 4, null);

public ContentLauncherLaunchURLExampleFragment(CastingPlayer selectedCastingPlayer) {
this.selectedCastingPlayer = selectedCastingPlayer;
}

/**
* Use this factory method to create a new instance of this fragment using the provided
* parameters.
*
* @param selectedCastingPlayer CastingPlayer that the casting app connected to
* @return A new instance of fragment ContentLauncherLaunchURLExampleFragment.
*/
public static ContentLauncherLaunchURLExampleFragment newInstance(CastingPlayer selectedCastingPlayer) {
return new ContentLauncherLaunchURLExampleFragment(selectedCastingPlayer);
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}

@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
this.launchUrlButtonClickListener =
v -> {
List<Endpoint> endpoints = selectedCastingPlayer.getEndpoints();
if(endpoints == null)
{
Log.e(TAG, "No Endpoints found on CastingPlayer");
return;
}

Endpoint endpoint = endpoints.stream().filter(e -> SAMPLE_ENDPOINT_VID.equals(e.getVendorId())).findFirst().get();
if(endpoint == null)
{
Log.e(TAG, "No Endpoint with chosen vendorID: " + SAMPLE_ENDPOINT_VID + " found on CastingPlayer");
return;
}

if(!endpoint.hasCluster(MatterClusters.ContentLauncherCluster.class))
{
Log.e(TAG, "Endpoint with chosen vendorID does not support ContentLauncher cluster");
return;
}

MatterClusters.ContentLauncherCluster cluster = endpoint.getCluster(MatterClusters.ContentLauncherCluster.class);
MatterCommands.ContentLauncherClusterLaunchURLCommand command = cluster.getCommand(MatterCommands.ContentLauncherClusterLaunchURLCommand.class);
if(command == null)
{
Log.e(TAG, "ContentLauncher cluster on Endpoint with chosen vendorID does not support LaunchURL command");
return;
}

MatterCommands.ContentLauncherClusterLaunchURLRequest request = new MatterCommands.ContentLauncherClusterLaunchURLRequest();
request.contentURL = ((EditText) getView().findViewById(R.id.contentUrlEditText)).getText().toString();
request.displayString = ((EditText) getView().findViewById(R.id.contentDisplayStringEditText)).getText().toString();
CompletableFuture<MatterCommands.ContentLauncherClusterResponse> responseFuture = command.invoke(request, null, 5000);
responseFuture.thenAccept(response -> {
Log.d(TAG, "Command response " + response);
TextView launchUrlStatus = getView().findViewById(R.id.launchUrlStatus);
getActivity().runOnUiThread(() -> launchUrlStatus.setText("Success! Response data: " + response.data));
})
.exceptionally(exc -> {
Log.e(TAG, "Command failure: " + exc.getMessage());
TextView launchUrlStatus = getView().findViewById(R.id.launchUrlStatus);
getActivity().runOnUiThread(() -> launchUrlStatus.setText("Command failure: " + exc.getMessage()));
return null;
});
};

return inflater.inflate(R.layout.fragment_content_launcher, container, false);
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Log.d(TAG, "ContentLauncherLaunchURLExampleFragment.onViewCreated called");
getView().findViewById(R.id.launchUrlButton).setOnClickListener(launchUrlButtonClickListener);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.matter.casting.clusters;

import com.matter.casting.core.MatterCluster;

public class MatterClusters {
public static class ContentLauncherCluster extends MatterCluster {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.matter.casting.clusters;

import com.matter.casting.core.MatterCommand;

public class MatterCommands {
public static class ContentLauncherClusterLaunchURLRequest {
public String contentURL;
public String displayString;
// Optional<ContentLauncherBrandingInformationStruct> brandingInformation;
}

public static class ContentLauncherClusterResponse {
public String data;
public Integer status;
}

public static class ContentLauncherClusterLaunchURLCommand
extends MatterCommand<
ContentLauncherClusterLaunchURLRequest, ContentLauncherClusterResponse> {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public interface CastingPlayer {

long getDeviceType();

List<Endpoint> getEndpoints();

@Override
String toString();

Expand Down Expand Up @@ -75,7 +77,7 @@ public interface CastingPlayer {
* com.matter.casting.core.CastingException. If the VerifyOrEstablishConnection fails, the
* CastingException will contain the error code and message from the CastingApp.
*/
CompletableFuture<Void> VerifyOrEstablishConnection(
CompletableFuture<Void> verifyOrEstablishConnection(
long commissioningWindowTimeoutSec, EndpointFilter desiredEndpointFilter);

/**
Expand All @@ -90,5 +92,7 @@ CompletableFuture<Void> VerifyOrEstablishConnection(
* com.matter.casting.core.CastingException. If the VerifyOrEstablishConnection fails, the
* CastingException will contain the error code and message from the CastingApp.
*/
CompletableFuture<Void> VerifyOrEstablishConnection();
CompletableFuture<Void> verifyOrEstablishConnection();

void disconnect();
}
Loading

0 comments on commit 16dcb31

Please sign in to comment.