package org.edx.mobile.util; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; import android.support.annotation.NonNull; import android.view.View; import android.widget.Toast; import com.google.inject.Inject; import org.edx.mobile.BuildConfig; import org.edx.mobile.R; import java.util.List; /** * Utility class for updating the app. */ public final class AppUpdateUtils { // Make this class non-instantiable private AppUpdateUtils() { throw new UnsupportedOperationException(); } @Inject private static Config config; /** * @param context A Context to query the applications info. * * @return Whether there are any apps registered to handle the update URIs. */ public static boolean canUpdate(@NonNull final Context context) { final Intent intent = new Intent(Intent.ACTION_VIEW); intent.addCategory(Intent.CATEGORY_BROWSABLE); final PackageManager packageManager = context.getPackageManager(); for (final Uri uri : config.getAppUpdateUris()) { intent.setData(uri); if (intent.resolveActivity(packageManager) != null) { return true; } } return false; } /** * Open a native app or a website on a web browser to update the app. * * @param context A Context for starting the new Activity. */ public static void openAppInAppStore(@NonNull final Context context) { final Intent intent = new Intent(Intent.ACTION_VIEW); /* The app store would be expected to be opened in a * separate task. The Play Store is already * configured to work this way by default. */ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); final List<Uri> uris = config.getAppUpdateUris(); if (uris.isEmpty()) return; /* Try to resolve the app store that was initially used to * install the install the app, if it was installed through * one, and it's resolved through one of the registered URIs. */ final PackageManager packageManager = context.getPackageManager(); final String installerPackageName = packageManager .getInstallerPackageName(BuildConfig.APPLICATION_ID); if (installerPackageName != null) { for (final Uri uri : uris) { intent.setData(uri); final List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0); for (final ResolveInfo resolveInfo : resolveInfoList) { final ActivityInfo activityInfo = resolveInfo.activityInfo; final String packageName = activityInfo.applicationInfo.packageName; if (packageName.equals(installerPackageName)) { /* The app store responsible for initially installing the app * has been found; try to start it's Activity explicitly by * setting it's component data, in order to avoid contention by * other applications that are also registered to handle the * same URI. */ intent.setClassName(packageName, activityInfo.name); try { context.startActivity(intent); return; } catch (ActivityNotFoundException e) { /* The application was uninstalled or updated before the * Activity was started. Remove the component information, * and continue iterating through the handling Activities. */ intent.setComponent(null); } } } } /* None of the registered URIs resolve to the app store that was initially * used to install the app. It has either been uninstalled, or it's not * officially supported by the developer. In either case we can disregard * it, and fall back to resolving to any of the registered URIs in order of * precedence. */ } // Iterate through all the registered URIs, and start // an Activity from the first one that can be resolved. for (final Uri uri : uris) { intent.setData(uri); try { context.startActivity(intent); return; } catch (ActivityNotFoundException e) { // Continue iterating until an Activity // is resolved against one of the URIs. } } // No Activity was resolved against any of the registered // URIS. Show a toast message to that effect. Toast.makeText(context, R.string.app_version_upgrade_app_store_unavailable, Toast.LENGTH_SHORT).show(); } /** * Generic click listener that opens an app store to display the app. This is created as a * convenience, because this utility seems to be mostly invoked from a click listener. */ public static final View.OnClickListener OPEN_APP_IN_APP_STORE_CLICK_LISTENER = new View.OnClickListener() { @Override public void onClick(final View v) { openAppInAppStore(v.getContext()); } }; }