...
 
Commits (17)
......@@ -11,17 +11,21 @@
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<application
android:name=".AppManager"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:name=".AppManager"
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/AppTheme">
<activity
android:name=".activities.OneClickOpsActivity"
android:exported="false"
android:label="@string/one_click_ops" />
<activity
android:name=".activities.RunningAppsActivity"
android:label="@string/running_apps"
android:exported="false" />
android:exported="false"
android:label="@string/running_apps" />
<activity
android:name=".activities.SettingsActivity"
android:exported="false" />
......@@ -30,14 +34,25 @@
android:exported="false" />
<activity
android:name=".activities.AppUsageActivity"
android:permission="android.permission.PACKAGE_USAGE_STATS"
android:exported="false" />
android:exported="false"
android:permission="android.permission.PACKAGE_USAGE_STATS" />
<activity
android:name=".activities.ClassViewerActivity"
android:exported="false" />
<activity
android:name=".activities.ClassListingActivity"
android:exported="false" />
android:exported="true"
android:label="@string/exodus">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="application/vnd.android.package-archive" />
</intent-filter>
</activity>
<activity
android:name=".activities.MainActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
......@@ -50,19 +65,36 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activities.AppInfoActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/app_info"
android:exported="false" />
<activity
android:name=".activities.AppDetailsActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="false" />
android:exported="true"
android:label="@string/app_info" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="application/vnd.android.package-archive" />
</intent-filter>
</activity>
<activity
android:name=".activities.ManifestViewerActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="false" />
android:exported="true"
android:label="@string/manifest_viewer">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="application/vnd.android.package-archive" />
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
......@@ -74,4 +106,4 @@
android:resource="@xml/provider_paths" />
</provider>
</application>
</manifest>
</manifest>
\ No newline at end of file
<h5>v2.5.9 (315):</h5>
<ul>
<li>&nbsp;<font color="#09868B">[Feature]</font> Merged App Info as a single tab in App Details</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Added option to reset all app ops</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Added option to revoke all dangerous app ops/permissions</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Highlight trackers in the component tabs</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Added option to save manifest and class dump</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Added the ability to grant/revoke development permissions</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Added sorting options for components, app ops and uses permissions tabs</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Added sort by wifi usage in the App Usage window</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Added launch button in the App Info tab</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Added never ask option to usage status prompt</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Added long click to select apps in the main window</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Added changelog within the app</li>
<li>&nbsp;<font color="red">[Fix]</font> Click to select apps during selection mode</li>
<li>&nbsp;<font color="red">[Fix]</font> Improved component blocker</li>
<li>&nbsp;<font color="red">[Fix]</font> Improved manifest loading for large apps</li>
<li>&nbsp;<font color="red">[Fix]</font> Improved tab loading performance</li>
<li>&nbsp;<font color="red">[Fix]</font> Fixed app ops checking and custom app ops for some devices</li>
<li>&nbsp;<font color="red">[Fix]</font> Disabled activity opening for disabled activities</li>
<li>&nbsp;<font color="red">[Fix]</font> Get real activity name for activities that use activity-alias</li>
<li>&nbsp;<font color="red">[Fix]</font> Fixed background colors</li>
<li>&nbsp;<font color="red">[Fix]</font> Fixed crashing when loading the services tab for non-root users</li>
<li>&nbsp;<font color="red">[Fix]</font> Fixed back button for class viewer which was not working</li>
<li>&nbsp;<font color="red">[Fix]</font> Changed block icon's colour to accent colour</li>
<li>&nbsp;<font color="red">[Fix]</font> Removed translation until the app is complete</li>
<li>&nbsp;<font color="red">[Fix]</font> Made links in the credit section clickable</li>
<li>&nbsp;<font color="red">[Fix]</font> Fixed various memory leaks</li>
</ul><br/>
<h5>v2.5.8 (289):</h5>
<ul>
<li>&nbsp;<font color="#09868B">[Feature]</font> Added import/export capabilities for blocking rules</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Added ability to select themes (night/day)</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Added mode, duration, accept time, reject time for app ops</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Highlight running services</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Highlight disabled components not disabled within App Manager</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Added swipe to refresh in the App Usage window</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Added screen time percentage with indicator</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Separate instructions and about pages with fullscreen dialog for both</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Rounded overflow menu (still incomplete)</li>
<li>&nbsp;<font color="red">[Fix]</font> Fixed various device/SDK specific app ops issues</li>
<li>&nbsp;<font color="red">[Fix]</font> Stability improvements of the entire apps</li>
<li>&nbsp;<font color="red">[Fix]</font> Added <tt>ACCESS_NETWORK_STATE</tt> permission to support older operating systems</li>
<li>&nbsp;<font color="red">[Fix]</font> Fixed deleting all IFW rules when selecting apply global rules</li>
<li>&nbsp;<font color="red">[Fix]</font> Fixed various search issues</li>
</ul><br/>
<h5>v2.5.7 (265):</h5>
<ul>
<li>&nbsp;<font color="#09868B">[Feature]</font> Initial support for ADB over TCP (port 5555) for non-root users</li>
<li>&nbsp;<font color="red">[Fix]</font> Fixed importing rules from Watt and Blocker</li>
<li>&nbsp;<font color="red">[Fix]</font> Display Aurora Droid in App Info window as a first priority over F-Droid</li>
<li>&nbsp;<font color="red">[Fix]</font> Improved performance for component blocking</li>
<li>&nbsp;<font color="red">[Fix]</font> Fixed app op mode detection issue</li>
</ul>
<p>
<b>For root users:</b> If you've skipped v2.5.6, you may need to apply all rules globally in Settings in order for them to work.
</p><br/>
<h5>v2.5.6 (233):</h5>
<ul>
<li>&nbsp;<font color="#09868B">[Feature]</font> Batch operations in the main window: clear app data, disable run in background, disable/kill/uninstall apps (click on the app icon to select)</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Full support of Blocker's exported files which was broken due to a bug on Blocker app</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Reimplementation of blocking activities, receivers, services and providers</li>
<li>&nbsp;<font color="red">[Fix]</font> Removed ConstraintLayout dependency therefore a potential decrease in app size</li>
<li>&nbsp;<font color="red">[Fix]</font> Fixed duplicate app usage warning in the App Info window</li>
<li>&nbsp;<font color="red">[Fix]</font> Fixed crash when an app icon is not found in App Details window</li>
</ul>
<p>
<b>Note for root users:</b> In order to ensure that the previous blocking rules are preserved with the new blocking implementation, this update reads from the previous rules consequently increasing the loading time in the main window. This feature will be removed in the next release but can still be simulated by applying global rules in settings.
</p><br/>
<h5>v2.5.5 (215):</h5>
<ul>
<li>&nbsp;<font color="#09868B">[Feature]</font> Added running apps/process viewer (requires root)</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Added usage details viewer</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Added apk updater and aurora store support</li>
<li>&nbsp;<font color="#09868B">[Feature]</font> Save modified values of app ops and permissions to the disk (on progress)</li>
<li>&nbsp;<font color="red">[Fix]</font> Uninstall support for non-root users</li>
<li>&nbsp;<font color="red">[Fix]</font> Restructure app usage</li>
<li>&nbsp;<font color="red">[Fix]</font> Added more clarity as well as improve performance in the app details window</li>
</ul>
......@@ -61,6 +61,8 @@ public class IOUtils {
output.write(buffer, 0, n);
count += n;
}
input.close();
if (output != null) output.close();
return count;
}
/**
......
......@@ -41,7 +41,7 @@ public class UriUtils {
@Nullable
public static String pathUriCache(@NonNull Context context, Uri uri, String nCache) {
File f = new File(context.getCacheDir(), nCache);
File f = new File(context.getFilesDir(), nCache);
try {
FileOutputStream fos = new FileOutputStream(f);
InputStream is = context.getContentResolver().openInputStream(uri);
......
......@@ -2,8 +2,9 @@ package io.github.muntashirakon.AppManager.activities;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ApplicationInfo;
import android.content.res.TypedArray;
import android.net.Uri;
import android.os.Bundle;
import android.view.Gravity;
import android.view.Menu;
......@@ -14,19 +15,22 @@ import android.widget.Toast;
import com.google.android.material.tabs.TabLayout;
import java.io.IOException;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.menu.MenuBuilder;
import androidx.appcompat.widget.SearchView;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.lifecycle.ViewModelProvider;
import androidx.viewpager.widget.ViewPager;
import io.github.muntashirakon.AppManager.AppManager;
import io.github.muntashirakon.AppManager.R;
import io.github.muntashirakon.AppManager.fragments.AppDetailsFragment;
import io.github.muntashirakon.AppManager.fragments.AppInfoFragment;
import io.github.muntashirakon.AppManager.utils.Utils;
import io.github.muntashirakon.AppManager.viewmodels.AppDetailsViewModel;
......@@ -36,61 +40,87 @@ public class AppDetailsActivity extends AppCompatActivity {
public AppDetailsViewModel model;
public SearchView searchView;
private String mPackageName;
private TypedArray mTabTitleIds;
private AppDetailsFragment[] fragments;
private Fragment[] fragments;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_details);
setSupportActionBar(findViewById(R.id.toolbar));
mPackageName = getIntent().getStringExtra(AppInfoActivity.EXTRA_PACKAGE_NAME);
if (mPackageName == null) {
Intent intent = getIntent();
// Check for package name
final String packageName = intent.getStringExtra(AppDetailsActivity.EXTRA_PACKAGE_NAME);
final Uri apkUri = intent.getData();
// Initialize tabs
mTabTitleIds = getResources().obtainTypedArray(R.array.TAB_TITLES);
fragments = new Fragment[mTabTitleIds.length()];
if (packageName == null && apkUri == null) {
Toast.makeText(this, getString(R.string.empty_package_name), Toast.LENGTH_LONG).show();
finish();
return;
}
// Set title
try {
setTitle(getPackageManager().getApplicationInfo(mPackageName, 0).loadLabel(getPackageManager()).toString());
} catch (PackageManager.NameNotFoundException ignored) {
finish();
return;
}
// Get model
model = ViewModelProvider.AndroidViewModelFactory.getInstance(AppManager.getInstance()).create(AppDetailsViewModel.class);
model.setPackageName(mPackageName);
// Initialize tabs
mTabTitleIds = getResources().obtainTypedArray(R.array.TAB_TITLES);
FragmentManager fragmentManager = getSupportFragmentManager();
ViewPager viewPager = findViewById(R.id.pager);
viewPager.setAdapter(new AppDetailsFragmentPagerAdapter(fragmentManager));
fragments = new AppDetailsFragment[mTabTitleIds.length()];
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
searchView = new SearchView(actionBar.getThemedContext());
actionBar.setDisplayShowCustomEnabled(true);
searchView.setQueryHint(getString(R.string.search));
((ImageView) searchView.findViewById(androidx.appcompat.R.id.search_button))
.setColorFilter(Utils.getThemeColor(this, android.R.attr.colorAccent));
((ImageView) searchView.findViewById(androidx.appcompat.R.id.search_close_btn))
.setColorFilter(Utils.getThemeColor(this, android.R.attr.colorAccent));
ActionBar.LayoutParams layoutParams = new ActionBar.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.END;
actionBar.setCustomView(searchView, layoutParams);
}
TabLayout tabLayout = findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(viewPager);
model = ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()).create(AppDetailsViewModel.class);
new Thread(() -> {
if (packageName != null) model.setPackageName(packageName);
else {
try {
model.setPackageUri(apkUri);
} catch (IOException e) {
e.printStackTrace();
runOnUiThread(() -> {
Toast.makeText(this, getString(R.string.empty_package_name), Toast.LENGTH_LONG).show();
finish();
});
return;
}
}
ApplicationInfo applicationInfo = model.getPackageInfo().applicationInfo;
runOnUiThread(() -> {
// Set title
setTitle(applicationInfo.loadLabel(getPackageManager()));
FragmentManager fragmentManager = getSupportFragmentManager();
ViewPager viewPager = findViewById(R.id.pager);
viewPager.setAdapter(new AppDetailsFragmentPagerAdapter(fragmentManager));
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
searchView = new SearchView(actionBar.getThemedContext());
actionBar.setDisplayShowCustomEnabled(true);
searchView.setQueryHint(getString(R.string.search));
((ImageView) searchView.findViewById(androidx.appcompat.R.id.search_button))
.setColorFilter(Utils.getThemeColor(this, android.R.attr.colorAccent));
((ImageView) searchView.findViewById(androidx.appcompat.R.id.search_close_btn))
.setColorFilter(Utils.getThemeColor(this, android.R.attr.colorAccent));
ActionBar.LayoutParams layoutParams = new ActionBar.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.END;
actionBar.setCustomView(searchView, layoutParams);
}
TabLayout tabLayout = findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(viewPager);
});
}).start();
// Check for the existence of package
model.getIsPackageExist().observe(this, isPackageExist -> {
if (!isPackageExist) {
Toast.makeText(this, packageName + ": " + getString(R.string.app_not_installed), Toast.LENGTH_LONG).show();
finish();
}
});
}
@Override
protected void onDestroy() {
model.onCleared();
super.onDestroy();
}
@SuppressLint("RestrictedApi")
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_app_details_actions, menu);
if (menu instanceof MenuBuilder) {
((MenuBuilder) menu).setOptionalIconsVisible(true);
}
......@@ -103,10 +133,6 @@ public class AppDetailsActivity extends AppCompatActivity {
if (id == android.R.id.home) {
finish();
return true;
} else if (id == R.id.action_app_info) {
Intent appInfoIntent = new Intent(this, AppInfoActivity.class);
appInfoIntent.putExtra(AppInfoActivity.EXTRA_PACKAGE_NAME, mPackageName);
startActivity(appInfoIntent);
}
return super.onOptionsItemSelected(item);
}
......@@ -119,8 +145,11 @@ public class AppDetailsActivity extends AppCompatActivity {
@NonNull
@Override
public AppDetailsFragment getItem(int position) {
if (fragments[position] == null) fragments[position] = new AppDetailsFragment(position);
public Fragment getItem(int position) {
if (fragments[position] == null) {
if (position == 0) fragments[position] = new AppInfoFragment();
else fragments[position] = new AppDetailsFragment(position);
}
return fragments[position];
}
......
......@@ -2,13 +2,12 @@ package io.github.muntashirakon.AppManager.activities;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.view.Gravity;
......@@ -50,28 +49,29 @@ import androidx.core.text.HtmlCompat;
import dalvik.system.DexClassLoader;
import io.github.muntashirakon.AppManager.R;
import io.github.muntashirakon.AppManager.StaticDataset;
import io.github.muntashirakon.AppManager.utils.IOUtils;
import io.github.muntashirakon.AppManager.utils.PackageUtils;
import io.github.muntashirakon.AppManager.utils.Utils;
import static com.google.classysharkandroid.utils.PackageUtils.apkCert;
import static com.google.classysharkandroid.utils.PackageUtils.convertS;
import static io.github.muntashirakon.AppManager.utils.IOUtils.deleteDir;
import static io.github.muntashirakon.AppManager.utils.IOUtils.readFully;
public class ClassListingActivity extends AppCompatActivity implements SearchView.OnQueryTextListener {
public static final String EXTRA_PACKAGE_NAME = "package_name";
private static final String APP_DEX = "app_dex";
private static final String EXODUS_CACHE_APK = "exodus_cache.apk";
private Intent inIntent;
private Intent intent;
private List<String> classList;
private List<String> classListAll;
private ListView mListView;
private TextView mEmptyView;
private int totalTrackersFound = 0;
private int totalClassesScanned = 0;
private String foundTrackerList = "";
private String[] signatures;
private int[] signatureCount;
private boolean[] signaturesFound;
private String[] Names;
private String[] tracker_names;
private boolean trackerClassesOnly;
private int totalIteration = 0;
private long totalTimeTaken = 0;
......@@ -85,26 +85,12 @@ public class ClassListingActivity extends AppCompatActivity implements SearchVie
@Override
protected void onDestroy() {
deleteCache(this);
clearApplicationData();
IOUtils.deleteDir(new File(getCacheDir().getParent(), APP_DEX));
IOUtils.deleteDir(getCodeCacheDir());
IOUtils.deleteDir(new File(getFilesDir(), EXODUS_CACHE_APK));
super.onDestroy();
}
private void clearApplicationData() {
String appCacheDirStr = getCacheDir().getParent();
if (appCacheDirStr == null) return;
File appCacheDir = new File(appCacheDirStr);
if (appCacheDir.exists()) {
String[] children = appCacheDir.list();
if (children == null) return;
for (String s : children) {
if (!s.equals("lib")) {
deleteDir(new File(appCacheDir, s));
}
}
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
......@@ -117,19 +103,8 @@ public class ClassListingActivity extends AppCompatActivity implements SearchVie
setContentView(R.layout.activity_class_listing);
setSupportActionBar(findViewById(R.id.toolbar));
mActionBar = getSupportActionBar();
intent = getIntent();
if (mActionBar != null) {
mPackageName = getIntent().getStringExtra(EXTRA_PACKAGE_NAME);
PackageManager pm = getPackageManager();
try {
assert mPackageName != null;
mAppName = pm.getApplicationInfo(mPackageName, 0).loadLabel(pm);
mActionBar.setTitle(mAppName);
mActionBar.setSubtitle(getString(R.string.tracker_classes));
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
Toast.makeText(this, R.string.app_not_installed, Toast.LENGTH_LONG).show();
finish();
}
mActionBar.setDisplayShowCustomEnabled(true);
SearchView searchView = new SearchView(mActionBar.getThemedContext());
......@@ -147,74 +122,69 @@ public class ClassListingActivity extends AppCompatActivity implements SearchVie
mActionBar.setCustomView(searchView, layoutParams);
}
inIntent = getIntent();
trackerClassesOnly = false;
mListView = findViewById(android.R.id.list);
mListView.setTextFilterEnabled(true);
mListView.setDividerHeight(0);
mListView.setEmptyView(findViewById(android.R.id.empty));
mEmptyView = findViewById(android.R.id.empty);
mListView.setEmptyView(mEmptyView);
mProgressIndicator = findViewById(R.id.progress_linear);
final Uri uriFromIntent = inIntent.getData();
classList = new ArrayList<>();
if (inIntent.getData() != null)
packageInfo = "<b>" + getString(R.string.source_dir) + ": </b>"
+ inIntent.getData().toString() + "\n";
else packageInfo = "";
Names = StaticDataset.getTrackerNames();
try {
InputStream uriStream = UriUtils.getStreamFromUri(ClassListingActivity.this, uriFromIntent);
final byte[] bytes = readFully(uriStream, -1, true);
uriStream.close();
new Thread(() -> {
try {
packageInfo += "\n<b>MD5sum:</b> " + convertS(MessageDigest.getInstance("md5").digest(bytes))
+ "\n<b>SHA1sum:</b> " + convertS(MessageDigest.getInstance("sha1").digest(bytes))
+ "\n<b>SHA256sum:</b> " + convertS(MessageDigest.getInstance("sha256").digest(bytes));
} catch (NoSuchAlgorithmException ignored) {}
PackageManager pm = getApplicationContext().getPackageManager();
PackageInfo mPackageInfo = null;
if (inIntent != null && inIntent.getAction() != null) {
if (inIntent.getAction().equals(Intent.ACTION_VIEW)) {
String archiveFilePath = UriUtils.pathUriCache(getApplicationContext(),
uriFromIntent, "cache.apk");
if (archiveFilePath != null)
mPackageInfo = pm.getPackageArchiveInfo(archiveFilePath, 64); // PackageManager.GET_SIGNATURES (Android Bug)
}
} else {
showProgress(true);
new Thread(() -> {
final Uri uriFromIntent = intent.getData();
classList = new ArrayList<>();
if (uriFromIntent != null) packageInfo = "<b>" + getString(R.string.source_dir) + ": </b>" + uriFromIntent.toString() + "\n";
else packageInfo = "";
tracker_names = StaticDataset.getTrackerNames();
signatures = StaticDataset.getTrackerCodeSignatures();
try {
InputStream uriStream = UriUtils.getStreamFromUri(ClassListingActivity.this, uriFromIntent);
final byte[] bytes = IOUtils.readFully(uriStream, -1, true);
uriStream.close();
new Thread(() -> {
try {
packageInfo += "\n<b>MD5sum:</b> " + convertS(MessageDigest.getInstance("md5").digest(bytes))
+ "\n<b>SHA1sum:</b> " + convertS(MessageDigest.getInstance("sha1").digest(bytes))
+ "\n<b>SHA256sum:</b> " + convertS(MessageDigest.getInstance("sha256").digest(bytes));
} catch (NoSuchAlgorithmException ignored) {}
final PackageManager pm = getApplicationContext().getPackageManager();
PackageInfo packageInfo = null;
String archiveFilePath = null;
if (uriFromIntent != null) {
String archiveFilePath = uriFromIntent.getPath();
if (intent.getAction() != null && intent.getAction().equals(Intent.ACTION_VIEW)) {
archiveFilePath = UriUtils.pathUriCache(getApplicationContext(),
uriFromIntent, EXODUS_CACHE_APK);
} else archiveFilePath = uriFromIntent.getPath();
if (archiveFilePath != null)
mPackageInfo = pm.getPackageArchiveInfo(archiveFilePath, 64); // PackageManager.GET_SIGNATURES (Android Bug)
packageInfo = pm.getPackageArchiveInfo(archiveFilePath, 64); // PackageManager.GET_SIGNATURES (Android Bug)
}
}
if (mPackageInfo != null) packageInfo += apkCert(mPackageInfo);
else packageInfo += "\n<i><b>FAILED to retrieve PackageInfo!</b></i>";
}).start();
new FillClassesNamesThread(bytes).start();
new StartDexLoaderThread(bytes).start();
} catch (Exception e) {
e.printStackTrace();
ActivityCompat.finishAffinity(this);
}
}
if (packageInfo != null) {
this.packageInfo += apkCert(packageInfo);
mPackageName = packageInfo.packageName;
final ApplicationInfo applicationInfo = packageInfo.applicationInfo;
applicationInfo.publicSourceDir = archiveFilePath;
applicationInfo.sourceDir = archiveFilePath;
mAppName = applicationInfo.loadLabel(pm);
runOnUiThread(() -> {
if (mActionBar != null) {
mActionBar.setTitle(mAppName);
mActionBar.setSubtitle(getString(R.string.tracker_classes));
}
});
} else this.packageInfo += "\n<i><b>FAILED to retrieve PackageInfo!</b></i>";
}).start();
public static void deleteCache(Context context) {
try {
File dir = context.getCacheDir();
deleteDir(dir);
if (Build.VERSION.SDK_INT >= 21) {
dir = context.getCodeCacheDir();
deleteDir(dir);
new FillClassesNamesThread(bytes).start();
new StartDexLoaderThread(bytes).start();
} catch (Exception e) {
e.printStackTrace();
ActivityCompat.finishAffinity(this);
}
} catch (Exception e) { e.printStackTrace();}
}).start();
}
@Override
......@@ -259,15 +229,15 @@ public class ClassListingActivity extends AppCompatActivity implements SearchVie
viewScanSummary();
return true;
case R.id.action_view_trackers:
StringBuilder statsMsg = new StringBuilder(Names[0] + "\n");
StringBuilder statsMsg = new StringBuilder(tracker_names[0] + "\n");
int i, j;
j = 1;
for (i = 1; i < Names.length; i++) {
if (Names[i - 1].equals(Names[i])) continue;
statsMsg.append(Names[i]).append("\n"); j++;
for (i = 1; i < tracker_names.length; i++) {
if (tracker_names[i - 1].equals(tracker_names[i])) continue;
statsMsg.append(tracker_names[i]).append("\n"); j++;
}
new MaterialAlertDialogBuilder(this, R.style.AppTheme_AlertDialog)
.setTitle(String.format(getString(R.string.trackers_and_classes), j, Names.length))
.setTitle(String.format(getString(R.string.trackers_and_classes), j, tracker_names.length))
.setNegativeButton(android.R.string.ok, null)
.setMessage(statsMsg.toString()).show();
return true;
......@@ -283,13 +253,17 @@ public class ClassListingActivity extends AppCompatActivity implements SearchVie
}
private void viewScanSummary() {
if (mProgressIndicator.isShown()) {
Toast.makeText(this, R.string.scanning_is_still_in_progress, Toast.LENGTH_SHORT).show();
return;
}
StringBuilder foundTrackersInfo = new StringBuilder();
if (totalTrackersFound > 0)
foundTrackersInfo.append("\n").append(getString(R.string.tracker_details)).append(":");
for (int i = 0, j = 0; i < signatures.length; i++) {
if (signaturesFound[i]) {
if (!foundTrackersInfo.toString().contains(Names[i]))
foundTrackersInfo.append("\n<b><u>").append(++j).append(". ").append(Names[i]).append("</b></u>\n");
if (!foundTrackersInfo.toString().contains(tracker_names[i]))
foundTrackersInfo.append("\n<b><u>").append(++j).append(". ").append(tracker_names[i]).append("</b></u>\n");
foundTrackersInfo.append(" ").append(signatures[i]).append(" (").append(signatureCount[i]).append(")\n");
}
}
......@@ -321,6 +295,16 @@ public class ClassListingActivity extends AppCompatActivity implements SearchVie
.show();
}
private void showProgress(boolean willShow) {
if (willShow) {
mProgressIndicator.show();
mEmptyView.setText(R.string.loading);
} else {
mProgressIndicator.hide();
mEmptyView.setText(R.string.no_tracker_class);
}
}
private class FillClassesNamesThread extends Thread {
private final byte[] bytes;
......@@ -335,7 +319,6 @@ public class ClassListingActivity extends AppCompatActivity implements SearchVie
classListAll = PackageUtils.getClassNames(bytes);
totalClassesScanned = classListAll.size();
StringBuilder found = new StringBuilder();
signatures = StaticDataset.getTrackerCodeSignatures();
signatureCount = new int[signatures.length];
signaturesFound = new boolean[signatures.length];
long t_start, t_end;
......@@ -348,9 +331,9 @@ public class ClassListingActivity extends AppCompatActivity implements SearchVie
classList.add(className);
signatureCount[i]++;
signaturesFound[i] = true;
if (found.toString().contains(Names[i])) break;
if (found.toString().contains(tracker_names[i])) break;
else found.append("<b>").append(++totalTrackersFound)
.append(". ").append(Names[i]).append("</b>\n");
.append(". ").append(tracker_names[i]).append("</b>\n");
break;
}
}
......@@ -374,7 +357,7 @@ public class ClassListingActivity extends AppCompatActivity implements SearchVie
mActionBar.setSubtitle(getString(R.string.all_classes));
}
mListView.setAdapter(mClassListingAdapter);
mProgressIndicator.hide();
showProgress(false);
if (classList.isEmpty() && totalClassesScanned == 0) {
// FIXME: Add support for odex (using root)
Toast.makeText(ClassListingActivity.this, R.string.system_odex_not_supported, Toast.LENGTH_LONG).show();
......
......@@ -17,6 +17,7 @@ import android.os.Build;
import android.os.Bundle;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.text.style.RelativeSizeSpan;
......@@ -68,10 +69,12 @@ import androidx.appcompat.widget.LinearLayoutCompat;
import androidx.appcompat.widget.SearchView;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.content.ContextCompat;
import androidx.core.text.HtmlCompat;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import io.github.muntashirakon.AppManager.BuildConfig;
import io.github.muntashirakon.AppManager.MainLoader;
import io.github.muntashirakon.AppManager.R;
import io.github.muntashirakon.AppManager.adb.AdbShell;
......@@ -162,7 +165,7 @@ public class MainActivity extends AppCompatActivity implements
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AppCompatDelegate.setDefaultNightMode((int) AppPref.get(AppPref.PREF_APP_THEME, AppPref.TYPE_INTEGER));
AppCompatDelegate.setDefaultNightMode((int) AppPref.get(AppPref.PrefKey.PREF_APP_THEME_INT));
setContentView(R.layout.activity_main);
setSupportActionBar(findViewById(R.id.toolbar));
ActionBar actionBar = getSupportActionBar();
......@@ -214,6 +217,9 @@ public class MainActivity extends AppCompatActivity implements
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(mAdapter);
checkFirstRun();
checkAppUpdate();
mBatchOpsManager = new BatchOpsManager(this);
Menu menu = mBottomAppBar.getMenu();
......@@ -291,7 +297,7 @@ public class MainActivity extends AppCompatActivity implements
@Override
protected void onPause() {
super.onPause();
AppPref.getInstance().setPref(AppPref.PREF_MAIN_WINDOW_SORT_ORDER, mSortBy);
AppPref.getInstance().setPref(AppPref.PrefKey.PREF_MAIN_WINDOW_SORT_ORDER_INT, mSortBy);
}
@SuppressLint("RestrictedApi")
......@@ -299,7 +305,7 @@ public class MainActivity extends AppCompatActivity implements
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main_actions, menu);
appUsageMenu = menu.findItem(R.id.action_app_usage);
if ((Boolean) AppPref.get(AppPref.PREF_USAGE_ACCESS_ENABLED, AppPref.TYPE_BOOLEAN)) {
if ((Boolean) AppPref.get(AppPref.PrefKey.PREF_USAGE_ACCESS_ENABLED_BOOL)) {
appUsageMenu.setVisible(true);
} else appUsageMenu.setVisible(false);
runningAppsMenu = menu.findItem(R.id.action_running_apps);
......@@ -396,6 +402,10 @@ public class MainActivity extends AppCompatActivity implements
Intent usageIntent = new Intent(this, AppUsageActivity.class);
startActivity(usageIntent);
return true;
case R.id.action_one_click_ops:
Intent onClickOpsIntent = new Intent(this, OneClickOpsActivity.class);
startActivity(onClickOpsIntent);
return true;
case R.id.action_apk_updater:
try {
if(!getPackageManager().getApplicationInfo(PACKAGE_NAME_APK_UPDATER, 0).enabled)
......@@ -467,15 +477,15 @@ public class MainActivity extends AppCompatActivity implements
protected void onStart() {
super.onStart();
// Check root
AppPref.getInstance().setPref(AppPref.PREF_ADB_MODE_ENABLED, false);
AppPref.getInstance().setPref(AppPref.PrefKey.PREF_ADB_MODE_ENABLED_BOOL, false);
if (!Utils.isRootGiven()) {
AppPref.getInstance().setPref(AppPref.PREF_ROOT_MODE_ENABLED, false);
AppPref.getInstance().setPref(AppPref.PrefKey.PREF_ROOT_MODE_ENABLED_BOOL, false);
// Check for adb
new Thread(() -> {
try {
AdbShell.CommandResult result = AdbShell.run("id");
if (!result.isSuccessful()) throw new IOException("Adb not available");
AppPref.getInstance().setPref(AppPref.PREF_ADB_MODE_ENABLED, true);
AppPref.getInstance().setPref(AppPref.PrefKey.PREF_ADB_MODE_ENABLED_BOOL, true);
runOnUiThread(() -> Toast.makeText(this, "Working on ADB mode", Toast.LENGTH_SHORT).show());
} catch (Exception ignored) {}
}).start();
......@@ -487,12 +497,12 @@ public class MainActivity extends AppCompatActivity implements
}
// Show/hide app usage menu
if (appUsageMenu != null) {
if ((Boolean) AppPref.get(AppPref.PREF_USAGE_ACCESS_ENABLED, AppPref.TYPE_BOOLEAN))
if ((Boolean) AppPref.get(AppPref.PrefKey.PREF_USAGE_ACCESS_ENABLED_BOOL))
appUsageMenu.setVisible(true);
else appUsageMenu.setVisible(false);
}
// Set sort by
mSortBy = (int) AppPref.get(AppPref.PREF_MAIN_WINDOW_SORT_ORDER, AppPref.TYPE_INTEGER);
mSortBy = (int) AppPref.get(AppPref.PrefKey.PREF_MAIN_WINDOW_SORT_ORDER_INT);
if (AppPref.isRootEnabled() || AppPref.isAdbEnabled()) {
if (runningAppsMenu != null) runningAppsMenu.setVisible(true);
if (sortByBlockedComponentMenu != null) sortByBlockedComponentMenu.setVisible(true);
......@@ -503,6 +513,38 @@ public class MainActivity extends AppCompatActivity implements
}
}
private void checkFirstRun() {
if (Utils.isAppInstalled()) {
new MaterialAlertDialogBuilder(this, R.style.AppTheme_AlertDialog)
.setTitle(R.string.instructions)
.setView(R.layout.dialog_instructions)
.setNegativeButton(android.R.string.ok, null)
.show();
AppPref.getInstance().setPref(AppPref.PrefKey.PREF_LAST_VERSION_CODE_LONG, (long) BuildConfig.VERSION_CODE);
}
}
private void checkAppUpdate() {
if (Utils.isAppUpdated()) {
new Thread(() -> {
final Spanned spannedChangelog = HtmlCompat.fromHtml(Utils.getContentFromAssets(this, "changelog.html"), HtmlCompat.FROM_HTML_MODE_COMPACT);
runOnUiThread(() ->
new MaterialAlertDialogBuilder(this, R.style.AppTheme_AlertDialog)
.setTitle(R.string.changelog)
.setMessage(spannedChangelog)
.setNegativeButton(android.R.string.ok, null)
.setNeutralButton(R.string.instructions, (dialog, which) -> {
new FullscreenDialog(this)
.setTitle(R.string.instructions)
.setView(R.layout.dialog_instructions)
.show();
})
.show());
}).start();
AppPref.getInstance().setPref(AppPref.PrefKey.PREF_LAST_VERSION_CODE_LONG, (long) BuildConfig.VERSION_CODE);
}
}
private void handleSelection() {
if (mPackageNames.size() == 0) {
mBottomAppBar.setVisibility(View.GONE);
......@@ -554,7 +596,7 @@ public class MainActivity extends AppCompatActivity implements
}
private void sortApplicationList(@SortOrder int sortBy) {
final Boolean isRootEnabled = (Boolean) AppPref.get(AppPref.PREF_ROOT_MODE_ENABLED, AppPref.TYPE_BOOLEAN);
final boolean isRootEnabled = AppPref.isRootEnabled();
if (sortBy != SORT_BY_APP_LABEL) sortApplicationList(SORT_BY_APP_LABEL);
Collections.sort(mApplicationItems, (o1, o2) -> {
switch (sortBy) {
......@@ -657,6 +699,8 @@ public class MainActivity extends AppCompatActivity implements
private static int mColorTransparent;
private static int mColorSemiTransparent;
private static int mColorHighlight;
private static int mColorDisabled;
private static int mColorStopped;
private static int mColorOrange;
private static int mColorPrimary;
private static int mColorSecondary;
......@@ -669,6 +713,8 @@ public class MainActivity extends AppCompatActivity implements
mColorTransparent = Color.TRANSPARENT;
mColorSemiTransparent = ContextCompat.getColor(mActivity, R.color.semi_transparent);
mColorHighlight = ContextCompat.getColor(mActivity, R.color.highlight);
mColorDisabled = ContextCompat.getColor(mActivity, R.color.disabled_user);
mColorStopped = ContextCompat.getColor(mActivity, R.color.stopped);
mColorOrange = ContextCompat.getColor(mActivity, R.color.orange);
mColorPrimary = Utils.getThemeColor(mActivity, android.R.attr.textColorPrimary);
mColorSecondary = Utils.getThemeColor(mActivity, android.R.attr.textColorSecondary);
......@@ -750,21 +796,20 @@ public class MainActivity extends AppCompatActivity implements
final ApplicationInfo info = item.applicationInfo;
// Add click listeners
holder.itemView.setOnClickListener(v -> {
Intent intent = new Intent(mActivity, AppInfoActivity.class);
intent.putExtra(AppInfoActivity.EXTRA_PACKAGE_NAME, info.packageName);
mActivity.startActivity(intent);
if (mPackageNames.size() == 0) {
Intent appDetailsIntent = new Intent(mActivity, AppDetailsActivity.class);
appDetailsIntent.putExtra(AppDetailsActivity.EXTRA_PACKAGE_NAME, info.packageName);
mActivity.startActivity(appDetailsIntent);
} else toggleSelection(item, position);
});
holder.itemView.setOnLongClickListener(v -> {
Intent appDetailsIntent = new Intent(mActivity, AppDetailsActivity.class);
appDetailsIntent.putExtra(AppDetailsActivity.EXTRA_PACKAGE_NAME, info.packageName);
mActivity.startActivity(appDetailsIntent);
toggleSelection(item, position);
return true;
});
// Alternate background colors: selected > disabled > regular
if (mPackageNames.contains(info.packageName))
holder.mainView.setBackgroundColor(mColorHighlight);
else if (!info.enabled)
holder.mainView.setBackgroundColor(ContextCompat.getColor(mActivity, R.color.disabled_user));
else if (!info.enabled) holder.mainView.setBackgroundColor(mColorDisabled);
else holder.mainView.setBackgroundColor(position % 2 == 0 ? mColorSemiTransparent : mColorTransparent);
// Add yellow star if the app is in debug mode
holder.favorite_icon.setVisibility(item.star ? View.VISIBLE : View.INVISIBLE);
......@@ -824,9 +869,9 @@ public class MainActivity extends AppCompatActivity implements
// Highlight searched query
holder.packageName.setText(Utils.getHighlightedText(info.packageName, mConstraint, mColorRed));
} else holder.packageName.setText(info.packageName);
// Set package name color to blue if the app is in stopped/force closed state
// Set package name color to dark cyan if the app is in stopped/force closed state
if ((info.flags & ApplicationInfo.FLAG_STOPPED) != 0)
holder.packageName.setTextColor(ContextCompat.getColor(mActivity, R.color.stopped));
holder.packageName.setTextColor(mColorStopped);
else holder.packageName.setTextColor(mColorSecondary);
// Set version (along with HW accelerated, debug and test only flags)
CharSequence version = holder.version.getText();
......@@ -834,12 +879,12 @@ public class MainActivity extends AppCompatActivity implements
if ((info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) version = "debug" + version;
if ((info.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0) version = "~" + version;
holder.version.setText(version);
// Set version color to green if the app is inactive
// Set version color to dark cyan if the app is inactive
if (Build.VERSION.SDK_INT >= 23) {
UsageStatsManager mUsageStats;
mUsageStats = mActivity.getSystemService(UsageStatsManager.class);
if (mUsageStats != null && mUsageStats.isAppInactive(info.packageName))
holder.version.setTextColor(ContextCompat.getColor(mActivity, R.color.stopped));
holder.version.setTextColor(mColorStopped);
else holder.version.setTextColor(mColorSecondary);
}
// Set app type: system or user app (along with large heap, suspended, multi-arch,
......@@ -867,17 +912,20 @@ public class MainActivity extends AppCompatActivity implements
if ((info.flags & ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC) !=0)
holder.size.setTextColor(mColorOrange);
else holder.size.setTextColor(mColorSecondary);
holder.icon.setOnClickListener(v -> {
if (MainActivity.mPackageNames.contains(info.packageName)) {
MainActivity.mPackageNames.remove(info.packageName);
MainActivity.mSelectedApplicationItems.remove(item);
} else {
MainActivity.mPackageNames.add(info.packageName);
MainActivity.mSelectedApplicationItems.add(item);
}
notifyItemChanged(position);
mActivity.handleSelection();
});
holder.icon.setOnClickListener(v -> toggleSelection(item, position));
}
public void toggleSelection(@NonNull ApplicationItem item, int position) {
ApplicationInfo info = item.applicationInfo;
if (mPackageNames.contains(info.packageName)) {
mPackageNames.remove(info.packageName);
mSelectedApplicationItems.remove(item);
} else {
mPackageNames.add(info.packageName);
mSelectedApplicationItems.add(item);
}
notifyItemChanged(position);
mActivity.handleSelection();
}
@Override
......
......@@ -5,7 +5,12 @@ package io.github.muntashirakon.AppManager.activities;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.net.Uri;
import android.os.Bundle;
import android.text.Spannable;
......@@ -17,24 +22,33 @@ import android.view.View;
import android.widget.Toast;
import com.google.android.material.progressindicator.ProgressIndicator;
import com.google.classysharkandroid.utils.UriUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.menu.MenuBuilder;
import androidx.appcompat.widget.AppCompatEditText;
import androidx.core.content.ContextCompat;
import io.github.muntashirakon.AppManager.R;
import io.github.muntashirakon.AppManager.utils.IOUtils;
import io.github.muntashirakon.AppManager.utils.Utils;
import io.github.muntashirakon.xmlapkparser.AXMLPrinter;
public class ManifestViewerActivity extends AppCompatActivity {
public static final String EXTRA_PACKAGE_NAME = "package_name";
public static final String EXTRA_PACKAGE_NAME = "pkg";
private static final String MANIFEST_CACHE_APK = "manifest_cache.apk";
private static final String MIME_XML = "text/xml";
private static final int RESULT_CODE_EXPORT = 849;
......@@ -53,31 +67,63 @@ public class ManifestViewerActivity extends AppCompatActivity {
private boolean isWrapped = true; // Wrap by default
private AppCompatEditText container;
private SpannableString formattedContent;
private String filePath;
private String archiveFilePath;
private String packageName;
@SuppressLint("WrongConstant")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_any_viewer);
setSupportActionBar(findViewById(R.id.toolbar));
mProgressIndicator = findViewById(R.id.progress_linear);
packageName = getIntent().getStringExtra(EXTRA_PACKAGE_NAME);
filePath = null;
String applicationLabel = null;
try {
assert packageName != null;
filePath = getPackageManager().getPackageInfo(packageName, 0).applicationInfo.sourceDir;
applicationLabel = getPackageManager().getApplicationInfo(packageName, 0).loadLabel(getPackageManager()).toString();
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
Toast.makeText(this, R.string.app_not_installed, Toast.LENGTH_LONG).show();
final Intent intent = getIntent();
final Uri packageUri = intent.getData();
packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
if (packageUri == null && packageName == null) {
Toast.makeText(this, getString(R.string.empty_package_name), Toast.LENGTH_LONG).show();
finish();
return;
}
setTitle("\u2707 " + applicationLabel);
setWrapped();
// new AsyncManifestLoaderPkg(ManifestViewerActivity.this).execute(packageName);
archiveFilePath = null;
new Thread(() -> {
final PackageManager pm = getApplicationContext().getPackageManager();
if (packageUri != null) {
PackageInfo packageInfo = null;
if (intent.getAction() != null && intent.getAction().equals(Intent.ACTION_VIEW)) {
archiveFilePath = UriUtils.pathUriCache(getApplicationContext(),
packageUri, MANIFEST_CACHE_APK);
} else archiveFilePath = packageUri.getPath();
if (archiveFilePath != null)
packageInfo = pm.getPackageArchiveInfo(archiveFilePath, 64); // PackageManager.GET_SIGNATURES (Android Bug)
if (packageInfo != null) {
packageName = packageInfo.packageName;
final ApplicationInfo applicationInfo = packageInfo.applicationInfo;
applicationInfo.publicSourceDir = archiveFilePath;
applicationInfo.sourceDir = archiveFilePath;
runOnUiThread(() -> {
setTitle(applicationInfo.loadLabel(pm));
setWrapped();
});
}
} else {
runOnUiThread(() -> {
try {
setTitle(pm.getApplicationInfo(packageName, 0).loadLabel(pm));
setWrapped();
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
});
}
}).start();
}
@Override
protected void onDestroy() {
IOUtils.deleteDir(getCodeCacheDir());
IOUtils.deleteDir(new File(getFilesDir(), MANIFEST_CACHE_APK));
super.onDestroy();
}
@SuppressLint("RestrictedApi")
......@@ -141,7 +187,7 @@ public class ManifestViewerActivity extends AppCompatActivity {
final int attrValueColor = ContextCompat.getColor(this, R.color.ocean_blue);
new Thread(() -> {
if (formattedContent == null) {
code = io.github.muntashirakon.AppManager.utils.Utils.getProperXml(AXMLPrinter.getManifestXMLFromAPK(filePath, "AndroidManifest.xml"));
getManifest();
if (code == null) {
runOnUiThread(() -> {
Toast.makeText(this, R.string.error, Toast.LENGTH_LONG).show();
......@@ -168,165 +214,116 @@ public class ManifestViewerActivity extends AppCompatActivity {
}).start();
}
// /**
// * This AsyncTask takes manifest file path as argument
// */
// private static class AsyncManifestLoaderPkg extends AsyncTask<String, Integer, Boolean> {
// private WeakReference<ManifestViewerActivity> mActivity = null;
//
// private AsyncManifestLoaderPkg(ManifestViewerActivity pActivity) {
// link(pActivity);
// }
//
// private void link(ManifestViewerActivity pActivity) {
// mActivity = new WeakReference<>(pActivity);
// }
//
// @Override
// protected void onPreExecute() {
// super.onPreExecute();
// if (mActivity.get() != null) mActivity.get().showProgressIndicator(true);
// }
//
// @Override
// protected Boolean doInBackground(String... strings) {
// String packageName = strings[0];
// XmlResourceParser xml = null;
// AssetManager mCurAm = null;
// Resources mCurResources = null;
// try {
// //https://stackoverflow.com/questions/35474016/store-and-extract-map-from-android-resource-file
// mCurAm = mActivity.get().createPackageContext(packageName,
// CONTEXT_IGNORE_SECURITY | CONTEXT_INCLUDE_CODE).getAssets();
// mCurResources = new Resources(mCurAm, mActivity.get().getResources().getDisplayMetrics(), null);
// } catch (PackageManager.NameNotFoundException e) {
// e.printStackTrace();
// return false;
// }
// try {
// xml = mCurAm.openXmlResourceParser("AndroidManifest.xml");
// //this.mInput.setText("/sdcard/" + getPkgName() + ".txt");
// code = io.github.muntashirakon.AppManager.utils.Utils.getProperXml(getXMLText(xml, mCurResources).toString());
// } catch (IOException e) {
// e.printStackTrace();
// return false;
// }
//
// return (code != null);
// }
//
// @Override
// protected void onPostExecute(Boolean result) {
// super.onPostExecute(result);
// if (mActivity.get() != null) {
// mActivity.get().showProgressIndicator(false);
// if (result)
// mActivity.get().displayContent();
// else
// mActivity.get().handleError();
// }
// }
// }
//
// protected static void insertSpaces(StringBuffer sb, int num) {
// if (sb == null)
// return;
// for (int i = 0; i < num; i++)
// sb.append(" ");
// }
//
// private static CharSequence getAttribs(XmlResourceParser xrp, Resources currentResources) {
// StringBuffer sb = new StringBuffer();
// for (int i = 0; i < xrp.getAttributeCount(); i++){
// if (xrp.getAttributeName(i).length()!=0)
// sb.append("\n" + xrp.getAttributeName(i) + "=\""
// + resolveValue(xrp.getAttributeValue(i), currentResources)
// + "\"");
// else
// sb.append("\n" + xrp.getAttributeType(i)
// +Integer.toHexString(xrp.getAttributeNameResource(i)) + "=\""
// + resolveValue(xrp.getAttributeValue(i), currentResources)
// + "\"");
// }
// return sb;
// }
//
// private static String resolveValue(String in, Resources r) {
// if (in == null )
// return "null";
// if (!in.startsWith("@"))
// return in;
// int num = Integer.parseInt(in.substring(1));
// try {
// return r.getString(num).replaceAll("&", "&amp;")
// .replaceAll("\"", "&quot;")//
// .replaceAll("'", "&apos;")
// .replaceAll("<", "&lt;")
// .replaceAll(">", "&gt;");
// } catch (NumberFormatException e) {
// e.printStackTrace();
// return in;
// } catch (RuntimeException e) {
// try {
// if (r.getResourceEntryName(num).length()>0)
// return r.getResourceTypeName(num)+"/"+r.getResourceEntryName(num);
// else return r.getResourceTypeName(num)+"/"+in;
// } catch (Resources.NotFoundException e2){
// e2.printStackTrace();
// return in;
// }
// }
// }
//
// static CharSequence getXMLText(XmlResourceParser xrp, Resources currentResources) {
// StringBuffer sb = new StringBuffer();
// int indent = 0;
// try {
// int eventType = xrp.getEventType();
// while (eventType != XmlPullParser.END_DOCUMENT) {
// // for sb
// switch (eventType) {
// case XmlPullParser.START_TAG:
// indent += 1;
// sb.append("\n");
// insertSpaces(sb, indent);
// sb.append("<" + xrp.getName());
// sb.append(getAttribs(xrp, currentResources));
// sb.append(">");
// break;
// case XmlPullParser.END_TAG:
// indent -= 1;
// sb.append("\n");
// insertSpaces(sb, indent);
// sb.append("</" + xrp.getName() + ">");
// break;
//
// case XmlPullParser.TEXT:
// sb.append("" + xrp.getText());
// break;
//
// case XmlPullParser.CDSECT:
// sb.append("<!CDATA[" + xrp.getText() + "]]>");
// break;
//
// case XmlPullParser.PROCESSING_INSTRUCTION:
// sb.append("<?" + xrp.getText() + "?>");
// break;
//
// case XmlPullParser.COMMENT:
// sb.append("<!--" + xrp.getText() + "-->");
// break;
// }
// eventType = xrp.nextToken();
// }
// } catch (IOException ioe) {
// ioe.printStackTrace();
// //showError("Reading XML", ioe);
// } catch (XmlPullParserException xppe) {
// xppe.printStackTrace();
// //showError("Parsing XML", xppe);
// }
// return sb;
// }
private void getManifest() {
if (archiveFilePath != null) {
code = Utils.getProperXml(AXMLPrinter.getManifestXMLFromAPK(archiveFilePath, "AndroidManifest.xml"));
} else {
AssetManager mCurAm;
XmlResourceParser xml;
Resources mCurResources;
try {
// https://stackoverflow.com/questions/35474016/store-and-extract-map-from-android-resource-file
mCurAm = ManifestViewerActivity.this.createPackageContext(packageName,
CONTEXT_IGNORE_SECURITY | CONTEXT_INCLUDE_CODE).getAssets();
mCurResources = new Resources(mCurAm, ManifestViewerActivity.this.getResources().getDisplayMetrics(), null);
xml = mCurAm.openXmlResourceParser("AndroidManifest.xml");
code = Utils.getProperXml(getXMLText(xml, mCurResources).toString());
} catch (IOException | PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}
protected static void insertSpaces(StringBuffer sb, int num) {
if (sb == null)
return;
for (int i = 0; i < num; i++)
sb.append(" ");
}
@NonNull
private static CharSequence getAttribs(@NonNull XmlResourceParser xrp, Resources currentResources) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < xrp.getAttributeCount(); i++){
if (xrp.getAttributeName(i).length()!=0)
sb.append("\n").append(xrp.getAttributeName(i)).append("=\"").append(resolveValue(xrp.getAttributeValue(i), currentResources)).append("\"");
else
sb.append("\n").append(xrp.getAttributeType(i)).append(Integer.toHexString(xrp.getAttributeNameResource(i)))
.append("=\"").append(resolveValue(xrp.getAttributeValue(i), currentResources)).append("\"");
}
return sb;
}
private static String resolveValue(String in, Resources r) {
if (in == null )
return "null";
if (!in.startsWith("@"))
return in;
int num = Integer.parseInt(in.substring(1));
try {
return r.getString(num).replaceAll("&", "&amp;")
.replaceAll("\"", "&quot;")//
.replaceAll("'", "&apos;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;");
} catch (NumberFormatException e) {
e.printStackTrace();
return in;
} catch (RuntimeException e) {
try {
if (r.getResourceEntryName(num).length()>0)
return r.getResourceTypeName(num)+"/"+r.getResourceEntryName(num);
else return r.getResourceTypeName(num)+"/"+in;
} catch (Resources.NotFoundException e2){
e2.printStackTrace();
return in;
}
}
}
@NonNull
static CharSequence getXMLText(@NonNull XmlResourceParser xrp, Resources currentResources) {
StringBuffer sb = new StringBuffer();
int indent = 0;
try {
int eventType = xrp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_TAG:
indent += 1;
sb.append("\n");
insertSpaces(sb, indent);
sb.append("<").append(xrp.getName()).append(getAttribs(xrp, currentResources)).append(">");
break;
case XmlPullParser.END_TAG:
indent -= 1;
sb.append("\n");
insertSpaces(sb, indent);
sb.append("</").append(xrp.getName()).append(">");
break;
case XmlPullParser.TEXT:
sb.append(xrp.getText());
break;
case XmlPullParser.CDSECT:
sb.append("<!CDATA[").append(xrp.getText()).append("]]>");
break;
case XmlPullParser.PROCESSING_INSTRUCTION:
sb.append("<?").append(xrp.getText()).append("?>");
break;
case XmlPullParser.COMMENT:
sb.append("<!--").append(xrp.getText()).append("-->");
break;
}
eventType = xrp.nextToken();
}
} catch (IOException | XmlPullParserException e) {
e.printStackTrace();
}
return sb;
}
}
package io.github.muntashirakon.AppManager.activities;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import io.github.muntashirakon.AppManager.R;
import io.github.muntashirakon.AppManager.utils.ListItemCreator;
import android.os.Bundle;
import android.view.MenuItem;
import com.google.android.material.progressindicator.ProgressIndicator;
public class OneClickOpsActivity extends AppCompatActivity {
private ListItemCreator mItemCreator;
private ProgressIndicator mProgressIndicator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_one_click_ops);
setSupportActionBar(findViewById(R.id.toolbar));
mItemCreator = new ListItemCreator(this, R.id.container);
mProgressIndicator = findViewById(R.id.progress_linear);
setItems();
}
private void setItems() {