Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-Implement App Install flow #34141

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5583,6 +5583,9 @@ cluster ApplicationLauncher = 1292 {
kSuccess = 0;
kAppNotAvailable = 1;
kSystemBusy = 2;
kPendingUserApproval = 3;
kDownloading = 4;
kInstalling = 5;
}

bitmap Feature : bitmap32 {
Expand Down
6 changes: 6 additions & 0 deletions examples/placeholder/linux/apps/app1/config.matter
Original file line number Diff line number Diff line change
Expand Up @@ -8236,6 +8236,9 @@ cluster ApplicationLauncher = 1292 {
kSuccess = 0;
kAppNotAvailable = 1;
kSystemBusy = 2;
kPendingUserApproval = 3;
kDownloading = 4;
kInstalling = 5;
}

bitmap Feature : bitmap32 {
Expand Down Expand Up @@ -8295,6 +8298,9 @@ cluster ApplicationLauncher = 1292 {
kSuccess = 0;
kAppNotAvailable = 1;
kSystemBusy = 2;
kPendingUserApproval = 3;
kDownloading = 4;
kInstalling = 5;
}

bitmap Feature : bitmap32 {
Expand Down
6 changes: 6 additions & 0 deletions examples/placeholder/linux/apps/app2/config.matter
Original file line number Diff line number Diff line change
Expand Up @@ -8193,6 +8193,9 @@ cluster ApplicationLauncher = 1292 {
kSuccess = 0;
kAppNotAvailable = 1;
kSystemBusy = 2;
kPendingUserApproval = 3;
kDownloading = 4;
kInstalling = 5;
}

bitmap Feature : bitmap32 {
Expand Down Expand Up @@ -8252,6 +8255,9 @@ cluster ApplicationLauncher = 1292 {
kSuccess = 0;
kAppNotAvailable = 1;
kSystemBusy = 2;
kPendingUserApproval = 3;
kDownloading = 4;
kInstalling = 5;
}

bitmap Feature : bitmap32 {
Expand Down
2 changes: 1 addition & 1 deletion examples/tv-app/android/App/platform-app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,4 @@ dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation 'com.google.zxing:core:3.3.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package com.matter.tv.server.handlers;

import android.content.Context;
import android.content.pm.PackageManager;
import android.util.Log;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import com.matter.tv.server.tvapp.Application;
import com.matter.tv.server.tvapp.ApplicationLauncherManager;
import com.matter.tv.server.tvapp.LauncherResponse;
import com.matter.tv.server.utils.EndpointsDataStore;
import com.matter.tv.server.utils.InstallationObserver;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class ApplicationLauncherManagerImpl implements ApplicationLauncherManager {

private static final String TAG = "ApplicationLauncherService";

private volatile boolean registered = false;
private PackageManager packageManager;
private EndpointsDataStore endpointsDataStore;

/** Hash Map of packageName & Install Status */
private Map<String, InstallationObserver.InstallStatus> lastReceivedInstallationStatus =
new HashMap<>();

private LiveData<InstallationObserver.InstallState> installStateLiveData;

public ApplicationLauncherManagerImpl(Context context) {
packageManager = context.getPackageManager();
endpointsDataStore = EndpointsDataStore.getInstance(context);
registerSelf(context);
}

private final Observer<InstallationObserver.InstallState> installStateObserver =
state -> {
lastReceivedInstallationStatus.put(state.getAppPackageName(), state.getStatus());
switch (state.getStatus()) {
case IN_PROGRESS:
// Installation is in progress
Log.d(TAG, "Installation of " + state.getAppPackageName() + " in progress");
break;
case SUCCEEDED:
// Installation succeeded
Log.d(TAG, "Installation of " + state.getAppPackageName() + " succeeded");
break;
case FAILED:
// Installation failed
Log.d(TAG, "Installation of " + state.getAppPackageName() + " failed");
break;
}
};

private void stopObservingInstallations() {
if (installStateLiveData != null) {
Log.d("InstallationObserver", "Stopped Observing");
installStateLiveData.removeObserver(installStateObserver);
}
}

public void unregister() {
stopObservingInstallations();
}

private void registerSelf(Context context) {
if (registered) {
Log.i(TAG, "Package update receiver for matter already registered");
return;
} else {
registered = true;
}
Log.i(TAG, "Registered the matter package updates receiver");

installStateLiveData = InstallationObserver.installationStates(context);
installStateLiveData.observeForever(installStateObserver);
Log.d(TAG, "Started Observing package installations");
}

@Override
public int[] getCatalogList() {
Log.i(TAG, "Get Catalog List");
return new int[] {123, 456, 89010};
}

@Override
public LauncherResponse launchApp(Application app, String data) {
Log.i(
TAG,
"Launch app id:" + app.applicationId + " cid:" + app.catalogVendorId + " data:" + data);

int status = 0;
String responseData = "";

// Installed Apps that have declared CSA product id & vendor id in their manifes
boolean matterEnabledAppdIsInstalled =
endpointsDataStore.getAllPersistedContentApps().containsKey(app.applicationId);
// Installed App
boolean appIsInstalled =
InstallationObserver.getInstalledPackages(packageManager).contains(app.applicationId);
boolean isAppInstalling =
Objects.equals(
lastReceivedInstallationStatus.get(app.applicationId),
InstallationObserver.InstallStatus.IN_PROGRESS);
boolean appInstallFailed =
Objects.equals(
lastReceivedInstallationStatus.get(app.applicationId),
InstallationObserver.InstallStatus.FAILED);

// This use-case can happen if app is installed
// but it does not support Matter
if (!matterEnabledAppdIsInstalled && appIsInstalled) {
Log.i(
TAG,
"Matter enabled app is not installed, but app is installed. Launching app's install page");
lazarkov marked this conversation as resolved.
Show resolved Hide resolved
status = LauncherResponse.STATUS_PENDING_USER_APPROVAL;
responseData = "App is installed, try updating";

//
// Add code to launch App Install Page
//

} else if (!matterEnabledAppdIsInstalled && !appIsInstalled) {
Log.i(
TAG,
"Matter enabled app is not installed and app is not installed. Launching app's install page");
if (isAppInstalling) {
Log.i(TAG, "App is installing");
status = LauncherResponse.STATUS_INSTALLING;
} else {
status = LauncherResponse.STATUS_PENDING_USER_APPROVAL;
if (appInstallFailed) {
responseData = "App install failed. Try again";
}
}

//
// Add code to launch App Install Page
//

} else if (matterEnabledAppdIsInstalled && appIsInstalled) {
Log.i(TAG, "Launching the app");
status = LauncherResponse.STATUS_SUCCESS;

//
// Add code to launch an app
//
}

return new LauncherResponse(status, responseData);
}

@Override
public LauncherResponse stopApp(Application app) {
Log.i(TAG, "Stop app id:" + app.applicationId + " cid:" + app.catalogVendorId);
return new LauncherResponse(LauncherResponse.STATUS_SUCCESS, "");
}

@Override
public LauncherResponse hideApp(Application app) {
Log.i(TAG, "Hide app id:" + app.applicationId + " cid:" + app.catalogVendorId);
return new LauncherResponse(LauncherResponse.STATUS_SUCCESS, "");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import chip.platform.PreferencesConfigurationManager;
import chip.platform.PreferencesKeyValueStoreManager;
import com.matter.tv.server.MatterCommissioningPrompter;
import com.matter.tv.server.handlers.ApplicationLauncherManagerImpl;
import com.matter.tv.server.tvapp.ApplicationLauncherManager;
import com.matter.tv.server.tvapp.ChannelManagerStub;
import com.matter.tv.server.tvapp.Clusters;
import com.matter.tv.server.tvapp.ContentLaunchManagerStub;
Expand All @@ -55,6 +57,8 @@ public class MatterServant {
private boolean mIsOn = true;
private int mOnOffEndpoint;
private int mLevelEndpoint;
private MatterCommissioningPrompter matterCommissioningPrompter;
private ApplicationLauncherManager applicationLauncherManager;

private MatterServant() {}

Expand All @@ -72,17 +76,25 @@ public void init(@NonNull Context context) {

this.context = context;

this.applicationLauncherManager = new ApplicationLauncherManagerImpl(context);

// The order is important, must
// first new TvApp to load dynamic library
// then chipPlatform to prepare platform
// then TvApp.preServerInit to initialize any server configuration
// then start ChipAppServer
// then TvApp.postServerInit to init app platform
//
// TODO: Move all of the bellow KeypadInputManager...LevelManagerStub to
// PlatformAppCommandDelegate
// There is no need for this complicated logic
mTvApp =
new TvApp(
(app, clusterId, endpoint) -> {
if (clusterId == Clusters.ClusterId_KeypadInput) {
app.setKeypadInputManager(endpoint, new KeypadInputManagerStub(endpoint));
} else if (clusterId == Clusters.ClusterId_ApplicationLauncher) {
app.setApplicationLauncherManager(endpoint, applicationLauncherManager);
} else if (clusterId == Clusters.ClusterId_WakeOnLan) {
app.setWakeOnLanManager(endpoint, new WakeOnLanManagerStub(endpoint));
} else if (clusterId == Clusters.ClusterId_MediaInput) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import android.content.SharedPreferences;
import android.util.JsonReader;
import android.util.JsonWriter;
import android.util.Log;
import com.matter.tv.app.api.SupportedCluster;
import com.matter.tv.server.model.ContentApp;
import java.io.IOException;
Expand Down Expand Up @@ -59,6 +60,7 @@ public Map<String, ContentApp> getAllPersistedContentApps() {
}

public void persistContentAppEndpoint(ContentApp app) {
Log.i(EndpointsDataStore.class.toString(), "Persist Content App Endpoint " + app.getAppName());
persistedContentApps.put(app.getAppName(), app);
discoveredEndpoints.edit().putString(app.getAppName(), serializeContentApp(app)).apply();
}
Expand Down
Loading
Loading