Add support for recommended scope

This commit is contained in:
tehcneko 2021-01-30 21:56:15 +08:00
parent 50ea40cd88
commit 7be4e66b98
8 changed files with 125 additions and 24 deletions

View File

@ -22,6 +22,7 @@ import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -30,6 +31,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;
import com.google.android.material.checkbox.MaterialCheckBox;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.snackbar.Snackbar;
import java.util.ArrayList;
@ -54,14 +56,17 @@ public class ScopeAdapter extends RecyclerView.Adapter<ScopeAdapter.ViewHolder>
private final ApplicationFilter filter;
private final SharedPreferences preferences;
private final String modulePackageName;
private final String moduleName;
private final MasterSwitch masterSwitch;
private List<PackageInfo> fullList, showList;
private List<String> checkedList;
private final List<String> recommendedList;
private boolean enabled = true;
private ApplicationInfo selectedInfo;
public ScopeAdapter(AppListActivity activity, String modulePackageName, MasterSwitch masterSwitch) {
public ScopeAdapter(AppListActivity activity, String moduleName, String modulePackageName, MasterSwitch masterSwitch) {
this.activity = activity;
this.moduleName = moduleName;
this.modulePackageName = modulePackageName;
this.masterSwitch = masterSwitch;
preferences = App.getPreferences();
@ -77,6 +82,8 @@ public class ScopeAdapter extends RecyclerView.Adapter<ScopeAdapter.ViewHolder>
notifyDataSetChanged();
}
});
ModuleUtil.InstalledModule module = ModuleUtil.getInstance().getModule(modulePackageName);
recommendedList = module.getScopeList();
enabled = ModuleUtil.getInstance().isModuleEnabled(modulePackageName);
refresh();
}
@ -91,12 +98,15 @@ public class ScopeAdapter extends RecyclerView.Adapter<ScopeAdapter.ViewHolder>
private void loadApps() {
activity.runOnUiThread(() -> masterSwitch.setChecked(enabled));
checkedList = AppHelper.getScopeList(modulePackageName);
if (checkedList.isEmpty() && hasRecommended()) {
checkRecommended();
}
fullList = pm.getInstalledPackages(PackageManager.GET_META_DATA);
List<String> installedList = new ArrayList<>();
List<PackageInfo> rmList = new ArrayList<>();
for (PackageInfo info : fullList) {
installedList.add(info.packageName);
if (info.packageName.equals(((ScopeAdapter) this).modulePackageName)) {
if (info.packageName.equals(this.modulePackageName)) {
rmList.add(info);
continue;
}
@ -153,9 +163,32 @@ public class ScopeAdapter extends RecyclerView.Adapter<ScopeAdapter.ViewHolder>
});
}
private void checkRecommended() {
checkedList.clear();
checkedList.addAll(recommendedList);
AppHelper.saveScopeList(modulePackageName, checkedList);
notifyDataSetChanged();
}
private boolean hasRecommended() {
return recommendedList != null && !recommendedList.isEmpty();
}
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
if (itemId == R.id.item_show_system) {
if (itemId == R.id.use_recommended) {
if (!checkedList.isEmpty()) {
new MaterialAlertDialogBuilder(activity)
.setTitle(R.string.use_recommended)
.setMessage(R.string.use_recommended_message)
.setPositiveButton(android.R.string.ok, (dialog, which) -> checkRecommended())
.setNegativeButton(android.R.string.cancel, null)
.show();
} else {
checkRecommended();
}
return true;
} else if (itemId == R.id.item_show_system) {
item.setChecked(!item.isChecked());
preferences.edit().putBoolean("show_system_apps", item.isChecked()).apply();
} else if (itemId == R.id.item_show_games) {
@ -227,6 +260,9 @@ public class ScopeAdapter extends RecyclerView.Adapter<ScopeAdapter.ViewHolder>
if (intent == null) {
menu.removeItem(R.id.menu_launch);
}
if (!hasRecommended()) {
menu.removeItem(R.id.use_recommended);
}
menu.findItem(R.id.item_show_system).setChecked(preferences.getBoolean("show_system_apps", false));
menu.findItem(R.id.item_show_games).setChecked(preferences.getBoolean("show_games", false));
menu.findItem(R.id.item_show_modules).setChecked(preferences.getBoolean("show_modules", false));
@ -276,7 +312,11 @@ public class ScopeAdapter extends RecyclerView.Adapter<ScopeAdapter.ViewHolder>
}
});
holder.appDescription.setText(activity.getString(R.string.app_description, info.packageName, info.versionName));
String description = activity.getString(R.string.app_description, info.packageName, info.versionName);
if (hasRecommended() && recommendedList.contains(info.packageName)) {
description += "\n" + activity.getString(R.string.requested_by_module);
}
holder.appDescription.setText(description);
holder.itemView.setOnCreateContextMenuListener((menu, v, menuInfo) -> {
activity.getMenuInflater().inflate(R.menu.menu_app_item, menu);
@ -391,6 +431,36 @@ public class ScopeAdapter extends RecyclerView.Adapter<ScopeAdapter.ViewHolder>
}
}
public boolean onBackPressed() {
if (masterSwitch.isChecked() && checkedList.isEmpty()) {
if (hasRecommended()) {
new MaterialAlertDialogBuilder(activity)
.setTitle(R.string.use_recommended)
.setMessage(R.string.no_scope_selected_has_recommended)
.setPositiveButton(android.R.string.ok, (dialog, which) -> checkRecommended())
.setNegativeButton(android.R.string.cancel, (dialog, which) -> {
ModuleUtil.getInstance().setModuleEnabled(modulePackageName, false);
Toast.makeText(activity, activity.getString(R.string.module_disabled_no_selection, moduleName), Toast.LENGTH_LONG).show();
activity.finish();
})
.show();
} else {
new MaterialAlertDialogBuilder(activity)
.setMessage(R.string.no_scope_selected)
.setPositiveButton(android.R.string.cancel, null)
.setNegativeButton(android.R.string.ok, (dialog, which) -> {
ModuleUtil.getInstance().setModuleEnabled(modulePackageName, false);
Toast.makeText(activity, activity.getString(R.string.module_disabled_no_selection, moduleName), Toast.LENGTH_LONG).show();
activity.finish();
})
.show();
}
return false;
} else {
return true;
}
}
public static String getAppLabel(ApplicationInfo info, PackageManager pm) {
return info.loadLabel(pm).toString();
}

