From 1017480296306df3d87c3dca93fe58844caf9407 Mon Sep 17 00:00:00 2001 From: amitnj <74272437+amitnj@users.noreply.github.com> Date: Wed, 25 May 2022 10:52:02 -0700 Subject: [PATCH] Implement sending message from the linux layer of the android tv-app::platform-app to the tv-app::content-app (#18696) * Implement sending message from the linux layer of the android tv-app::platform-app to the tv-app::content-app. Resolving conflicts * Review fixes. Used more appropriate datatype wrappers. introduced null checks for hardcoded sample apps. * Squashed commit of the following: commit bde9a1bc2cb7e2d996fa4bd22e0cc3e8aab7c08a Author: Restyled.io Date: Fri May 20 22:25:44 2022 +0000 Restyled by clang-format commit 27c0f3d1bdf55a485855f547a9602b48e52ac888 Author: Restyled.io Date: Fri May 20 22:25:33 2022 +0000 Restyled by google-java-format commit 56bb8b487e885d3aadacc319400a3cd08567ffc3 Author: Restyled.io Date: Fri May 20 22:25:15 2022 +0000 Restyled by whitespace commit 22ba2749e33b40d5c4d0b01ec35c2bd048b1e91c Author: Amit Jain Date: Fri May 20 15:15:45 2022 -0700 Implement sending message from the linux layer of the android tv-app::platform-app to the tv-app::content-app. * More restyling fixes. * One more restyle commit. --- .../App/.idea/deploymentTargetDropDown.xml | 17 ++++ .../content-app/src/main/AndroidManifest.xml | 5 +- .../receiver/MatterCommandReceiver.java | 2 + .../src/main/res/raw/static_matter_clusters | 13 +-- .../com/matter/tv/server/MainActivity.java | 9 -- .../server/fragments/ContentAppFragment.java | 56 +++++------- .../ContentAppEndpointManagerImpl.java | 31 +++++++ .../matter/tv/server/model/ContentApp.java | 56 ++++++++++++ .../receivers/ContentAppDiscoveryService.java | 88 +++++++++++++------ .../service/ContentAppAgentService.java | 6 +- .../tv/server/service/MatterServant.java | 21 +++++ .../server/service/MatterServantService.java | 10 +++ .../matter/tv/server/utils/ResourceUtils.java | 78 +++++++++++++--- .../src/main/res/layout/applist_item.xml | 9 +- examples/tv-app/android/BUILD.gn | 4 + .../tv-app/android/include/cluster-init.cpp | 8 +- .../AppContentLauncherManager.cpp | 11 ++- .../AppContentLauncherManager.h | 17 ++-- examples/tv-app/android/java/AppImpl.cpp | 74 +++++++++++++++- examples/tv-app/android/java/AppImpl.h | 31 +++++-- .../java/ContentAppCommandDelegate.cpp | 59 +++++++++++++ .../android/java/ContentAppCommandDelegate.h | 72 +++++++++++++++ examples/tv-app/android/java/TVApp-JNI.cpp | 22 +++++ .../chip/tvapp/ContentAppEndpointManager.java | 6 ++ .../tvapp/ContentAppEndpointManagerStub.java | 15 ++++ .../java/src/com/tcl/chip/tvapp/TvApp.java | 10 +++ 26 files changed, 608 insertions(+), 122 deletions(-) create mode 100644 examples/tv-app/android/App/.idea/deploymentTargetDropDown.xml create mode 100644 examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/handlers/ContentAppEndpointManagerImpl.java create mode 100644 examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/model/ContentApp.java create mode 100644 examples/tv-app/android/java/ContentAppCommandDelegate.cpp create mode 100644 examples/tv-app/android/java/ContentAppCommandDelegate.h create mode 100644 examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentAppEndpointManager.java create mode 100644 examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentAppEndpointManagerStub.java diff --git a/examples/tv-app/android/App/.idea/deploymentTargetDropDown.xml b/examples/tv-app/android/App/.idea/deploymentTargetDropDown.xml new file mode 100644 index 00000000000000..2bb42d2582175f --- /dev/null +++ b/examples/tv-app/android/App/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/tv-app/android/App/content-app/src/main/AndroidManifest.xml b/examples/tv-app/android/App/content-app/src/main/AndroidManifest.xml index c67adc438f879a..9ba74639750d1d 100644 --- a/examples/tv-app/android/App/content-app/src/main/AndroidManifest.xml +++ b/examples/tv-app/android/App/content-app/src/main/AndroidManifest.xml @@ -16,7 +16,10 @@ android:supportsRtl="true" android:enabled="true" android:theme="@style/Theme.ContentApp"> - + + + + diff --git a/examples/tv-app/android/App/content-app/src/main/java/com/example/contentapp/receiver/MatterCommandReceiver.java b/examples/tv-app/android/App/content-app/src/main/java/com/example/contentapp/receiver/MatterCommandReceiver.java index 0f01b9ee71f394..a5c6a2c9d9807e 100644 --- a/examples/tv-app/android/App/content-app/src/main/java/com/example/contentapp/receiver/MatterCommandReceiver.java +++ b/examples/tv-app/android/App/content-app/src/main/java/com/example/contentapp/receiver/MatterCommandReceiver.java @@ -28,6 +28,8 @@ public void onReceive(Context context, Intent intent) { new StringBuilder() .append("Received matter command: ") .append(intent.getAction()) + .append(". Payload : ") + .append(new String(commandPayload)) .toString()); PendingIntent pendingIntent = diff --git a/examples/tv-app/android/App/content-app/src/main/res/raw/static_matter_clusters b/examples/tv-app/android/App/content-app/src/main/res/raw/static_matter_clusters index 836bfe530df37a..626a145b863f7a 100644 --- a/examples/tv-app/android/App/content-app/src/main/res/raw/static_matter_clusters +++ b/examples/tv-app/android/App/content-app/src/main/res/raw/static_matter_clusters @@ -1,13 +1,16 @@ { "clusters": [ { - "identifier": "0x050a", - "features": "CS" + "identifier": 1289, + "features": ["NV", "LK", "NK"] }, { - "identifier": "0x0506", - "features": "AS", - "optionalCommands" : [4, 5] + "identifier": 1290, + "features": ["CS"], + }, + { + "identifier": 1286, + "features": ["AS"], } ] } \ No newline at end of file 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 05824a77bce180..fbeed33502a439 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,21 +7,12 @@ 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.receivers.ContentAppDiscoveryService; import java.util.LinkedHashMap; public class MainActivity extends AppCompatActivity { private LinkedHashMap packages = new LinkedHashMap<>(); - @Override - protected void onRestart() { - super.onRestart(); - packages.clear(); - ContentAppDiscoveryService.getReceiverInstance() - .initializeMatterApps(this.getApplicationContext()); - } - private BottomNavigationView.OnNavigationItemSelectedListener navListener = item -> { Fragment selectedFragment = null; 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 4372c550f3f49e..89b8804af3677d 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 @@ -17,11 +17,10 @@ import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import com.matter.tv.server.R; +import com.matter.tv.server.model.ContentApp; import com.matter.tv.server.receivers.ContentAppDiscoveryService; -import com.matter.tv.server.service.ContentAppAgentService; +import com.matter.tv.server.service.MatterServant; import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.Map.Entry; /** * A simple {@link Fragment} subclass. Use the {@link ContentAppFragment#newInstance} factory method @@ -33,8 +32,6 @@ public class ContentAppFragment extends Fragment { private BroadcastReceiver broadcastReceiver; private ListView pkgUpdatesView; - private LinkedHashMap packages = new LinkedHashMap<>(); - public ContentAppFragment() { // Required empty public constructor } @@ -65,8 +62,10 @@ public void onCreate(Bundle savedInstanceState) { public void onResume() { super.onResume(); - ArrayList> lst = new ArrayList<>(packages.entrySet()); - + ContentAppDiscoveryService.getReceiverInstance().registerSelf(getContext()); + ArrayList lst = + new ArrayList( + ContentAppDiscoveryService.getReceiverInstance().getDiscoveredContentApps().keySet()); ContentAppListAdapter adapter = new ContentAppListAdapter(getContext(), R.layout.applist_item, lst); @@ -82,14 +81,12 @@ public View onCreateView( return inflater.inflate(R.layout.fragment_content_app, container, false); } - private class ContentAppListAdapter extends ArrayAdapter> { + private class ContentAppListAdapter extends ArrayAdapter { private int layout; public ContentAppListAdapter( - @NonNull Context context, - int resource, - @NonNull ArrayList> packages) { + @NonNull Context context, int resource, @NonNull ArrayList packages) { super(context, resource, packages); layout = resource; } @@ -104,22 +101,25 @@ public View getView(int position, @Nullable View convertView, @NonNull ViewGroup ViewHolder viewHolder = new ViewHolder(); viewHolder.appName = convertView.findViewById(R.id.appNameTextView); - viewHolder.appDetails = convertView.findViewById(R.id.appDetailsTextView); - viewHolder.appName.setText(getItem(position).getKey()); - viewHolder.appDetails.setText(getItem(position).getValue()); + viewHolder.appName.setText(getItem(position)); viewHolder.sendMessageButton = convertView.findViewById(R.id.sendMessageButton); viewHolder.sendMessageButton.setText(R.string.send_command); viewHolder.sendMessageButton.setOnClickListener( view -> { Log.i(TAG, "Button was clicked for " + position); - ContentAppAgentService.sendCommand( - getActivity().getApplicationContext(), getItem(position).getKey()); + for (ContentApp app : + ContentAppDiscoveryService.getReceiverInstance() + .getDiscoveredContentApps() + .values()) { + if (app.getAppName().equals(getItem(position))) { + MatterServant.get().sendTestMessage(app.getEndpointId(), "My Native Message"); + } + } }); convertView.setTag(viewHolder); } else { mainViewHolder = (ViewHolder) convertView.getTag(); - mainViewHolder.appName.setText(getItem(position).getKey()); - mainViewHolder.appDetails.setText(getItem(position).getValue()); + mainViewHolder.appName.setText(getItem(position)); } return convertView; } @@ -132,18 +132,14 @@ private void registerReceiver(ArrayAdapter adapter) { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); String packageName = intent.getStringExtra("com.matter.tv.server.appagent.add.pkg"); - if (action.equals("com.matter.tv.server.appagent.add")) { - packages.put( - packageName, intent.getStringExtra("com.matter.tv.server.appagent.add.clusters")); + if (action.equals("com.matter.tv.server.appagent.add") + || action.equals("com.matter.tv.server.appagent.remove")) { adapter.clear(); - adapter.addAll(packages.entrySet().toArray()); + adapter.addAll( + ContentAppDiscoveryService.getReceiverInstance() + .getDiscoveredContentApps() + .entrySet()); adapter.notifyDataSetChanged(); - } else if (action.equals("com.matter.tv.server.appagent.remove")) { - if (packages.remove(packageName) != null) { - adapter.clear(); - adapter.addAll(packages.entrySet().toArray()); - adapter.notifyDataSetChanged(); - } } } }; @@ -152,14 +148,10 @@ public void onReceive(Context context, Intent intent) { getContext() .registerReceiver( broadcastReceiver, new IntentFilter("com.matter.tv.server.appagent.remove")); - - ContentAppDiscoveryService.getReceiverInstance().registerSelf(getContext()); - ContentAppDiscoveryService.getReceiverInstance().initializeMatterApps(getContext()); } public class ViewHolder { TextView appName; - TextView appDetails; Button sendMessageButton; } } diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/handlers/ContentAppEndpointManagerImpl.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/handlers/ContentAppEndpointManagerImpl.java new file mode 100644 index 00000000000000..035e7cd84e8c44 --- /dev/null +++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/handlers/ContentAppEndpointManagerImpl.java @@ -0,0 +1,31 @@ +package com.matter.tv.server.handlers; + +import android.content.Context; +import android.util.Log; +import com.matter.tv.server.model.ContentApp; +import com.matter.tv.server.receivers.ContentAppDiscoveryService; +import com.matter.tv.server.service.ContentAppAgentService; +import com.tcl.chip.tvapp.ContentAppEndpointManager; + +public class ContentAppEndpointManagerImpl implements ContentAppEndpointManager { + + private static final String TAG = "MatterMainActivity"; + private final Context context; + + public ContentAppEndpointManagerImpl(Context context) { + this.context = context; + } + + public String sendCommand(int endpointId, String commandPayload) { + Log.d(TAG, "Received a command for endpointId " + endpointId + ". Message " + commandPayload); + for (ContentApp app : + ContentAppDiscoveryService.getReceiverInstance().getDiscoveredContentApps().values()) { + if (app.getEndpointId() == endpointId) { + Log.d( + TAG, "Sending a command for endpointId " + endpointId + ". Message " + commandPayload); + ContentAppAgentService.sendCommand(context, app.getAppName(), commandPayload); + } + } + return "Success"; + } +} diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/model/ContentApp.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/model/ContentApp.java new file mode 100644 index 00000000000000..5d021c198f5830 --- /dev/null +++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/model/ContentApp.java @@ -0,0 +1,56 @@ +package com.matter.tv.server.model; + +import com.matter.tv.app.api.SupportedCluster; +import java.util.Collections; +import java.util.Set; + +public class ContentApp { + + private String appName; + private String vendorName; + private int vendorId; + private int productId; + private Set supportedClusters; + private int endpoint; + + public ContentApp( + String appName, + String vendorName, + int vendorId, + int productId, + Set supportedClusters) { + this.vendorName = vendorName; + this.appName = appName; + this.vendorId = vendorId; + this.productId = productId; + this.supportedClusters = supportedClusters; + } + + public String getAppName() { + return appName; + } + + public String getVendorName() { + return vendorName; + } + + public int getVendorId() { + return vendorId; + } + + public int getProductId() { + return productId; + } + + public int getEndpointId() { + return endpoint; + } + + public void setEndpointId(int endpoint) { + this.endpoint = endpoint; + } + + public Set getSupportedClusters() { + return Collections.unmodifiableSet(supportedClusters); + } +} diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/receivers/ContentAppDiscoveryService.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/receivers/ContentAppDiscoveryService.java index b5b3793d0b414b..0c4555db2b86e0 100644 --- a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/receivers/ContentAppDiscoveryService.java +++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/receivers/ContentAppDiscoveryService.java @@ -11,26 +11,39 @@ import android.os.Bundle; import android.util.Log; import com.matter.tv.app.api.MatterIntentConstants; +import com.matter.tv.app.api.SupportedCluster; +import com.matter.tv.server.model.ContentApp; import com.matter.tv.server.utils.ResourceUtils; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; public class ContentAppDiscoveryService extends BroadcastReceiver { private static final String TAG = "ContentAppDiscoveryService"; - static final String CLUSTERS_RESOURCE_METADATA_KEY = "com.matter.app_agent_api.clusters"; + static final String CLUSTERS_RESOURCE_METADATA_KEY = "com.matter.tv.app.api.clusters"; + static final String MATTER_VENDOR_NAME_METADATA_KEY = "com.matter.tv.app.api.vendor_name"; + static final String MATTER_VENDOR_ID_METADATA_KEY = "com.matter.tv.app.api.vendor_id"; + static final String MATTER_PRODUCT_ID_METADATA_KEY = "com.matter.tv.app.api.product_id"; + private static final String ANDROID_PACKAGE_REMOVED_ACTION = "android.intent.action.PACKAGE_REMOVED"; private static final String ANDROID_PACKAGE_ADDED_ACTION = "android.intent.action.PACKAGE_ADDED"; + private static final String ANDROID_PACKAGE_REPLACED_ACTION = + "android.intent.action.PACKAGE_REPLACED"; private static ResourceUtils resourceUtils = ResourceUtils.getInstance(); private static final ContentAppDiscoveryService instance = new ContentAppDiscoveryService(); - public ContentAppDiscoveryService() {} + private ContentAppDiscoveryService() {} private volatile boolean registered = false; + private Map applications = new HashMap<>(); + @Override public void onReceive(Context context, Intent intent) { final String intentAction = intent.getAction(); @@ -41,10 +54,8 @@ public void onReceive(Context context, Intent intent) { } switch (intentAction) { - case Intent.ACTION_BOOT_COMPLETED: - // discoveryAgent.init(); - break; case ANDROID_PACKAGE_ADDED_ACTION: + case ANDROID_PACKAGE_REPLACED_ACTION: handlePackageAdded(intent, context); break; case ANDROID_PACKAGE_REMOVED_ACTION: @@ -62,8 +73,6 @@ public void onReceive(Context context, Intent intent) { private void handlePackageAdded(final Intent intent, final Context context) { String pkg = intent.getData().getSchemeSpecificPart(); - Log.i(TAG, pkg + " Added. MATTERSERVER"); - handlePackageAdded(context, pkg); } @@ -72,24 +81,35 @@ private void handlePackageAdded(Context context, String pkg) { try { ApplicationInfo appInfo = pm.getApplicationInfo(pkg, PackageManager.GET_META_DATA); if (appInfo.metaData == null) { - Log.i(TAG, pkg + " has no metadata."); return; } int resId = appInfo.metaData.getInt(CLUSTERS_RESOURCE_METADATA_KEY, 0); + int vendorId = appInfo.metaData.getInt(MATTER_VENDOR_ID_METADATA_KEY, -1); + int productId = appInfo.metaData.getInt(MATTER_PRODUCT_ID_METADATA_KEY, -1); + String vendorName = appInfo.metaData.getString(MATTER_VENDOR_NAME_METADATA_KEY, ""); + + if (vendorId == -1 || productId == -1) { + return; + } + + Set supportedClusters; Log.d(TAG, "got static capability for package " + pkg + ", resourceId: " + resId); if (resId != 0) { Resources res = pm.getResourcesForApplication(appInfo); - String rawJson = resourceUtils.getRawTextResource(res, resId); - Log.d(TAG, "Got capabilities resource:\n" + rawJson); - - Intent in = new Intent("com.matter.tv.server.appagent.add"); - Bundle extras = new Bundle(); - extras.putString("com.matter.tv.server.appagent.add.pkg", pkg); - extras.putString("com.matter.tv.server.appagent.add.clusters", rawJson); - in.putExtras(extras); - context.sendBroadcast(in); + supportedClusters = resourceUtils.getSupportedClusters(res, resId); + } else { + supportedClusters = new HashSet<>(); } + + ContentApp app = new ContentApp(pkg, vendorName, vendorId, productId, supportedClusters); + applications.put(pkg, app); + + Intent in = new Intent("com.matter.tv.server.appagent.add"); + Bundle extras = new Bundle(); + extras.putString("com.matter.tv.server.appagent.add.pkg", pkg); + in.putExtras(extras); + context.sendBroadcast(in); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "Could not find package " + pkg, e); } @@ -97,7 +117,10 @@ private void handlePackageAdded(Context context, String pkg) { private void handlePackageRemoved(final Intent intent, final Context context) { String pkg = intent.getData().getSchemeSpecificPart(); - Log.i(TAG, pkg + " Removed. MATTERSERVER"); + Log.i(TAG, pkg + " Removed."); + + applications.remove(pkg); + Intent in = new Intent("com.matter.tv.server.appagent.remove"); Bundle extras = new Bundle(); extras.putString("com.matter.tv.server.appagent.add.pkg", pkg); @@ -106,25 +129,26 @@ private void handlePackageRemoved(final Intent intent, final Context context) { } public void registerSelf(Context context) { - Log.i(TAG, "Starting the registration of the matter package update receiver"); if (registered) { Log.i(TAG, "Package update receiver for matter already registered"); return; } else { registered = true; } + registerPackageAction(context, ANDROID_PACKAGE_ADDED_ACTION); + registerPackageAction(context, ANDROID_PACKAGE_REMOVED_ACTION); + registerPackageAction(context, ANDROID_PACKAGE_REPLACED_ACTION); + initializeMatterApps(context); + Log.i(TAG, "Registered the matter package updates receiver"); + } - Log.i(TAG, "Trying to register the matter package update receiver"); - IntentFilter pckAdded = new IntentFilter(ANDROID_PACKAGE_ADDED_ACTION); - pckAdded.addDataScheme("package"); - context.registerReceiver(this, pckAdded); - IntentFilter pckRemoved = new IntentFilter(ANDROID_PACKAGE_REMOVED_ACTION); - pckRemoved.addDataScheme("package"); - context.registerReceiver(this, pckRemoved); - Log.i(TAG, "Registered the matter package update receiver"); + private void registerPackageAction(final Context context, final String action) { + IntentFilter intent = new IntentFilter(action); + intent.addDataScheme("package"); + context.registerReceiver(this, intent); } - public void initializeMatterApps(Context context) { + private void initializeMatterApps(Context context) { Set matterApps = getMatterApps(context); for (String matterApp : matterApps) { handlePackageAdded(context, matterApp); @@ -156,4 +180,12 @@ private Set getMatterApps(Context context) { public static ContentAppDiscoveryService getReceiverInstance() { return instance; } + + public Map getDiscoveredContentApps() { + return Collections.unmodifiableMap(applications); + } + + public ContentApp getDiscoveredContentApp(String appName) { + return applications.get(appName); + } } diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/ContentAppAgentService.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/ContentAppAgentService.java index 40e997a692e211..b1799990f99575 100644 --- a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/ContentAppAgentService.java +++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/ContentAppAgentService.java @@ -51,10 +51,10 @@ public IBinder onBind(final Intent intent) { return null; } - public static void sendCommand(Context context, String packageName) { + public static void sendCommand(Context context, String packageName, String payload) { Intent in = new Intent(MatterIntentConstants.ACTION_MATTER_COMMAND); Bundle extras = new Bundle(); - extras.putByteArray(MatterIntentConstants.EXTRA_COMMAND_PAYLOAD, "test payload".getBytes()); + extras.putByteArray(MatterIntentConstants.EXTRA_COMMAND_PAYLOAD, payload.getBytes()); in.putExtras(extras); in.setPackage(packageName); int flags = Intent.FLAG_INCLUDE_STOPPED_PACKAGES; @@ -84,7 +84,7 @@ public int onStartCommand(final Intent intent, final int flags, final int startI Log.d( TAG, "Command response " - + intent.getByteArrayExtra(MatterIntentConstants.EXTRA_RESPONSE_PAYLOAD)); + + new String(intent.getByteArrayExtra(MatterIntentConstants.EXTRA_RESPONSE_PAYLOAD))); // Send the response back to the client. } 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 15e8ee5c4cdd9c..574aadb0313ca4 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 @@ -28,6 +28,8 @@ import chip.platform.NsdManagerServiceResolver; import chip.platform.PreferencesConfigurationManager; import chip.platform.PreferencesKeyValueStoreManager; +import com.matter.tv.server.handlers.ContentAppEndpointManagerImpl; +import com.matter.tv.server.model.ContentApp; import com.tcl.chip.tvapp.ChannelManagerStub; import com.tcl.chip.tvapp.Clusters; import com.tcl.chip.tvapp.ContentLaunchManagerStub; @@ -62,7 +64,12 @@ public static MatterServant get() { return SingletonHolder.instance; } + private Context context; + public void init(@NonNull Context context) { + + this.context = context; + // The order is important, must // first new TvApp to load dynamic library // then chipPlatform to prepare platform @@ -145,4 +152,18 @@ public void sendCustomCommand(String customCommand) { public void updateLevel(int value) { mTvApp.setCurrentLevel(mLevelEndpoint, value); } + + public int addContentApp(ContentApp app) { + return mTvApp.addContentApp( + app.getVendorName(), + app.getVendorId(), + app.getAppName(), + app.getProductId(), + "1.0", + new ContentAppEndpointManagerImpl(context)); + } + + public void sendTestMessage(int endpoint, String message) { + mTvApp.sendTestMessage(endpoint, message); + } } diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/MatterServantService.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/MatterServantService.java index 9efc03f29d2a81..cc68f49c09be07 100644 --- a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/MatterServantService.java +++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/service/MatterServantService.java @@ -12,6 +12,8 @@ import androidx.core.app.NotificationCompat; import com.matter.tv.server.MainActivity; import com.matter.tv.server.R; +import com.matter.tv.server.model.ContentApp; +import com.matter.tv.server.receivers.ContentAppDiscoveryService; public class MatterServantService extends Service { private static final String CHANNEL_ID = "Matter"; @@ -19,7 +21,15 @@ public class MatterServantService extends Service { @Override public void onCreate() { super.onCreate(); + // Start Matter Server MatterServant.get().init(this.getApplicationContext()); + + // Register for packages updates + ContentAppDiscoveryService.getReceiverInstance().registerSelf(this.getApplicationContext()); + for (ContentApp app : + ContentAppDiscoveryService.getReceiverInstance().getDiscoveredContentApps().values()) { + app.setEndpointId(MatterServant.get().addContentApp(app)); + } } @Nullable diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/utils/ResourceUtils.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/utils/ResourceUtils.java index 769b039efbd267..71ccc02f1799b2 100644 --- a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/utils/ResourceUtils.java +++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/utils/ResourceUtils.java @@ -1,15 +1,24 @@ package com.matter.tv.server.utils; import android.content.res.Resources; +import android.util.JsonReader; import android.util.Log; -import java.io.BufferedReader; +import com.matter.tv.app.api.SupportedCluster; import java.io.IOException; import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** Util class for dealing with android Resources. */ public class ResourceUtils { private static final String TAG = "ResourceUtils"; private static final ResourceUtils resourceUtils = new ResourceUtils(); + private static final String KEY_CLUSTERS = "clusters"; + private static final String KEY_CLUSTER_ID = "identifier"; + private static final String KEY_FEATURES = "features"; + private static final String KEY_OPTIONAL_COMMANDS = "optionalCommands"; private ResourceUtils() {} @@ -17,35 +26,76 @@ public static ResourceUtils getInstance() { return resourceUtils; } + /** + * { "clusters": [ { "identifier": 1234, "features": ["CS"] }, { "identifier": 1235, "features": + * ["AS"], "optionalCommands" : [4, 5] } ] } + */ + /** * Attempts to open the resource given by the resourceId as a raw resource and return its contents * as a string. * * @param resources Resources object containing the resource * @param resId resourceId - * @return String containing the contents of the resource file. Null if an error occurred. + * @return Set containing the clusters defined in the resource file. Empty set + * if there is a parsing error. */ - public String getRawTextResource(final Resources resources, final int resId) { - // TODO: Enforce some size limit on raw resource text? We don't want to - // potentially take a long time reading an image file or something like that. + public Set getSupportedClusters(final Resources resources, final int resId) { + Set supportedClusters = new HashSet<>(); // openRawResource cannot return null, it either succeeds or throws an exception - try (BufferedReader reader = - new BufferedReader(new InputStreamReader(resources.openRawResource(resId)))) { + try (JsonReader reader = + new JsonReader(new InputStreamReader(resources.openRawResource(resId)))) { - String line; - StringBuilder result = new StringBuilder(); + reader.beginObject(); - while ((line = reader.readLine()) != null) { - result.append(line).append('\n'); + if (reader.hasNext() && reader.nextName().equals(KEY_CLUSTERS)) { + reader.beginArray(); + while (reader.hasNext()) { + reader.beginObject(); + SupportedCluster cluster = new SupportedCluster(); + while (reader.hasNext()) { + String name = reader.nextName(); + if (name.equals(KEY_CLUSTER_ID)) { + cluster.clusterIdentifier = reader.nextInt(); + } else if (name.equals(KEY_FEATURES)) { + List features = new ArrayList<>(); + reader.beginArray(); + while (reader.hasNext()) { + features.add(reader.nextString()); + } + reader.endArray(); + cluster.features = features.toArray(new String[features.size()]); + } else if (name.equals(KEY_OPTIONAL_COMMANDS)) { + List commands = new ArrayList<>(); + reader.beginArray(); + while (reader.hasNext()) { + commands.add(reader.nextInt()); + } + reader.endArray(); + int[] commandIds = new int[commands.size()]; + int i = 0; + for (Integer command : commands) { + commandIds[i++] = command; + } + cluster.optionalCommandIdentifiers = commandIds; + } else { + reader.skipValue(); + } + } + supportedClusters.add(cluster); + reader.endObject(); + } + reader.endArray(); + } else { + Log.e(TAG, "Invalid resource (Key:'clusters' not found) for id " + resId); } - return result.toString(); + reader.endObject(); } catch (IOException e) { Log.e(TAG, "Error reading raw resource for id " + resId); } catch (Resources.NotFoundException e) { Log.e(TAG, "Could not find raw resource for id " + resId); } - - return null; + return supportedClusters; } } diff --git a/examples/tv-app/android/App/platform-app/src/main/res/layout/applist_item.xml b/examples/tv-app/android/App/platform-app/src/main/res/layout/applist_item.xml index 6d3dd40d2c2400..678d76814268a5 100644 --- a/examples/tv-app/android/App/platform-app/src/main/res/layout/applist_item.xml +++ b/examples/tv-app/android/App/platform-app/src/main/res/layout/applist_item.xml @@ -9,17 +9,10 @@ android:layout_height="wrap_content" android:text="TextView" /> - -