...
 
Commits (46)
name: Debug Build
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Clone the repository
uses: actions/checkout@v2
- name: Set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew assembleDebug
- name: Store generated APK file
uses: actions/upload-artifact@v1
with:
name: AppManager
path: ./app/build/outputs/apk/debug/app-debug.apk
.gradle
.idea
/crowdin.properties
/local.properties
/.idea/workspace.xml
/.idea/libraries
......
# App Manager
![Debug Build](https://github.com/MuntashirAkon/AppManager/workflows/Debug%20Build/badge.svg)
Yet another android package manager and viewer but...
......@@ -20,16 +21,23 @@ Yet another android package manager and viewer but...
- Apk files can be shared (hence the use of a provider)
- It can be used to clear app data or app cache (requires root/ADB)
- Batch operations: clear app data, disable run in background, disable/kill/uninstall apps
- One-click operations: block ads/tracker components, block components by signature, block multiple app ops
...and other minor features such as uninstalling/enabling/disabling apps, displaying app installation info, opening on F-Droid, Aurora Droid or Aurora Store.
...and other minor features such as installing/uninstalling/updating/enabling/disabling apps, displaying app installation info, opening on F-Droid, Aurora Droid or Aurora Store.
It basically combined the features of five or six apps that any tech-savy person is needed to install in order to have a “life”.
It basically combines the features of 5 or 6 apps that any tech-savvy person needs to use in order to have a life.
See the instructions page within the app for more information on how to use the app.
Until it is available in the official repo,
<a href="https://apt.izzysoft.de/fdroid/index/apk/io.github.muntashirakon.AppManager"><img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" alt="Get it on IzzyOnDroid" width="200dp" /></a>
Join our Telegram channel: https://t.me/AppManagerAndroid
Telegram Support Group: https://t.me/AppManagerAndroid
Telegram Update Channel: https://t.me/AppManagerChannel
Help us translate App Manager at Crowdin: https://crwd.in/android-app-manager
### Mirrors
......
......@@ -22,6 +22,15 @@ android {
throw new GradleException("Could not read version.properties!")
}
signingConfigs {
debug {
storeFile file('dev_keystore.jks')
storePassword 'kJCp!Bda#PBdN2RLK%yMK@hatq&69E'
keyPassword 'kJCp!Bda#PBdN2RLK%yMK@hatq&69E'
keyAlias 'key0'
}
}
buildTypes {
release {
minifyEnabled false
......@@ -29,6 +38,7 @@ android {
}
debug {
applicationIdSuffix '.debug'
signingConfig signingConfigs.debug
}
}
lintOptions {
......@@ -54,5 +64,5 @@ dependencies {
implementation 'com.jaredrummler:android-shell:1.0.0'
implementation 'com.tananaev:adblib:1.2'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.4'
// debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.4'
}
......@@ -10,6 +10,7 @@
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<application
android:name=".AppManager"
android:allowBackup="false"
......
<h5>v2.5.10 (324)</h5>
&#x2022; <font color="#09868B">[Feature]</font> Added 1-click operations (as 1-Click Ops in the menu section in the main window): block (ads and) trackers, component blocking by signatures, app op blocking<br />
&#x2022; <font color="#09868B">[Feature]</font> Added support for external apk: You can now open apk files from your file manager. You can view app details, manifest or scan for trackers directly from there<br />
&#x2022; <font color="#09868B">[Feature]</font> Added persistent apps filtering option in the main window<br />
&#x2022; <font color="#09868B">[Feature]</font> Alternative manifest viewer for installed apks<br />
&#x2022; <font color="#09868B">[Feature]</font> Display number of trackers as a tag in the App Info tab<br />
&#x2022; <font color="#09868B">[Feature]</font> Added a select all option in the bottom bar in the main window in selection mode<br />
&#x2022; <font color="#09868B">[Feature]</font> Added links to source code and community<br />
&#x2022; <font color="#09868B">[Feature]</font> Added support for installing/updating apk files in the App Info tab (incomplete)<br />
&#x2022; <font color="#09868B">[Feature]</font> Added an option to import existing disabled components in the Import/Export settings (incomplete)<br />
&#x2022; <font color="#09868B">[Feature]</font> Added split apk information in App Info tab<br />
&#x2022; <font color="#09868B">[Feature]</font> Added an option to open Termux in the main window (incomplete)<br />
&#x2022; <font color="#09868B">[Feature]</font> Initial support for app banner<br />
&#x2022; <font color="red">[Fix]</font> Fixed inconsistency of enable and disable in the App Info tab<br />
&#x2022; <font color="red">[Fix]</font> Fixed issue with persistent app cache<br />
&#x2022; <font color="red">[Fix]</font> Fixed scrolling issue on settings page<br />
&#x2022; <font color="red">[Fix]</font> Fixed crashes when switching to the components tabs for non-root users<br />
&#x2022; <font color="red">[Fix]</font> Fixed crash when trying to view summary while scanning is still in progress in the exodus page<br />
&#x2022; <font color="red">[Fix]</font> Fixed crashes on devices that does not support data usage<br />
&#x2022; <font color="red">[Fix]</font> Fixed crash when trying to view manifest of an split apk<br />
&#x2022; <font color="red">[Fix]</font> Fixed wrong package installer name in the App Info<br />
&#x2022; <font color="red">[Fix]</font> Fixed changelog formatting for old devices<br />
<br />
<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/>
&#x2022; <font color="#09868B">[Feature]</font> Merged App Info as a single tab in App Details<br />
&#x2022; <font color="#09868B">[Feature]</font> Added option to reset all app ops<br />
&#x2022; <font color="#09868B">[Feature]</font> Added option to revoke all dangerous app ops/permissions<br />
&#x2022; <font color="#09868B">[Feature]</font> Highlight trackers in the component tabs<br />
&#x2022; <font color="#09868B">[Feature]</font> Added option to save manifest and class dump<br />
&#x2022; <font color="#09868B">[Feature]</font> Added the ability to grant/revoke development permissions<br />
&#x2022; <font color="#09868B">[Feature]</font> Added sorting options for components, app ops and uses permissions tabs<br />
&#x2022; <font color="#09868B">[Feature]</font> Added sort by wifi usage in the App Usage window<br />
&#x2022; <font color="#09868B">[Feature]</font> Added launch button in the App Info tab<br />
&#x2022; <font color="#09868B">[Feature]</font> Added never ask option to usage status prompt<br />
&#x2022; <font color="#09868B">[Feature]</font> Added long click to select apps in the main window<br />
&#x2022; <font color="#09868B">[Feature]</font> Added changelog within the app<br />
&#x2022; <font color="red">[Fix]</font> Click to select apps during selection mode<br />
&#x2022; <font color="red">[Fix]</font> Improved component blocker<br />
&#x2022; <font color="red">[Fix]</font> Improved manifest loading for large apps<br />
&#x2022; <font color="red">[Fix]</font> Improved tab loading performance<br />
&#x2022; <font color="red">[Fix]</font> Fixed app ops checking and custom app ops for some devices<br />
&#x2022; <font color="red">[Fix]</font> Disabled activity opening for disabled activities<br />
&#x2022; <font color="red">[Fix]</font> Get real activity name for activities that use activity-alias<br />
&#x2022; <font color="red">[Fix]</font> Fixed background colors<br />
&#x2022; <font color="red">[Fix]</font> Fixed crashing when loading the services tab for non-root users<br />
&#x2022; <font color="red">[Fix]</font> Fixed back button for class viewer which was not working<br />
&#x2022; <font color="red">[Fix]</font> Changed block icon's colour to accent colour<br />
&#x2022; <font color="red">[Fix]</font> Removed translation until the app is complete<br />
&#x2022; <font color="red">[Fix]</font> Made links in the credit section clickable<br />
&#x2022; <font color="red">[Fix]</font> Fixed various memory leaks<br />
<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/>
&#x2022; <font color="#09868B">[Feature]</font> Added import/export capabilities for blocking rules<br />
&#x2022; <font color="#09868B">[Feature]</font> Added ability to select themes (night/day)<br />
&#x2022; <font color="#09868B">[Feature]</font> Added mode, duration, accept time, reject time for app ops<br />
&#x2022; <font color="#09868B">[Feature]</font> Highlight running services<br />
&#x2022; <font color="#09868B">[Feature]</font> Highlight disabled components not disabled within App Manager<br />
&#x2022; <font color="#09868B">[Feature]</font> Added swipe to refresh in the App Usage window<br />
&#x2022; <font color="#09868B">[Feature]</font> Added screen time percentage with indicator<br />
&#x2022; <font color="#09868B">[Feature]</font> Separate instructions and about pages with fullscreen dialog for both<br />
&#x2022; <font color="#09868B">[Feature]</font> Rounded overflow menu (still incomplete)<br />
&#x2022; <font color="red">[Fix]</font> Fixed various device/SDK specific app ops issues<br />
&#x2022; <font color="red">[Fix]</font> Stability improvements of the entire apps<br />
&#x2022; <font color="red">[Fix]</font> Added <tt>ACCESS_NETWORK_STATE</tt> permission to support older operating systems<br />
&#x2022; <font color="red">[Fix]</font> Fixed deleting all IFW rules when selecting apply global rules<br />
&#x2022; <font color="red">[Fix]</font> Fixed various search issues<br />
<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>
&#x2022; <font color="#09868B">[Feature]</font> Initial support for ADB over TCP (port 5555) for non-root users<br />
&#x2022; <font color="red">[Fix]</font> Fixed importing rules from Watt and Blocker<br />
&#x2022; <font color="red">[Fix]</font> Display Aurora Droid in App Info window as a first priority over F-Droid<br />
&#x2022; <font color="red">[Fix]</font> Improved performance for component blocking<br />
&#x2022; <font color="red">[Fix]</font> Fixed app op mode detection issue<br />
<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>
&#x2022; <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)<br />
&#x2022; <font color="#09868B">[Feature]</font> Full support of Blocker's exported files which was broken due to a bug on Blocker app<br />
&#x2022; <font color="#09868B">[Feature]</font> Reimplementation of blocking activities, receivers, services and providers<br />
&#x2022; <font color="red">[Fix]</font> Removed ConstraintLayout dependency therefore a potential decrease in app size<br />
&#x2022; <font color="red">[Fix]</font> Fixed duplicate app usage warning in the App Info window<br />
&#x2022; <font color="red">[Fix]</font> Fixed crash when an app icon is not found in App Details window<br />
<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>
&#x2022; <font color="#09868B">[Feature]</font> Added running apps/process viewer (requires root)<br />
&#x2022; <font color="#09868B">[Feature]</font> Added usage details viewer<br />
&#x2022; <font color="#09868B">[Feature]</font> Added apk updater and aurora store support<br />
&#x2022; <font color="#09868B">[Feature]</font> Save modified values of app ops and permissions to the disk (on progress)<br />
&#x2022; <font color="red">[Fix]</font> Uninstall support for non-root users<br />
&#x2022; <font color="red">[Fix]</font> Restructure app usage<br />
&#x2022; <font color="red">[Fix]</font> Added more clarity as well as improve performance in the app details window<br />
/*
* Copyright 2015 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.classysharkandroid.utils;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class IOUtils {
public static int copy(InputStream input, OutputStream output) throws IOException {
long count = copyLarge(input, output);
if (count > Integer.MAX_VALUE) {
return -1;
}
return (int) count;
}
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
static long copyLarge(@NonNull InputStream input, OutputStream output) throws IOException {
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
long count = 0;
int n;
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
input.close();
if (output != null) output.close();
return count;
}
/**
* Format xml file to correct indentation ...
*/
@Nullable
public static String getProperXml(String dirtyXml) {
try {
Document document = DocumentBuilderFactory.newInstance()
.newDocumentBuilder()
.parse(new InputSource(new ByteArrayInputStream(dirtyXml.getBytes(StandardCharsets.UTF_8))));
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nodeList = (NodeList) xPath.evaluate("//text()[normalize-space()='']",
document, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); ++i) {
Node node = nodeList.item(i);
node.getParentNode().removeChild(node);
}
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
StringWriter stringWriter = new StringWriter();
StreamResult streamResult = new StreamResult(stringWriter);
transformer.transform(new DOMSource(document), streamResult);
return stringWriter.toString();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
......@@ -27,6 +27,7 @@ import java.io.InputStream;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.github.muntashirakon.AppManager.utils.IOUtils;
public class UriUtils {
......@@ -46,7 +47,7 @@ public class UriUtils {
FileOutputStream fos = new FileOutputStream(f);
InputStream is = context.getContentResolver().openInputStream(uri);
if (is == null) return null;
IOUtils.copyLarge(is, fos);
IOUtils.copy(is, fos);
return f.getPath();
} catch (IOException e) {
return null;
......
package io.github.muntashirakon.AppManager;
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import androidx.loader.content.AsyncTaskLoader;
import io.github.muntashirakon.AppManager.activities.MainActivity;
import io.github.muntashirakon.AppManager.storage.compontents.ComponentsBlocker;
import io.github.muntashirakon.AppManager.types.ApplicationItem;
import io.github.muntashirakon.AppManager.utils.AppPref;
import io.github.muntashirakon.AppManager.utils.Tuple;
import io.github.muntashirakon.AppManager.utils.Utils;
import java.util.ArrayList;
import java.util.List;
public class MainLoader extends AsyncTaskLoader<List<ApplicationItem>> {
private List<ApplicationItem> mData;
private PackageIntentReceiver mPackageObserver;
private PackageManager mPackageManager;
public MainLoader(Context context) {
super(context);
mPackageManager = getContext().getPackageManager();
}
@SuppressLint("PackageManagerGetSignatures")
@Override
public List<ApplicationItem> loadInBackground() {
List<ApplicationItem> itemList = new ArrayList<>();
String pName;
final boolean isRootEnabled = AppPref.isRootEnabled();
if (MainActivity.packageList != null) {
String[] aList = MainActivity.packageList.split("[\\r\\n]+");
for (String s : aList) {
ApplicationItem item = new ApplicationItem();
if (s.endsWith("*")) {
item.star = true;
pName = s.substring(0, s.length() - 1);
} else pName = s;
try {
ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(pName, PackageManager.GET_META_DATA);
item.applicationInfo = applicationInfo;
item.label = applicationInfo.loadLabel(mPackageManager).toString();
item.date = mPackageManager.getPackageInfo(applicationInfo.packageName, 0).lastUpdateTime; // .firstInstallTime;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
item.sha = Utils.getIssuerAndAlg(mPackageManager.getPackageInfo(applicationInfo.packageName, PackageManager.GET_SIGNING_CERTIFICATES));
} else {
item.sha = Utils.getIssuerAndAlg(mPackageManager.getPackageInfo(applicationInfo.packageName, PackageManager.GET_SIGNATURES));
}
if (Build.VERSION.SDK_INT >= 26) {
item.size = (long) -1 * applicationInfo.targetSdkVersion;
}
if (isRootEnabled) {
try (ComponentsBlocker cb = ComponentsBlocker.getInstance(getContext(), pName, true)) {
item.blockedCount = cb.componentCount();
}
}
itemList.add(item);
} catch (PackageManager.NameNotFoundException ignored) {}
}
} else {
List<ApplicationInfo> applicationInfoList = mPackageManager.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo applicationInfo : applicationInfoList) {
ApplicationItem item = new ApplicationItem();
item.applicationInfo = applicationInfo;
item.star = ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
item.label = applicationInfo.loadLabel(mPackageManager).toString();
if (Build.VERSION.SDK_INT >= 26) {
item.size = (long) -1 * applicationInfo.targetSdkVersion;
}
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
item.sha = Utils.getIssuerAndAlg(mPackageManager.getPackageInfo(applicationInfo.packageName, PackageManager.GET_SIGNING_CERTIFICATES));
} else {
item.sha = Utils.getIssuerAndAlg(mPackageManager.getPackageInfo(applicationInfo.packageName, PackageManager.GET_SIGNATURES));
}
item.date = mPackageManager.getPackageInfo(applicationInfo.packageName, 0).lastUpdateTime; // .firstInstallTime;
} catch (PackageManager.NameNotFoundException e) {
item.date = 0L;
item.sha = new Tuple<>("?", "?");
}
if (isRootEnabled) {
try (ComponentsBlocker cb = ComponentsBlocker.getInstance(getContext(), applicationInfo.packageName, true)) {
item.blockedCount = cb.componentCount();
}
}
itemList.add(item);
}
}
return itemList;
}
/**
* Called when there is new data to deliver to the client. The
* super class will take care of delivering it; the implementation
* here just adds a little more logic.
*/
@Override
public void deliverResult(List<ApplicationItem> data) {
if (isReset()) {
// An async query came in while the loader is stopped. We
// don't need the result.
if (data != null) {
onReleaseResources(data);
}
}
List<ApplicationItem> olddata = mData;
mData = data;
if (isStarted()) {
// If the Loader is currently started, we can immediately
// deliver its results.
super.deliverResult(data);
}
// At this point we can release the resources associated with
// 'olddata' if needed; now that the new result is delivered we
// know that it is no longer in use.
if (olddata != null) {
onReleaseResources(olddata);
}
}
/**
* Handles a request to start the Loader.
*/
@Override
protected void onStartLoading() {
if (mData != null) {
// If we currently have a result available, deliver it
// immediately.
deliverResult(mData);
}
// Start watching for changes in the app data.
if (mPackageObserver == null) {
mPackageObserver = new PackageIntentReceiver(this);
}
if (takeContentChanged() || mData == null) {
// If the data has changed since the last time it was loaded
// or is not currently available, start a load.
forceLoad();
}
}
/**
* Handles a request to stop the Loader.
*/
@Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
/**
* Handles a request to cancel a load.
*/
@Override
public void onCanceled(List<ApplicationItem> data) {
super.onCanceled(data);
// At this point we can release the resources associated with 'data'
// if needed.
onReleaseResources(data);
}
/**
* Handles a request to completely reset the Loader.
*/
@Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
// At this point we can release the resources associated with 'data'
// if needed.
if (mData != null) {
onReleaseResources(mData);
mData = null;
}
// Stop monitoring for changes.
if (mPackageObserver != null) {
getContext().unregisterReceiver(mPackageObserver);
mPackageObserver = null;
}
}
/**
* Helper function to take care of releasing resources associated
* with an actively loaded data set.
*/
@SuppressWarnings("unused")
private void onReleaseResources(List<ApplicationItem> data) {
// For a simple List<> there is nothing to do. For something
// like a Cursor, we would close it here.
}
/**
* Helper class to look for interesting changes to the installed apps
* so that the loader can be updated.
*/
public static class PackageIntentReceiver extends BroadcastReceiver {
final MainLoader mLoader;
public PackageIntentReceiver(MainLoader loader) {
mLoader = loader;
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
mLoader.getContext().registerReceiver(this, filter);
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
mLoader.getContext().registerReceiver(this, sdFilter);
}
@Override
public void onReceive(Context context, Intent intent) {
mLoader.onContentChanged();
}
}
}
......@@ -45,9 +45,11 @@ public class AppDetailsActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
model = ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()).create(AppDetailsViewModel.class);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_details);
setSupportActionBar(findViewById(R.id.toolbar));
// Get model
Intent intent = getIntent();
// Check for package name
final String packageName = intent.getStringExtra(AppDetailsActivity.EXTRA_PACKAGE_NAME);
......@@ -60,21 +62,15 @@ public class AppDetailsActivity extends AppCompatActivity {
finish();
return;
}
// Get model
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;
}
else model.setPackageUri(apkUri);
if (model.getPackageInfo() == null) {
runOnUiThread(() -> {
Toast.makeText(this, getString(R.string.failed_to_fetch_package_info_possibly_a_split_apk), Toast.LENGTH_LONG).show();
finish();
});
return;
}
ApplicationInfo applicationInfo = model.getPackageInfo().applicationInfo;
runOnUiThread(() -> {
......@@ -101,21 +97,21 @@ public class AppDetailsActivity extends AppCompatActivity {
}
TabLayout tabLayout = findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(viewPager);
// Check for the existence of package
model.getIsPackageExist().observe(this, isPackageExist -> {
if (!isPackageExist) {
Toast.makeText(this, R.string.app_not_installed, Toast.LENGTH_LONG).show();
finish();
}
});
});
}).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() {
public void finish() {
model.onCleared();
super.onDestroy();
super.finish();
}
@SuppressLint("RestrictedApi")
......
......@@ -5,8 +5,6 @@ import android.app.Activity;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
......@@ -29,7 +27,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.progressindicator.ProgressIndicator;
import com.google.android.material.textview.MaterialTextView;
import java.lang.ref.WeakReference;
import java.text.Collator;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
......@@ -45,6 +42,7 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.menu.MenuBuilder;
import io.github.muntashirakon.AppManager.R;
import io.github.muntashirakon.AppManager.fragments.AppUsageDetailsDialogFragment;
import io.github.muntashirakon.AppManager.types.IconLoaderThread;
import io.github.muntashirakon.AppManager.types.ScrollSafeSwipeRefreshLayout;
import io.github.muntashirakon.AppManager.usage.AppUsageStatsManager;
import io.github.muntashirakon.AppManager.usage.Utils.IntervalType;
......@@ -56,7 +54,8 @@ import static io.github.muntashirakon.AppManager.usage.Utils.USAGE_TODAY;
import static io.github.muntashirakon.AppManager.usage.Utils.USAGE_WEEKLY;
import static io.github.muntashirakon.AppManager.usage.Utils.USAGE_YESTERDAY;
public class AppUsageActivity extends AppCompatActivity implements ListView.OnItemClickListener, ScrollSafeSwipeRefreshLayout.OnRefreshListener {
public class AppUsageActivity extends AppCompatActivity implements ListView.OnItemClickListener,
ScrollSafeSwipeRefreshLayout.OnRefreshListener {
@IntDef(value = {
SORT_BY_APP_LABEL,
SORT_BY_LAST_USED,
......@@ -329,7 +328,7 @@ public class AppUsageActivity extends AppCompatActivity implements ListView.OnIt
MaterialTextView screenTime;
MaterialTextView percentUsage;
ProgressIndicator usageIndicator;
IconAsyncTask iconLoader;
IconLoaderThread iconLoader;
}
AppUsageAdapter(@NonNull Activity activity) {
......@@ -376,7 +375,7 @@ public class AppUsageActivity extends AppCompatActivity implements ListView.OnIt
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
if(holder.iconLoader != null) holder.iconLoader.cancel(true);
if(holder.iconLoader != null) holder.iconLoader.interrupt();
}
final AppUsageStatsManager.PackageUS packageUS = mAdapterList.get(position);
final int percentUsage = (int) (packageUS.screenTime * 100f / totalScreenTime);
......@@ -385,8 +384,8 @@ public class AppUsageActivity extends AppCompatActivity implements ListView.OnIt
ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(packageUS.packageName, 0);
holder.appLabel.setText(mPackageManager.getApplicationLabel(applicationInfo));
// Set icon
holder.iconLoader = new IconAsyncTask(holder.appIcon, applicationInfo);
holder.iconLoader.execute();
holder.iconLoader = new IconLoaderThread(holder.appIcon, applicationInfo);
holder.iconLoader.start();
} catch (PackageManager.NameNotFoundException e) {
holder.appLabel.setText(packageUS.packageName);
holder.appIcon.setImageDrawable(mPackageManager.getDefaultActivityIcon());
......@@ -400,9 +399,7 @@ public class AppUsageActivity extends AppCompatActivity implements ListView.OnIt
}
String screenTimesWithTimesOpened;
// Set times opened
screenTimesWithTimesOpened = String.format(packageUS.timesOpened == 1 ?
mActivity.getString(R.string.one_time_opened)
: mActivity.getString(R.string.no_of_times_opened), packageUS.timesOpened);
screenTimesWithTimesOpened = mActivity.getResources().getQuantityString(R.plurals.no_of_times_opened, packageUS.timesOpened, packageUS.timesOpened);
// Set screen time
screenTimesWithTimesOpened += ", " + Utils.getFormattedDuration(mActivity, packageUS.screenTime);
holder.screenTime.setText(screenTimesWithTimesOpened);
......@@ -425,43 +422,5 @@ public class AppUsageActivity extends AppCompatActivity implements ListView.OnIt
holder.usageIndicator.setProgress(percentUsage);
return convertView;
}
private static class IconAsyncTask extends AsyncTask<Void, Integer, Drawable> {
private WeakReference<ImageView> imageView = null;
ApplicationInfo info;
private IconAsyncTask(ImageView pImageViewWeakReference, ApplicationInfo info) {
link(pImageViewWeakReference);
this.info = info;
}
private void link(ImageView pImageViewWeakReference) {
imageView = new WeakReference<>(pImageViewWeakReference);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
if (imageView.get() != null)
imageView.get().setVisibility(View.INVISIBLE);
}
@Override
protected Drawable doInBackground(Void... voids) {
if (!isCancelled())
return info.loadIcon(mPackageManager);
return null;
}
@Override
protected void onPostExecute(Drawable drawable) {
super.onPostExecute(drawable);
if (imageView.get() != null){
imageView.get().setImageDrawable(drawable);
imageView.get().setVisibility(View.VISIBLE);
}
}
}
}
}
......@@ -237,7 +237,7 @@ public class ClassListingActivity extends AppCompatActivity implements SearchVie
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, tracker_names.length))
.setTitle(getString(R.string.trackers_and_classes, j, tracker_names.length))
.setNegativeButton(android.R.string.ok, null)
.setMessage(statsMsg.toString()).show();
return true;
......@@ -274,14 +274,13 @@ public class ClassListingActivity extends AppCompatActivity implements SearchVie
TextView showText = new TextView(this);
int paddingSize = getResources().getDimensionPixelSize(R.dimen.padding_medium);
showText.setPadding(paddingSize, paddingSize, paddingSize, paddingSize);
showText.setText(HtmlCompat.fromHtml(String.format(getString(R.string.tested_signatures_on_classes_and_time_taken),
showText.setText(HtmlCompat.fromHtml(getString(R.string.tested_signatures_on_classes_and_time_taken,
signatures.length, totalClassesScanned, totalTimeTaken, totalIteration, foundTrackerList + foundTrackersInfo + packageInfo)
.replaceAll(" ", "&nbsp;").replaceAll("\n", "<br/>"), HtmlCompat.FROM_HTML_MODE_LEGACY));
showText.setMovementMethod(new ScrollingMovementMethod());
showText.setTextIsSelectable(true);
new MaterialAlertDialogBuilder(this, R.style.AppTheme_AlertDialog)
.setTitle(String.format(getString(R.string.trackers_and_classes),
totalTrackersFound, classList.size()))
.setTitle(getString(R.string.trackers_and_classes, totalTrackersFound, classList.size()))
.setView(showText)
.setIcon(R.drawable.ic_frost_classysharkexodus_black_24dp)
.setNegativeButton(android.R.string.ok, null)
......
......@@ -95,7 +95,7 @@ public class ManifestViewerActivity extends AppCompatActivity {
packageUri, MANIFEST_CACHE_APK);
} else archiveFilePath = packageUri.getPath();
if (archiveFilePath != null)
packageInfo = pm.getPackageArchiveInfo(archiveFilePath, 64); // PackageManager.GET_SIGNATURES (Android Bug)
packageInfo = pm.getPackageArchiveInfo(archiveFilePath, 0);
if (packageInfo != null) {
packageName = packageInfo.packageName;
final ApplicationInfo applicationInfo = packageInfo.applicationInfo;
......@@ -105,6 +105,8 @@ public class ManifestViewerActivity extends AppCompatActivity {
setTitle(applicationInfo.loadLabel(pm));
setWrapped();
});
} else { // Possibly a split apk
runOnUiThread(this::setWrapped);
}
} else {
runOnUiThread(() -> {
......@@ -223,9 +225,9 @@ public class ManifestViewerActivity extends AppCompatActivity {
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);
mCurAm = createPackageContext(packageName, CONTEXT_IGNORE_SECURITY
| CONTEXT_INCLUDE_CODE).getAssets();
mCurResources = new Resources(mCurAm, getResources().getDisplayMetrics(), null);
xml = mCurAm.openXmlResourceParser("AndroidManifest.xml");
code = Utils.getProperXml(getXMLText(xml, mCurResources).toString());
} catch (IOException | PackageManager.NameNotFoundException e) {
......
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.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.Editable;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.progressindicator.ProgressIndicator;
import com.google.android.material.textfield.TextInputEditText;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import io.github.muntashirakon.AppManager.R;
import io.github.muntashirakon.AppManager.storage.RulesStorageManager;
import io.github.muntashirakon.AppManager.storage.compontents.ExternalComponentsImporter;
import io.github.muntashirakon.AppManager.storage.compontents.TrackerComponentUtils;
import io.github.muntashirakon.AppManager.utils.AppPref;
import io.github.muntashirakon.AppManager.utils.ListItemCreator;
import io.github.muntashirakon.AppManager.utils.PackageUtils;
public class OneClickOpsActivity extends AppCompatActivity {
private ListItemCreator mItemCreator;
......@@ -24,10 +41,257 @@ public class OneClickOpsActivity extends AppCompatActivity {
}
private void setItems() {
mItemCreator.addItemWithTitle("Not yet implemented");
mItemCreator.addItemWithTitleSubtitle(getString(R.string.block_trackers),
getString(R.string.block_trackers_description))
.setOnClickListener(v -> blockTrackers());
mItemCreator.addItemWithTitleSubtitle(getString(R.string.block_components_dots),
getString(R.string.block_components_description))
.setOnClickListener(v -> blockComponents());
mItemCreator.addItemWithTitleSubtitle(getString(R.string.deny_app_ops_dots),
getString(R.string.deny_app_ops_description))
.setOnClickListener(v -> blockAppOps());
mItemCreator.addItemWithTitleSubtitle(getString(R.string.clear_data_from_uninstalled_apps),
getString(R.string.clear_data_from_uninstalled_apps_description))
.setOnClickListener(v -> clearData());
mItemCreator.addItemWithTitleSubtitle(getString(R.string.clear_app_cache),
getString(R.string.clear_app_cache_description))
.setOnClickListener(v -> clearAppCache());
mProgressIndicator.hide();
}
private void blockTrackers() {
if (!AppPref.isRootEnabled()) {
Toast.makeText(this, R.string.only_works_in_root_mode, Toast.LENGTH_SHORT).show();
return;
}
mProgressIndicator.show();
new Thread(() -> {
HashMap<ApplicationInfo, Integer> trackerCount = new HashMap<>();
HashMap<String, RulesStorageManager.Type> trackersPerPackage;
for (ApplicationInfo applicationInfo:
getPackageManager().getInstalledApplications(PackageManager.GET_META_DATA)) {
trackersPerPackage = TrackerComponentUtils.getTrackerComponentsForPackage(applicationInfo.packageName);
if (!trackersPerPackage.isEmpty()) {
trackerCount.put(applicationInfo, trackersPerPackage.size());
}
}
if (!trackerCount.isEmpty()) {
List<String> selectedPackages = new ArrayList<>();
List<ApplicationInfo> applicationInfoList = new ArrayList<>(trackerCount.keySet());
for (ApplicationInfo info: applicationInfoList) selectedPackages.add(info.packageName);
String[] trackerPackages = selectedPackages.toArray(new String[0]);
String[] trackerPackagesWithTrackerCount = new String[trackerCount.size()];
for (int i = 0; i<trackerCount.size(); ++i) trackerPackagesWithTrackerCount[i] = "(" + trackerCount.get(applicationInfoList.get(i)) + ") " + getPackageManager().getApplicationLabel(applicationInfoList.get(i));
boolean[] checkedItems = new boolean[selectedPackages.size()];
Arrays.fill(checkedItems, true);
runOnUiThread(() -> {
mProgressIndicator.hide();
new MaterialAlertDialogBuilder(this, R.style.AppTheme_AlertDialog)
.setMultiChoiceItems(trackerPackagesWithTrackerCount, checkedItems, (dialog, which, isChecked) -> {
if (!isChecked) selectedPackages.remove(trackerPackages[which]);
else selectedPackages.add(trackerPackages[which]);
})
.setTitle(R.string.found_trackers)
.setPositiveButton(R.string.apply, (dialog, which) -> {
mProgressIndicator.show();
new Thread(() -> {
List<String> failedPackages = ExternalComponentsImporter.applyFromTrackingComponents(this, selectedPackages);
if (!failedPackages.isEmpty()) {
runOnUiThread(() -> {
new MaterialAlertDialogBuilder(this, R.style.AppTheme_AlertDialog)
.setTitle(R.string.failed_packages)
.setItems((CharSequence[]) failedPackages.toArray(), null)
.setNegativeButton(android.R.string.ok, null)
.show();
mProgressIndicator.hide();
});
} else runOnUiThread(() -> {
Toast.makeText(this, R.string.the_operation_was_successful, Toast.LENGTH_SHORT).show();
mProgressIndicator.hide();
});
}).start();
})
.setNegativeButton(android.R.string.cancel, (dialog, which) -> mProgressIndicator.hide())
.show();
});
} else {
runOnUiThread(() -> {
Toast.makeText(this, R.string.no_tracker_found, Toast.LENGTH_SHORT).show();
mProgressIndicator.hide();
});
}
}).start();
}
private void blockComponents() {
if (!AppPref.isRootEnabled()) {
Toast.makeText(this, R.string.only_works_in_root_mode, Toast.LENGTH_SHORT).show();
return;
}
View view = getLayoutInflater().inflate(R.layout.dialog_input_signatures, null);
new MaterialAlertDialogBuilder(this, R.style.AppTheme_AlertDialog)
.setTitle(R.string.block_components_dots)
.setView(view)
.setPositiveButton(R.string.search, (dialog, which) -> {
final Editable signaturesEditable = ((TextInputEditText) view.findViewById(R.id.input_signatures)).getText();
if (signaturesEditable == null) return;
mProgressIndicator.show();
new Thread(() -> {
String[] signatures = signaturesEditable.toString().split("\\s+");
if (signatures.length == 0) return;
HashMap<ApplicationInfo, Integer> componentsCount = new HashMap<>();
HashMap<String, RulesStorageManager.Type> componentsPerPackage;
for (ApplicationInfo applicationInfo:
getPackageManager().getInstalledApplications(PackageManager.GET_META_DATA)) {
componentsPerPackage = PackageUtils.getFilteredComponents(applicationInfo.packageName, signatures);
if (!componentsPerPackage.isEmpty())
componentsCount.put(applicationInfo, componentsPerPackage.size());
}
if (!componentsCount.isEmpty()) {
List<String> selectedPackages = new ArrayList<>();
List<ApplicationInfo> applicationInfoList = new ArrayList<>(componentsCount.keySet());
for (ApplicationInfo info: applicationInfoList) selectedPackages.add(info.packageName);
String[] filteredPackages = selectedPackages.toArray(new String[0]);
String[] filteredPackagesWithComponentCount = new String[componentsCount.size()];
for (int i = 0; i<componentsCount.size(); ++i) filteredPackagesWithComponentCount[i] = "(" + componentsCount.get(applicationInfoList.get(i)) + ") " + getPackageManager().getApplicationLabel(applicationInfoList.get(i));
boolean[] checkedItems = new boolean[selectedPackages.size()];
Arrays.fill(checkedItems, true);
runOnUiThread(() -> {
mProgressIndicator.hide();
new MaterialAlertDialogBuilder(this, R.style.AppTheme_AlertDialog)
.setMultiChoiceItems(filteredPackagesWithComponentCount, checkedItems, (dialog1, which1, isChecked) -> {
if (!isChecked) selectedPackages.remove(filteredPackages[which1]);
else selectedPackages.add(filteredPackages[which1]);
})
.setTitle(R.string.filtered_packages)
.setPositiveButton(R.string.apply, (dialog1, which1) -> {
mProgressIndicator.show();
new Thread(() -> {
List<String> failedPackages = ExternalComponentsImporter.applyFilteredComponents(this, selectedPackages, signatures);
if (!failedPackages.isEmpty()) {
runOnUiThread(() -> {
new MaterialAlertDialogBuilder(this, R.style.AppTheme_AlertDialog)
.setTitle(R.string.failed_packages)
.setItems((CharSequence[]) failedPackages.toArray(), null)
.setNegativeButton(android.R.string.ok, null)
.show();
mProgressIndicator.hide();
});
} else runOnUiThread(() -> {
Toast.makeText(this, R.string.the_operation_was_successful, Toast.LENGTH_SHORT).show();
mProgressIndicator.hide();
});
}).start();
})
.setNegativeButton(android.R.string.cancel, (dialog1, which1) -> mProgressIndicator.hide())
.show();
});
} else {
runOnUiThread(() -> {
Toast.makeText(this, R.string.no_tracker_found, Toast.LENGTH_SHORT).show();
mProgressIndicator.hide();
});
}
}).start();
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
private void blockAppOps() {
if (!AppPref.isRootEnabled() && !AppPref.isAdbEnabled()) {
Toast.makeText(this, R.string.only_works_in_root_or_adb_mode, Toast.LENGTH_SHORT).show();
return;
}
View view = getLayoutInflater().inflate(R.layout.dialog_input_app_ops, null);
new MaterialAlertDialogBuilder(this, R.style.AppTheme_AlertDialog)
.setTitle(R.string.deny_app_ops_dots)
.setView(view)
.setPositiveButton(R.string.search, (dialog, which) -> {
final Editable appOpsEditable = ((TextInputEditText) view.findViewById(R.id.input_app_ops)).getText();
if (appOpsEditable == null) return;
mProgressIndicator.show();
new Thread(() -> {
String[] appOpsStr = appOpsEditable.toString().split("\\s+");
if (appOpsStr.length == 0) return;
int[] appOps = new int[appOpsStr.length];
try {
for (int i = 0; i < appOpsStr.length; ++i)
appOps[i] = Integer.parseInt(appOpsStr[i]);
} catch (Exception e) {
runOnUiThread(() -> {
Toast.makeText(this, R.string.failed_to_parse_some_numbers, Toast.LENGTH_SHORT).show();
mProgressIndicator.hide();
});
return;
}
HashMap<ApplicationInfo, Integer> appOpsCount = new HashMap<>();
for (ApplicationInfo applicationInfo:
getPackageManager().getInstalledApplications(PackageManager.GET_META_DATA)) {
int size = PackageUtils.getFilteredAppOps(applicationInfo.packageName, appOps).size();
if (size > 0) appOpsCount.put(applicationInfo, size);
}
if (!appOpsCount.isEmpty()) {
List<String> selectedPackages = new ArrayList<>();
List<ApplicationInfo> applicationInfoList = new ArrayList<>(appOpsCount.keySet());
for (ApplicationInfo info: applicationInfoList) selectedPackages.add(info.packageName);
String[] filteredPackages = selectedPackages.toArray(new String[0]);
String[] filteredPackagesWithAppOpCount = new String[appOpsCount.size()];
for (int i = 0; i<appOpsCount.size(); ++i) filteredPackagesWithAppOpCount[i] = "(" + appOpsCount.get(applicationInfoList.get(i)) + ") " + getPackageManager().getApplicationLabel(applicationInfoList.get(i));
boolean[] checkedItems = new boolean[selectedPackages.size()];
Arrays.fill(checkedItems, true);
runOnUiThread(() -> {
mProgressIndicator.hide();
new MaterialAlertDialogBuilder(this, R.style.AppTheme_AlertDialog)
.setMultiChoiceItems(filteredPackagesWithAppOpCount, checkedItems, (dialog1, which1, isChecked) -> {
if (!isChecked) selectedPackages.remove(filteredPackages[which1]);
else selectedPackages.add(filteredPackages[which1]);
})
.setTitle(R.string.filtered_packages)
.setPositiveButton(R.string.apply, (dialog1, which1) -> {
mProgressIndicator.show();
new Thread(() -> {
List<String> failedPackages = ExternalComponentsImporter.applyFilteredAppOps(this, selectedPackages, appOps);
if (!failedPackages.isEmpty()) {
runOnUiThread(() -> {
new MaterialAlertDialogBuilder(this, R.style.AppTheme_AlertDialog)
.setTitle(R.string.failed_packages)
.setItems((CharSequence[]) failedPackages.toArray(), null)
.setNegativeButton(android.R.string.ok, null)
.show();
mProgressIndicator.hide();
});
} else runOnUiThread(() -> {
Toast.makeText(this, R.string.the_operation_was_successful, Toast.LENGTH_SHORT).show();
mProgressIndicator.hide();
});
}).start();
})
.setNegativeButton(android.R.string.cancel, (dialog1, which1) -> mProgressIndicator.hide())
.show();
});
} else {
runOnUiThread(() -> {
Toast.makeText(this, R.string.no_tracker_found, Toast.LENGTH_SHORT).show();
mProgressIndicator.hide();
});
}
}).start();
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
private void clearData() {
// TODO
Toast.makeText(this, "Not implemented yet.", Toast.LENGTH_SHORT).show();
}
private void clearAppCache() {
// TODO
Toast.makeText(this, "Not implemented yet.", Toast.LENGTH_SHORT).show();
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
......
......@@ -4,8 +4,6 @@ import android.annotation.SuppressLint;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.format.Formatter;
import android.view.Gravity;
......@@ -25,7 +23,6 @@ import android.widget.Toast;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.progressindicator.ProgressIndicator;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
......@@ -43,8 +40,9 @@ import androidx.core.content.ContextCompat;
import io.github.muntashirakon.AppManager.R;
import io.github.muntashirakon.AppManager.appops.AppOpsManager;
import io.github.muntashirakon.AppManager.appops.AppOpsService;
import io.github.muntashirakon.AppManager.storage.compontents.ComponentsBlocker;
import io.github.muntashirakon.AppManager.runner.Runner;
import io.github.muntashirakon.AppManager.storage.compontents.ComponentsBlocker;
import io.github.muntashirakon.AppManager.types.IconLoaderThread;
import io.github.muntashirakon.AppManager.utils.AppPref;
import io.github.muntashirakon.AppManager.utils.Utils;
......@@ -245,7 +243,7 @@ public class RunningAppsActivity extends AppCompatActivity implements SearchView
MaterialButton killBtn;
MaterialButton forceStopBtn;
MaterialButton disableBackgroundRunBtn;
IconAsyncTask iconLoader;
IconLoaderThread iconLoader;
}
@Override
......@@ -266,14 +264,14 @@ public class RunningAppsActivity extends AppCompatActivity implements SearchView
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
if (holder.iconLoader != null) holder.iconLoader.cancel(true);
if (holder.iconLoader != null) holder.iconLoader.interrupt();
}
ProcessItem processItem = mAdapterList.get(position);
ApplicationInfo applicationInfo = processItem.applicationInfo;
String processName = processItem.name;
// Load icon
holder.iconLoader = new IconAsyncTask(holder.icon, applicationInfo);
holder.iconLoader.execute();
holder.iconLoader = new IconLoaderThread(holder.icon, applicationInfo);
holder.iconLoader.start();
// Set process name
if (mConstraint != null && processName.toLowerCase(Locale.ROOT).contains(mConstraint)) {
// Highlight searched query
......@@ -287,11 +285,11 @@ public class RunningAppsActivity extends AppCompatActivity implements SearchView
holder.packageName.setText(applicationInfo.packageName);