View File

@ -14,15 +14,12 @@ import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.SearchView;
import androidx.recyclerview.widget.DividerItemDecoration;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.snackbar.Snackbar;
import io.github.lsposed.manager.R;
import io.github.lsposed.manager.adapters.AppHelper;
import io.github.lsposed.manager.adapters.ScopeAdapter;
import io.github.lsposed.manager.databinding.ActivityAppListBinding;
import io.github.lsposed.manager.util.LinearLayoutManagerFix;
import io.github.lsposed.manager.util.ModuleUtil;
import me.zhanghai.android.fastscroll.FastScrollerBuilder;
public class AppListActivity extends BaseActivity {
@ -38,12 +35,11 @@ public class AppListActivity extends BaseActivity {
}
};
private final Handler handler = new Handler(Looper.getMainLooper());
private String modulePackageName;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
modulePackageName = getIntent().getStringExtra("modulePackageName");
String modulePackageName = getIntent().getStringExtra("modulePackageName");
String moduleName = getIntent().getStringExtra("moduleName");
binding = ActivityAppListBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
@ -54,7 +50,7 @@ public class AppListActivity extends BaseActivity {
bar.setDisplayHomeAsUpEnabled(true);
bar.setTitle(moduleName);
bar.setSubtitle(modulePackageName);
scopeAdapter = new ScopeAdapter(this, modulePackageName, binding.masterSwitch);
scopeAdapter = new ScopeAdapter(this, moduleName, modulePackageName, binding.masterSwitch);
scopeAdapter.setHasStableIds(true);
binding.recyclerView.setAdapter(scopeAdapter);
binding.recyclerView.setLayoutManager(new LinearLayoutManagerFix(this));
@ -119,16 +115,7 @@ public class AppListActivity extends BaseActivity {
@Override
public void onBackPressed() {
if (searchView.isIconified()) {
if (binding.masterSwitch.isChecked() && AppHelper.getScopeList(modulePackageName).isEmpty()) {
new MaterialAlertDialogBuilder(this)
.setMessage(R.string.no_scope_selected)
.setPositiveButton(android.R.string.cancel, null)
.setNegativeButton(android.R.string.ok, (dialog, which) -> {
ModuleUtil.getInstance().setModuleEnabled(modulePackageName, false);
super.onBackPressed();
})
.show();
} else {
if (scopeAdapter.onBackPressed()) {
super.onBackPressed();
}
} else {

View File

@ -3,7 +3,6 @@ package io.github.lsposed.manager.ui.activity;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;

View File

@ -16,6 +16,7 @@ import com.google.android.material.snackbar.Snackbar;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
@ -24,6 +25,7 @@ import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import io.github.lsposed.manager.App;
import io.github.lsposed.manager.BuildConfig;
import io.github.lsposed.manager.Constants;
import io.github.lsposed.manager.R;
import io.github.lsposed.manager.adapters.AppHelper;
@ -256,6 +258,7 @@ public final class ModuleUtil {
public PackageInfo pkg;
private String appName; // loaded lazyily
private String description; // loaded lazyily
private List<String> scopeList; // loaded lazyily
private InstalledModule(PackageInfo pkg, boolean isFramework) {
this.app = pkg.applicationInfo;
@ -320,6 +323,20 @@ public final class ModuleUtil {
return this.description;
}
public List<String> getScopeList() {
if (this.scopeList == null) {
try {
int scopeListResourceId = app.metaData.getInt("xposedscope");
if (scopeListResourceId != 0) {
scopeList = Arrays.asList(pm.getResourcesForApplication(BuildConfig.APPLICATION_ID).getStringArray(scopeListResourceId));
}
} catch (Exception e) {
e.printStackTrace();
}
}
return this.scopeList;
}
public PackageInfo getPackageInfo() {
return pkg;
}

View File

@ -14,6 +14,10 @@
android:icon="@drawable/ic_settings"
app:showAsAction="ifRoom" />
<item
android:id="@+id/use_recommended"
android:title="@string/use_recommended" />
<item
android:id="@+id/item_show_games"
android:checkable="true"

View File

@ -154,4 +154,9 @@
<string name="failed_to_save_scope_list">作用域列表保存失败</string>
<string name="module_settings">模块设置</string>
<string name="app_description">%s\n版本%s</string>
<string name="use_recommended">推荐</string>
<string name="no_scope_selected_has_recommended">未选择任何应用。选择推荐的应用?</string>
<string name="use_recommended_message">选择推荐的应用?</string>
<string name="requested_by_module">推荐的应用</string>
<string name="module_disabled_no_selection">由于未选择任何应用,模块 %s 已被禁用。</string>
</resources>

View File

@ -162,4 +162,9 @@
<string name="failed_to_save_scope_list">Failed save scope list</string>
<string name="module_settings">Module settings</string>
<string name="app_description">%s\nVersion: %s</string>
<string name="use_recommended">Recommended</string>
<string name="no_scope_selected_has_recommended">You did not select any app. Select recommended apps?</string>
<string name="use_recommended_message">Select recommended apps?</string>
<string name="requested_by_module">Recommended</string>
<string name="module_disabled_no_selection">Module %s has been disabled since no app selected.</string>
</resources>

View File

@ -1,8 +1,22 @@
androidCompileSdkVersion=30
androidMinSdkVersion=26
## For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
#
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx1024m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
#
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
#Sat Jan 30 19:20:27 CST 2021
androidTargetSdkVersion=30
androidCompileNdkVersion=22.0.7026061
android.prefabVersion=1.1.2
apiCode=93
androidCompileSdkVersion=30
androidMinSdkVersion=26
org.gradle.jvmargs=-Xmx2048M
android.useAndroidX=true
android.enableJetifier=true
android.prefabVersion=1.1.2