diff --git a/app/build.gradle b/app/build.gradle index 93604ef9..a99a923f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -143,8 +143,8 @@ dependencies { implementation 'me.zhanghai.android.appiconloader:appiconloader-glide:1.2.0' implementation 'me.zhanghai.android.fastscroll:library:1.1.5' implementation files('libs/WeatherView-2.0.3.aar') - compileOnly project(":hiddenapi-stubs") implementation project(':manager-service') + compileOnly project(":hiddenapi-stubs") } configurations { diff --git a/app/src/main/java/io/github/lsposed/manager/App.java b/app/src/main/java/io/github/lsposed/manager/App.java index 9658dc51..3c0b1f36 100644 --- a/app/src/main/java/io/github/lsposed/manager/App.java +++ b/app/src/main/java/io/github/lsposed/manager/App.java @@ -42,8 +42,6 @@ import okhttp3.Cache; import okhttp3.OkHttpClient; import rikka.material.app.DayNightDelegate; -import static io.github.lsposed.manager.receivers.LSPosedManagerServiceClient.testBinder; - public class App extends Application { public static final String TAG = "LSPosedManager"; @SuppressLint("StaticFieldLeak") @@ -63,7 +61,6 @@ public class App extends Application { public void onCreate() { super.onCreate(); - testBinder(); if (!BuildConfig.DEBUG) { try { Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> { diff --git a/app/src/main/java/io/github/lsposed/manager/ConfigManager.java b/app/src/main/java/io/github/lsposed/manager/ConfigManager.java new file mode 100644 index 00000000..bcbcc0ca --- /dev/null +++ b/app/src/main/java/io/github/lsposed/manager/ConfigManager.java @@ -0,0 +1,225 @@ +/* + * This file is part of LSPosed. + * + * LSPosed is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LSPosed is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LSPosed. If not, see . + * + * Copyright (C) 2021 LSPosed Contributors + */ + +package io.github.lsposed.manager; + +import android.content.pm.PackageInfo; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; + +import java.util.ArrayList; +import java.util.List; + +import io.github.lsposed.manager.receivers.LSPosedManagerServiceClient; + +public class ConfigManager { + + public static int getXposedApiVersion() { + try { + return LSPosedManagerServiceClient.getXposedApiVersion(); + } catch (RemoteException | NullPointerException e) { + // TODO: + e.printStackTrace(); + return -1; + } + } + + public static String getXposedVersionName() { + try { + return LSPosedManagerServiceClient.getXposedVersionName(); + } catch (RemoteException | NullPointerException e) { + // TODO: + e.printStackTrace(); + return null; + } + } + + public static int getXposedVersionCode() { + try { + return LSPosedManagerServiceClient.getXposedVersionCode(); + } catch (RemoteException | NullPointerException e) { + // TODO: + e.printStackTrace(); + return -1; + } + } + + public static List getInstalledPackagesFromAllUsers(int flags) { + List list = new ArrayList<>(); + try { + list.addAll(LSPosedManagerServiceClient.getInstalledPackagesFromAllUsers(flags)); + } catch (RemoteException | NullPointerException e) { + // TODO: + e.printStackTrace(); + } + return list; + } + + public static String[] getEnabledModules() { + try { + return LSPosedManagerServiceClient.enabledModules(); + } catch (RemoteException | NullPointerException e) { + // TODO: + e.printStackTrace(); + return new String[0]; + } + } + + public static boolean setModuleEnabled(String packageName, boolean enable) { + try { + return enable ? LSPosedManagerServiceClient.enableModule(packageName) : LSPosedManagerServiceClient.disableModule(packageName); + } catch (RemoteException | NullPointerException e) { + // TODO: + e.printStackTrace(); + return false; + } + } + + public static boolean setModuleScope(String packageName, List uidList) { + try { + int[] uids = new int[uidList.size()]; + for (int i = 0; i < uidList.size(); i++) { + uids[i] = uidList.get(i); + } + return LSPosedManagerServiceClient.setModuleScope(packageName, uids); + } catch (RemoteException | NullPointerException e) { + // TODO: + e.printStackTrace(); + return false; + } + } + + public static List getModuleScope(String packageName) { + List list = new ArrayList<>(); + try { + for (int uid : LSPosedManagerServiceClient.getModuleScope(packageName)) { + list.add(uid); + } + } catch (RemoteException | NullPointerException e) { + // TODO: + e.printStackTrace(); + } + return list; + } + + public static boolean isResourceHookEnabled() { + try { + return LSPosedManagerServiceClient.isResourceHook(); + } catch (RemoteException | NullPointerException e) { + // TODO: + e.printStackTrace(); + return false; + } + } + + public static boolean setResourceHookEnabled(boolean enabled) { + try { + LSPosedManagerServiceClient.setResourceHook(enabled); + return true; + } catch (RemoteException | NullPointerException e) { + // TODO: + e.printStackTrace(); + return false; + } + } + + public static boolean isVerboseLogEnabled() { + try { + return LSPosedManagerServiceClient.isVerboseLog(); + } catch (RemoteException | NullPointerException e) { + // TODO: + e.printStackTrace(); + return false; + } + } + + public static boolean setVerboseLogEnabled(boolean enabled) { + try { + LSPosedManagerServiceClient.setVerboseLog(enabled); + return true; + } catch (RemoteException | NullPointerException e) { + // TODO: + e.printStackTrace(); + return false; + } + } + + public static int getVariant() { + try { + return LSPosedManagerServiceClient.getVariant(); + } catch (RemoteException | NullPointerException e) { + // TODO: + e.printStackTrace(); + return 1; + } + } + + public static String getVariantString() { + int variant = getVariant(); + switch (variant) { + case 1: + return "YAHFA"; + case 2: + return "SandHook"; + default: + return "Unknown"; + } + } + + public static boolean setVariant(int variant) { + try { + LSPosedManagerServiceClient.setVariant(variant); + return true; + } catch (RemoteException | NullPointerException e) { + // TODO: + e.printStackTrace(); + return false; + } + } + + public static boolean isPermissive() { + try { + return LSPosedManagerServiceClient.isPermissive(); + } catch (RemoteException | NullPointerException e) { + // TODO: + e.printStackTrace(); + return true; + } + } + + public static ParcelFileDescriptor getVerboseLog() { + try { + return LSPosedManagerServiceClient.getVerboseLog(); + } catch (RemoteException | NullPointerException e) { + // TODO: + e.printStackTrace(); + return null; + } + } + + public static ParcelFileDescriptor getModulesLog() { + try { + return LSPosedManagerServiceClient.getModulesLog(); + } catch (RemoteException | NullPointerException e) { + // TODO: + e.printStackTrace(); + return null; + } + } +} diff --git a/app/src/main/java/io/github/lsposed/manager/Constants.java b/app/src/main/java/io/github/lsposed/manager/Constants.java index 852002a0..9561d9c0 100644 --- a/app/src/main/java/io/github/lsposed/manager/Constants.java +++ b/app/src/main/java/io/github/lsposed/manager/Constants.java @@ -22,61 +22,13 @@ package io.github.lsposed.manager; import android.widget.Toast; -@SuppressWarnings({"FieldCanBeLocal", "FieldMayBeFinal", "unused"}) public class Constants { - private static int xposedApiVersion = -1; - private static String xposedVersion = null; - private static int xposedVersionCode = -1; - private static String xposedVariant = null; - private static String baseDir = null; - private static String logDir = null; - private static String miscDir = null; - private static boolean permissive = true; - - public static int getXposedApiVersion() { - return xposedApiVersion; - } - - public static String getXposedVersion() { - return xposedVersion; - } - - public static int getXposedVersionCode() { - return xposedVersionCode; - } - - public static String getXposedVariant() { - return xposedVariant; - } - - public static String getEnabledModulesListFile() { - return getBaseDir() + "conf/enabled_modules.list"; - } - - public static String getModulesListFile() { - return getBaseDir() + "conf/modules.list"; - } - - public static String getConfDir() { - return getBaseDir() + "conf/"; - } - - public static String getBaseDir() { - return baseDir; - } + private static final String logDir = null; public static String getLogDir() { return logDir; } - public static String getMiscDir() { - return miscDir; - } - - public static boolean isPermissive() { - return permissive; - } - public static void showErrorToast(int type) { Toast.makeText(App.getInstance(), R.string.app_destroyed, Toast.LENGTH_LONG).show(); } diff --git a/app/src/main/java/io/github/lsposed/manager/adapters/AppHelper.java b/app/src/main/java/io/github/lsposed/manager/adapters/AppHelper.java index b052a180..1412a7f0 100644 --- a/app/src/main/java/io/github/lsposed/manager/adapters/AppHelper.java +++ b/app/src/main/java/io/github/lsposed/manager/adapters/AppHelper.java @@ -28,26 +28,15 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.view.MenuItem; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; import java.util.List; -import io.github.lsposed.manager.Constants; import io.github.lsposed.manager.R; public class AppHelper { public static final String SETTINGS_CATEGORY = "de.robv.android.xposed.category.MODULE_SETTINGS"; - private static final String BASE_PATH = Constants.getBaseDir(); - private static final String SCOPE_LIST_PATH = "conf/%s.conf"; - - private static final HashMap> scopeList = new HashMap<>(); public static Intent getSettingsIntent(String packageName, PackageManager packageManager) { // taken from @@ -124,52 +113,4 @@ public class AppHelper { return (PackageInfo a, PackageInfo b) -> displayNameComparator.compare(a.applicationInfo, b.applicationInfo); } } - - public static List getEnabledModuleList() { - Path path = Paths.get(Constants.getEnabledModulesListFile()); - List s = new ArrayList<>(); - try { - s = Files.readAllLines(path); - } catch (IOException e) { - e.printStackTrace(); - } - return s; - } - - public static List getScopeList(String modulePackageName) { - if (scopeList.containsKey(modulePackageName)) { - return scopeList.get(modulePackageName); - } - Path path = Paths.get(BASE_PATH + String.format(SCOPE_LIST_PATH, modulePackageName)); - List s = new ArrayList<>(); - try { - s = Files.readAllLines(path); - scopeList.put(modulePackageName, s); - } catch (IOException e) { - e.printStackTrace(); - } - return s; - } - - public static boolean saveScopeList(String modulePackageName, List list) { - Path path = Paths.get(BASE_PATH + String.format(SCOPE_LIST_PATH, modulePackageName)); - if (list.size() == 0) { - scopeList.put(modulePackageName, list); - try { - Files.delete(path); - return true; - } catch (IOException e) { - e.printStackTrace(); - return false; - } - } - try { - Files.write(path, list); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - scopeList.put(modulePackageName, list); - return true; - } } diff --git a/app/src/main/java/io/github/lsposed/manager/adapters/ScopeAdapter.java b/app/src/main/java/io/github/lsposed/manager/adapters/ScopeAdapter.java index c3eee247..24b70b41 100644 --- a/app/src/main/java/io/github/lsposed/manager/adapters/ScopeAdapter.java +++ b/app/src/main/java/io/github/lsposed/manager/adapters/ScopeAdapter.java @@ -20,6 +20,7 @@ package io.github.lsposed.manager.adapters; +import android.annotation.SuppressLint; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; @@ -61,13 +62,15 @@ import com.google.android.material.snackbar.Snackbar; import java.util.ArrayList; import java.util.Calendar; -import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import io.github.lsposed.manager.App; import io.github.lsposed.manager.BuildConfig; +import io.github.lsposed.manager.ConfigManager; import io.github.lsposed.manager.R; import io.github.lsposed.manager.ui.activity.AppListActivity; import io.github.lsposed.manager.ui.fragment.CompileDialogFragment; @@ -78,6 +81,7 @@ import rikka.widget.switchbar.SwitchBar; import static android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS; +@SuppressLint("NotifyDataSetChanged") public class ScopeAdapter extends RecyclerView.Adapter implements Filterable { private final AppListActivity activity; @@ -86,9 +90,12 @@ public class ScopeAdapter extends RecyclerView.Adapter private final String modulePackageName; private final String moduleName; private final SwitchBar masterSwitch; - private List fullList, showList; - private List checkedList; - private final List recommendedList; + private final List moduleList = new ArrayList<>(); + private final List recommendedList = new ArrayList<>(); + private final List searchList = new ArrayList<>(); + private final Map> sharedUidMap = new HashMap<>(); + private List showList = new ArrayList<>(); + private List checkedList = new ArrayList<>(); private boolean enabled = true; private ApplicationInfo selectedInfo; @@ -98,17 +105,15 @@ public class ScopeAdapter extends RecyclerView.Adapter this.modulePackageName = modulePackageName; this.masterSwitch = masterSwitch; preferences = App.getPreferences(); - fullList = showList = Collections.emptyList(); - checkedList = Collections.emptyList(); pm = activity.getPackageManager(); masterSwitch.setOnCheckedChangeListener((view, isChecked) -> { + if (!ModuleUtil.getInstance().setModuleEnabled(modulePackageName, isChecked)) { + return false; + } enabled = isChecked; - ModuleUtil.getInstance().setModuleEnabled(modulePackageName, enabled); notifyDataSetChanged(); return true; }); - ModuleUtil.InstalledModule module = ModuleUtil.getInstance().getModule(modulePackageName); - recommendedList = module.getScopeList(); refresh(); } @@ -120,54 +125,79 @@ public class ScopeAdapter extends RecyclerView.Adapter } private void loadApps() { + List appList = ConfigManager.getInstalledPackagesFromAllUsers(PackageManager.GET_META_DATA); + checkedList = ConfigManager.getModuleScope(modulePackageName); + moduleList.clear(); + recommendedList.clear(); + searchList.clear(); + showList.clear(); + enabled = ModuleUtil.getInstance().isModuleEnabled(modulePackageName); activity.runOnUiThread(() -> masterSwitch.setChecked(enabled)); - checkedList = AppHelper.getScopeList(modulePackageName); - if (checkedList.isEmpty() && hasRecommended()) { - checkRecommended(); - } - fullList = pm.getInstalledPackages(PackageManager.GET_META_DATA); - List installedList = new ArrayList<>(); - List rmList = new ArrayList<>(); - for (PackageInfo info : fullList) { - installedList.add(info.packageName); - if (info.packageName.equals(BuildConfig.APPLICATION_ID) || info.packageName.equals(this.modulePackageName)) { - rmList.add(info); + + ArrayList removeList = new ArrayList<>(); + ArrayList installedList = new ArrayList<>(); + List recommendedPackageNames = ModuleUtil.getInstance().getModule(modulePackageName).getScopeList(); + for (PackageInfo info : appList) { + int uid = info.applicationInfo.uid; + + if (!installedList.contains(uid)) installedList.add(uid); + + List sharedUidList = sharedUidMap.computeIfAbsent(uid, k -> new ArrayList<>()); + if (!sharedUidList.contains(info)) sharedUidList.add(info); + + if (info.packageName.equals(this.modulePackageName)) { + if (!checkedList.contains(uid)) checkedList.add(uid); + if (!moduleList.contains(uid)) moduleList.add(uid); + removeList.add(info); continue; } - if (checkedList.contains(info.packageName) || info.packageName.equals("android")) { + if (recommendedPackageNames != null && recommendedPackageNames.contains(info.packageName) && !recommendedList.contains(uid)) { + recommendedList.add(uid); + continue; + } + + if (info.packageName.equals(BuildConfig.APPLICATION_ID)) { + removeList.add(info); + continue; + } + if (checkedList.contains(uid) || info.packageName.equals("android")) { continue; } if (!preferences.getBoolean("show_modules", false)) { if (info.applicationInfo.metaData != null && info.applicationInfo.metaData.containsKey("xposedmodule")) { - rmList.add(info); + removeList.add(info); continue; } } if (!preferences.getBoolean("show_games", false)) { if (info.applicationInfo.category == ApplicationInfo.CATEGORY_GAME) { - rmList.add(info); + removeList.add(info); continue; } //noinspection deprecation if ((info.applicationInfo.flags & ApplicationInfo.FLAG_IS_GAME) != 0) { - rmList.add(info); + removeList.add(info); continue; } } if ((info.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) { - rmList.add(info); + removeList.add(info); continue; } if (!preferences.getBoolean("show_system_apps", false) && (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - rmList.add(info); + removeList.add(info); } } checkedList.retainAll(installedList); - if (rmList.size() > 0) { - fullList.removeAll(rmList); + if (selectedNothing() && hasRecommended()) { + checkRecommended(); } - showList = sortApps(fullList); + searchList.addAll(appList); + if (removeList.size() > 0) { + searchList.removeAll(removeList); + } + showList = sortApps(searchList); activity.onDataReady(); } @@ -183,8 +213,8 @@ public class ScopeAdapter extends RecyclerView.Adapter } }; Comparator recommendedComparator = (a, b) -> { - boolean aRecommended = hasRecommended() && recommendedList.contains(a.packageName); - boolean bRecommended = hasRecommended() && recommendedList.contains(b.packageName); + boolean aRecommended = hasRecommended() && recommendedList.contains(a.applicationInfo.uid); + boolean bRecommended = hasRecommended() && recommendedList.contains(b.applicationInfo.uid); if (aRecommended == bRecommended) { return frameworkComparator.compare(a, b); } else if (aRecommended) { @@ -194,8 +224,8 @@ public class ScopeAdapter extends RecyclerView.Adapter } }; list.sort((a, b) -> { - boolean aChecked = checkedList.contains(a.packageName); - boolean bChecked = checkedList.contains(b.packageName); + boolean aChecked = checkedList.contains(a.applicationInfo.uid); + boolean bChecked = checkedList.contains(b.applicationInfo.uid); if (aChecked == bChecked) { return recommendedComparator.compare(a, b); } else if (aChecked) { @@ -208,14 +238,12 @@ public class ScopeAdapter extends RecyclerView.Adapter } private void checkRecommended() { - checkedList.clear(); checkedList.addAll(recommendedList); - AppHelper.saveScopeList(modulePackageName, checkedList); - notifyDataSetChanged(); + ConfigManager.setModuleScope(modulePackageName, checkedList); } private boolean hasRecommended() { - return recommendedList != null && !recommendedList.isEmpty(); + return !recommendedList.isEmpty(); } public boolean onOptionsItemSelected(MenuItem item) { @@ -225,11 +253,15 @@ public class ScopeAdapter extends RecyclerView.Adapter new AlertDialog.Builder(activity) .setTitle(R.string.use_recommended) .setMessage(R.string.use_recommended_message) - .setPositiveButton(android.R.string.ok, (dialog, which) -> checkRecommended()) + .setPositiveButton(android.R.string.ok, (dialog, which) -> { + checkRecommended(); + notifyDataSetChanged(); + }) .setNegativeButton(android.R.string.cancel, null) .show(); } else { checkRecommended(); + notifyDataSetChanged(); } return true; } else if (itemId == R.id.item_show_system) { @@ -359,7 +391,7 @@ public class ScopeAdapter extends RecyclerView.Adapter }); SpannableStringBuilder sb = new SpannableStringBuilder(android ? "" : activity.getString(R.string.app_description, info.packageName, info.versionName)); holder.appDescription.setVisibility(View.VISIBLE); - if (hasRecommended() && recommendedList.contains(info.packageName)) { + if (hasRecommended() && recommendedList.contains(info.applicationInfo.uid)) { if (!android) sb.append("\n"); String recommended = activity.getString(R.string.requested_by_module); sb.append(recommended); @@ -390,9 +422,9 @@ public class ScopeAdapter extends RecyclerView.Adapter }); holder.checkbox.setOnCheckedChangeListener(null); - holder.checkbox.setChecked(checkedList.contains(info.packageName)); + holder.checkbox.setChecked(checkedList.contains(info.applicationInfo.uid)); - holder.checkbox.setOnCheckedChangeListener((v, isChecked) -> onCheckedChange(v, isChecked, info.packageName)); + holder.checkbox.setOnCheckedChangeListener((v, isChecked) -> onCheckedChange(v, isChecked, info.applicationInfo.uid)); holder.itemView.setOnClickListener(v -> { if (enabled) holder.checkbox.toggle(); }); @@ -421,20 +453,27 @@ public class ScopeAdapter extends RecyclerView.Adapter AsyncTask.THREAD_POOL_EXECUTOR.execute(this::loadApps); } - protected void onCheckedChange(CompoundButton buttonView, boolean isChecked, String packageName) { + protected void onCheckedChange(CompoundButton buttonView, boolean isChecked, int uid) { if (isChecked) { - checkedList.add(packageName); + checkedList.add(uid); } else { - checkedList.remove(packageName); + checkedList.remove((Integer) uid); } - if (!AppHelper.saveScopeList(modulePackageName, checkedList)) { + if (!ConfigManager.setModuleScope(modulePackageName, checkedList)) { activity.makeSnackBar(R.string.failed_to_save_scope_list, Snackbar.LENGTH_SHORT); if (!isChecked) { - checkedList.add(packageName); + checkedList.add(uid); } else { - checkedList.remove(packageName); + checkedList.remove((Integer) uid); } buttonView.setChecked(!isChecked); + } else { + List sharedUidList = sharedUidMap.get(uid); + if (sharedUidList != null) { + for (PackageInfo info : sharedUidList) { + notifyItemChanged(showList.indexOf(info)); + } + } } } @@ -466,11 +505,11 @@ public class ScopeAdapter extends RecyclerView.Adapter @Override protected FilterResults performFiltering(CharSequence constraint) { if (constraint.toString().isEmpty()) { - showList = fullList; + showList = searchList; } else { ArrayList filtered = new ArrayList<>(); String filter = constraint.toString().toLowerCase(); - for (PackageInfo info : fullList) { + for (PackageInfo info : searchList) { if (lowercaseContains(getAppLabel(info.applicationInfo, pm), filter) || lowercaseContains(info.packageName, filter)) { filtered.add(info); @@ -487,13 +526,22 @@ public class ScopeAdapter extends RecyclerView.Adapter } } + private boolean selectedNothing() { + List list = new ArrayList<>(checkedList); + list.removeAll(moduleList); + return list.isEmpty(); + } + public boolean onBackPressed() { - if (masterSwitch.isChecked() && checkedList.isEmpty()) { + if (masterSwitch.isChecked() && selectedNothing()) { AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle(R.string.use_recommended); builder.setMessage(hasRecommended() ? R.string.no_scope_selected_has_recommended : R.string.no_scope_selected); if (hasRecommended()) { - builder.setPositiveButton(android.R.string.ok, (dialog, which) -> checkRecommended()); + builder.setPositiveButton(android.R.string.ok, (dialog, which) -> { + checkRecommended(); + notifyDataSetChanged(); + }); } else { builder.setPositiveButton(android.R.string.cancel, null); } diff --git a/app/src/main/java/io/github/lsposed/manager/receivers/LSPosedManagerServiceClient.java b/app/src/main/java/io/github/lsposed/manager/receivers/LSPosedManagerServiceClient.java index 2880e533..6bae7642 100644 --- a/app/src/main/java/io/github/lsposed/manager/receivers/LSPosedManagerServiceClient.java +++ b/app/src/main/java/io/github/lsposed/manager/receivers/LSPosedManagerServiceClient.java @@ -2,40 +2,148 @@ package io.github.lsposed.manager.receivers; import android.content.pm.PackageInfo; import android.os.IBinder; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; -import android.util.Log; import java.util.List; import io.github.lsposed.lspd.ILSPManagerService; -import io.github.lsposed.manager.App; +import io.github.lsposed.lspd.utils.ParceledListSlice; public class LSPosedManagerServiceClient { + @SuppressWarnings("FieldMayBeFinal") private static IBinder binder = null; private static ILSPManagerService service = null; - public static void testBinder() { - if (service == null && binder != null) { - service = ILSPManagerService.Stub.asInterface(binder); - } - if (service == null) { - return; - } - int ver = -1; - try { - ver = service.getVersion(); - } catch (RemoteException e) { - e.printStackTrace(); - } - Log.i(App.TAG, "Got version " + ver); + /* + public static void testBinder() { + if (binder == null) { + } + if (service == null && binder != null) { + service = ILSPManagerService.Stub.asInterface(binder); + } + if (service == null) { + return; + } + int ver = -1; + try { + ver = service.getVersion(); + } catch (RemoteException e) { + e.printStackTrace(); + } + Log.i(App.TAG, "Got version " + ver); - List ps = null; - try { - ps = service.getInstalledPackagesFromAllUsers(0).getList(); - } catch (RemoteException e) { - e.printStackTrace(); + List ps = null; + try { + ps = service.getInstalledPackagesFromAllUsers(0).getList(); + } catch (RemoteException e) { + e.printStackTrace(); + } + Log.i(App.TAG, String.valueOf(ps)); } - Log.i(App.TAG, String.valueOf(ps)); + */ + private static void ensureService() throws NullPointerException { + if (service == null) { + if (binder != null) { + service = ILSPManagerService.Stub.asInterface(binder); + } else { + throw new NullPointerException("binder is null"); + } + } + } + + public static int getXposedApiVersion() throws RemoteException, NullPointerException { + ensureService(); + return service.getXposedApiVersion(); + } + + public static String getXposedVersionName() throws RemoteException, NullPointerException { + ensureService(); + return service.getXposedVersionName(); + } + + public static int getXposedVersionCode() throws RemoteException, NullPointerException { + ensureService(); + return service.getXposedVersionCode(); + } + + + public static List getInstalledPackagesFromAllUsers(int flags) throws RemoteException, NullPointerException { + ensureService(); + ParceledListSlice parceledListSlice = service.getInstalledPackagesFromAllUsers(flags); + // + return parceledListSlice.getList(); + } + + public static String[] enabledModules() throws RemoteException, NullPointerException { + ensureService(); + return service.enabledModules(); + } + + + public static boolean enableModule(String packageName) throws RemoteException, NullPointerException { + ensureService(); + return service.enableModule(packageName); + } + + public static boolean disableModule(String packageName) throws RemoteException, NullPointerException { + ensureService(); + return service.disableModule(packageName); + } + + public static boolean setModuleScope(String packageName, int[] uid) throws RemoteException, NullPointerException { + ensureService(); + return service.setModuleScope(packageName, uid); + } + + public static int[] getModuleScope(String packageName) throws RemoteException, NullPointerException { + ensureService(); + return service.getModuleScope(packageName); + } + + public static boolean isResourceHook() throws RemoteException, NullPointerException { + ensureService(); + return service.isResourceHook(); + } + + public static void setResourceHook(boolean enabled) throws RemoteException, NullPointerException { + ensureService(); + service.setResourceHook(enabled); + } + + public static boolean isVerboseLog() throws RemoteException, NullPointerException { + ensureService(); + return service.isVerboseLog(); + } + + public static void setVerboseLog(boolean enabled) throws RemoteException, NullPointerException { + ensureService(); + service.setVerboseLog(enabled); + } + + public static int getVariant() throws RemoteException, NullPointerException { + ensureService(); + return service.getVariant(); + } + + public static void setVariant(int variant) throws RemoteException, NullPointerException { + ensureService(); + service.setVariant(variant); + } + + public static boolean isPermissive() throws RemoteException, NullPointerException { + ensureService(); + return service.isPermissive(); + } + + public static ParcelFileDescriptor getVerboseLog() throws RemoteException, NullPointerException { + ensureService(); + return service.getVerboseLog(); + } + + public static ParcelFileDescriptor getModulesLog() throws RemoteException, NullPointerException { + ensureService(); + return service.getModulesLog(); } } diff --git a/app/src/main/java/io/github/lsposed/manager/ui/activity/LogsActivity.java b/app/src/main/java/io/github/lsposed/manager/ui/activity/LogsActivity.java index 01d6507f..47abe4b3 100644 --- a/app/src/main/java/io/github/lsposed/manager/ui/activity/LogsActivity.java +++ b/app/src/main/java/io/github/lsposed/manager/ui/activity/LogsActivity.java @@ -56,6 +56,7 @@ import java.util.Calendar; import java.util.List; import io.github.lsposed.manager.BuildConfig; +import io.github.lsposed.manager.ConfigManager; import io.github.lsposed.manager.Constants; import io.github.lsposed.manager.R; import io.github.lsposed.manager.databinding.ActivityLogsBinding; @@ -115,14 +116,10 @@ public class LogsActivity extends BaseActivity { binding.recyclerView.setAdapter(adapter); layoutManager = new LinearLayoutManagerFix(this); binding.recyclerView.setLayoutManager(layoutManager); - try { - if (Files.readAllBytes(Paths.get(Constants.getMiscDir(), "disable_verbose_log"))[0] == 49) { - binding.slidingTabs.setVisibility(View.GONE); - } else { - RecyclerViewKt.addVerticalPadding(binding.recyclerView, 48, 0); - } - } catch (Exception e) { + if (!ConfigManager.isVerboseLogEnabled()) { binding.slidingTabs.setVisibility(View.GONE); + } else { + RecyclerViewKt.addVerticalPadding(binding.recyclerView, 48, 0); } binding.slidingTabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { @Override diff --git a/app/src/main/java/io/github/lsposed/manager/ui/activity/MainActivity.java b/app/src/main/java/io/github/lsposed/manager/ui/activity/MainActivity.java index 140bcf96..ef3e189d 100644 --- a/app/src/main/java/io/github/lsposed/manager/ui/activity/MainActivity.java +++ b/app/src/main/java/io/github/lsposed/manager/ui/activity/MainActivity.java @@ -35,7 +35,7 @@ import com.google.android.material.snackbar.Snackbar; import java.util.Locale; -import io.github.lsposed.manager.Constants; +import io.github.lsposed.manager.ConfigManager; import io.github.lsposed.manager.R; import io.github.lsposed.manager.databinding.ActivityMainBinding; import io.github.lsposed.manager.databinding.DialogAboutBinding; @@ -59,7 +59,7 @@ public class MainActivity extends BaseActivity { setContentView(binding.getRoot()); HolidayHelper.setup(this); binding.status.setOnClickListener(v -> { - if (Constants.getXposedVersionCode() != -1) { + if (ConfigManager.getXposedApiVersion() != -1) { new StatusDialogBuilder(this) .setTitle(R.string.info) .setPositiveButton(android.R.string.ok, null) @@ -87,18 +87,18 @@ public class MainActivity extends BaseActivity { Glide.with(binding.appIcon) .load(GlideHelper.wrapApplicationInfoForIconLoader(getApplicationInfo())) .into(binding.appIcon); - String installedXposedVersion = Constants.getXposedVersion(); + String installedXposedVersion = ConfigManager.getXposedVersionName(); int cardBackgroundColor; if (installedXposedVersion != null) { - binding.statusTitle.setText(String.format(Locale.US, "%s %s", getString(R.string.Activated), Constants.getXposedVariant())); - if (!Constants.isPermissive()) { + binding.statusTitle.setText(String.format(Locale.US, "%s %s", getString(R.string.Activated), ConfigManager.getVariantString())); + if (!ConfigManager.isPermissive()) { if (Helpers.currentHoliday == Helpers.Holidays.LUNARNEWYEAR) { cardBackgroundColor = 0xfff05654; } else { cardBackgroundColor = ResourcesKt.resolveColor(getTheme(), R.attr.colorNormal); } binding.statusIcon.setImageResource(R.drawable.ic_check_circle); - binding.statusSummary.setText(String.format(Locale.US, "%s (%d)", installedXposedVersion, Constants.getXposedVersionCode())); + binding.statusSummary.setText(String.format(Locale.US, "%s (%d)", installedXposedVersion, ConfigManager.getXposedVersionCode())); } else { cardBackgroundColor = ResourcesKt.resolveColor(getTheme(), R.attr.colorError); binding.statusIcon.setImageResource(R.drawable.ic_warning); @@ -129,7 +129,7 @@ public class MainActivity extends BaseActivity { @Override public void onClick(View v) { - if (requireInstalled && Constants.getXposedVersion() == null) { + if (requireInstalled && ConfigManager.getXposedVersionName() == null) { Snackbar.make(binding.snackbar, R.string.lsposed_not_active, Snackbar.LENGTH_LONG).show(); } else { Intent intent = new Intent(); diff --git a/app/src/main/java/io/github/lsposed/manager/ui/activity/ModulesActivity.java b/app/src/main/java/io/github/lsposed/manager/ui/activity/ModulesActivity.java index b4a2a434..63a8dec2 100644 --- a/app/src/main/java/io/github/lsposed/manager/ui/activity/ModulesActivity.java +++ b/app/src/main/java/io/github/lsposed/manager/ui/activity/ModulesActivity.java @@ -57,7 +57,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; -import io.github.lsposed.manager.Constants; +import io.github.lsposed.manager.ConfigManager; import io.github.lsposed.manager.R; import io.github.lsposed.manager.adapters.AppHelper; import io.github.lsposed.manager.ui.activity.base.ListActivity; @@ -188,7 +188,7 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi sb.append(getString(R.string.module_empty_description)); } - int installedXposedVersion = Constants.getXposedApiVersion(); + int installedXposedVersion = ConfigManager.getXposedApiVersion(); String warningText = null; if (item.minVersion == 0) { warningText = getString(R.string.no_min_version_specified); diff --git a/app/src/main/java/io/github/lsposed/manager/ui/activity/SettingsActivity.java b/app/src/main/java/io/github/lsposed/manager/ui/activity/SettingsActivity.java index 839972d6..13662435 100644 --- a/app/src/main/java/io/github/lsposed/manager/ui/activity/SettingsActivity.java +++ b/app/src/main/java/io/github/lsposed/manager/ui/activity/SettingsActivity.java @@ -29,7 +29,6 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.ViewGroup; -import android.widget.Toast; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; @@ -45,15 +44,11 @@ import com.google.android.material.snackbar.Snackbar; import com.takisoft.preferencex.PreferenceFragmentCompat; import com.takisoft.preferencex.SimpleMenuPreference; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Calendar; import java.util.Locale; import io.github.lsposed.manager.BuildConfig; -import io.github.lsposed.manager.Constants; +import io.github.lsposed.manager.ConfigManager; import io.github.lsposed.manager.R; import io.github.lsposed.manager.databinding.ActivitySettingsBinding; import io.github.lsposed.manager.ui.activity.base.BaseActivity; @@ -101,7 +96,7 @@ public class SettingsActivity extends BaseActivity { getSupportFragmentManager().beginTransaction() .add(R.id.container, new SettingsFragment()).commit(); } - if (Constants.getXposedVersion() == null) { + if (ConfigManager.getXposedVersionName() == null) { Snackbar.make(binding.snackbar, R.string.lsposed_not_active, Snackbar.LENGTH_LONG).show(); } } @@ -142,9 +137,6 @@ public class SettingsActivity extends BaseActivity { } public static class SettingsFragment extends PreferenceFragmentCompat { - private static final Path enableResourcesFlag = Paths.get(Constants.getConfDir(), "enable_resources"); - private static final Path disableVerboseLogsFlag = Paths.get(Constants.getMiscDir(), "disable_verbose_log"); - private static final Path variantFlag = Paths.get(Constants.getMiscDir(), "variant"); ActivityResultLauncher backupLauncher = registerForActivityResult(new ActivityResultContracts.CreateDocument(), uri -> { if (uri != null) { @@ -204,36 +196,23 @@ public class SettingsActivity extends BaseActivity { public void onCreatePreferencesFix(Bundle savedInstanceState, String rootKey) { addPreferencesFromResource(R.xml.prefs); - boolean installed = Constants.getXposedVersion() != null; + boolean installed = ConfigManager.getXposedVersionName() != null; SwitchPreference prefVerboseLogs = findPreference("disable_verbose_log"); if (prefVerboseLogs != null) { if (requireActivity().getApplicationInfo().uid / 100000 != 0) { prefVerboseLogs.setVisible(false); } else { prefVerboseLogs.setEnabled(installed); - try { - prefVerboseLogs.setChecked(Files.readAllBytes(disableVerboseLogsFlag)[0] == 49); - } catch (Exception e) { - e.printStackTrace(); - } - prefVerboseLogs.setOnPreferenceChangeListener((preference, newValue) -> { - try { - Files.write(disableVerboseLogsFlag, ((boolean) newValue) ? new byte[]{49, 0} : new byte[]{48, 0}); - return true; - } catch (IOException e) { - e.printStackTrace(); - Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_SHORT).show(); - return false; - } - }); + prefVerboseLogs.setChecked(!ConfigManager.isVerboseLogEnabled()); + prefVerboseLogs.setOnPreferenceChangeListener((preference, newValue) -> ConfigManager.setVerboseLogEnabled(!(boolean) newValue)); } } SwitchPreference prefEnableResources = findPreference("enable_resources"); if (prefEnableResources != null) { prefEnableResources.setEnabled(installed); - prefEnableResources.setChecked(Files.exists(enableResourcesFlag)); - prefEnableResources.setOnPreferenceChangeListener(new OnFlagChangeListener(enableResourcesFlag)); + prefEnableResources.setChecked(ConfigManager.isResourceHookEnabled()); + prefEnableResources.setOnPreferenceChangeListener((preference, newValue) -> ConfigManager.setResourceHookEnabled((boolean) newValue)); } SimpleMenuPreference prefVariant = findPreference("variant"); @@ -242,21 +221,8 @@ public class SettingsActivity extends BaseActivity { prefVariant.setVisible(false); } else { prefVariant.setEnabled(installed); - try { - prefVariant.setValue(new String(Files.readAllBytes(variantFlag)).trim()); - } catch (Exception e) { - e.printStackTrace(); - } - prefVariant.setOnPreferenceChangeListener((preference, newValue) -> { - try { - Files.write(variantFlag, ((String) newValue).getBytes()); - return true; - } catch (IOException e) { - e.printStackTrace(); - Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_SHORT).show(); - return false; - } - }); + prefVariant.setValue(String.valueOf(ConfigManager.getVariant())); + prefVariant.setOnPreferenceChangeListener((preference, newValue) -> ConfigManager.setVariant(Integer.parseInt((String) newValue))); } } @@ -320,30 +286,6 @@ public class SettingsActivity extends BaseActivity { } } - private class OnFlagChangeListener implements Preference.OnPreferenceChangeListener { - private final Path flag; - - OnFlagChangeListener(Path flag) { - this.flag = flag; - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - boolean enabled = (Boolean) newValue; - try { - if (enabled) { - Files.createFile(flag); - } else { - Files.delete(flag); - } - } catch (Exception e) { - e.printStackTrace(); - Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_SHORT).show(); - } - return (enabled == Files.exists(flag)); - } - } - @Override public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { BorderRecyclerView recyclerView = (BorderRecyclerView) super.onCreateRecyclerView(inflater, parent, savedInstanceState); diff --git a/app/src/main/java/io/github/lsposed/manager/ui/activity/base/BaseActivity.java b/app/src/main/java/io/github/lsposed/manager/ui/activity/base/BaseActivity.java index c25f7ba0..1ecb6636 100644 --- a/app/src/main/java/io/github/lsposed/manager/ui/activity/base/BaseActivity.java +++ b/app/src/main/java/io/github/lsposed/manager/ui/activity/base/BaseActivity.java @@ -33,7 +33,7 @@ import androidx.appcompat.app.AlertDialog; import io.github.lsposed.manager.App; import io.github.lsposed.manager.BuildConfig; -import io.github.lsposed.manager.Constants; +import io.github.lsposed.manager.ConfigManager; import io.github.lsposed.manager.R; import io.github.lsposed.manager.util.NavUtil; import io.github.lsposed.manager.util.theme.ThemeUtil; @@ -53,7 +53,7 @@ public class BaseActivity extends MaterialActivity { super.onCreate(savedInstanceState); // make sure the versions are consistent - String coreVersionStr = Constants.getXposedVersion(); + String coreVersionStr = ConfigManager.getXposedVersionName(); if (coreVersionStr != null) { if (!BuildConfig.VERSION_NAME.equals(coreVersionStr)) { new AlertDialog.Builder(this) diff --git a/app/src/main/java/io/github/lsposed/manager/ui/fragment/StatusDialogBuilder.java b/app/src/main/java/io/github/lsposed/manager/ui/fragment/StatusDialogBuilder.java index 3eee9704..5f06ff46 100644 --- a/app/src/main/java/io/github/lsposed/manager/ui/fragment/StatusDialogBuilder.java +++ b/app/src/main/java/io/github/lsposed/manager/ui/fragment/StatusDialogBuilder.java @@ -38,7 +38,7 @@ import java.util.Locale; import dalvik.system.VMRuntime; import io.github.lsposed.manager.BuildConfig; -import io.github.lsposed.manager.Constants; +import io.github.lsposed.manager.ConfigManager; import io.github.lsposed.manager.R; import io.github.lsposed.manager.databinding.StatusInstallerBinding; @@ -66,20 +66,20 @@ public class StatusDialogBuilder extends AlertDialog.Builder { super(context); StatusInstallerBinding binding = StatusInstallerBinding.inflate(LayoutInflater.from(context), null, false); - String installedXposedVersion = Constants.getXposedVersion(); + String installedXposedVersion = ConfigManager.getXposedVersionName(); String mAppVer = String.format("%s (%s)", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE); binding.manager.setText(mAppVer); if (installedXposedVersion != null) { - binding.api.setText(String.format(Locale.US, "%s.0", Constants.getXposedApiVersion())); - binding.framework.setText(String.format(Locale.US, "%s (%s)", installedXposedVersion, Constants.getXposedVersionCode())); + binding.api.setText(String.format(Locale.US, "%s.0", ConfigManager.getXposedApiVersion())); + binding.framework.setText(String.format(Locale.US, "%s (%s)", installedXposedVersion, ConfigManager.getXposedVersionCode())); } binding.androidVersion.setText(context.getString(R.string.android_sdk, getAndroidVersion(), Build.VERSION.RELEASE, Build.VERSION.SDK_INT)); binding.manufacturer.setText(getUIFramework()); binding.cpu.setText(getCompleteArch()); - if (Constants.isPermissive()) { + if (ConfigManager.isPermissive()) { binding.selinux.setVisibility(View.VISIBLE); binding.selinux.setText(HtmlCompat.fromHtml(context.getString(R.string.selinux_permissive), HtmlCompat.FROM_HTML_MODE_LEGACY)); } diff --git a/app/src/main/java/io/github/lsposed/manager/util/BackupUtils.java b/app/src/main/java/io/github/lsposed/manager/util/BackupUtils.java index adcbf0dd..17ab129a 100644 --- a/app/src/main/java/io/github/lsposed/manager/util/BackupUtils.java +++ b/app/src/main/java/io/github/lsposed/manager/util/BackupUtils.java @@ -34,8 +34,6 @@ import java.util.Map; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; -import io.github.lsposed.manager.adapters.AppHelper; - public class BackupUtils { private static final int VERSION = 1; @@ -56,11 +54,11 @@ public class BackupUtils { JSONObject moduleObject = new JSONObject(); moduleObject.put("enable", ModuleUtil.getInstance().isModuleEnabled(module.packageName)); moduleObject.put("package", module.packageName); - List scope = AppHelper.getScopeList(module.packageName); + //List scope = AppHelper.getScopeList(module.packageName); JSONArray scopeArray = new JSONArray(); - for (String s : scope) { - scopeArray.put(s); - } + //for (String s : scope) { + // scopeArray.put(s); + //} moduleObject.put("scope", scopeArray); modulesArray.put(moduleObject); } @@ -110,7 +108,7 @@ public class BackupUtils { for (int j = 0; j < scopeArray.length(); j++) { scope.add(scopeArray.getString(j)); } - AppHelper.saveScopeList(name, scope); + //AppHelper.saveScopeList(name, scope); } } } diff --git a/app/src/main/java/io/github/lsposed/manager/util/ModuleUtil.java b/app/src/main/java/io/github/lsposed/manager/util/ModuleUtil.java index b6ffd677..5340f777 100644 --- a/app/src/main/java/io/github/lsposed/manager/util/ModuleUtil.java +++ b/app/src/main/java/io/github/lsposed/manager/util/ModuleUtil.java @@ -25,12 +25,10 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Build; -import android.util.Log; import androidx.annotation.NonNull; -import java.io.IOException; -import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; @@ -40,8 +38,7 @@ import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import io.github.lsposed.manager.App; -import io.github.lsposed.manager.Constants; -import io.github.lsposed.manager.adapters.AppHelper; +import io.github.lsposed.manager.ConfigManager; import io.github.lsposed.manager.repo.RepoLoader; import io.github.lsposed.manager.repo.model.OnlineModule; @@ -57,7 +54,7 @@ public final class ModuleUtil { private ModuleUtil() { pm = App.getInstance().getPackageManager(); - enabledModules = AppHelper.getEnabledModuleList(); + enabledModules = new ArrayList<>(Arrays.asList(ConfigManager.getEnabledModules())); } public static synchronized ModuleUtil getInstance() { @@ -148,16 +145,18 @@ public final class ModuleUtil { return installedModules; } - public void setModuleEnabled(String packageName, boolean enabled) { + public boolean setModuleEnabled(String packageName, boolean enabled) { + if (!ConfigManager.setModuleEnabled(packageName, enabled)) { + return false; + } if (enabled) { if (!enabledModules.contains(packageName)) { enabledModules.add(packageName); - updateModulesList(); } } else { enabledModules.remove(packageName); - updateModulesList(); } + return true; } public boolean isModuleEnabled(String packageName) { @@ -179,7 +178,7 @@ public final class ModuleUtil { } public synchronized void updateModulesList() { - try { + /*try { Log.i(App.TAG, "ModuleUtil -> updating modules.list"); PrintWriter modulesList = new PrintWriter(Constants.getModulesListFile()); PrintWriter enabledModulesList = new PrintWriter(Constants.getEnabledModulesListFile()); @@ -192,7 +191,7 @@ public final class ModuleUtil { enabledModulesList.close(); } catch (IOException e) { Log.e(App.TAG, "ModuleUtil -> cannot write " + Constants.getModulesListFile(), e); - } + }*/ } public void addListener(ModuleListener listener) { diff --git a/app/src/main/java/io/github/lsposed/manager/util/theme/ThemeColorPreference.java b/app/src/main/java/io/github/lsposed/manager/util/theme/ThemeColorPreference.java index d3960f9d..d46f8cbf 100644 --- a/app/src/main/java/io/github/lsposed/manager/util/theme/ThemeColorPreference.java +++ b/app/src/main/java/io/github/lsposed/manager/util/theme/ThemeColorPreference.java @@ -28,12 +28,10 @@ import android.os.Parcelable; import android.util.AttributeSet; import android.widget.ImageView; -import androidx.core.content.ContextCompat; import androidx.core.content.res.TypedArrayUtils; import androidx.preference.DialogPreference; import androidx.preference.PreferenceViewHolder; -import com.takisoft.colorpicker.ColorStateDrawable; import com.takisoft.preferencex.PreferenceFragmentCompat; import io.github.lsposed.manager.R;