...
 
Commits (45)
......@@ -53,4 +53,6 @@ dependencies {
implementation 'com.jaredrummler:android-shell:1.0.0'
implementation 'com.tananaev:adblib:1.2'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.4'
}
/*
* 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.reflector;
import java.util.LinkedList;
import java.util.List;
public class ClassesNamesList {
private List<String> list;
public ClassesNamesList() {
list = new LinkedList<>();
}
public void add(String className) {
list.add(className);
}
public int size() { return this.list.size(); }
public List<String> getClassNames() {
return this.list;
}
public String getClassName(int position) {
return this.list.get(position);
}
}
......@@ -89,16 +89,15 @@ public class Reflector {
if (x.lastIndexOf(".") != -1) {
y = x.substring(0, x.lastIndexOf("."));
words.add(new TaggedWord("\npackage ", TAG.MODIFIER));
words.add(new TaggedWord("package ", TAG.MODIFIER));
words.add(new TaggedWord(y, TAG.IDENTIFIER));
words.add(new TaggedWord(";", TAG.MODIFIER));
words.add(new TaggedWord(";\n", TAG.MODIFIER));
}
try {
fields = currentClass.getDeclaredFields(); // NoClassDefFoundError ccc71/at/xposed/blocks/at_block_manage_accounts$5
constructors = currentClass.getDeclaredConstructors();
methods = currentClass.getDeclaredMethods();
} catch (NoClassDefFoundError e){
e.printStackTrace();
return e.toString();
......@@ -181,14 +180,14 @@ public class Reflector {
words.add(new TaggedWord("\n{", TAG.IDENTIFIER));
words.add(new TaggedWord("\n" +
"/*\n" +
" * Field Definitions.\n" +
" */", TAG.DOCUMENT));
" /*\n" +
" * Field Definitions.\n" +
" */", TAG.DOCUMENT));
for (Field field : fields) {
int md = field.getModifiers();
words.add(new TaggedWord("\n " + Modifier.toString(md) + " ", TAG.MODIFIER));
words.add(new TaggedWord("\n " + Modifier.toString(md) + " ", TAG.MODIFIER));
words.add(new TaggedWord(ClassTypeAlgorithm.TypeName(field.getType().getName(), null) + " ",
TAG.IDENTIFIER));
words.add(new TaggedWord(field.getName() + ";", TAG.DOCUMENT));
......@@ -198,9 +197,9 @@ public class Reflector {
// http://stackoverflow.com/questions/140537/how-to-use-java-reflection-when-the-enum-type-is-a-class
words.add(new TaggedWord("\n" +
"/*\n" +
" * Declared Constructors.\n" +
" */\n", TAG.DOCUMENT));
" /*\n" +
" * Declared Constructors.\n" +
" */\n", TAG.DOCUMENT));
x = ClassTypeAlgorithm.TypeName(currentClass.getName(), null);
for (Constructor constructor : constructors) {
int md = constructor.getModifiers();
......
......@@ -21,11 +21,7 @@ import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
......@@ -47,21 +43,6 @@ import androidx.annotation.Nullable;
public class IOUtils {
public static void bytesToFile(byte[] bytes, File result) throws IOException {
BufferedOutputStream bos =
new BufferedOutputStream(new FileOutputStream(result));
bos.write(bytes);
bos.flush();
bos.close();
}
@NonNull
public static byte[] toByteArray(InputStream input) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
copy(input, output);
return output.toByteArray();
}
public static int copy(InputStream input, OutputStream output) throws IOException {
long count = copyLarge(input, output);
if (count > Integer.MAX_VALUE) {
......
......@@ -11,7 +11,7 @@ import android.os.Build;
import androidx.loader.content.AsyncTaskLoader;
import io.github.muntashirakon.AppManager.activities.MainActivity;
import io.github.muntashirakon.AppManager.compontents.ComponentsBlocker;
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;
......
package io.github.muntashirakon.AppManager;
public class StaticDataset {
private static String[] trackerCodeSignatures;
private static String[] trackerNames;
public static String[] getTrackerCodeSignatures() {
if (trackerCodeSignatures == null) {
trackerCodeSignatures = AppManager.getContext().getResources().getStringArray(R.array.tracker_signatures);
}
return trackerCodeSignatures;
}
public static String[] getTrackerNames() {
if (trackerNames == null) {
trackerNames = AppManager.getContext().getResources().getStringArray(R.array.tracker_names);
}
return trackerNames;
}
}
......@@ -8,46 +8,43 @@ import android.os.Bundle;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Toast;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
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.lifecycle.Lifecycle;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
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.utils.Utils;
import io.github.muntashirakon.AppManager.viewmodels.AppDetailsViewModel;
public class AppDetailsActivity extends AppCompatActivity {
public static final String EXTRA_PACKAGE_NAME = "pkg";
public static String sConstraint;
public AppDetailsViewModel model;
public SearchView searchView;
private String mPackageName;
private TypedArray mTabTitleIds;
AppDetailsFragmentStateAdapter appDetailsFragmentStateAdapter;
ViewPager2 viewPager2;
AppDetailsFragment[] fragments;
private int pageNo;
private boolean fragmentLoaded = false;
private AppDetailsFragment[] fragments;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_details);
setSupportActionBar(findViewById(R.id.toolbar));
sConstraint = null;
mPackageName = getIntent().getStringExtra(AppInfoActivity.EXTRA_PACKAGE_NAME);
if (mPackageName == null) {
Toast.makeText(this, getString(R.string.empty_package_name), Toast.LENGTH_LONG).show();
......@@ -61,16 +58,18 @@ public class AppDetailsActivity extends AppCompatActivity {
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();
appDetailsFragmentStateAdapter = new AppDetailsFragmentStateAdapter(fragmentManager, getLifecycle());
viewPager2 = findViewById(R.id.pager);
viewPager2.setAdapter(appDetailsFragmentStateAdapter);
ViewPager viewPager = findViewById(R.id.pager);
viewPager.setAdapter(new AppDetailsFragmentPagerAdapter(fragmentManager));
fragments = new AppDetailsFragment[mTabTitleIds.length()];
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
final SearchView searchView = new SearchView(actionBar.getThemedContext());
searchView = new SearchView(actionBar.getThemedContext());
actionBar.setDisplayShowCustomEnabled(true);
searchView.setQueryHint(getString(R.string.search));
......@@ -83,78 +82,9 @@ public class AppDetailsActivity extends AppCompatActivity {
ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.END;
actionBar.setCustomView(searchView, layoutParams);
viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
/**
* Page selected is called in two ways: 1) Called before fragment when a tab is
* pressed directly, 2) Called after the fragment when dragging is used. In the
* first way, we cannot call reset() on the fragments adapter since it's not loaded,
* but we can do that for the second case.
* @param position Position of the view
*/
@Override
public void onPageSelected(@AppDetailsFragment.Property int position) {
super.onPageSelected(position);
pageNo = position;
if (fragments[position] != null) { // Fragment is created (the second case)
fragmentLoaded = true;
fixSearch();
} else fragmentLoaded = false;
}
/**
* Our interest is {@link ViewPager2#SCROLL_STATE_IDLE}. It's called in two ways in
* respect to {@link ViewPager2.OnPageChangeCallback#onPageSelected(int)}. We need
* to check whether the fragment adapter was refreshed in the function above. If it
* doesn't we'll refresh here.
* @param state Current scroll state
*/
@Override
public void onPageScrollStateChanged(int state) {
super.onPageScrollStateChanged(state);
switch (state) {
case ViewPager2.SCROLL_STATE_DRAGGING:
break;
case ViewPager2.SCROLL_STATE_IDLE:
if (!fragmentLoaded && fragments[pageNo] != null) {
fragmentLoaded = true;
fixSearch();
}
break;
case ViewPager2.SCROLL_STATE_SETTLING:
break;
}
}
private void fixSearch() {
switch (pageNo) {
case AppDetailsFragment.ACTIVITIES:
case AppDetailsFragment.SERVICES:
case AppDetailsFragment.RECEIVERS:
case AppDetailsFragment.PROVIDERS:
case AppDetailsFragment.APP_OPS:
case AppDetailsFragment.USES_PERMISSIONS:
case AppDetailsFragment.PERMISSIONS:
searchView.setVisibility(View.VISIBLE);
if (fragments[pageNo] != null) {
searchView.setOnQueryTextListener(fragments[pageNo]);
fragments[pageNo].resetFilter();
}
break;
case AppDetailsFragment.FEATURES:
case AppDetailsFragment.CONFIGURATION:
case AppDetailsFragment.SIGNATURES:
case AppDetailsFragment.SHARED_LIBRARY_FILES:
case AppDetailsFragment.NONE:
default:
searchView.setVisibility(View.GONE);
}
}
});
}
TabLayout tabLayout = findViewById(R.id.tab_layout);
new TabLayoutMediator(tabLayout, viewPager2, true,
(tab, position) -> tab.setText(mTabTitleIds.getText(position))).attach();
tabLayout.setupWithViewPager(viewPager);
}
@SuppressLint("RestrictedApi")
......@@ -182,21 +112,27 @@ public class AppDetailsActivity extends AppCompatActivity {
}
// For tab layout
private class AppDetailsFragmentStateAdapter extends FragmentStateAdapter {
AppDetailsFragmentStateAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
super(fragmentManager, lifecycle);
private class AppDetailsFragmentPagerAdapter extends FragmentPagerAdapter {
AppDetailsFragmentPagerAdapter(@NonNull FragmentManager fragmentManager) {
super(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
@NonNull
@Override
public Fragment createFragment(@AppDetailsFragment.Property int position) {
if (position == pageNo) fragmentLoaded = false;
return (fragments[position] = new AppDetailsFragment(position));
public AppDetailsFragment getItem(int position) {
if (fragments[position] == null) fragments[position] = new AppDetailsFragment(position);
return fragments[position];
}
@Override
public int getItemCount() {
public int getCount() {
return mTabTitleIds.length();
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return mTabTitleIds.getText(position);
}
}
}
......@@ -57,20 +57,21 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.menu.MenuBuilder;
import androidx.core.app.ShareCompat;
import androidx.core.content.FileProvider;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import io.github.muntashirakon.AppManager.BuildConfig;
import io.github.muntashirakon.AppManager.R;
import io.github.muntashirakon.AppManager.fragments.RulesTypeSelectionDialogFragment;
import io.github.muntashirakon.AppManager.runner.Runner;
import io.github.muntashirakon.AppManager.types.ScrollSafeSwipeRefreshLayout;
import io.github.muntashirakon.AppManager.usage.AppUsageStatsManager;
import io.github.muntashirakon.AppManager.utils.AppPref;
import io.github.muntashirakon.AppManager.utils.ListItemCreator;
import io.github.muntashirakon.AppManager.utils.RunnerUtils;
import io.github.muntashirakon.AppManager.utils.Tuple;
import io.github.muntashirakon.AppManager.utils.Utils;
import static io.github.muntashirakon.AppManager.utils.IOUtils.deleteDir;
public class AppInfoActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {
public class AppInfoActivity extends AppCompatActivity implements ScrollSafeSwipeRefreshLayout.OnRefreshListener {
public static final String EXTRA_PACKAGE_NAME = "pkg";
private static final String UID_STATS_PATH = "/proc/uid_stat/";
......@@ -100,7 +101,7 @@ public class AppInfoActivity extends AppCompatActivity implements SwipeRefreshLa
@SuppressLint("SimpleDateFormat")
private SimpleDateFormat mDateFormatter = new SimpleDateFormat("EE LLL dd yyyy kk:mm:ss");
private ListItemCreator mList;
private SwipeRefreshLayout mSwipeRefresh;
private ScrollSafeSwipeRefreshLayout mSwipeRefresh;
private int mAccentColor;
private CharSequence mPackageLabel;
private ProgressIndicator mProgressIndicator;
......@@ -278,12 +279,18 @@ public class AppInfoActivity extends AppCompatActivity implements SwipeRefreshLa
if ((mApplicationInfo.flags & ApplicationInfo.FLAG_LARGE_HEAP) != 0)
addChip(R.string.requested_large_heap, R.color.red);
if ((mApplicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0)
addChip(R.string.stopped, R.color.blue_green);
if (!mApplicationInfo.enabled) addChip(R.string.disabled_app, R.color.disabled_app);
addChip(R.string.stopped, R.color.stopped);
if (!mApplicationInfo.enabled) addChip(R.string.disabled_app, R.color.disabled_user);
}
private void setHorizontalView() {
mHorizontalLayout.removeAllViews();
// Set open
final Intent launchIntentForPackage = mPackageManager.getLaunchIntentForPackage(mPackageName);
if (launchIntentForPackage != null) {
addToHorizontalLayout(R.string.launch_app, R.drawable.ic_open_in_new_black_24dp)
.setOnClickListener(v -> startActivity(launchIntentForPackage));
}
// Set uninstall
addToHorizontalLayout(R.string.uninstall, R.drawable.ic_delete_black_24dp).setOnClickListener(v -> {
final boolean isSystemApp = (mApplicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
......@@ -294,7 +301,7 @@ public class AppInfoActivity extends AppCompatActivity implements SwipeRefreshLa
R.string.uninstall_system_app_message : R.string.uninstall_app_message)
.setPositiveButton(R.string.uninstall, (dialog, which) -> new Thread(() -> {
// Try without root first then with root
if (Runner.run(this, String.format("pm uninstall --user 0 %s", mPackageName)).isSuccessful()) {
if (RunnerUtils.uninstallPackage(mPackageName).isSuccessful()) {
runOnUiThread(() -> {
Toast.makeText(mActivity, String.format(getString(R.string.uninstalled_successfully), mPackageLabel), Toast.LENGTH_LONG).show();
finish();
......@@ -318,7 +325,7 @@ public class AppInfoActivity extends AppCompatActivity implements SwipeRefreshLa
if (mApplicationInfo.enabled) {
// Disable app
addToHorizontalLayout(R.string.disable, R.drawable.ic_block_black_24dp).setOnClickListener(v -> new Thread(() -> {
if (Runner.run(this, String.format("pm disable %s", mPackageName)).isSuccessful()) {
if (RunnerUtils.disablePackage(mPackageName).isSuccessful()) {
// Refresh
runOnUiThread(this::getPackageInfoOrFinish);
} else {
......@@ -328,7 +335,7 @@ public class AppInfoActivity extends AppCompatActivity implements SwipeRefreshLa
} else {
// Enable app
addToHorizontalLayout(R.string.enable, R.drawable.ic_baseline_get_app_24).setOnClickListener(v -> new Thread(() -> {
if (Runner.run(this, String.format("pm enable %s", mPackageName)).isSuccessful()) {
if (RunnerUtils.enablePackage(mPackageName).isSuccessful()) {
// Refresh
runOnUiThread(this::getPackageInfoOrFinish);
} else {
......@@ -339,7 +346,7 @@ public class AppInfoActivity extends AppCompatActivity implements SwipeRefreshLa
// Force stop
if ((mApplicationInfo.flags & ApplicationInfo.FLAG_STOPPED) == 0) {
addToHorizontalLayout(R.string.force_stop, R.drawable.ic_baseline_power_settings_new_24).setOnClickListener(v -> new Thread(() -> {
if (Runner.run(this, String.format("am force-stop %s", mPackageName)).isSuccessful()) {
if (RunnerUtils.forceStopPackage(mPackageName).isSuccessful()) {
// Refresh
runOnUiThread(this::getPackageInfoOrFinish);
} else {
......@@ -591,13 +598,13 @@ public class AppInfoActivity extends AppCompatActivity implements SwipeRefreshLa
private List<String> getSharedPrefs(@NonNull String sourceDir) {
File sharedPath = new File(sourceDir + "/shared_prefs");
return Runner.run(this, String.format("ls %s/*.xml", sharedPath.getAbsolutePath())).getOutputAsList();
return Runner.runCommand(String.format("ls %s/*.xml", sharedPath.getAbsolutePath())).getOutputAsList();
}
private List<String> getDatabases(@NonNull String sourceDir) {
File sharedPath = new File(sourceDir + "/databases");
// FIXME: SQLite db doesn't necessarily have .db extension
return Runner.run(this, String.format("ls %s/*.db", sharedPath.getAbsolutePath())).getOutputAsList();
return Runner.runCommand(String.format("ls %s/*.db", sharedPath.getAbsolutePath())).getOutputAsList();
}
private void openAsFolderInFM(String dir) {
......@@ -665,7 +672,9 @@ public class AppInfoActivity extends AppCompatActivity implements SwipeRefreshLa
.setMessage(R.string.grant_usage_acess_message)
.setPositiveButton(R.string.go, (dialog, which) -> startActivityForResult(new Intent(
Settings.ACTION_USAGE_ACCESS_SETTINGS), 0))
.setNegativeButton(getString(android.R.string.cancel), null)
.setNegativeButton(android.R.string.cancel, null)
.setNeutralButton(R.string.never_ask, (dialog, which) ->
AppPref.getInstance().setPref(AppPref.PREF_USAGE_ACCESS_ENABLED, false))
.setCancelable(false)
.show();
return;
......@@ -694,7 +703,7 @@ public class AppInfoActivity extends AppCompatActivity implements SwipeRefreshLa
if (AppPref.isRootEnabled() || AppPref.isAdbEnabled()) {
mList.setOpen(v -> new Thread(() -> {
// Clear data
if (Runner.run(this, String.format("pm clear %s", mPackageName)).isSuccessful()) {
if (RunnerUtils.clearPackageData(mPackageName).isSuccessful()) {
runOnUiThread(this::getPackageInfoOrFinish);
}
}).start());
......@@ -718,7 +727,7 @@ public class AppInfoActivity extends AppCompatActivity implements SwipeRefreshLa
String extCache = cacheDir.getAbsolutePath().replace(getPackageName(), mPackageName);
command.append(" ").append(extCache);
}
if (Runner.run(this, command.toString()).isSuccessful()) {
if (Runner.runCommand(command.toString()).isSuccessful()) {
runOnUiThread(this::getPackageInfoOrFinish);
}
}).start());
......
......@@ -43,9 +43,9 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.menu.MenuBuilder;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import io.github.muntashirakon.AppManager.R;
import io.github.muntashirakon.AppManager.fragments.AppUsageDetailsDialogFragment;
import io.github.muntashirakon.AppManager.types.ScrollSafeSwipeRefreshLayout;
import io.github.muntashirakon.AppManager.usage.AppUsageStatsManager;
import io.github.muntashirakon.AppManager.usage.Utils.IntervalType;
import io.github.muntashirakon.AppManager.utils.Tuple;
......@@ -56,14 +56,15 @@ 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, SwipeRefreshLayout.OnRefreshListener {
public class AppUsageActivity extends AppCompatActivity implements ListView.OnItemClickListener, ScrollSafeSwipeRefreshLayout.OnRefreshListener {
@IntDef(value = {
SORT_BY_APP_LABEL,
SORT_BY_LAST_USED,
SORT_BY_MOBILE_DATA,
SORT_BY_PACKAGE_NAME,
SORT_BY_SCREEN_TIME,
SORT_BY_TIMES_OPENED
SORT_BY_TIMES_OPENED,
SORT_BY_WIFI_DATA
})
private @interface SortOrder {}
private static final int SORT_BY_APP_LABEL = 0;
......@@ -72,17 +73,20 @@ public class AppUsageActivity extends AppCompatActivity implements ListView.OnIt
private static final int SORT_BY_PACKAGE_NAME = 3;
private static final int SORT_BY_SCREEN_TIME = 4;
private static final int SORT_BY_TIMES_OPENED = 5;
private static final int SORT_BY_WIFI_DATA = 6;
private static final int[] sSortMenuItemIdsMap = {
R.id.action_sort_by_app_label, R.id.action_sort_by_last_used,
R.id.action_sort_by_mobile_data, R.id.action_sort_by_package_name,
R.id.action_sort_by_screen_time, R.id.action_sort_by_times_opened};
R.id.action_sort_by_screen_time, R.id.action_sort_by_times_opened,
R.id.action_sort_by_wifi_data};
private ProgressIndicator mProgressIndicator;
private SwipeRefreshLayout mSwipeRefresh;
private ScrollSafeSwipeRefreshLayout mSwipeRefresh;
private AppUsageAdapter mAppUsageAdapter;
List<AppUsageStatsManager.PackageUS> mPackageUSList;
private static long totalScreenTime;
private static PackageManager mPackageManager;
private @IntervalType int current_interval = USAGE_TODAY;
private @SortOrder int mSortBy;
......@@ -107,6 +111,8 @@ public class AppUsageActivity extends AppCompatActivity implements ListView.OnIt
listView.setAdapter(mAppUsageAdapter);
listView.setOnItemClickListener(this);
mPackageManager = getPackageManager();
mSwipeRefresh = findViewById(R.id.swipe_refresh);
mSwipeRefresh.setColorSchemeColors(Utils.getThemeColor(this, android.R.attr.colorAccent));
mSwipeRefresh.setProgressBackgroundColorSchemeColor(Utils.getThemeColor(this, android.R.attr.colorPrimary));
......@@ -143,6 +149,12 @@ public class AppUsageActivity extends AppCompatActivity implements ListView.OnIt
else getAppUsage();
}
@Override
protected void onDestroy() {
mPackageManager = null;
super.onDestroy();
}
@Override
public void onRefresh() {
mSwipeRefresh.setRefreshing(false);
......@@ -197,6 +209,10 @@ public class AppUsageActivity extends AppCompatActivity implements ListView.OnIt
setSortBy(SORT_BY_TIMES_OPENED);
item.setChecked(true);
return true;
case R.id.action_sort_by_wifi_data:
setSortBy(SORT_BY_WIFI_DATA);
item.setChecked(true);
return true;
}
return super.onOptionsItemSelected(item);
}
......@@ -229,15 +245,19 @@ public class AppUsageActivity extends AppCompatActivity implements ListView.OnIt
case SORT_BY_LAST_USED:
return -o1.lastUsageTime.compareTo(o2.lastUsageTime);
case SORT_BY_MOBILE_DATA:
Long o1Data = o1.mobileData.getFirst() + o1.mobileData.getSecond();
Long o2Data = o2.mobileData.getFirst() + o2.mobileData.getSecond();
return -o1Data.compareTo(o2Data);
Long o1MData = o1.mobileData.getFirst() + o1.mobileData.getSecond();
Long o2MData = o2.mobileData.getFirst() + o2.mobileData.getSecond();
return -o1MData.compareTo(o2MData);
case SORT_BY_PACKAGE_NAME:
return o1.packageName.compareTo(o2.packageName);
return o1.packageName.compareToIgnoreCase(o2.packageName);
case SORT_BY_SCREEN_TIME:
return -o1.screenTime.compareTo(o2.screenTime);
case SORT_BY_TIMES_OPENED:
return -o1.timesOpened.compareTo(o2.timesOpened);
case SORT_BY_WIFI_DATA:
Long o1WData = o1.wifiData.getFirst() + o1.wifiData.getSecond();
Long o2WData = o2.wifiData.getFirst() + o2.wifiData.getSecond();
return -o1WData.compareTo(o2WData);
}
return 0;
}));
......@@ -297,7 +317,6 @@ public class AppUsageActivity extends AppCompatActivity implements ListView.OnIt
private LayoutInflater mLayoutInflater;
private List<AppUsageStatsManager.PackageUS> mAdapterList;
private static PackageManager mPackageManager;
private Activity mActivity;
static class ViewHolder {
......@@ -315,7 +334,6 @@ public class AppUsageActivity extends AppCompatActivity implements ListView.OnIt
AppUsageAdapter(@NonNull Activity activity) {
mLayoutInflater = activity.getLayoutInflater();
mPackageManager = activity.getPackageManager();
mActivity = activity;
}
......
......@@ -28,9 +28,7 @@ import android.widget.Toast;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.progressindicator.ProgressIndicator;
import com.google.classysharkandroid.dex.DexLoaderBuilder;
import com.google.classysharkandroid.reflector.ClassesNamesList;
import com.google.classysharkandroid.reflector.Reflector;
import com.google.classysharkandroid.utils.IOUtils;
import com.google.classysharkandroid.utils.UriUtils;
import java.io.File;
......@@ -38,7 +36,6 @@ import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
......@@ -51,8 +48,9 @@ import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.text.HtmlCompat;
import dalvik.system.DexClassLoader;
import dalvik.system.DexFile;
import io.github.muntashirakon.AppManager.R;
import io.github.muntashirakon.AppManager.StaticDataset;
import io.github.muntashirakon.AppManager.utils.PackageUtils;
import io.github.muntashirakon.AppManager.utils.Utils;
import static com.google.classysharkandroid.utils.PackageUtils.apkCert;
......@@ -64,8 +62,8 @@ public class ClassListingActivity extends AppCompatActivity implements SearchVie
public static final String EXTRA_PACKAGE_NAME = "package_name";
private Intent inIntent;
private ClassesNamesList classList;
private ClassesNamesList classListAll;
private List<String> classList;
private List<String> classListAll;
private ListView mListView;
private int totalTrackersFound = 0;
private int totalClassesScanned = 0;
......@@ -161,15 +159,14 @@ public class ClassListingActivity extends AppCompatActivity implements SearchVie
mProgressIndicator = findViewById(R.id.progress_linear);
final Uri uriFromIntent = inIntent.getData();
classList = new ClassesNamesList();
classListAll = new ClassesNamesList();
classList = new ArrayList<>();
if (inIntent.getData() != null)
packageInfo = "<b>" + getString(R.string.source_dir) + ": </b>"
+ inIntent.getData().toString() + "\n";
else packageInfo = "";
Names = getResources().getStringArray(R.array.tracker_names);
Names = StaticDataset.getTrackerNames();
try {
InputStream uriStream = UriUtils.getStreamFromUri(ClassListingActivity.this, uriFromIntent);
final byte[] bytes = readFully(uriStream, -1, true);
......@@ -275,7 +272,7 @@ public class ClassListingActivity extends AppCompatActivity implements SearchVie
.setMessage(statsMsg.toString()).show();
return true;
case R.id.action_toggle_class_listing:
mClassListingAdapter.setDefaultList((trackerClassesOnly ? classList : classListAll).getClassNames());
mClassListingAdapter.setDefaultList(trackerClassesOnly ? classList : classListAll);
mActionBar.setSubtitle(getString((trackerClassesOnly ? R.string.tracker_classes : R.string.all_classes)));
trackerClassesOnly = !trackerClassesOnly;
mListView.setAdapter(mClassListingAdapter);
......@@ -335,50 +332,33 @@ public class ClassListingActivity extends AppCompatActivity implements SearchVie
public void run() {
try {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
long threadId = Thread.currentThread().getId();
File incomeFile = File.createTempFile("classes" + threadId, ".dex", getCacheDir());
IOUtils.bytesToFile(bytes, incomeFile);
File optimizedFile = File.createTempFile("opt" + threadId, ".dex", getCacheDir());
DexFile dx = DexFile.loadDex(incomeFile.getPath(), optimizedFile.getPath(), 0);
classListAll = PackageUtils.getClassNames(bytes);
totalClassesScanned = classListAll.size();
StringBuilder found = new StringBuilder();
signatures = getResources().getStringArray(R.array.tracker_signatures);
signatures = StaticDataset.getTrackerCodeSignatures();
signatureCount = new int[signatures.length];
signaturesFound = new boolean[signatures.length];
long t_start, t_end;
t_start = System.currentTimeMillis();
for (Enumeration<String> classNames = dx.entries(); classNames.hasMoreElements(); ) {
String className = classNames.nextElement();
classListAll.add(className);
totalClassesScanned++;
if (className.length() > 8) {
if (className.contains(".")) {
for (int i = 0; i < signatures.length; i++) {
totalIteration++;
if (className.contains(signatures[i])) {
classList.add(className);
signatureCount[i]++;
signaturesFound[i] = true;
if (found.toString().contains(Names[i])) break;
else found.append("<b>").append(++totalTrackersFound)
.append(". ").append(Names[i]).append("</b>\n");
break;
}
for (String className : classListAll) {
if (className.length() > 8 && className.contains(".")) {
for (int i = 0; i < signatures.length; i++) {
totalIteration++;
if (className.contains(signatures[i])) {
classList.add(className);
signatureCount[i]++;
signaturesFound[i] = true;
if (found.toString().contains(Names[i])) break;
else found.append("<b>").append(++totalTrackersFound)
.append(". ").append(Names[i]).append("</b>\n");
break;
}
}
}
}
t_end = System.currentTimeMillis();
totalTimeTaken = t_end - t_start;
dx.close();
if (totalTrackersFound > 0) foundTrackerList = getString(R.string.found_trackers) + "\n" + found;
ClassListingActivity.this.deleteFile("*");
//noinspection ResultOfMethodCallIgnored
incomeFile.delete();
//noinspection ResultOfMethodCallIgnored
optimizedFile.delete();
} catch (Exception e) {
// ODEX, need to see how to handle
e.printStackTrace();
......@@ -387,16 +367,15 @@ public class ClassListingActivity extends AppCompatActivity implements SearchVie
ClassListingActivity.this.runOnUiThread(() -> {
mClassListingAdapter = new ClassListingAdapter(ClassListingActivity.this);
if (!trackerClassesOnly) {
mClassListingAdapter.setDefaultList(classList.getClassNames());
mClassListingAdapter.setDefaultList(classList);
mActionBar.setSubtitle(getString(R.string.tracker_classes));
} else {
mClassListingAdapter.setDefaultList(classListAll.getClassNames());
mClassListingAdapter.setDefaultList(classListAll);
mActionBar.setSubtitle(getString(R.string.all_classes));
}
// mListView.setAdapter(mAdapter);
mListView.setAdapter(mClassListingAdapter);
mProgressIndicator.hide();
if (classList.getClassNames().isEmpty() && totalClassesScanned == 0) {
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();
finish();
......@@ -426,8 +405,8 @@ public class ClassListingActivity extends AppCompatActivity implements SearchVie
Class<?> loadClass;
try {
loadClass = loader.loadClass((!trackerClassesOnly ? classList
: classListAll).getClassName((int) (parent
.getAdapter()).getItemId(position)));
: classListAll).get((int) (parent.getAdapter())
.getItemId(position)));
Reflector reflector = new Reflector(loadClass);
......@@ -437,9 +416,8 @@ public class ClassListingActivity extends AppCompatActivity implements SearchVie
Intent intent = new Intent(ClassListingActivity.this,
ClassViewerActivity.class);
intent.putExtra(ClassViewerActivity.EXTRA_CLASS_NAME,
(!trackerClassesOnly ? classList : classListAll)
.getClassName((int) (parent.getAdapter())
.getItemId(position)));
(!trackerClassesOnly ? classList : classListAll).get((int)
(parent.getAdapter()).getItemId(position)));
intent.putExtra(ClassViewerActivity.EXTRA_CLASS_DUMP, reflector.toString());
intent.putExtra(ClassViewerActivity.EXTRA_APP_NAME, mAppName);
startActivity(intent);
......
......@@ -4,23 +4,31 @@ package io.github.muntashirakon.AppManager.activities;
// Some of them might be slightly modified
import android.annotation.SuppressLint;
import android.graphics.Color;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.view.View;
import android.widget.Toast;
import com.google.android.material.progressindicator.ProgressIndicator;
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.Nullable;
import androidx.appcompat.app.ActionBar;
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;
......@@ -29,9 +37,10 @@ public class ClassViewerActivity extends AppCompatActivity {
public static final String EXTRA_CLASS_NAME = "class_name";
public static final String EXTRA_CLASS_DUMP = "class_dump";
public static final String EXTRA_IS_WRAPPED = "is_wrapped";
private static final String MIME_JAVA = "text/x-java-source";
private static final int RESULT_CODE_EXPORT = 849;
public final static Pattern KEYWORDS = Pattern.compile
private static final Pattern KEYWORDS = Pattern.compile
("\\b(abstract|and|arguments|as(m|sert|sociativity)?|auto|break|" +
"case|catch|chan|char|class|con(st|tinue|venience)|continue|" +
"de(bugger|f|fault|fer|in|init|l|lete)|didset|do(ne)?|dynamic" +
......@@ -50,76 +59,91 @@ public class ClassViewerActivity extends AppCompatActivity {
"using|var|virtual|void|volatile|weak|wh(ere|ile)|willset|" +
"with|yield)\\b", Pattern.MULTILINE);
public final static Pattern TYPES = Pattern.compile
private static final Pattern TYPES = Pattern.compile
("\\b(j?bool(ean)?|[uj]?(byte|char|double|float|int(eger)?|" +
"long|short))\\b", Pattern.MULTILINE);
public final static Pattern CC_COMMENT = Pattern.compile
private static final Pattern CC_COMMENT = Pattern.compile
("//.*$|(\"(?:\\\\[^\"]|\\\\\"|.)*?\")|(?s)/\\*.*?\\*/",
Pattern.MULTILINE);
public final static Pattern CLASS = Pattern.compile
private static final Pattern CLASS = Pattern.compile
("\\b[A-Z][A-Za-z0-9_]+\\b", Pattern.MULTILINE);
private String classDump;
private SpannableString formattedContent;
private boolean isWrapped = true; // Wrap by default
private AppCompatEditText container;
private ProgressIndicator mProgressIndicator;
String className;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getIntent().getBooleanExtra(EXTRA_IS_WRAPPED, false))
setContentView(R.layout.activity_any_viewer_wrapped);
else setContentView(R.layout.activity_any_viewer);
setContentView(R.layout.activity_any_viewer);
setSupportActionBar(findViewById(R.id.toolbar));
mProgressIndicator = findViewById(R.id.progress_linear);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
CharSequence appName = getIntent().getCharSequenceExtra(ClassViewerActivity.EXTRA_APP_NAME);
String className = getIntent().getStringExtra(ClassViewerActivity.EXTRA_CLASS_NAME);
CharSequence appName = getIntent().getCharSequenceExtra(EXTRA_APP_NAME);
className = getIntent().getStringExtra(EXTRA_CLASS_NAME);
if (className != null) {
String barName = className.substring(className.lastIndexOf(".") + 1);
String barName;
try {
barName = className.substring(className.lastIndexOf(".") + 1);
} catch (Exception e) {
barName = className;
}
actionBar.setSubtitle(barName);
}
if (appName != null) actionBar.setTitle(appName);
else actionBar.setTitle(R.string.class_viewer);
}
classDump = getIntent().getStringExtra(EXTRA_CLASS_DUMP);
setWrapped();
}
classDump = getIntent().getStringExtra(ClassViewerActivity.EXTRA_CLASS_DUMP);
private void setWrapped() {
if (container != null) container.setVisibility(View.GONE);
if (isWrapped) container = findViewById(R.id.any_view_wrapped);
else container = findViewById(R.id.any_view);
container.setVisibility(View.VISIBLE);
container.setKeyListener(null);
container.setTextColor(ContextCompat.getColor(this, R.color.dark_orange));
displayContent();
isWrapped = !isWrapped;
}
private void displayContent() {
mProgressIndicator.show();
final TextView textView = findViewById(R.id.any_view);
final int typeClassColor = ContextCompat.getColor(this, R.color.ocean_blue);
final int keywordsColor = ContextCompat.getColor(this, R.color.dark_orange);
final int commentColor = Color.GREEN;
final int commentColor = ContextCompat.getColor(this, R.color.textColorSecondary);
new Thread(() -> {
SpannableString spannableString = new SpannableString(classDump);
Matcher matcher = TYPES.matcher(classDump);
while (matcher.find()) {
spannableString.setSpan(new ForegroundColorSpan(typeClassColor), matcher.start(),
matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.usePattern(CLASS);
while (matcher.find()) {
spannableString.setSpan(new ForegroundColorSpan(typeClassColor), matcher.start(),
matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.usePattern(KEYWORDS);
while (matcher.find()) {
spannableString.setSpan(new ForegroundColorSpan(keywordsColor), matcher.start(),
matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.usePattern(CC_COMMENT);
while (matcher.find()) {
spannableString.setSpan(new ForegroundColorSpan(commentColor), matcher.start(),
matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
if (formattedContent == null) {
formattedContent = new SpannableString(classDump);
Matcher matcher = TYPES.matcher(classDump);
while (matcher.find()) {
formattedContent.setSpan(new ForegroundColorSpan(typeClassColor), matcher.start(),
matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.usePattern(CLASS);
while (matcher.find()) {
formattedContent.setSpan(new ForegroundColorSpan(typeClassColor), matcher.start(),
matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.usePattern(KEYWORDS);
while (matcher.find()) {
formattedContent.setSpan(new ForegroundColorSpan(keywordsColor), matcher.start(),
matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
matcher.usePattern(CC_COMMENT);
while (matcher.find()) {
formattedContent.setSpan(new ForegroundColorSpan(commentColor), matcher.start(),
matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
runOnUiThread(() -> {
textView.setText(spannableString);
container.setText(formattedContent);
mProgressIndicator.hide();
});
}).start();
......@@ -137,16 +161,35 @@ public class ClassViewerActivity extends AppCompatActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
final int id = item.getItemId();
if (id == android.R.id.home) {
finish();
return true;
} else if (id == R.id.action_wrap) {
getIntent().putExtra(EXTRA_IS_WRAPPED, !getIntent().getBooleanExtra(EXTRA_IS_WRAPPED, false));
recreate();
return true;
switch (item.getItemId()) {
case android.R.id.home: finish(); return true;
case R.id.action_wrap: setWrapped(); return true;
case R.id.action_save:
String fileName = className + ".java";
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType(MIME_JAVA);
intent.putExtra(Intent.EXTRA_TITLE, fileName);
startActivityForResult(intent, RESULT_CODE_EXPORT);
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != Activity.RESULT_OK) return;
if (requestCode != RESULT_CODE_EXPORT) return;
if (data == null) return;
Uri uri = data.getData();
if(uri == null) return;
try (OutputStream outputStream = getContentResolver().openOutputStream(uri)) {
Objects.requireNonNull(outputStream).write(classDump.getBytes());
outputStream.flush();
Toast.makeText(this, R.string.saved_successfully, Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, R.string.saving_failed, Toast.LENGTH_SHORT).show();
}
}
}
......@@ -43,7 +43,6 @@ import java.io.IOException;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.NoSuchAlgorithmException;
import java.text.Collator;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
......@@ -73,7 +72,6 @@ import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import io.github.muntashirakon.AppManager.MainLoader;
import io.github.muntashirakon.AppManager.R;
import io.github.muntashirakon.AppManager.adb.AdbShell;
......@@ -81,6 +79,7 @@ import io.github.muntashirakon.AppManager.batchops.BatchOpsManager;
import io.github.muntashirakon.AppManager.fragments.RulesTypeSelectionDialogFragment;
import io.github.muntashirakon.AppManager.types.ApplicationItem;
import io.github.muntashirakon.AppManager.types.FullscreenDialog;
import io.github.muntashirakon.AppManager.types.ScrollSafeSwipeRefreshLayout;
import io.github.muntashirakon.AppManager.utils.AppPref;
import io.github.muntashirakon.AppManager.utils.Utils;
......@@ -88,7 +87,7 @@ import static androidx.appcompat.app.ActionBar.LayoutParams;
public class MainActivity extends AppCompatActivity implements
SearchView.OnQueryTextListener, LoaderManager.LoaderCallbacks<List<ApplicationItem>>,
SwipeRefreshLayout.OnRefreshListener {
ScrollSafeSwipeRefreshLayout.OnRefreshListener {
public static final String EXTRA_PACKAGE_LIST = "EXTRA_PACKAGE_LIST";
public static final String EXTRA_LIST_NAME = "EXTRA_LIST_NAME";
......@@ -144,7 +143,7 @@ public class MainActivity extends AppCompatActivity implements
private SearchView mSearchView;
private ProgressIndicator mProgressIndicator;
private LoaderManager mLoaderManager;
private SwipeRefreshLayout mSwipeRefresh;
private ScrollSafeSwipeRefreshLayout mSwipeRefresh;
private BottomAppBar mBottomAppBar;
private MaterialTextView mBottomAppBarCounter;
private LinearLayoutCompat mMainLayout;
......@@ -227,6 +226,9 @@ public class MainActivity extends AppCompatActivity implements
});
mBottomAppBar.setOnMenuItemClickListener(item -> {
switch (item.getItemId()) {
case R.id.action_block_trackers:
handleBatchOp(BatchOpsManager.OP_BLOCK_TRACKERS, R.string.alert_failed_to_disable_trackers);
return true;
case R.id.action_clear_data:
handleBatchOp(BatchOpsManager.OP_CLEAR_DATA, R.string.alert_failed_to_clear_data);
return true;
......@@ -462,21 +464,22 @@ public class MainActivity extends AppCompatActivity implements
}
@Override
protected void onResume() {
super.onResume();
protected void onStart() {
super.onStart();
// Check root
if (!Utils.isRootGiven(this)) {
AppPref.getInstance().setPref(AppPref.PREF_ADB_MODE_ENABLED, false);
if (!Utils.isRootGiven()) {
AppPref.getInstance().setPref(AppPref.PREF_ROOT_MODE_ENABLED, false);
// Check for adb
new Thread(() -> {
try {
AdbShell.run("id");
AdbShell.CommandResult result = AdbShell.run("id");
if (!result.isSuccessful()) throw new IOException("Adb not available");
AppPref.getInstance().setPref(AppPref.PREF_ADB_MODE_ENABLED, true);
} catch (IOException | InterruptedException | NoSuchAlgorithmException e) {
AppPref.getInstance().setPref(AppPref.PREF_ADB_MODE_ENABLED, false);
}
runOnUiThread(() -> Toast.makeText(this, "Working on ADB mode", Toast.LENGTH_SHORT).show());
} catch (Exception ignored) {}
}).start();
} else AppPref.getInstance().setPref(AppPref.PREF_ADB_MODE_ENABLED, false);
}
// Set filter
if (mAdapter != null && mSearchView != null && !TextUtils.isEmpty(mConstraint)) {
mSearchView.setIconified(false);
......@@ -651,13 +654,13 @@ public class MainActivity extends AppCompatActivity implements
private List<ApplicationItem> mDefaultList;
private List<ApplicationItem> mAdapterList;
private int mColorTransparent;
private int mColorSemiTransparent;
private int mColorHighlight;
private int mColorOrange;
private int mColorPrimary;
private int mColorSecondary;
private int mColorRed;
private static int mColorTransparent;
private static int mColorSemiTransparent;
private static int mColorHighlight;
private static int mColorOrange;
private static int mColorPrimary;
private static int mColorSecondary;
private static int mColorRed;
MainRecyclerAdapter(@NonNull MainActivity activity) {
mActivity = activity;
......@@ -675,8 +678,7 @@ public class MainActivity extends AppCompatActivity implements
void setDefaultList(List<ApplicationItem> list) {
mDefaultList = list;
mAdapterList = list;
if(MainActivity.mConstraint != null
&& !MainActivity.mConstraint.equals("")) {
if(!TextUtils.isEmpty(MainActivity.mConstraint)) {
getFilter().filter(MainActivity.mConstraint);
}
notifyDataSetChanged();
......@@ -742,12 +744,10 @@ public class MainActivity extends AppCompatActivity implements
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
// Cancel an existing icon loading operation
if (holder.iconLoader != null) holder.iconLoader.cancel(true);
final ApplicationItem item = mAdapterList.get(position);
final ApplicationInfo info = item.applicationInfo;
View view = holder.mainView;
// Add click listeners
holder.itemView.setOnClickListener(v -> {
Intent intent = new Intent(mActivity, AppInfoActivity.class);
......@@ -760,19 +760,19 @@ public class MainActivity extends AppCompatActivity implements
mActivity.startActivity(appDetailsIntent);
return true;
});
// Alternate background colors
// Alternate background colors: selected > disabled > regular
if (mPackageNames.contains(info.packageName))
view.setBackgroundColor(mColorHighlight);
holder.mainView.setBackgroundColor(mColorHighlight);
else if (!info.enabled)
view.setBackgroundColor(ContextCompat.getColor(mActivity, R.color.disabled_app));
else view.setBackgroundColor(position % 2 == 0 ? mColorSemiTransparent : mColorTransparent);
holder.mainView.setBackgroundColor(ContextCompat.getColor(mActivity, R.color.disabled_user));
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);
try {
PackageInfo packageInfo = mPackageManager.getPackageInfo(info.packageName, 0);
// Set version name
holder.version.setText(packageInfo.versionName);
// Set date and (if available,) days
// Set date and (if available,) days between first install and last update
String lastUpdateDate = sSimpleDateFormat.format(new Date(packageInfo.lastUpdateTime));
if (packageInfo.firstInstallTime == packageInfo.lastUpdateTime)
holder.date.setText(lastUpdateDate);
......@@ -792,10 +792,10 @@ public class MainActivity extends AppCompatActivity implements
holder.date.setTextColor(mColorOrange);
else holder.date.setTextColor(mColorSecondary);
// Set kernel user ID
holder.shared_id.setText(String.format(Locale.getDefault(),"%d", info.uid));
holder.sharedId.setText(String.format(Locale.getDefault(),"%d", info.uid));
// Set kernel user ID text color to orange if the package is shared
if (packageInfo.sharedUserId != null) holder.shared_id.setTextColor(mColorOrange);
else holder.shared_id.setTextColor(mColorSecondary);
if (packageInfo.sharedUserId != null) holder.sharedId.setTextColor(mColorOrange);
else holder.sharedId.setTextColor(mColorSecondary);
// Set issuer
String issuer;
try {
......@@ -811,7 +811,7 @@ public class MainActivity extends AppCompatActivity implements
holder.iconLoader = new IconAsyncTask(holder.icon, info);
holder.iconLoader.execute();
// Set app label
if (mConstraint != null && item.label.toLowerCase(Locale.ROOT).contains(mConstraint)) {
if (!TextUtils.isEmpty(mConstraint) && item.label.toLowerCase(Locale.ROOT).contains(mConstraint)) {
// Highlight searched query
holder.label.setText(Utils.getHighlightedText(item.label, mConstraint, mColorRed));
} else holder.label.setText(item.label);
......@@ -820,13 +820,13 @@ public class MainActivity extends AppCompatActivity implements
holder.label.setTextColor(Color.RED);
else holder.label.setTextColor(mColorPrimary);
// Set package name
if (mConstraint != null && info.packageName.toLowerCase(Locale.ROOT).contains(mConstraint)) {
if (!TextUtils.isEmpty(mConstraint) && info.packageName.toLowerCase(Locale.ROOT).contains(mConstraint)) {
// 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
if ((info.flags & ApplicationInfo.FLAG_STOPPED) != 0)
holder.packageName.setTextColor(ContextCompat.getColor(mActivity, R.color.blue_green));
holder.packageName.setTextColor(ContextCompat.getColor(mActivity, R.color.stopped));
else holder.packageName.setTextColor(mColorSecondary);
// Set version (along with HW accelerated, debug and test only flags)
CharSequence version = holder.version.getText();
......@@ -839,7 +839,7 @@ public class MainActivity extends AppCompatActivity implements
UsageStatsManager mUsageStats;
mUsageStats = mActivity.getSystemService(UsageStatsManager.class);
if (mUsageStats != null && mUsageStats.isAppInactive(info.packageName))
holder.version.setTextColor(Color.GREEN);
holder.version.setTextColor(ContextCompat.getColor(mActivity, R.color.stopped));
else holder.version.setTextColor(mColorSecondary);
}
// Set app type: system or user app (along with large heap, suspended, multi-arch,
......@@ -926,7 +926,7 @@ public class MainActivity extends AppCompatActivity implements
TextView isSystemApp;
TextView date;
TextView size;
TextView shared_id;
TextView sharedId;
TextView issuer;
TextView sha;
MainRecyclerAdapter.IconAsyncTask iconLoader;
......@@ -942,7 +942,7 @@ public class MainActivity extends AppCompatActivity implements
isSystemApp = itemView.findViewById(R.id.isSystem);
date = itemView.findViewById(R.id.date);
size = itemView.findViewById(R.id.size);
shared_id = itemView.findViewById(R.id.shareid);
sharedId = itemView.findViewById(R.id.shareid);
issuer = itemView.findViewById(R.id.issuer);
sha = itemView.findViewById(R.id.sha);
}
......
......@@ -3,39 +3,43 @@ package io.github.muntashirakon.AppManager.activities;
// NOTE: Commented lines were taken from View2ManifestActivity.java