Skip to content

Commit

Permalink
Implement multi-user mode and no-root fallback in activity shortcuts
Browse files Browse the repository at this point in the history
If App Manager has enough permissions, it will use “Search assistant” to
launch the activity when a shortcut is triggered.

Signed-off-by: Muntashir Al-Islam <[email protected]>
  • Loading branch information
MuntashirAkon committed Dec 21, 2023
1 parent 55e01a6 commit 707c37e
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ static void launchApp(@NonNull FragmentActivity activity, @NonNull FreezeUnfreez
if (launchIntent == null) {
return;
}
// launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_TASK_ON_HOME | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
if ((shortcutInfo.flags & FLAG_ON_OPEN_APP_NO_TASK) != 0) {
launchIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ protected void onAuthenticated(@Nullable Bundle savedInstanceState) {
mViewModel = new ViewModelProvider(this).get(FreezeUnfreezeViewModel.class);
if (!SelfPermissions.canFreezeUnfreezePackages()) {
UIUtils.displayShortToast(R.string.only_works_in_root_or_adb_mode);
finishAndRemoveTask();
finish();
return;
}
FreezeUnfreezeShortcutInfo i = FreezeUnfreeze.getShortcutInfo(getIntent());
Expand All @@ -57,13 +57,13 @@ protected void onAuthenticated(@Nullable Bundle savedInstanceState) {
mViewModel.addToPendingShortcuts(i);
mViewModel.checkNextFrozen();
} else {
finishAndRemoveTask();
finish();
return;
}
mViewModel.mIsFrozenLiveData.observe(this, shortcutInfoBooleanPair -> {
if (shortcutInfoBooleanPair == null) {
// End of queue reached
finishAndRemoveTask();
finish();
return;
}
FreezeUnfreezeShortcutInfo shortcutInfo = shortcutInfoBooleanPair.first;
Expand Down Expand Up @@ -102,7 +102,7 @@ protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (!SelfPermissions.canFreezeUnfreezePackages()) {
UIUtils.displayShortToast(R.string.only_works_in_root_or_adb_mode);
finishAndRemoveTask();
finish();
return;
}
FreezeUnfreezeShortcutInfo shortcutInfo = FreezeUnfreeze.getShortcutInfo(getIntent());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
package io.github.muntashirakon.AppManager.details;

import android.Manifest;
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandleHidden;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.google.android.material.dialog.MaterialAlertDialogBuilder;
Expand All @@ -19,64 +23,87 @@
import io.github.muntashirakon.AppManager.BuildConfig;
import io.github.muntashirakon.AppManager.R;
import io.github.muntashirakon.AppManager.compat.ActivityManagerCompat;
import io.github.muntashirakon.AppManager.compat.ManifestCompat;
import io.github.muntashirakon.AppManager.self.SelfPermissions;
import io.github.muntashirakon.AppManager.utils.ContextUtils;
import io.github.muntashirakon.AppManager.utils.ThreadUtils;
import io.github.muntashirakon.AppManager.utils.UIUtils;

public class ActivityLauncherShortcutActivity extends BaseActivity {
public static final String EXTRA_PKG = BuildConfig.APPLICATION_ID + ".intent.EXTRA.shortcut.pkg";
public static final String EXTRA_CLS = BuildConfig.APPLICATION_ID + ".intent.EXTRA.shortcut.cls";
public static final String EXTRA_AST = BuildConfig.APPLICATION_ID + ".intent.EXTRA.shortcut.ast";
public static final String EXTRA_USR = BuildConfig.APPLICATION_ID + ".intent.EXTRA.shortcut.usr";
private static final String EXTRA_PKG = BuildConfig.APPLICATION_ID + ".intent.EXTRA.shortcut.pkg";
private static final String EXTRA_CLS = BuildConfig.APPLICATION_ID + ".intent.EXTRA.shortcut.cls";
private static final String EXTRA_AST = BuildConfig.APPLICATION_ID + ".intent.EXTRA.shortcut.ast";
private static final String EXTRA_USR = BuildConfig.APPLICATION_ID + ".intent.EXTRA.shortcut.usr";

public static Intent getShortcutIntent(@NonNull Context context, @NonNull String pkg, @NonNull String clazz,
@UserIdInt int userId, boolean launchViaAssist) {
return new Intent()
.setClass(context, ActivityLauncherShortcutActivity.class)
.putExtra(ActivityLauncherShortcutActivity.EXTRA_PKG, pkg)
.putExtra(ActivityLauncherShortcutActivity.EXTRA_CLS, clazz)
.putExtra(ActivityLauncherShortcutActivity.EXTRA_USR, userId)
.putExtra(ActivityLauncherShortcutActivity.EXTRA_AST, launchViaAssist)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
}

@Override
protected void onAuthenticated(@Nullable Bundle savedInstanceState) {
Intent intent = getIntent();
if (!Intent.ACTION_CREATE_SHORTCUT.equals(intent.getAction()) || !intent.hasExtra(EXTRA_PKG) || !intent.hasExtra(EXTRA_CLS)) {
// Invalid intent
finishAndRemoveTask();
finish();
return;
}
intent.setAction(null);
intent.setClassName(intent.getStringExtra(EXTRA_PKG), intent.getStringExtra(EXTRA_CLS));
int userId = intent.getIntExtra(EXTRA_USR, UserHandleHidden.myUserId());
boolean launchViaAssist = intent.getBooleanExtra(EXTRA_AST, false);
int currentUserId = UserHandleHidden.myUserId();
int userId = intent.getIntExtra(EXTRA_USR, currentUserId);
boolean canLaunchViaAssist = SelfPermissions.checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS);
boolean launchViaAssist = intent.getBooleanExtra(EXTRA_AST, false) && canLaunchViaAssist;
intent.removeExtra(EXTRA_PKG);
intent.removeExtra(EXTRA_CLS);
intent.removeExtra(EXTRA_AST);
intent.removeExtra(EXTRA_USR);
if (launchViaAssist && SelfPermissions.checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS)) {
ActivityManagerCompat.startActivityViaAssist(ContextUtils.getContext(), intent.getComponent(), () -> {
CountDownLatch waitForInteraction = new CountDownLatch(1);
ThreadUtils.postOnMainThread(() -> new MaterialAlertDialogBuilder(this)
.setTitle(R.string.launch_activity_dialog_title)
.setMessage(R.string.launch_activity_dialog_message)
.setCancelable(false)
.setOnDismissListener((dialog) -> {
waitForInteraction.countDown();
finishAndRemoveTask();
})
.setNegativeButton(R.string.close, null)
.show());
try {
waitForInteraction.await(10, TimeUnit.MINUTES);
} catch (InterruptedException ignore) {
}
});
if (launchViaAssist && !SelfPermissions.checkSelfOrRemotePermission(ManifestCompat.permission.START_ANY_ACTIVITY)) {
launchViaAssist(intent.getComponent());
} else {
try {
ActivityManagerCompat.startActivity(intent, userId);
} catch (RemoteException e) {
e.printStackTrace();
UIUtils.displayLongToast("Error: " + e.getMessage());
// Try assist instead
if (canLaunchViaAssist) {
launchViaAssist(intent.getComponent());
}
}
finishAndRemoveTask();
finish();
}
}

@Override
public boolean getTransparentBackground() {
return true;
}

private void launchViaAssist(@NonNull ComponentName cn) {
ActivityManagerCompat.startActivityViaAssist(ContextUtils.getContext(), cn, () -> {
CountDownLatch waitForInteraction = new CountDownLatch(1);
ThreadUtils.postOnMainThread(() -> new MaterialAlertDialogBuilder(this)
.setTitle(R.string.launch_activity_dialog_title)
.setMessage(R.string.launch_activity_dialog_message)
.setCancelable(false)
.setOnDismissListener((dialog) -> {
waitForInteraction.countDown();
finish();
})
.setNegativeButton(R.string.close, null)
.show());
try {
waitForInteraction.await(10, TimeUnit.MINUTES);
} catch (InterruptedException ignore) {
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ private void getActivityView(@NonNull ViewHolder holder, int index) {
});
}
holder.shortcutBtn.setOnClickListener(v -> {
PackageItemShortcutInfo<ActivityInfo> shortcutInfo = new PackageItemShortcutInfo<>(activityInfo, ActivityInfo.class, !componentItem.canLaunch && componentItem.canLaunchAssist);
PackageItemShortcutInfo<ActivityInfo> shortcutInfo = new PackageItemShortcutInfo<>(activityInfo, ActivityInfo.class, mUserId, componentItem.canLaunchAssist);
shortcutInfo.setName(componentItem.label);
shortcutInfo.setIcon(UIUtils.getBitmapFromDrawable(activityInfo.loadIcon(packageManager)));
CreateShortcutDialogFragment dialog = CreateShortcutDialogFragment.getInstance(shortcutInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

package io.github.muntashirakon.AppManager.details;

import android.annotation.UserIdInt;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageItemInfo;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandleHidden;

import androidx.annotation.NonNull;
import androidx.core.os.ParcelCompat;
Expand All @@ -21,15 +23,18 @@
public class PackageItemShortcutInfo<T extends PackageItemInfo & Parcelable> extends ShortcutInfo {
private final T mPackageItemInfo;
private final Class<T> mClazz;
@UserIdInt
private final int mUserId;
private final boolean mLaunchViaAssist;

public PackageItemShortcutInfo(@NonNull T packageItemInfo, @NonNull Class<T> clazz) {
this(packageItemInfo, clazz, false);
public PackageItemShortcutInfo(@NonNull T packageItemInfo, @NonNull Class<T> clazz, @UserIdInt int userId) {
this(packageItemInfo, clazz, userId, false);
}

public PackageItemShortcutInfo(@NonNull T packageItemInfo, @NonNull Class<T> clazz, boolean launchViaAssist) {
public PackageItemShortcutInfo(@NonNull T packageItemInfo, @NonNull Class<T> clazz, @UserIdInt int userId, boolean launchViaAssist) {
mPackageItemInfo = packageItemInfo;
mClazz = clazz;
mUserId = userId;
if (packageItemInfo instanceof ActivityInfo) {
mLaunchViaAssist = launchViaAssist;
} else mLaunchViaAssist = false;
Expand All @@ -40,6 +45,7 @@ public PackageItemShortcutInfo(Parcel in) {
super(in);
mClazz = (Class<T>) Objects.requireNonNull(ParcelCompat.readSerializable(in, Class.class.getClassLoader(), Class.class));
mPackageItemInfo = ParcelCompat.readParcelable(in, mClazz.getClassLoader(), mClazz);
mUserId = in.readInt();
mLaunchViaAssist = ParcelCompat.readBoolean(in);
}

Expand All @@ -48,6 +54,7 @@ public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeSerializable(mClazz);
dest.writeParcelable(mPackageItemInfo, flags);
dest.writeInt(mUserId);
ParcelCompat.writeBoolean(dest, mLaunchViaAssist);
}

Expand Down Expand Up @@ -80,17 +87,11 @@ private Intent getIntent() {

@NonNull
private Intent getProxyIntent(@NonNull Context context) {
Intent intent = new Intent();
intent.setClass(context, ActivityLauncherShortcutActivity.class);
intent.putExtra(ActivityLauncherShortcutActivity.EXTRA_PKG, mPackageItemInfo.packageName);
intent.putExtra(ActivityLauncherShortcutActivity.EXTRA_CLS, mPackageItemInfo.name);
intent.putExtra(ActivityLauncherShortcutActivity.EXTRA_AST, mLaunchViaAssist);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
return intent;
return ActivityLauncherShortcutActivity.getShortcutIntent(context, mPackageItemInfo.packageName,
mPackageItemInfo.name, mUserId, mLaunchViaAssist);
}

private boolean requireProxy() {
return !BuildConfig.APPLICATION_ID.equals(mPackageItemInfo.packageName);
return !BuildConfig.APPLICATION_ID.equals(mPackageItemInfo.packageName) && mUserId != UserHandleHidden.myUserId();
}
}

0 comments on commit 707c37e

Please sign in to comment.