From ce3b61aad4f11d0d7b10bc2b1a7a3f38e445973d Mon Sep 17 00:00:00 2001 From: Theray Tharow Date: Wed, 11 Feb 2026 19:49:36 -0500 Subject: [PATCH 1/7] first start of this got most things working might switch to id 1000 at some point. --- Android.bp | 36 ++ app/build.gradle | 25 +- app/dev_keystore.jks | Bin 2142 -> 4655 bytes app/src/main/AndroidManifest.xml | 15 +- .../muntashirakon/AppManager/AppManager.java | 4 +- .../apk/installer/InstallerDialogHelper.java | 20 ++ .../installer/PackageInstallerActivity.java | 29 +- .../apk/installer/PackageInstallerCompat.java | 12 + .../self/life/BuildExpiryChecker.java | 15 +- .../AppManager/settings/Ops.java | 1 + .../muntashirakon/AppManager/utils/Utils.java | 5 + app/src/platform/AndroidManifest.xml | 331 ++++++++++++++++++ .../installer/PackageUninstallerActivity.java | 109 ++++++ platform/default-permissions.xml | 12 + platform/permissions.xml | 30 ++ platform/sysconfig.xml | 10 + 16 files changed, 646 insertions(+), 8 deletions(-) create mode 100644 Android.bp create mode 100644 app/src/platform/AndroidManifest.xml create mode 100644 app/src/platform/java/io/github/muntashirakon/AppManager/apk/installer/PackageUninstallerActivity.java create mode 100644 platform/default-permissions.xml create mode 100644 platform/permissions.xml create mode 100644 platform/sysconfig.xml diff --git a/Android.bp b/Android.bp new file mode 100644 index 00000000000..7bd35e3c73c --- /dev/null +++ b/Android.bp @@ -0,0 +1,36 @@ +android_app_import { + name: "AppManager", + owner: "Muntashirakon", + apk: "app/build/outputs/apk_from_bundle/platformRelease/app-platform-release-universal-unsigned.apk", + certificate: "platform", + dex_preopt: { + enabled: false, + }, + privileged: true, + presigned: false, + overrides: ["PackageInstaller"], + required: [ + "io.github.muntashirakon.AppManager_sysconfig.xml", + "default-permissions_io.github.muntashirakon.AppManager.xml", + "privapp-permissions_io.github.muntashirakon.AppManager.xml" + ], +} + + +prebuilt_etc { + name: "io.github.muntashirakon.AppManager_sysconfig.xml", + src: "platform/sysconfig.xml", + sub_dir: "sysconfig", +} + +prebuilt_etc { + name: "privapp-permissions_io.github.muntashirakon.AppManager.xml", + src: "platform/permissions.xml", + sub_dir: "permissions", +} + +prebuilt_etc { + name: "default-permissions_io.github.muntashirakon.AppManager.xml", + src: "platform/default-permissions.xml", + sub_dir: "default-permissions" +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 068cc152c38..645853eef0a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -35,21 +35,40 @@ android { keyPassword 'kJCp!Bda#PBdN2RLK%yMK@hatq&69E' keyAlias 'key0' } + platform { // Platform signing key used by eng rom images https://github.com/LineageOS/android_build/blob/lineage-23.2/target/product/security/platform.pk8 + storeFile file('dev_keystore.jks') + storePassword 'kJCp!Bda#PBdN2RLK%yMK@hatq&69E' + keyPassword 'kJCp!Bda#PBdN2RLK%yMK@hatq&69E' + keyAlias 'key1' + } } - buildTypes { release { minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' resValue "string", "app_name", "App Manager" } debug { applicationIdSuffix '.debug' versionNameSuffix '-DEBUG' - signingConfig signingConfigs.debug resValue "string", "app_name", "AM Debug" + // Value must be changed manually for platform debug + // TODO 11FEB26 make debug signingConfig selection automatic. + signingConfig = signingConfigs.debug; } } + flavorDimensions "version" + productFlavors { + standard { + dimension "version" + } + platform { + dimension "version" + applicationIdSuffix ".platform" + versionNameSuffix "-PLATFORM" + } + } + lint { checkReleaseBuilds false abortOnError false diff --git a/app/dev_keystore.jks b/app/dev_keystore.jks index c2abe7bb892f17b8435abe94137566354a78b7c9..9466e5438f25490cc8a5cde23f748f307d8c83b9 100644 GIT binary patch delta 2589 zcmcImXHb)C7XCgGKtQB~DmO?+LEwuJqzHl(2`DXeje-gSN*6-CABoZw1tUt8Vvy@4 zfJAx`h;*eZL1{`rDS{xb6h*jr*WGnycXnoHcYnO|&Uxm%GtW8moaemb{14^e&4tYc z007v291uWWa1X-(0C@YbXr3(qumcboxdH9s;8x}Uk)RTr00^=JNHTKfo;fg69nw@r zXO{_2i#tR+YHtgaSO>j z$7iP*zq1WH53A;JuIfcQ=X)CWEkM0+|P}t|Iq;hl3vID7-IqfY7t3zHF(oX^XN8gd_D1W<<|zux0$-e3pl8UsYjq5rYN`6^>CrD)VzC-s z9!sY*3)PPF#5gG`x?m-vtI|oGqOPYrmq*RhK6F2uIBa|LY@J#EK#c(-dw$isfZ$QsVkq*Ofo)OXni>hK~(AFlXJ5>v>ih z|NJAWHfd*qdSDf=A$Ivn$m?ewl_pQN&^QU_C-8E0Z2U#x$3ud?3S3DdX?6n?w3Lz8 zk*KT5x7!d!LMw=7L`Rv?XhbQ!L~iy=Rk& zSAxew7|CtAOZmb4b=C8ddmI9oCs;=;Sy04kpoQxzCr|sE`ubIGo%O}94v+dPUcMF2$;4YE z(!M3_NPo=dI!rMCJg^q}vKo)>>ersdJcTpwKm6L<+53Et(S@cH8vE*xx^j^sU_{vQXyv=!^9V^AMK{)W8Q8IQ!oy@|pO<}EIf$|w;nBixy% z4M=0bS%nAdC!DXby2!wTnDZyU35R#k_02y3%JDY_8T|6!;s!O_EO4bUq7?@?LyV^_ zb0}-KY<-QGyG^FgYPCt2f>+*3yTZiZG)*(Y`g<`eIKE+nVtW8qEbAp%fZsPNqHAwMj^By60C{yO^ki z4Pg;k{L<~bmdmiELTl5Ac6tJBfnl>K(vGdY=7wk7)b7SnR;*`>O$Hsu8>vuH1gvym zIl89w@RRm)nQ1#k=LRZ|z&AuX)*JXAI}oO>k>7hK1Uj{D?hh__w^XbsoNH;=B_52< zCBBqk&gjZLI==0ZAi(aViiNZR09mwcj1M3(A~_eq2C}hn0cGjYv3(A`Um#u%E~Qwr z*i~K-L~i>hDFwp=ad9B*ksK&CdprgO349NBptz0P;EP@!eu2JTZWs|r=sU`Z;yvyc z?0d=0%U8<5%bkef-}c^ZoEyby=zA{E&+FXZ5T1WR5dT1AAgRBT-~vTZZhw7i@Z(lJ z>*oVWit<9R7Nmi}9yp)@VIdb$UiIzLAk2^O-xB{H@rM5_?mr|>2Bm)P@%BDKkU`$< zMFs9)BZDB270OO2;w5PPGG6FD-%$YgYMSlz3FM?p2{c&8>d;sf1R&#efGKlXN(PQ@ z?>-(eq9CVxHTt4LnFT6XHlN}tgF}vjIQ>ZO-rejSmsTs{BLDU{%P=bey;_EWQPzSxb?F{w#RT=A|T)>Z&UqBXHU>L zpO0xy>@d?G76SGiiqGwi915+{d)Oro(?Z@!t2!{+-FjQphSbMxTe;uF3-xSp>$>bV zGG#wb&OwvQm8HxY<16}c`xawXc8v)Rso1;!Dl<7lKz3l5sQz@Vx&*QuOqG_ukf(z7 zPNK}A>CHiyh}@=wOZ^X5OiGsJQgdp3!-eYEP6og=B(0uLV;k&uM0@3~oLnvEJxg9y zJT;_+bE$lLOCm)&!A diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 450f0d1f135..f13d8d41450 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -600,6 +600,14 @@ android:launchMode="singleInstance" android:taskAffinity="" android:theme="@style/AppTheme.TransparentBackground"> + + + + + + + + @@ -728,7 +736,12 @@ android:label="@string/interceptor" android:taskAffinity="" android:windowSoftInputMode="stateUnchanged"> - + + + + + + diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/AppManager.java b/app/src/main/java/io/github/muntashirakon/AppManager/AppManager.java index 79a3f945971..85923903509 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/AppManager.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/AppManager.java @@ -15,6 +15,7 @@ import org.lsposed.hiddenapibypass.HiddenApiBypass; import java.security.Security; +import java.util.Objects; import dalvik.system.ZipPathValidator; import io.github.muntashirakon.AppManager.misc.AMExceptionHandler; @@ -48,7 +49,8 @@ public void onCreate() { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !Utils.isRoboUnitTest()) { + //noinspection ConstantValue Don't use the HiddenApiBypass in Platform as we are whilelisted and it caused infighting if enabled + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !Utils.isRoboUnitTest() && !Objects.equals(BuildConfig.FLAVOR, "platform")) { HiddenApiBypass.addHiddenApiExemptions("L"); } } diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/InstallerDialogHelper.java b/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/InstallerDialogHelper.java index 7797732eb81..7bf4431a0f2 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/InstallerDialogHelper.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/InstallerDialogHelper.java @@ -146,6 +146,26 @@ public void showInstallConfirmationDialog(@StringRes int installButtonRes, mMessage.setText(R.string.install_app_message); mFragmentContainer.setVisibility(View.GONE); } + public void showSessionConfirmationDialog(@StringRes int installButtonRes, + @NonNull OnClickButtonsListener onClickButtonsListener) { + mTitleBuilder.setTitle(R.string.confirm_installation) + .setStartIcon(R.drawable.ic_get_app) + .setSubtitle(null) + .setEndIcon(null, null); + // Buttons + mNeutralBtn.setVisibility(View.GONE); + mPositiveBtn.setVisibility(View.VISIBLE); + mPositiveBtn.setText(installButtonRes); + mPositiveBtn.setOnClickListener(v -> onClickButtonsListener.triggerInstall()); + mNegativeBtn.setVisibility(View.VISIBLE); + mNegativeBtn.setText(R.string.cancel); + mNegativeBtn.setOnClickListener(v -> onClickButtonsListener.triggerCancel()); + // Body + mLayout.setVisibility(View.GONE); + mMessage.setVisibility(View.VISIBLE); + mMessage.setText(R.string.install_app_message); + mFragmentContainer.setVisibility(View.GONE); + } public void showApkChooserDialog(@StringRes int installButtonRes, Fragment fragment, @NonNull OnClickButtonsListener onClickButtonsListener, diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/PackageInstallerActivity.java b/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/PackageInstallerActivity.java index 50c17dabefa..c4c49f2e042 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/PackageInstallerActivity.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/PackageInstallerActivity.java @@ -2,6 +2,8 @@ package io.github.muntashirakon.AppManager.apk.installer; +import static io.github.muntashirakon.AppManager.apk.installer.PackageInstallerCompat.ACTION_CONFIRM_INSTALL; +import static io.github.muntashirakon.AppManager.apk.installer.PackageInstallerCompat.ACTION_CONFIRM_PRE_APPROVAL; import static io.github.muntashirakon.AppManager.apk.installer.PackageInstallerCompat.STATUS_FAILURE_ABORTED; import static io.github.muntashirakon.AppManager.apk.installer.PackageInstallerCompat.STATUS_FAILURE_BLOCKED; import static io.github.muntashirakon.AppManager.apk.installer.PackageInstallerCompat.STATUS_FAILURE_CONFLICT; @@ -109,11 +111,14 @@ public static Intent getLaunchableInstance(@NonNull Context context, ApkSource a @NonNull public static Intent getLaunchableInstance(@NonNull Context context, @NonNull String packageName) { Intent intent = new Intent(context, PackageInstallerActivity.class); - intent.setData(Uri.parse("package:" + packageName)); + intent.putExtra(EXTRA_INSTALL_EXISTING, true); + intent.putExtra(EXTRA_PACKAGE_NAME, packageName); return intent; } private static final String EXTRA_APK_FILE_LINK = "link"; + public static final String EXTRA_INSTALL_EXISTING = "install_existing"; + public static final String EXTRA_PACKAGE_NAME = "pkg"; public static final String ACTION_PACKAGE_INSTALLED = BuildConfig.APPLICATION_ID + ".action.PACKAGE_INSTALLED"; private int mSessionId = -1; @@ -194,6 +199,10 @@ protected void onAuthenticated(@Nullable Bundle savedInstanceState) { onNewIntent(intent); return; } + if (ACTION_CONFIRM_INSTALL.equals(intent.getAction()) || ACTION_CONFIRM_PRE_APPROVAL.equals(intent.getAction())) { + mSessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1); + intent.putExtra(EXTRA_APK_FILE_LINK, ApkSource.getApkSource(new Uri.Builder().build(), "")); + } mModel = new ViewModelProvider(this).get(PackageInstallerViewModel.class); if (!bindService( new Intent(this, PackageInstallerService.class), mServiceConnection, BIND_AUTO_CREATE)) { @@ -210,6 +219,24 @@ protected void onAuthenticated(@Nullable Bundle savedInstanceState) { } } mModel.packageInfoLiveData().observe(this, newPackageInfo -> { + if (newPackageInfo == null && + (ACTION_CONFIRM_INSTALL.equals(intent.getAction()) || + ACTION_CONFIRM_PRE_APPROVAL.equals(intent.getAction()))) { + PackageInstallerCompat installerCompat = PackageInstallerCompat.getNewInstance(); + mDialogHelper.showSessionConfirmationDialog(R.string.install, new InstallerDialogHelper.OnClickButtonsListener() { + @Override + public void triggerInstall() { + installerCompat.setPermissionsResult(mSessionId, true); + finish(); + } + @Override + public void triggerCancel() { + installerCompat.setPermissionsResult(mSessionId, false); + finish(); + } + }); + return; + } if (newPackageInfo == null) { mDialogHelper.showParseFailedDialog(v -> triggerCancel()); return; diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/PackageInstallerCompat.java b/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/PackageInstallerCompat.java index c536eadcf36..88f98cd1bdf 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/PackageInstallerCompat.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/PackageInstallerCompat.java @@ -89,6 +89,9 @@ public final class PackageInstallerCompat { // For rootless installer to prevent PackageInstallerService from hanging public static final String ACTION_INSTALL_INTERACTION_BEGIN = BuildConfig.APPLICATION_ID + ".action.INSTALL_INTERACTION_BEGIN"; public static final String ACTION_INSTALL_INTERACTION_END = BuildConfig.APPLICATION_ID + ".action.INSTALL_INTERACTION_END"; + public static final String ACTION_CONFIRM_PRE_APPROVAL = "android.content.pm.action.CONFIRM_PRE_APPROVAL"; + public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL"; + public static final String EXTRA_UNINSTALL_ALL_USERS = "android.intent.extra.UNINSTALL_ALL_USERS"; @IntDef({ STATUS_SUCCESS, @@ -813,6 +816,15 @@ private boolean commit(int userId) { } return mFinalStatus == PackageInstaller.STATUS_SUCCESS; } + public void setPermissionsResult(int sessionId, boolean accepted) { + try { + mPackageInstaller = PackageManagerCompat.getPackageInstaller(); + mPackageInstaller.setPermissionsResult(sessionId, accepted); + } catch (RemoteException e) { + callFinish(STATUS_FAILURE_SESSION_COMMIT); + Log.e(TAG, "SetPermissionsResult: failed to set permission result for given session", e); + } + } @SuppressWarnings("BooleanMethodIsAlwaysInverted") private boolean openSession(@UserIdInt int userId, @InstallFlags int installFlags, diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/self/life/BuildExpiryChecker.java b/app/src/main/java/io/github/muntashirakon/AppManager/self/life/BuildExpiryChecker.java index 671a9b8d8f4..de2e6699eb7 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/self/life/BuildExpiryChecker.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/self/life/BuildExpiryChecker.java @@ -20,9 +20,10 @@ import io.github.muntashirakon.AppManager.BuildConfig; import io.github.muntashirakon.AppManager.R; +import io.github.muntashirakon.AppManager.utils.Utils; public final class BuildExpiryChecker { - @IntDef({BUILD_TYPE_DEBUG, BUILD_TYPE_ALPHA, BUILD_TYPE_BETA, BUILD_TYPE_RC, BUILD_TYPE_STABLE}) + @IntDef({BUILD_TYPE_DEBUG, BUILD_TYPE_ALPHA, BUILD_TYPE_BETA, BUILD_TYPE_RC, BUILD_TYPE_STABLE, BUILD_TYPE_PLATFORM}) @Retention(RetentionPolicy.SOURCE) private @interface BuildType {} @@ -31,6 +32,7 @@ public final class BuildExpiryChecker { private static final int BUILD_TYPE_BETA = 2; private static final int BUILD_TYPE_RC = 3; private static final int BUILD_TYPE_STABLE = 4; + private static final int BUILD_TYPE_PLATFORM = 5; private static final long[] TIME_SPAN_MILLIS = new long[]{ 2L * 30 * 24 * 60 * 60_000, // 2 months @@ -38,6 +40,7 @@ public final class BuildExpiryChecker { 6L * 30 * 24 * 60 * 60_000, // 6 months 6L * 30 * 24 * 60 * 60_000, // 6 months 18L * 30 * 24 * 60 * 60_000, // 18 months + 18L * 30 * 24 * 60 * 60_000, // 18 months }; private static final long[] WARNING_PERIOD_MILLIS = new long[]{ @@ -46,6 +49,7 @@ public final class BuildExpiryChecker { 30L * 24 * 60 * 60_000, // 1 month 30L * 24 * 60 * 60_000, // 1 month 3L * 30 * 24 * 60 * 60_000, // 3 months + 3L * 30 * 24 * 60 * 60_000, // 3 months }; @NonNull @@ -69,7 +73,8 @@ public static AlertDialog getBuildExpiredDialog(@NonNull FragmentActivity activi activity.startActivity(intent); activity.finishAndRemoveTask(); }); - if (getBuildType() == BUILD_TYPE_STABLE) { + // Platform + if (getBuildType() == BUILD_TYPE_STABLE || getBuildType() == BUILD_TYPE_PLATFORM) { builder.setNeutralButton(R.string.action_continue, continueClickListener); } return builder.create(); @@ -107,6 +112,9 @@ private static Uri getUpdateUri() { if (getBuildType() == BUILD_TYPE_DEBUG) { return Uri.parse("https://github.com/MuntashirAkon/AppManager/actions/workflows/debug_build.yml"); } + if (getBuildType() == BUILD_TYPE_PLATFORM) { // Rom Builders Should Set this value //TODO: 8FEB26 Link to relevant AM Docs page + return Uri.parse(System.getProperty("ro.appmanager.update.url", "https://tharow.net/AppManager")); + } // TODO: 3/12/22 For Stable builds, check F-Droid too return Uri.parse("https://github.com/MuntashirAkon/AppManager/releases"); } @@ -116,6 +124,9 @@ private static int getBuildType() { if (BuildConfig.DEBUG) { return BUILD_TYPE_DEBUG; } + if (Utils.isPlatform()) { + return BUILD_TYPE_PLATFORM; + } String[] versionParts = BuildConfig.VERSION_NAME.split("-"); if (versionParts.length == 1) { return BUILD_TYPE_STABLE; diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/settings/Ops.java b/app/src/main/java/io/github/muntashirakon/AppManager/settings/Ops.java index 86e5e48944a..63e4ba5b812 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/settings/Ops.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/settings/Ops.java @@ -76,6 +76,7 @@ public class Ops { public static final String MODE_ADB_OVER_TCP = "adb_tcp"; public static final String MODE_ADB_WIFI = "adb_wifi"; public static final String MODE_NO_ROOT = "no-root"; + public static final String MODE_PLATFORM = "platform"; @IntDef({ STATUS_SUCCESS, diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/utils/Utils.java b/app/src/main/java/io/github/muntashirakon/AppManager/utils/Utils.java index 06a9120675e..b1993264bbc 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/utils/Utils.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/utils/Utils.java @@ -680,4 +680,9 @@ public static String prettyPrintObject(@Nullable T obj) { public static boolean isRoboUnitTest() { return "robolectric".equals(Build.FINGERPRINT); } + + public static boolean isPlatform() { + // noinspection ConstantValue + return BuildConfig.FLAVOR.equals("platform"); + } } diff --git a/app/src/platform/AndroidManifest.xml b/app/src/platform/AndroidManifest.xml new file mode 100644 index 00000000000..c76c1f5e6ed --- /dev/null +++ b/app/src/platform/AndroidManifest.xml @@ -0,0 +1,331 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/platform/java/io/github/muntashirakon/AppManager/apk/installer/PackageUninstallerActivity.java b/app/src/platform/java/io/github/muntashirakon/AppManager/apk/installer/PackageUninstallerActivity.java new file mode 100644 index 00000000000..d1cd7441c89 --- /dev/null +++ b/app/src/platform/java/io/github/muntashirakon/AppManager/apk/installer/PackageUninstallerActivity.java @@ -0,0 +1,109 @@ +package io.github.muntashirakon.AppManager.apk.installer; + +import static io.github.muntashirakon.AppManager.utils.UIUtils.displayLongToast; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.UserHandleHidden; +import android.util.Log; + +import androidx.annotation.Nullable; +import androidx.annotation.RequiresPermission; + +import java.util.Objects; + +import io.github.muntashirakon.AppManager.BaseActivity; +import io.github.muntashirakon.AppManager.R; +import io.github.muntashirakon.AppManager.compat.ActivityManagerCompat; +import io.github.muntashirakon.AppManager.compat.PackageManagerCompat; +import io.github.muntashirakon.AppManager.self.SelfPermissions; +import io.github.muntashirakon.AppManager.utils.ThreadUtils; +import io.github.muntashirakon.AppManager.utils.UIUtils; +import io.github.muntashirakon.dialog.ScrollableDialogBuilder; + +public class PackageUninstallerActivity extends BaseActivity { + public static final String TAG = PackageUninstallerActivity.class.getSimpleName(); + private String mPackageName; + private ApplicationInfo mApplicationInfo; + private BaseActivity mActivity; + private String mAppLabel; + private int mUserId; + + @Override + public boolean getTransparentBackground() { + return true; + } + + + @RequiresPermission(anyOf = {Manifest.permission.REQUEST_DELETE_PACKAGES, Manifest.permission.DELETE_PACKAGES}) + @Override + protected void onAuthenticated(@Nullable Bundle savedInstanceState) { + this.mPackageName = Objects.requireNonNull(getIntent().getDataString()).replaceFirst("package:", ""); + boolean isUninstallFromAllUsers = getIntent().getBooleanExtra(PackageInstallerCompat.EXTRA_UNINSTALL_ALL_USERS, false); + this.mActivity = this; + this.mUserId = UserHandleHidden.myUserId(); + try { + this.mApplicationInfo = PackageManagerCompat.getApplicationInfo(mPackageName, ApplicationInfo.FLAG_INSTALLED, mUserId); + } catch (RemoteException | PackageManager.NameNotFoundException e) { + Log.e(TAG, "Uninstaller: onAuthenticated: getApplicationInfo", e); + throw new RuntimeException(e); + } + this.mAppLabel = mApplicationInfo.loadLabel(getPackageManager()).toString(); + uninstallDialog(isUninstallFromAllUsers); + } + + private void uninstallDialog(boolean isUninstallFromAllUsers) { + if (mUserId != UserHandleHidden.myUserId() && !SelfPermissions.checkSelfOrRemotePermission(Manifest.permission.DELETE_PACKAGES)) { + // Could be for work profile + try { + Intent uninstallIntent = new Intent(Intent.ACTION_DELETE); + uninstallIntent.setData(Uri.parse("package:" + mPackageName)); + ActivityManagerCompat.startActivity(uninstallIntent, mUserId); + } catch (Throwable th) { + UIUtils.displayLongToast("Error: " + th.getLocalizedMessage()); + } + return; + } + final boolean isSystemApp = (mApplicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + ScrollableDialogBuilder builder = new ScrollableDialogBuilder(mActivity, + isSystemApp ? R.string.uninstall_system_app_message : R.string.uninstall_app_message) + .setTitle(mAppLabel) + .setCheckboxLabel(R.string.keep_data_and_app_signing_signatures) + .setPositiveButton(R.string.uninstall, (dialog, which, keepData) -> ThreadUtils.postOnBackgroundThread(() -> { + PackageInstallerCompat installer = PackageInstallerCompat.getNewInstance(); + installer.setAppLabel(mAppLabel); + boolean uninstalled = installer.uninstall(mPackageName, mUserId, keepData); + ThreadUtils.postOnMainThread(() -> { + if (uninstalled) { + displayLongToast(R.string.uninstalled_successfully, mAppLabel); + mActivity.finish(); + } else { + displayLongToast(R.string.failed_to_uninstall, mAppLabel); + } + }); + })) + .setNegativeButton(R.string.cancel, (dialog, which, keepData) -> { + if (dialog != null) dialog.cancel(); + finish(); + }); + if ((mApplicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { + builder.setNeutralButton(R.string.uninstall_updates, (dialog, which, keepData) -> + ThreadUtils.postOnBackgroundThread(() -> { + PackageInstallerCompat installer = PackageInstallerCompat.getNewInstance(); + installer.setAppLabel(mAppLabel); + boolean isSuccessful = installer.uninstall(mPackageName, UserHandleHidden.USER_ALL, keepData); + if (isSuccessful) { + ThreadUtils.postOnMainThread(() -> displayLongToast(R.string.update_uninstalled_successfully, mAppLabel)); + } else { + ThreadUtils.postOnMainThread(() -> displayLongToast(R.string.failed_to_uninstall_updates, mAppLabel)); + } + })); + } + builder.show(); + } +} \ No newline at end of file diff --git a/platform/default-permissions.xml b/platform/default-permissions.xml new file mode 100644 index 00000000000..d27fd8823df --- /dev/null +++ b/platform/default-permissions.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/platform/permissions.xml b/platform/permissions.xml new file mode 100644 index 00000000000..e14f39b9ce4 --- /dev/null +++ b/platform/permissions.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platform/sysconfig.xml b/platform/sysconfig.xml new file mode 100644 index 00000000000..8645eb15a8c --- /dev/null +++ b/platform/sysconfig.xml @@ -0,0 +1,10 @@ + + + + + + + + From bfeea1d61be7d1e9f37d4e7570fde2adb7d4c02b Mon Sep 17 00:00:00 2001 From: Theray Tharow Date: Fri, 13 Feb 2026 13:37:54 -0500 Subject: [PATCH 2/7] First Git Ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c0ddc152cbc..7573c719a3a 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ basement_dist *.jks *~ scripts/KeyStore.sh +/app/platform/* From 23a3afb1127086fd8b7d1ad861d04eb82e893532 Mon Sep 17 00:00:00 2001 From: Theray Tharow Date: Sat, 14 Feb 2026 01:34:47 -0500 Subject: [PATCH 3/7] Working on Session Installation new Platform Mode --- .gitignore | 1 - app/.gitignore | 1 + app/build.gradle | 2 +- app/src/main/AndroidManifest.xml | 10 +++++ .../installer/PackageInstallerActivity.java | 44 +++++++++---------- .../self/life/BuildExpiryChecker.java | 4 +- .../AppManager/settings/MainPreferences.java | 9 +--- .../settings/ModeOfOpsPreference.java | 15 ++----- .../AppManager/settings/Ops.java | 25 ++++++++++- .../muntashirakon/AppManager/utils/Utils.java | 6 +-- app/src/main/res/values/arrays.xml | 1 + app/src/main/res/values/strings.xml | 1 + app/src/platform/AndroidManifest.xml | 17 ++++++- .../installer/PackageUninstallerActivity.java | 13 +----- .../content/pm/PackageInstallerHidden.java | 6 +++ .../main/java/android/view/WindowHidden.java | 11 +++++ platform/permissions.xml | 3 ++ 17 files changed, 104 insertions(+), 65 deletions(-) create mode 100644 hiddenapi/src/main/java/android/view/WindowHidden.java diff --git a/.gitignore b/.gitignore index 7573c719a3a..c0ddc152cbc 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,3 @@ basement_dist *.jks *~ scripts/KeyStore.sh -/app/platform/* diff --git a/app/.gitignore b/app/.gitignore index c2fdceef700..27cc6bff3c3 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -2,3 +2,4 @@ *.apk .cxx *~ +/platform \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 645853eef0a..f18295c7ec2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -54,7 +54,7 @@ android { resValue "string", "app_name", "AM Debug" // Value must be changed manually for platform debug // TODO 11FEB26 make debug signingConfig selection automatic. - signingConfig = signingConfigs.debug; + signingConfig = signingConfigs.platform; } } flavorDimensions "version" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f13d8d41450..c8a643de077 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -152,7 +152,17 @@ + + + + + unsafeCast(getWindow()).addSystemFlags(1 << 19); } + mModel = new ViewModelProvider(this).get(PackageInstallerViewModel.class); if (!bindService( new Intent(this, PackageInstallerService.class), mServiceConnection, BIND_AUTO_CREATE)) { @@ -212,31 +219,24 @@ protected void onAuthenticated(@Nullable Bundle savedInstanceState) { mApkQueue.addAll(ApkQueueItem.fromIntent(intent, Utils.getRealReferrer(this))); } - ApkSource apkSource = IntentCompat.getUnwrappedParcelableExtra(intent, EXTRA_APK_FILE_LINK, ApkSource.class); + ApkSource apkSource; + if (ACTION_CONFIRM_INSTALL.equals(intent.getAction()) || ACTION_CONFIRM_PRE_APPROVAL.equals(intent.getAction())) { + mSessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1); + try { + Uri uri = Uri.fromFile(new File(Refine.unsafeCast(PackageManagerCompat.getPackageInstaller().getSessionInfo(mSessionId)).getResolvedBaseApkPath())); + apkSource = ApkSource.getApkSource(uri, null); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } else { + apkSource = IntentCompat.getUnwrappedParcelableExtra(intent, EXTRA_APK_FILE_LINK, ApkSource.class); + } if (apkSource != null) { synchronized (mApkQueue) { mApkQueue.add(ApkQueueItem.fromApkSource(apkSource)); } } mModel.packageInfoLiveData().observe(this, newPackageInfo -> { - if (newPackageInfo == null && - (ACTION_CONFIRM_INSTALL.equals(intent.getAction()) || - ACTION_CONFIRM_PRE_APPROVAL.equals(intent.getAction()))) { - PackageInstallerCompat installerCompat = PackageInstallerCompat.getNewInstance(); - mDialogHelper.showSessionConfirmationDialog(R.string.install, new InstallerDialogHelper.OnClickButtonsListener() { - @Override - public void triggerInstall() { - installerCompat.setPermissionsResult(mSessionId, true); - finish(); - } - @Override - public void triggerCancel() { - installerCompat.setPermissionsResult(mSessionId, false); - finish(); - } - }); - return; - } if (newPackageInfo == null) { mDialogHelper.showParseFailedDialog(v -> triggerCancel()); return; diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/self/life/BuildExpiryChecker.java b/app/src/main/java/io/github/muntashirakon/AppManager/self/life/BuildExpiryChecker.java index de2e6699eb7..86d2f9481ec 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/self/life/BuildExpiryChecker.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/self/life/BuildExpiryChecker.java @@ -20,7 +20,7 @@ import io.github.muntashirakon.AppManager.BuildConfig; import io.github.muntashirakon.AppManager.R; -import io.github.muntashirakon.AppManager.utils.Utils; +import io.github.muntashirakon.AppManager.settings.Ops; public final class BuildExpiryChecker { @IntDef({BUILD_TYPE_DEBUG, BUILD_TYPE_ALPHA, BUILD_TYPE_BETA, BUILD_TYPE_RC, BUILD_TYPE_STABLE, BUILD_TYPE_PLATFORM}) @@ -124,7 +124,7 @@ private static int getBuildType() { if (BuildConfig.DEBUG) { return BUILD_TYPE_DEBUG; } - if (Utils.isPlatform()) { + if (Ops.isPlatform()) { return BUILD_TYPE_PLATFORM; } String[] versionParts = BuildConfig.VERSION_NAME.split("-"); diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/settings/MainPreferences.java b/app/src/main/java/io/github/muntashirakon/AppManager/settings/MainPreferences.java index 8793357e9ba..f3e4ec14c3a 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/settings/MainPreferences.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/settings/MainPreferences.java @@ -33,13 +33,6 @@ public static MainPreferences getInstance(@Nullable String key, boolean dualPane return preferences; } - private static final List MODE_NAMES = Arrays.asList( - Ops.MODE_AUTO, - Ops.MODE_ROOT, - Ops.MODE_ADB_OVER_TCP, - Ops.MODE_ADB_WIFI, - Ops.MODE_NO_ROOT); - private FragmentActivity mActivity; private Preference mModePref; private Preference mLocalePref; @@ -76,7 +69,7 @@ public void onStart() { super.onStart(); if (mModePref != null) { mModePref.setSummary(getString(R.string.mode_of_op_with_inferred_mode_of_op, - mModes[MODE_NAMES.indexOf(Ops.getMode())], Ops.getInferredMode(mActivity))); + mModes[Ops.MODE_NAMES.indexOf(Ops.getMode())], Ops.getInferredMode(mActivity))); } if (mLocalePref != null) { mLocalePref.setSummary(getLanguageName()); diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/settings/ModeOfOpsPreference.java b/app/src/main/java/io/github/muntashirakon/AppManager/settings/ModeOfOpsPreference.java index 2b2fbefde23..d24b4212fe9 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/settings/ModeOfOpsPreference.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/settings/ModeOfOpsPreference.java @@ -41,13 +41,6 @@ import io.github.muntashirakon.widget.TextInputTextView; public class ModeOfOpsPreference extends Fragment { - private static final List MODE_NAMES = Arrays.asList( - Ops.MODE_AUTO, - Ops.MODE_ROOT, - Ops.MODE_ADB_OVER_TCP, - Ops.MODE_ADB_WIFI, - Ops.MODE_NO_ROOT); - private MaterialTextView mInferredModeView; private MaterialTextView mRemoteServerStatusView; private MaterialTextView mRemoteServicesStatusView; @@ -113,7 +106,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R || Utils.isTv(requireContext())) { disabledItems = Collections.singletonList(Ops.MODE_ADB_WIFI); } else disabledItems = null; - changeModeView.setOnClickListener(v -> new SearchableSingleChoiceDialogBuilder<>(requireActivity(), MODE_NAMES, mModes) + changeModeView.setOnClickListener(v -> new SearchableSingleChoiceDialogBuilder<>(requireActivity(), Ops.MODE_NAMES, mModes) .setTitle(R.string.pref_mode_of_operations) .setSelection(mCurrentMode) .addDisabledItems(disabledItems) @@ -210,7 +203,7 @@ private void updateViews() { TextViewCompat.setCompoundDrawableTintList(mModeOfOpsView, mColorActive); mModeOfOpsView.setTextColor(mColorActive); mModeOfOpsView.setCompoundDrawablesRelativeWithIntrinsicBounds(mIconProgress, 0, 0, 0); - mModeOfOpsView.setText(getString(R.string.status_connecting_via_mode, mModes[MODE_NAMES.indexOf(mCurrentMode)])); + mModeOfOpsView.setText(getString(R.string.status_connecting_via_mode, mModes[Ops.MODE_NAMES.indexOf(mCurrentMode)])); } else { int uid = Users.getSelfOrRemoteUid(); boolean goodMode = !badInferredMode(mCurrentMode, uid); @@ -223,14 +216,14 @@ private void updateViews() { CharSequence mode; if (serverActive && uid != Process.myUid()) { mode = "remote service"; - } else mode = mModes[MODE_NAMES.indexOf(mCurrentMode)]; + } else mode = mModes[Ops.MODE_NAMES.indexOf(mCurrentMode)]; mModeOfOpsView.setText(getString(R.string.status_connected_via_mode, mode)); } else { mInferredModeView.setTextColor(mColorError); TextViewCompat.setCompoundDrawableTintList(mModeOfOpsView, mColorError); mModeOfOpsView.setTextColor(mColorError); mModeOfOpsView.setCompoundDrawablesRelativeWithIntrinsicBounds(mIconInactive, 0, 0, 0); - mModeOfOpsView.setText(getString(R.string.status_not_connected_via_mode, mModes[MODE_NAMES.indexOf(mCurrentMode)])); + mModeOfOpsView.setText(getString(R.string.status_not_connected_via_mode, mModes[Ops.MODE_NAMES.indexOf(mCurrentMode)])); } } // Server diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/settings/Ops.java b/app/src/main/java/io/github/muntashirakon/AppManager/settings/Ops.java index 63e4ba5b812..5943b58e37b 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/settings/Ops.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/settings/Ops.java @@ -29,11 +29,14 @@ import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.List; import java.util.Locale; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import io.github.muntashirakon.AppManager.BuildConfig; import io.github.muntashirakon.AppManager.R; import io.github.muntashirakon.AppManager.adb.AdbConnectionManager; import io.github.muntashirakon.AppManager.adb.AdbPairingService; @@ -66,7 +69,7 @@ public class Ops { public static final String TAG = Ops.class.getSimpleName(); - @StringDef({MODE_AUTO, MODE_ROOT, MODE_ADB_OVER_TCP, MODE_ADB_WIFI, MODE_NO_ROOT}) + @StringDef({MODE_AUTO, MODE_ROOT, MODE_ADB_OVER_TCP, MODE_ADB_WIFI, MODE_NO_ROOT, MODE_PLATFORM}) @Retention(RetentionPolicy.SOURCE) public @interface Mode { } @@ -78,6 +81,14 @@ public class Ops { public static final String MODE_NO_ROOT = "no-root"; public static final String MODE_PLATFORM = "platform"; + public static final List MODE_NAMES = Arrays.asList( + Ops.MODE_AUTO, + Ops.MODE_ROOT, + Ops.MODE_ADB_OVER_TCP, + Ops.MODE_ADB_WIFI, + Ops.MODE_NO_ROOT, + Ops.MODE_PLATFORM); + @IntDef({ STATUS_SUCCESS, STATUS_FAILURE, @@ -167,6 +178,12 @@ public static boolean isAdb() { return sIsAdb; } + @AnyThread + public static boolean isPlatform() { + //noinspection ConstantValue + return "platform".equals(BuildConfig.FLAVOR); + } + /** * Whether the current App Manager session is authenticated by the user. It does two things: *
    @@ -261,6 +278,9 @@ public static int init(@NonNull Context context, boolean force) { } return STATUS_SUCCESS; } + if (MODE_PLATFORM.equals(mode)) { + return initPermissionsWithSuccess(); + } if (!force && isAMServiceUpAndRunning(context, mode)) { // An instance of AMService is already running return sIsAdb ? STATUS_SUCCESS : initPermissionsWithSuccess(); @@ -326,6 +346,9 @@ public static boolean hasRoot() { @NoOps // Although we've used Ops checks, its overall usage does not affect anything private static void autoDetectRootSystemOrAdbAndPersist(@NonNull Context context) { sIsRoot = sDirectRoot; + if (isPlatform()) { + setMode(MODE_PLATFORM); + } if (sDirectRoot) { // Root permission was granted setMode(MODE_ROOT); diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/utils/Utils.java b/app/src/main/java/io/github/muntashirakon/AppManager/utils/Utils.java index b1993264bbc..6e9d16d0fa6 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/utils/Utils.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/utils/Utils.java @@ -53,6 +53,7 @@ import io.github.muntashirakon.AppManager.apk.signing.SignerInfo; import io.github.muntashirakon.AppManager.compat.PackageManagerCompat; import io.github.muntashirakon.AppManager.misc.OsEnvironment; +import io.github.muntashirakon.AppManager.self.SelfPermissions; public class Utils { public static final String TERMUX_LOGIN_PATH = OsEnvironment.getDataDirectoryRaw() + "/data/com.termux/files/usr/bin/login"; @@ -680,9 +681,4 @@ public static String prettyPrintObject(@Nullable T obj) { public static boolean isRoboUnitTest() { return "robolectric".equals(Build.FINGERPRINT); } - - public static boolean isPlatform() { - // noinspection ConstantValue - return BuildConfig.FLAVOR.equals("platform"); - } } diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 4b6287b254a..b3f944a2640 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -36,6 +36,7 @@ @string/adb_over_tcp @string/wireless_debugging @string/no_root + @string/platform @string/type_boolean diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 83b4d6306e3..33f48f66ea2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1558,4 +1558,5 @@ Selected %d out of %d items ADB local server port Waiting for Wi-Fi + Platform diff --git a/app/src/platform/AndroidManifest.xml b/app/src/platform/AndroidManifest.xml index c76c1f5e6ed..61f5e96f7be 100644 --- a/app/src/platform/AndroidManifest.xml +++ b/app/src/platform/AndroidManifest.xml @@ -1,5 +1,8 @@ + xmlns:tools="http://schemas.android.com/tools" + android:sharedUserId="android.uid.system" + android:sharedUserLabel="@string/system" + tools:ignore="Deprecated"> @@ -15,6 +19,10 @@ + + - + + + + + + diff --git a/app/src/platform/java/io/github/muntashirakon/AppManager/apk/installer/PackageUninstallerActivity.java b/app/src/platform/java/io/github/muntashirakon/AppManager/apk/installer/PackageUninstallerActivity.java index d1cd7441c89..ce40b7e70e4 100644 --- a/app/src/platform/java/io/github/muntashirakon/AppManager/apk/installer/PackageUninstallerActivity.java +++ b/app/src/platform/java/io/github/muntashirakon/AppManager/apk/installer/PackageUninstallerActivity.java @@ -43,7 +43,7 @@ public boolean getTransparentBackground() { @RequiresPermission(anyOf = {Manifest.permission.REQUEST_DELETE_PACKAGES, Manifest.permission.DELETE_PACKAGES}) @Override protected void onAuthenticated(@Nullable Bundle savedInstanceState) { - this.mPackageName = Objects.requireNonNull(getIntent().getDataString()).replaceFirst("package:", ""); + this.mPackageName = Objects.requireNonNull(getIntent().getData()).getHost(); boolean isUninstallFromAllUsers = getIntent().getBooleanExtra(PackageInstallerCompat.EXTRA_UNINSTALL_ALL_USERS, false); this.mActivity = this; this.mUserId = UserHandleHidden.myUserId(); @@ -58,17 +58,6 @@ protected void onAuthenticated(@Nullable Bundle savedInstanceState) { } private void uninstallDialog(boolean isUninstallFromAllUsers) { - if (mUserId != UserHandleHidden.myUserId() && !SelfPermissions.checkSelfOrRemotePermission(Manifest.permission.DELETE_PACKAGES)) { - // Could be for work profile - try { - Intent uninstallIntent = new Intent(Intent.ACTION_DELETE); - uninstallIntent.setData(Uri.parse("package:" + mPackageName)); - ActivityManagerCompat.startActivity(uninstallIntent, mUserId); - } catch (Throwable th) { - UIUtils.displayLongToast("Error: " + th.getLocalizedMessage()); - } - return; - } final boolean isSystemApp = (mApplicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; ScrollableDialogBuilder builder = new ScrollableDialogBuilder(mActivity, isSystemApp ? R.string.uninstall_system_app_message : R.string.uninstall_app_message) diff --git a/hiddenapi/src/main/java/android/content/pm/PackageInstallerHidden.java b/hiddenapi/src/main/java/android/content/pm/PackageInstallerHidden.java index 84ef335cfe9..272e390939c 100644 --- a/hiddenapi/src/main/java/android/content/pm/PackageInstallerHidden.java +++ b/hiddenapi/src/main/java/android/content/pm/PackageInstallerHidden.java @@ -54,4 +54,10 @@ public static class SessionParams { @RequiresApi(Build.VERSION_CODES.P) public String installerPackageName; } + + public static class SessionInfo { + public String getResolvedBaseApkPath() { + return HiddenUtil.throwUOE(); + } + } } diff --git a/hiddenapi/src/main/java/android/view/WindowHidden.java b/hiddenapi/src/main/java/android/view/WindowHidden.java new file mode 100644 index 00000000000..04e6e45cb87 --- /dev/null +++ b/hiddenapi/src/main/java/android/view/WindowHidden.java @@ -0,0 +1,11 @@ +package android.view; + +import dev.rikka.tools.refine.RefineAs; +import misc.utils.HiddenUtil; + +@RefineAs(Window.class) +public class WindowHidden { + public void addSystemFlags(int flags) { + HiddenUtil.throwUOE(flags); + } +} diff --git a/platform/permissions.xml b/platform/permissions.xml index e14f39b9ce4..fe3e23233bb 100644 --- a/platform/permissions.xml +++ b/platform/permissions.xml @@ -26,5 +26,8 @@ + + + From e9d612a418796b46673af6e7aec0860dc093fb6e Mon Sep 17 00:00:00 2001 From: Theray Tharow Date: Sat, 14 Feb 2026 01:38:46 -0500 Subject: [PATCH 4/7] Forget the manifest Changes --- app/src/platform/AndroidManifest.xml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app/src/platform/AndroidManifest.xml b/app/src/platform/AndroidManifest.xml index 61f5e96f7be..89c3180b69b 100644 --- a/app/src/platform/AndroidManifest.xml +++ b/app/src/platform/AndroidManifest.xml @@ -19,10 +19,6 @@ - - - - - - - - From f35c35b0ca62a1d8c6b82236efe226de6d0cdcd1 Mon Sep 17 00:00:00 2001 From: Theray Tharow Date: Tue, 3 Mar 2026 15:55:41 -0500 Subject: [PATCH 5/7] Update. (#3) * Discontinue debug variants Signed-off-by: Muntashir Al-Islam * Delete debug_build.yml Signed-off-by: Muntashir Al-Islam * Fix resource leak in LogcatHelper#getLastLogLine Thanks: CyberSecurity-NCC Signed-off-by: Muntashir Al-Islam * Upgrade dependencies - Android SDK to 36 - AGP to 8.13.2 - AndroidX Activity to 1.11.0 - AndroidX Core to 1.17.0 - AndroidX Splashscreen to 1.2.0 - AndroidX Swipe Refresh to 1.2.0 - BouncyCastle to 1.83 - Gson to 2.13.2 - JetBrains Annotation to 26.1.0 - LibAdb to 3.1.1 - Material Components to 1.13.0 - Zstd to 1.5.7-7 - Robolectric to 4.16.1 Signed-off-by: Muntashir Al-Islam * Check whether pin shortcut is supported before requesting a shortcut Signed-off-by: Muntashir Al-Islam * SplitInputStream: close all underlying streams even if one close fails Signed-off-by: Sanjay Malakar * interceptor: Intercept some popular Chinese apps Signed-off-by: Fei Yang --------- Signed-off-by: Muntashir Al-Islam Signed-off-by: Sanjay Malakar Signed-off-by: Fei Yang Co-authored-by: Muntashir Al-Islam Co-authored-by: Sanjay Malakar Co-authored-by: Fei1Yang --- .github/workflows/debug_build.yml | 89 ------------------- app/build.gradle | 5 ++ app/src/main/AndroidManifest.xml | 12 +++ .../AppManager/editor/EditorThemes.java | 4 +- .../AppManager/fm/FmAdapter.java | 2 +- .../AppManager/fm/FmPathListAdapter.java | 2 +- .../logcat/helper/LogcatHelper.java | 13 ++- .../AppManager/misc/AdvancedSearchView.java | 4 +- .../AppManager/misc/HelpActivity.java | 2 +- .../settings/AdvancedPreferences.java | 1 + .../CreateShortcutDialogFragment.java | 4 +- docs/raw/en/index.html | 22 +++-- docs/raw/en/intro/main.tex | 10 +-- docs/raw/en/strings.xml | 2 +- .../muntashirakon/io/SplitInputStream.java | 12 ++- .../MultiSelectionActionsView.java | 2 +- .../widget/FloatingActionButtonGroup.java | 2 +- .../widget/MaterialAutoCompleteTextView.java | 4 +- .../muntashirakon/widget/SearchView.java | 6 +- libcore/ui/src/main/res/values/styles.xml | 6 +- versions.gradle | 36 ++++---- 21 files changed, 92 insertions(+), 148 deletions(-) delete mode 100644 .github/workflows/debug_build.yml diff --git a/.github/workflows/debug_build.yml b/.github/workflows/debug_build.yml deleted file mode 100644 index 493b5489ddb..00000000000 --- a/.github/workflows/debug_build.yml +++ /dev/null @@ -1,89 +0,0 @@ -name: Debug Build - -on: - push: - branches: - - 'master' - - 'AppManager-*' - paths-ignore: - - 'fastlane/**' - - 'scripts/**' - - '*.md' - pull_request: - branches: [ master ] - paths-ignore: - - 'fastlane/**' - - 'scripts/**' - - '*.md' - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Clone the repository - uses: actions/checkout@v4 - with: - submodules: 'recursive' - - name: Set up JDK - uses: actions/setup-java@v4 - with: - distribution: 'zulu' - java-version: '21' - cache: 'gradle' - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - name: Get app version - run: echo "APP_VERSION=v$(cat ./app/build.gradle | grep -m1 versionName | awk -F \" '{print $2}')" >> $GITHUB_ENV - - name: Get app name - run: echo "APP_NAME=AppManager_${{ env.APP_VERSION }}-DEBUG#${{ github.run_number }}" >> $GITHUB_ENV - - name: Inject run number - run: sed -i -e 's|versionName "\([0-9\.]\+\)"|versionName "\1-${{ github.run_number }}"|' ./app/build.gradle - - name: Build with Gradle - run: ./gradlew packageDebugUniversalApk - - name: Rename the APK file - run: mv ./app/build/outputs/apk_from_bundle/debug/app-debug-universal.apk ./${{ env.APP_NAME }}.apk - - name: Store generated APK file - uses: actions/upload-artifact@v4 - with: - name: ${{ env.APP_NAME }} - path: ./${{ env.APP_NAME }}.apk - - name: Upload APK to telegram - uses: appleboy/telegram-action@master - with: - to: ${{ secrets.TELEGRAM_TO }} - token: ${{ secrets.TELEGRAM_TOKEN }} - document: ./${{ env.APP_NAME }}.apk - format: markdown - message: | - *AM Debug ${{ env.APP_VERSION }} Run#${{ github.run_number }}* - - ${{ github.event.head_commit.message }} - continue-on-error: true - - name: Checkout AMInsecureDebugBuilds - uses: actions/checkout@v4 - with: - repository: MuntashirAkon/AMInsecureDebugBuilds - token: ${{ secrets.AMIDB_TOKEN }} - fetch-depth: 0 - ref: master - path: upload-repo - - name: Copy APK to AMInsecureDebugBuilds - run: | - cp ${{ env.APP_NAME }}.apk upload-repo/ - cd upload-repo - git config --local user.name "github-actions[bot]" - git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" - bash ../scripts/keep-five.sh - git add ${{ env.APP_NAME }}.apk - echo "${{ github.run_number }}: ${{ github.event.head_commit.message }}" > commit_msg.txt - git commit -F commit_msg.txt - rm commit_msg.txt - cd .. - - name: Push changes to GitHub - uses: ad-m/github-push-action@master - with: - repository: MuntashirAkon/AMInsecureDebugBuilds - github_token: ${{ secrets.AMIDB_TOKEN }} - branch: master - force: true - directory: upload-repo diff --git a/app/build.gradle b/app/build.gradle index 645853eef0a..e0f8d11c722 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,6 +24,11 @@ android { ] } } + externalNativeBuild { + cmake { + arguments "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON" + } + } // Add build time to BuildConfig buildConfigField "long", "BUILD_TIME_MILLIS", "${buildTime()}" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f13d8d41450..306f83b1bc0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -845,11 +845,17 @@ + + + + + + @@ -906,11 +912,17 @@ + + + + + + diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/editor/EditorThemes.java b/app/src/main/java/io/github/muntashirakon/AppManager/editor/EditorThemes.java index 304d8b39532..529407cada0 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/editor/EditorThemes.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/editor/EditorThemes.java @@ -69,10 +69,10 @@ private static void fixColor(@NonNull Context context, @NonNull EditorColorSchem scheme.setColor(EditorColorScheme.LINE_NUMBER_BACKGROUND, SurfaceColors.SURFACE_2.getColor(context)); scheme.setColor(EditorColorScheme.COMPLETION_WND_BACKGROUND, SurfaceColors.SURFACE_1.getColor(context)); scheme.setColor(EditorColorScheme.HIGHLIGHTED_DELIMITERS_FOREGROUND, Color.RED); - int thumbColor = MaterialColors.getColor(context, com.google.android.material.R.attr.colorControlActivated, EditorColor.class.getSimpleName()); + int thumbColor = MaterialColors.getColor(context, androidx.appcompat.R.attr.colorControlActivated, EditorColor.class.getSimpleName()); scheme.setColor(EditorColorScheme.SCROLL_BAR_THUMB, thumbColor); scheme.setColor(EditorColorScheme.SCROLL_BAR_THUMB_PRESSED, thumbColor); - int trackColor = ColorUtils.setAlphaComponent(MaterialColors.getColor(context, com.google.android.material.R.attr.colorControlNormal, EditorColor.class.getSimpleName()), 0x39); + int trackColor = ColorUtils.setAlphaComponent(MaterialColors.getColor(context, androidx.appcompat.R.attr.colorControlNormal, EditorColor.class.getSimpleName()), 0x39); scheme.setColor(EditorColorScheme.SCROLL_BAR_TRACK, trackColor); } diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/fm/FmAdapter.java b/app/src/main/java/io/github/muntashirakon/AppManager/fm/FmAdapter.java index b08ffcc7054..fade812d4a2 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/fm/FmAdapter.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/fm/FmAdapter.java @@ -346,7 +346,7 @@ public ViewHolder(@NonNull View itemView) { icon = itemView.findViewById(android.R.id.icon); symbolicLinkIcon = itemView.findViewById(R.id.symbolic_link_icon); action = itemView.findViewById(android.R.id.button1); - action.setContentDescription(itemView.getContext().getString(com.google.android.material.R.string.abc_action_menu_overflow_description)); + action.setContentDescription(itemView.getContext().getString(androidx.appcompat.R.string.abc_action_menu_overflow_description)); title = itemView.findViewById(android.R.id.title); subtitle = itemView.findViewById(android.R.id.summary); action.setIconResource(io.github.muntashirakon.ui.R.drawable.ic_more_vert); diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/fm/FmPathListAdapter.java b/app/src/main/java/io/github/muntashirakon/AppManager/fm/FmPathListAdapter.java index ac2241b05bb..e056b16d8bd 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/fm/FmPathListAdapter.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/fm/FmPathListAdapter.java @@ -150,7 +150,7 @@ public void onBindViewHolder(@NonNull PathHolder holder, int position) { return true; }); holder.textView.setTextColor(mCurrentPosition == position - ? MaterialColors.getColor(holder.textView, com.google.android.material.R.attr.colorPrimary) + ? MaterialColors.getColor(holder.textView, androidx.appcompat.R.attr.colorPrimary) : MaterialColors.getColor(holder.textView, android.R.attr.textColorSecondary)); } diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/logcat/helper/LogcatHelper.java b/app/src/main/java/io/github/muntashirakon/AppManager/logcat/helper/LogcatHelper.java index d65e00fad5c..05ed33f6cbd 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/logcat/helper/LogcatHelper.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/logcat/helper/LogcatHelper.java @@ -53,16 +53,15 @@ public static Process getLogcatProcess(@LogBufferId int buffers) throws IOExcept @Nullable public static String getLastLogLine(@LogBufferId int buffers) { Process dumpLogcatProcess = null; - BufferedReader reader; String result = null; try { dumpLogcatProcess = ProcessCompat.exec(getLogcatArgs(buffers, true)); - reader = new BufferedReader(new InputStreamReader(dumpLogcatProcess - .getInputStream()), 8192); - - String line; - while ((line = reader.readLine()) != null) { - result = line; + try (BufferedReader reader = new BufferedReader(new InputStreamReader(dumpLogcatProcess + .getInputStream()), 8192)) { + String line; + while ((line = reader.readLine()) != null) { + result = line; + } } } catch (IOException e) { Log.e(TAG, e); diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/misc/AdvancedSearchView.java b/app/src/main/java/io/github/muntashirakon/AppManager/misc/AdvancedSearchView.java index 01fc3396e05..ab81a6c16ac 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/misc/AdvancedSearchView.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/misc/AdvancedSearchView.java @@ -152,8 +152,8 @@ public AdvancedSearchView(@NonNull Context context, @Nullable AttributeSet attrs public AdvancedSearchView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(wrap(context, attrs, defStyleAttr, DEF_STYLE_RES), attrs, defStyleAttr); context = getContext(); - mSearchSrcTextView = findViewById(com.google.android.material.R.id.search_src_text); - mSearchTypeSelectionButton = findViewById(com.google.android.material.R.id.search_mag_icon); + mSearchSrcTextView = findViewById(androidx.appcompat.R.id.search_src_text); + mSearchTypeSelectionButton = findViewById(androidx.appcompat.R.id.search_mag_icon); mSearchTypeSelectionButton.setImageResource(R.drawable.ic_filter_menu); mSearchTypeSelectionButton.setBackground(UiUtils.getDrawable(context, android.R.attr.selectableItemBackgroundBorderless)); mSearchTypeSelectionButton.setOnClickListener(onClickSearchIcon); diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/misc/HelpActivity.java b/app/src/main/java/io/github/muntashirakon/AppManager/misc/HelpActivity.java index b2af3fdd0fd..0456dc0edd6 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/misc/HelpActivity.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/misc/HelpActivity.java @@ -85,7 +85,7 @@ protected void onAuthenticated(@Nullable Bundle savedInstanceState) { Button nextButton = findViewById(R.id.next_button); Button previousButton = findViewById(R.id.previous_button); mSearchView = findViewById(R.id.search_bar); - mSearchView.findViewById(com.google.android.material.R.id.search_close_btn).setOnClickListener(v -> { + mSearchView.findViewById(androidx.appcompat.R.id.search_close_btn).setOnClickListener(v -> { mWebView.clearMatches(); mSearchView.setQuery(null, false); Transition sharedAxis = new MaterialSharedAxis(MaterialSharedAxis.Y, true); diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/settings/AdvancedPreferences.java b/app/src/main/java/io/github/muntashirakon/AppManager/settings/AdvancedPreferences.java index 383249d50de..2c0afe8a119 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/settings/AdvancedPreferences.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/settings/AdvancedPreferences.java @@ -140,6 +140,7 @@ public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable S .setPositiveButton(R.string.save, (dialog, which, inputText, isChecked) -> { if (inputText != null && TextUtils.isDigitsOnly(inputText)) { int c = Integer.decode(inputText.toString()); + // TODO: 10/18/25 If the port number is different, restart the local server with this new port if possible. Prefs.Misc.setAdbLocalServerPort(c); adbLsPort.setSummary(String.valueOf(c)); } diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/shortcut/CreateShortcutDialogFragment.java b/app/src/main/java/io/github/muntashirakon/AppManager/shortcut/CreateShortcutDialogFragment.java index 2509e04cf18..62eb1e4d655 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/shortcut/CreateShortcutDialogFragment.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/shortcut/CreateShortcutDialogFragment.java @@ -187,7 +187,9 @@ private void requestPinShortcut(@NonNull ShortcutInfo shortcutInfo) { .setIntent(shortcutIntent) .build(); - if (!ShortcutManagerCompat.requestPinShortcut(context, shortcutInfoCompat, null)) { + boolean shortcutPinned = ShortcutManagerCompat.isRequestPinShortcutSupported(context) + && ShortcutManagerCompat.requestPinShortcut(context, shortcutInfoCompat, null); + if (!shortcutPinned) { new MaterialAlertDialogBuilder(context) .setTitle(context.getString(R.string.error_creating_shortcut)) .setMessage(context.getString(R.string.error_verbose_pin_shortcut)) diff --git a/docs/raw/en/index.html b/docs/raw/en/index.html index ccbaf6d2892..acb61caaaf4 100644 --- a/docs/raw/en/index.html +++ b/docs/raw/en/index.html @@ -152,7 +152,7 @@ Backup
  1. 7 App Ops

    image

    App Manager

    User Manual

    v4.0.5

    27 July 2025

    Copyright © 2020–2025 Muntashir Al-Islam

    “Wisely and slow. They stumble that run fast.” — Friar +Interface

    image

    App Manager

    User Manual

    v4.0.5

    22 February 2026

    Copyright © 2020–2025 Muntashir Al-Islam

    “Wisely and slow. They stumble that run fast.” — Friar Laurence, Romeo and Juliet

    1 Introduction

    App Manager is an advanced package manager for Android. It offers numerous features and thus, requires a user manual to assist its users. This document acts as a user manual for App Manager in the sense that it @@ -188,7 +188,7 @@ Sources

    App Manager is distributed using the following sources. Unofficial sources may distribute modified versions of App Manager, and you alone shall be responsible for the consequences of using such a -distribution.

    1. Official F-Droid repository.1
      Link: https://f-droid.org/packages/io.github.muntashirakon.AppManager

    2. GitHub repository.
      Normal releases: https://github.com/MuntashirAkon/AppManager/releases
      Debug releases: https://github.com/MuntashirAkon/AMInsecureDebugBuilds

    3. Telegram.
      Normal releases: https://t.me/AppManagerChannel
      Debug releases: https://t.me/AppManagerDebug

    1.4.2 Submitting patches

    Repositories located on sites other than GitHub are currently considered mirrors, and pull/merge requests submitted on those sites -will not be accepted.2 Instead, patches (as +will not be accepted.1 Instead, patches (as .patch files) can be submitted via email attachments. Signing-off is a requirement. See the CONTRIBUTING file located at the source root for more information.

    Notice.

    In the case of submitting patches via email, the whole conversation @@ -218,7 +218,7 @@ NFTs, or any other financial instruments that claim to represent this project, its code, or its community. Any such attempts are unauthorized and not affiliated with this project in any way.

    1.6 Contact

    App Manager Community
    Email: am4android [at] riseup -[dot] net
    GitHub: https://github.com/AMCommunity
    Twitter/X: https://x.com/AppManagerNews
    Mastodon: @appmanager@floss.social

    Muntashir Al-Islam3
    Email: muntashirakon [at] +[dot] net
    GitHub: https://github.com/AMCommunity
    Twitter/X: https://x.com/AppManagerNews
    Mastodon: @appmanager@floss.social

    Muntashir Al-Islam2
    Email: muntashirakon [at] riseup [dot] net
    GitHub: https://github.com/MuntashirAkon
    Twitter/X: https://x.com/Muntashir
    Mastodon: @muntashir@infosec.exchange

    2 Pages

    2.1 Main Page

    Main page lists all the installed, uninstalled and backed up applications. A single click on any installed application item opens the respective App Details page. For the @@ -231,7 +231,7 @@ items can be sorted in various ways. It is also possible to filter items using the filter option in the list options. Filtering is also possible from the search bar with additional -support for the regular expressions.

    Figure 1: An application list item in the main +support for the regular expressions.
    Figure 1: An application list item in the main page

    2.1.1 Batch Operations

    Batch operations or operations on multiple applications are also available within this page. Multiple selection mode can be activated by @@ -1005,7 +1005,11 @@ delete).

    2.5.3 Configurations Tab

    Configurations tab can be used to configure the selected -packages.

    2.5.3.1 Profile ID

    The unique ID for this profile, currently set based on the profile +packages.

    Uninstalling a List of Applications..

    Since uninstalling is a one time event, it is not part of the +configurations. However, it is still possible to uninstall the +applications belonging to a profile. To do this, you can filter the +applications in the Main page using a profile, select all the filtered +applications, and uninstall them.

    2.5.3.1 Profile ID

    The unique ID for this profile, currently set based on the profile name. The profile ID can be used to trigger the profile from a third-party application.

    2.5.3.2 Comment

    This is the text that will be displayed in the profiles page. If not set, the current configurations will be displayed instead.

    2.5.3.3 State

    Denotes how certain configured options will behave by default. For @@ -2256,7 +2260,7 @@ difference between my app (AppManager) and this app is that the latter also provides you the ability to revoke internet permissions (by writing ip tables). One crucial problem that I faced during the development of -the app ops API is the lack of documentation in English language.

    7.2 Introduction to App Ops

    Figure 2: How app ops work

    Figure 1 describes the process of changing +the app ops API is the lack of documentation in English language.

    7.2 Introduction to App Ops

    Figure 2: How app ops work

    Figure 1 describes the process of changing and processing permission. AppOpsManager can be used to manage permissions in Settings app. AppOpsManager is also useful in determining if a certain permission (or operation) is granted @@ -2447,6 +2451,6 @@ <OP> an AppOps operation. <MODE> one of allow, ignore, deny, or default <USER_ID> the user id under which the package is installed. If --user is not -specified, the current user is assumed.


    1. For distributing normal releases only↩︎

    2. GitHub pull requests will be merged manually using the +specified, the current user is assumed.


    1. GitHub pull requests will be merged manually using the corresponding patches. As a result, GitHub may wrongfully mark them -closed instead of merged.↩︎

    2. You can also address me as “Muntashir Akon”↩︎

    \ No newline at end of file +closed instead of merged.↩︎
  2. You can also address me as “Muntashir Akon”↩︎

\ No newline at end of file diff --git a/docs/raw/en/intro/main.tex b/docs/raw/en/intro/main.tex index b714816f2c1..57b23f9e6de 100644 --- a/docs/raw/en/intro/main.tex +++ b/docs/raw/en/intro/main.tex @@ -49,14 +49,14 @@ \subsection{Binary Distribution Sources}\label{subsec:binary-distribution-source App Manager is distributed using the following sources. Unofficial sources may distribute modified versions of App Manager, and you alone shall be responsible for the consequences of using such a distribution. \begin{enumerate} - \item Official F-Droid repository.\footnote{For distributing normal releases only}\\ + \item Official F-Droid repository.\\ \textit{Link:} \url{https://f-droid.org/packages/io.github.muntashirakon.AppManager} + \item IzzyOnDroid repository.\\ + \textit{Link:} \url{https://apt.izzysoft.de/fdroid/index/apk/io.github.muntashirakon.AppManager} \item GitHub repository.\\ - \textit{Normal releases:} \url{https://github.com/MuntashirAkon/AppManager/releases}\\ - \textit{Debug releases:} \url{https://github.com/MuntashirAkon/AMInsecureDebugBuilds} + \textit{Link:} \url{https://github.com/MuntashirAkon/AppManager/releases} \item Telegram.\\ - \textit{Normal releases:} \url{https://t.me/AppManagerChannel}\\ - \textit{Debug releases:} \url{https://t.me/AppManagerDebug} + \textit{Link:} \url{https://t.me/AppManagerChannel} \end{enumerate} %%!!>> diff --git a/docs/raw/en/strings.xml b/docs/raw/en/strings.xml index bb86da74a5c..3607371ca1d 100644 --- a/docs/raw/en/strings.xml +++ b/docs/raw/en/strings.xml @@ -21,7 +21,7 @@ App Manager is an advanced package manager for Android. It offers numerous features and thus, requires a user\nmanual to assist its users. This document acts as a user manual for App Manager in the sense that it aims to describe\nevery feature that App Manager has to offer. This document also serves as the ``official\'\' guidelines for\nApp Manager and represents the expected behavior of App Manager. Translations can misinterpret this document (which is\nin English). Therefore, a capable user should read the English version of the document to get the most out\nof it. There may as well be other unofficial or third-party resources, such as blog articles, videos, forums, and chat\ngroups. While these resources may be useful to many people, they may not be up-to-date with the latest\nversion of App Manager. In case of any deviations detected in App Manager from this document, they should be reported in the\n\\href{https://github.com/MuntashirAkon/AppManager/issues}{App Manager issue tracker}. \\begin{itemize}\n \\item \\textbf{AM} --- Short name for App Manager.\n \\item \\textbf{Block/Unblock} --- Used for component blocking or unblocking. How components are blocked depends on\n the user preferences.\n \\item \\textbf{IFW} --- Short form of Intent Firewall.\n \\item \\textbf{Ops} --- Short name for operations, e.g.\\ app ops, batch ops, 1-click ops\n \\item \\textbf{SAF} --- Short for Storage Access Framework, an abstraction used by Android to allow apps to use or\n serve files without worrying about the underlying file system.\n \\item \\textbf{SSAID} --- Short form of \\texttt{Settings.Secure.ANDROID\\_ID}. It is a device identifier assigned to\n each app (Android Oreo onwards). It is generated from the combination of the signing certificate of the app\n and the SSAID set for the package \\texttt{android}. As a result, it is guaranteed to be the same for an app unless\n the user chooses to format the device. It is widely used for tracking.\n \\item \\textbf{Tracker} --- Denotes tracker components throughout the document and in App Manager except in the\n \\hyperref[sec:scanner-page]{scanner page}. Trackers include libraries such as crash reporters, analytics,\n profiling, identification, ad, location, etc. Thus, they are not equal in functions. There is no distinction or bias\n among the open-source and closed-source libraries that promote tracking.\n\\end{itemize} At present, the supported version is v4.0.1.\nPrevious versions of App Manager may contain security vulnerabilities and should not be used. - App Manager is distributed using the following sources. Unofficial sources may distribute modified versions of App\nManager, and you alone shall be responsible for the consequences of using such a distribution.\n\\begin{enumerate}\n \\item Official F-Droid repository.\\footnote{For distributing normal releases only}\\\\\n \\textit{Link:} \\url{https://f-droid.org/packages/io.github.muntashirakon.AppManager}\n \\item GitHub repository.\\\\\n \\textit{Normal releases:} \\url{https://github.com/MuntashirAkon/AppManager/releases}\\\\\n \\textit{Debug releases:} \\url{https://github.com/MuntashirAkon/AMInsecureDebugBuilds}\n \\item Telegram.\\\\\n \\textit{Normal releases:} \\url{https://t.me/AppManagerChannel}\\\\\n \\textit{Debug releases:} \\url{https://t.me/AppManagerDebug}\n\\end{enumerate} + App Manager is distributed using the following sources. Unofficial sources may distribute modified versions of App\nManager, and you alone shall be responsible for the consequences of using such a distribution.\n\\begin{enumerate}\n \\item Official F-Droid repository.\\\\\n \\textit{Link:} \\url{https://f-droid.org/packages/io.github.muntashirakon.AppManager}\n \\item IzzyOnDroid repository.\\\\\n \\textit{Link:} \\url{https://apt.izzysoft.de/fdroid/index/apk/io.github.muntashirakon.AppManager}\n \\item GitHub repository.\\\\\n \\textit{Link:} \\url{https://github.com/MuntashirAkon/AppManager/releases}\n \\item Telegram.\\\\\n \\textit{Link:} \\url{https://t.me/AppManagerChannel}\n\\end{enumerate} All but GitHub are mirrors. The tags should always be up-to-date, but the master branch may not.\nIf you want to clone the master branch, use the GitHub link instead of the others. App Manager does not accept translations via pull/merge requests. Translations are managed through\nHosted Weblate. To translate App Manager, visit \\url{https://hosted.weblate.org/engage/app-manager/}.\nPlease read the \\textbf{Info} section before getting started. There are many ways a user can contribute, such as creating helpful issues, attending discussions,\nimproving documentation and translations, reporting unknown libraries or trackers, reviewing the source code,\nand reporting security vulnerabilities. diff --git a/libcore/io/src/main/java/io/github/muntashirakon/io/SplitInputStream.java b/libcore/io/src/main/java/io/github/muntashirakon/io/SplitInputStream.java index 12f9c40731f..37846eeb94d 100644 --- a/libcore/io/src/main/java/io/github/muntashirakon/io/SplitInputStream.java +++ b/libcore/io/src/main/java/io/github/muntashirakon/io/SplitInputStream.java @@ -79,9 +79,19 @@ public long skip(long n) throws IOException { @Override public void close() throws IOException { + IOException failure = null; for (InputStream stream : mInputStreams) { - stream.close(); + try { + stream.close(); + } catch (IOException e) { + if (failure == null) { + failure = e; + } else { + failure.addSuppressed(e); + } + } } + if (failure != null) throw failure; } @Override diff --git a/libcore/ui/src/main/java/io/github/muntashirakon/multiselection/MultiSelectionActionsView.java b/libcore/ui/src/main/java/io/github/muntashirakon/multiselection/MultiSelectionActionsView.java index 40e20050c38..8c477761ed4 100644 --- a/libcore/ui/src/main/java/io/github/muntashirakon/multiselection/MultiSelectionActionsView.java +++ b/libcore/ui/src/main/java/io/github/muntashirakon/multiselection/MultiSelectionActionsView.java @@ -489,7 +489,7 @@ private void renderItems() { ReflowMenuItemView overflowView = getNewOptionItem(); addView(overflowView, new LinearLayoutCompat.LayoutParams(allocatedWidth, ViewGroup.LayoutParams.MATCH_PARENT)); // init - CharSequence tooltipText = getContext().getString(com.google.android.material.R.string.abc_action_menu_overflow_description); + CharSequence tooltipText = getContext().getString(androidx.appcompat.R.string.abc_action_menu_overflow_description); overflowView.setIcon(ContextCompat.getDrawable(getContext(), R.drawable.ic_more_horiz)); overflowView.setTitle(tooltipText); overflowView.setContentDescription(tooltipText); diff --git a/libcore/ui/src/main/java/io/github/muntashirakon/widget/FloatingActionButtonGroup.java b/libcore/ui/src/main/java/io/github/muntashirakon/widget/FloatingActionButtonGroup.java index 79b9e5b50b8..ca6aadc53ab 100644 --- a/libcore/ui/src/main/java/io/github/muntashirakon/widget/FloatingActionButtonGroup.java +++ b/libcore/ui/src/main/java/io/github/muntashirakon/widget/FloatingActionButtonGroup.java @@ -41,7 +41,7 @@ public FloatingActionButtonGroup(Context context, @Nullable AttributeSet attrs, layoutParams.setMargins(margin, margin, margin, margin); fab.setLayoutParams(layoutParams); fab.setUseCompatPadding(false); - setMainFabOpenedBackgroundColor(MaterialColors.getColor(this, com.google.android.material.R.attr.colorError)); + setMainFabOpenedBackgroundColor(MaterialColors.getColor(this, androidx.appcompat.R.attr.colorError)); setMainFabOpenedIconColor(MaterialColors.getColor(this, com.google.android.material.R.attr.colorOnError)); setMainFabClosedBackgroundColor(MaterialColors.getColor(this, com.google.android.material.R.attr.colorSecondaryContainer)); setMainFabClosedIconColor(MaterialColors.getColor(this, com.google.android.material.R.attr.colorOnSecondaryContainer)); diff --git a/libcore/ui/src/main/java/io/github/muntashirakon/widget/MaterialAutoCompleteTextView.java b/libcore/ui/src/main/java/io/github/muntashirakon/widget/MaterialAutoCompleteTextView.java index a9f1e2a7a6f..01baa064323 100644 --- a/libcore/ui/src/main/java/io/github/muntashirakon/widget/MaterialAutoCompleteTextView.java +++ b/libcore/ui/src/main/java/io/github/muntashirakon/widget/MaterialAutoCompleteTextView.java @@ -22,7 +22,7 @@ public MaterialAutoCompleteTextView(@NonNull Context context) { } public MaterialAutoCompleteTextView(@NonNull Context context, @Nullable AttributeSet attributeSet) { - this(context, attributeSet, com.google.android.material.R.attr.autoCompleteTextViewStyle); + this(context, attributeSet, androidx.appcompat.R.attr.autoCompleteTextViewStyle); } @SuppressLint("RestrictedApi") @@ -36,7 +36,7 @@ public MaterialAutoCompleteTextView(@NonNull Context context, @Nullable Attribut attributeSet, com.google.android.material.R.styleable.MaterialAutoCompleteTextView, defStyleAttr, - com.google.android.material.R.style.Widget_AppCompat_AutoCompleteTextView); + androidx.appcompat.R.style.Widget_AppCompat_AutoCompleteTextView); Drawable popupListSelector = attributes.getDrawable(R.styleable.MaterialAutoCompleteTextView_android_dropDownSelector); if (popupListSelector != null) { diff --git a/libcore/ui/src/main/java/io/github/muntashirakon/widget/SearchView.java b/libcore/ui/src/main/java/io/github/muntashirakon/widget/SearchView.java index 6f264812678..ee7bb21e164 100644 --- a/libcore/ui/src/main/java/io/github/muntashirakon/widget/SearchView.java +++ b/libcore/ui/src/main/java/io/github/muntashirakon/widget/SearchView.java @@ -54,9 +54,9 @@ public SearchView(@NonNull Context context, @Nullable AttributeSet attrs, int de super(wrap(context, attrs, defStyleAttr, defStyleRes), attrs, defStyleAttr); context = getContext(); - mCloseButton = findViewById(com.google.android.material.R.id.search_close_btn); - mSearchSrcTextView = findViewById(com.google.android.material.R.id.search_src_text); - mSearchEditFrame = findViewById(com.google.android.material.R.id.search_edit_frame); + mCloseButton = findViewById(androidx.appcompat.R.id.search_close_btn); + mSearchSrcTextView = findViewById(androidx.appcompat.R.id.search_src_text); + mSearchEditFrame = findViewById(androidx.appcompat.R.id.search_edit_frame); mElevation = getElevation(); final TintTypedArray a = ThemeEnforcement.obtainTintedStyledAttributes( diff --git a/libcore/ui/src/main/res/values/styles.xml b/libcore/ui/src/main/res/values/styles.xml index b60b40a67bb..64cbc5c78df 100644 --- a/libcore/ui/src/main/res/values/styles.xml +++ b/libcore/ui/src/main/res/values/styles.xml @@ -335,12 +335,12 @@