Reload ModulesFragment after user removed or added (#1465)
Co-authored-by: LoveSy <shana@zju.edu.cn>
This commit is contained in:
parent
61c447091f
commit
6a735d2064
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
package org.lsposed.manager;
|
package org.lsposed.manager;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
@ -116,6 +117,10 @@ public class App extends Application {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String TAG = "LSPosedManager";
|
public static final String TAG = "LSPosedManager";
|
||||||
|
private static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
|
||||||
|
private static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
|
||||||
|
private static final String ACTION_USER_INFO_CHANGED = "android.intent.action.USER_INFO_CHANGED";
|
||||||
|
private static final String EXTRA_REMOVED_FOR_ALL_USERS = "android.intent.extra.REMOVED_FOR_ALL_USERS";
|
||||||
private static App instance = null;
|
private static App instance = null;
|
||||||
private static OkHttpClient okHttpClient;
|
private static OkHttpClient okHttpClient;
|
||||||
private static Cache okHttpCache;
|
private static Cache okHttpCache;
|
||||||
|
|
@ -138,6 +143,7 @@ public class App extends Application {
|
||||||
return !Process.isApplicationUid(Process.myUid());
|
return !Process.isApplicationUid(Process.myUid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("WrongConstant")
|
||||||
private void setCrashReport() {
|
private void setCrashReport() {
|
||||||
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
|
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
|
||||||
|
|
||||||
|
|
@ -182,17 +188,39 @@ public class App extends Application {
|
||||||
DayNightDelegate.setDefaultNightMode(ThemeUtil.getDarkTheme());
|
DayNightDelegate.setDefaultNightMode(ThemeUtil.getDarkTheme());
|
||||||
LocaleDelegate.setDefaultLocale(getLocale());
|
LocaleDelegate.setDefaultLocale(getLocale());
|
||||||
|
|
||||||
|
IntentFilter intentFilter = new IntentFilter();
|
||||||
|
intentFilter.addAction("org.lsposed.manager.NOTIFICATION");
|
||||||
registerReceiver(new BroadcastReceiver() {
|
registerReceiver(new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent inIntent) {
|
||||||
int userId = intent.getIntExtra(Intent.EXTRA_USER, 0);
|
var intent = (Intent) inIntent.getParcelableExtra(Intent.EXTRA_INTENT);
|
||||||
String packageName = intent.getStringExtra("android.intent.extra.PACKAGES");
|
Log.d(TAG, "onReceive: " + intent);
|
||||||
boolean packageFullyRemoved = intent.getBooleanExtra(Intent.ACTION_PACKAGE_FULLY_REMOVED, false);
|
switch (intent.getAction()) {
|
||||||
if (packageName != null) {
|
case Intent.ACTION_PACKAGE_ADDED:
|
||||||
ModuleUtil.getInstance().reloadSingleModule(packageName, userId, packageFullyRemoved);
|
case Intent.ACTION_PACKAGE_CHANGED:
|
||||||
|
case Intent.ACTION_PACKAGE_FULLY_REMOVED:
|
||||||
|
case Intent.ACTION_UID_REMOVED: {
|
||||||
|
var userId = intent.getIntExtra(Intent.EXTRA_USER, 0);
|
||||||
|
var packageName = intent.getStringExtra("android.intent.extra.PACKAGES");
|
||||||
|
var packageRemovedForAllUsers = intent.getBooleanExtra(EXTRA_REMOVED_FOR_ALL_USERS, false);
|
||||||
|
var isXposedModule = intent.getBooleanExtra("isXposedModule", false);
|
||||||
|
if (packageName != null) {
|
||||||
|
if (isXposedModule)
|
||||||
|
ModuleUtil.getInstance().reloadSingleModule(packageName, userId, packageRemovedForAllUsers);
|
||||||
|
else
|
||||||
|
App.getExecutorService().submit(() -> AppHelper.getAppList(true));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ACTION_USER_ADDED:
|
||||||
|
case ACTION_USER_REMOVED:
|
||||||
|
case ACTION_USER_INFO_CHANGED: {
|
||||||
|
App.getExecutorService().submit(() -> ModuleUtil.getInstance().reloadInstalledModules());
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, new IntentFilter(Intent.ACTION_PACKAGE_CHANGED));
|
}, intentFilter);
|
||||||
|
|
||||||
UpdateUtil.loadRemoteVersion();
|
UpdateUtil.loadRemoteVersion();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
package org.lsposed.manager.adapters;
|
package org.lsposed.manager.adapters;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
|
|
@ -44,6 +45,7 @@ public class AppHelper {
|
||||||
private static List<PackageInfo> appList;
|
private static List<PackageInfo> appList;
|
||||||
private static final ConcurrentHashMap<PackageInfo, CharSequence> appLabel = new ConcurrentHashMap<>();
|
private static final ConcurrentHashMap<PackageInfo, CharSequence> appLabel = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@SuppressLint("WrongConstant")
|
||||||
public static Intent getSettingsIntent(String packageName, int userId) {
|
public static Intent getSettingsIntent(String packageName, int userId) {
|
||||||
Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
|
Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
|
||||||
intentToResolve.addCategory(SETTINGS_CATEGORY);
|
intentToResolve.addCategory(SETTINGS_CATEGORY);
|
||||||
|
|
@ -63,6 +65,7 @@ public class AppHelper {
|
||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("WrongConstant")
|
||||||
public static Intent getLaunchIntentForPackage(String packageName, int userId) {
|
public static Intent getLaunchIntentForPackage(String packageName, int userId) {
|
||||||
Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
|
Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
|
||||||
intentToResolve.addCategory(Intent.CATEGORY_INFO);
|
intentToResolve.addCategory(Intent.CATEGORY_INFO);
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,7 @@ public class ScopeAdapter extends EmptyStateRecyclerView.EmptyStateAdapter<Scope
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("WrongConstant")
|
||||||
public boolean onContextItemSelected(@NonNull MenuItem item) {
|
public boolean onContextItemSelected(@NonNull MenuItem item) {
|
||||||
ApplicationInfo info = selectedInfo;
|
ApplicationInfo info = selectedInfo;
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@
|
||||||
package org.lsposed.manager.ui.fragment;
|
package org.lsposed.manager.ui.fragment;
|
||||||
|
|
||||||
import static android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS;
|
import static android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS;
|
||||||
import static androidx.recyclerview.widget.RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
|
@ -37,6 +36,7 @@ import android.text.TextUtils;
|
||||||
import android.text.style.ForegroundColorSpan;
|
import android.text.style.ForegroundColorSpan;
|
||||||
import android.text.style.StyleSpan;
|
import android.text.style.StyleSpan;
|
||||||
import android.text.style.TypefaceSpan;
|
import android.text.style.TypefaceSpan;
|
||||||
|
import android.util.SparseArray;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
@ -72,9 +72,9 @@ import org.lsposed.manager.App;
|
||||||
import org.lsposed.manager.ConfigManager;
|
import org.lsposed.manager.ConfigManager;
|
||||||
import org.lsposed.manager.R;
|
import org.lsposed.manager.R;
|
||||||
import org.lsposed.manager.adapters.AppHelper;
|
import org.lsposed.manager.adapters.AppHelper;
|
||||||
import org.lsposed.manager.databinding.SwiperefreshRecyclerviewBinding;
|
|
||||||
import org.lsposed.manager.databinding.FragmentPagerBinding;
|
import org.lsposed.manager.databinding.FragmentPagerBinding;
|
||||||
import org.lsposed.manager.databinding.ItemModuleBinding;
|
import org.lsposed.manager.databinding.ItemModuleBinding;
|
||||||
|
import org.lsposed.manager.databinding.SwiperefreshRecyclerviewBinding;
|
||||||
import org.lsposed.manager.repo.RepoLoader;
|
import org.lsposed.manager.repo.RepoLoader;
|
||||||
import org.lsposed.manager.ui.dialog.BlurBehindDialogBuilder;
|
import org.lsposed.manager.ui.dialog.BlurBehindDialogBuilder;
|
||||||
import org.lsposed.manager.ui.widget.EmptyStateRecyclerView;
|
import org.lsposed.manager.ui.widget.EmptyStateRecyclerView;
|
||||||
|
|
@ -95,12 +95,12 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi
|
||||||
private static final PackageManager pm = App.getInstance().getPackageManager();
|
private static final PackageManager pm = App.getInstance().getPackageManager();
|
||||||
private static final ModuleUtil moduleUtil = ModuleUtil.getInstance();
|
private static final ModuleUtil moduleUtil = ModuleUtil.getInstance();
|
||||||
private static final RepoLoader repoLoader = RepoLoader.getInstance();
|
private static final RepoLoader repoLoader = RepoLoader.getInstance();
|
||||||
private static final List<UserInfo> users = ConfigManager.getUsers();
|
|
||||||
protected FragmentPagerBinding binding;
|
protected FragmentPagerBinding binding;
|
||||||
protected SearchView searchView;
|
protected SearchView searchView;
|
||||||
private SearchView.OnQueryTextListener searchListener;
|
private SearchView.OnQueryTextListener searchListener;
|
||||||
|
|
||||||
final ArrayList<ModuleAdapter> adapters = new ArrayList<>();
|
SparseArray<ModuleAdapter> adapters = new SparseArray<>();
|
||||||
|
PagerAdapter pagerAdapter = null;
|
||||||
|
|
||||||
private ModuleUtil.InstalledModule selectedModule;
|
private ModuleUtil.InstalledModule selectedModule;
|
||||||
|
|
||||||
|
|
@ -110,24 +110,22 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi
|
||||||
searchListener = new SearchView.OnQueryTextListener() {
|
searchListener = new SearchView.OnQueryTextListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onQueryTextSubmit(String query) {
|
public boolean onQueryTextSubmit(String query) {
|
||||||
adapters.forEach(adapter -> adapter.getFilter().filter(query));
|
forEachAdaptor(adapter -> adapter.getFilter().filter(query));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onQueryTextChange(String query) {
|
public boolean onQueryTextChange(String query) {
|
||||||
adapters.forEach(adapter -> adapter.getFilter().filter(query));
|
forEachAdaptor(adapter -> adapter.getFilter().filter(query));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (users != null) {
|
private void forEachAdaptor(Consumer<? super ModuleAdapter> action) {
|
||||||
for (var user : users) {
|
var snapshot = adapters;
|
||||||
var adapter = new ModuleAdapter(user);
|
for (var i = 0; i < snapshot.size(); ++i) {
|
||||||
adapter.setHasStableIds(true);
|
action.accept(snapshot.valueAt(i));
|
||||||
adapter.setStateRestorationPolicy(PREVENT_WHEN_EMPTY);
|
|
||||||
adapters.add(adapter);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,7 +147,8 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi
|
||||||
binding = FragmentPagerBinding.inflate(inflater, container, false);
|
binding = FragmentPagerBinding.inflate(inflater, container, false);
|
||||||
binding.appBar.setLiftable(true);
|
binding.appBar.setLiftable(true);
|
||||||
setupToolbar(binding.toolbar, binding.clickView, R.string.Modules, R.menu.menu_modules);
|
setupToolbar(binding.toolbar, binding.clickView, R.string.Modules, R.menu.menu_modules);
|
||||||
binding.viewPager.setAdapter(new PagerAdapter(this));
|
pagerAdapter = new PagerAdapter(this);
|
||||||
|
binding.viewPager.setAdapter(pagerAdapter);
|
||||||
binding.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
binding.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onPageSelected(int position) {
|
public void onPageSelected(int position) {
|
||||||
|
|
@ -159,29 +158,22 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi
|
||||||
|
|
||||||
new TabLayoutMediator(binding.tabLayout, binding.viewPager, (tab, position) -> {
|
new TabLayoutMediator(binding.tabLayout, binding.viewPager, (tab, position) -> {
|
||||||
if (position < adapters.size()) {
|
if (position < adapters.size()) {
|
||||||
tab.setText(adapters.get(position).getUser().name);
|
tab.setText(adapters.valueAt(position).getUser().name);
|
||||||
}
|
}
|
||||||
}).attach();
|
}).attach();
|
||||||
|
|
||||||
if (users != null && users.size() != 1) {
|
binding.tabLayout.addOnLayoutChangeListener((view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
|
||||||
binding.viewPager.setUserInputEnabled(true);
|
ViewGroup vg = (ViewGroup) binding.tabLayout.getChildAt(0);
|
||||||
binding.tabLayout.setVisibility(View.VISIBLE);
|
int tabLayoutWidth = IntStream.range(0, binding.tabLayout.getTabCount()).map(i -> vg.getChildAt(i).getWidth()).sum();
|
||||||
binding.tabLayout.addOnLayoutChangeListener((view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
|
if (tabLayoutWidth <= binding.getRoot().getWidth()) {
|
||||||
ViewGroup vg = (ViewGroup) binding.tabLayout.getChildAt(0);
|
binding.tabLayout.setTabMode(TabLayout.MODE_FIXED);
|
||||||
int tabLayoutWidth = IntStream.range(0, binding.tabLayout.getTabCount()).map(i -> vg.getChildAt(i).getWidth()).sum();
|
binding.tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
|
||||||
if (tabLayoutWidth <= binding.getRoot().getWidth()) {
|
}
|
||||||
binding.tabLayout.setTabMode(TabLayout.MODE_FIXED);
|
});
|
||||||
binding.tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
binding.fab.show();
|
|
||||||
} else {
|
|
||||||
binding.viewPager.setUserInputEnabled(false);
|
|
||||||
binding.tabLayout.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
binding.fab.setOnClickListener(v -> {
|
binding.fab.setOnClickListener(v -> {
|
||||||
var bundle = new Bundle();
|
var bundle = new Bundle();
|
||||||
var user = adapters.get(binding.viewPager.getCurrentItem()).getUser();
|
var user = adapters.valueAt(binding.viewPager.getCurrentItem()).getUser();
|
||||||
bundle.putParcelable("userInfo", user);
|
bundle.putParcelable("userInfo", user);
|
||||||
var f = new RecyclerViewDialogFragment();
|
var f = new RecyclerViewDialogFragment();
|
||||||
f.setArguments(bundle);
|
f.setArguments(bundle);
|
||||||
|
|
@ -190,7 +182,7 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi
|
||||||
|
|
||||||
moduleUtil.addListener(this);
|
moduleUtil.addListener(this);
|
||||||
repoLoader.addListener(this);
|
repoLoader.addListener(this);
|
||||||
updateModuleSummary();
|
onModulesReloaded();
|
||||||
|
|
||||||
return binding.getRoot();
|
return binding.getRoot();
|
||||||
}
|
}
|
||||||
|
|
@ -214,23 +206,48 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
adapters.forEach(ModuleAdapter::refresh);
|
forEachAdaptor(ModuleAdapter::refresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSingleModuleReloaded(ModuleUtil.InstalledModule module) {
|
public void onSingleModuleReloaded(ModuleUtil.InstalledModule module) {
|
||||||
adapters.forEach(ModuleAdapter::refresh);
|
forEachAdaptor(ModuleAdapter::refresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onModulesReloaded() {
|
public void onModulesReloaded() {
|
||||||
adapters.forEach(ModuleAdapter::refresh);
|
var users = moduleUtil.getUsers();
|
||||||
|
if (users == null) return;
|
||||||
|
|
||||||
|
if (users.size() != 1) {
|
||||||
|
binding.viewPager.setUserInputEnabled(true);
|
||||||
|
binding.tabLayout.setVisibility(View.VISIBLE);
|
||||||
|
binding.fab.show();
|
||||||
|
} else {
|
||||||
|
binding.viewPager.setUserInputEnabled(false);
|
||||||
|
binding.tabLayout.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
var tmp = new SparseArray<ModuleAdapter>(users.size());
|
||||||
|
var snapshot = adapters;
|
||||||
|
for (var user : users) {
|
||||||
|
if (snapshot.indexOfKey(user.id) >= 0) {
|
||||||
|
tmp.put(user.id, snapshot.get(user.id));
|
||||||
|
} else {
|
||||||
|
var adapter = new ModuleAdapter(user);
|
||||||
|
adapter.setHasStableIds(true);
|
||||||
|
tmp.put(user.id, adapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapters = tmp;
|
||||||
|
forEachAdaptor(ModuleAdapter::refresh);
|
||||||
|
runOnUiThread(pagerAdapter::notifyDataSetChanged);
|
||||||
updateModuleSummary();
|
updateModuleSummary();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRepoLoaded() {
|
public void onRepoLoaded() {
|
||||||
adapters.forEach(ModuleAdapter::refresh);
|
forEachAdaptor(ModuleAdapter::refresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateModuleSummary() {
|
private void updateModuleSummary() {
|
||||||
|
|
@ -261,6 +278,7 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("WrongConstant")
|
||||||
@Override
|
@Override
|
||||||
public boolean onContextItemSelected(@NonNull MenuItem item) {
|
public boolean onContextItemSelected(@NonNull MenuItem item) {
|
||||||
if (selectedModule == null) {
|
if (selectedModule == null) {
|
||||||
|
|
@ -348,9 +366,9 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi
|
||||||
if (fragment == null || arguments == null) {
|
if (fragment == null || arguments == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
int position = arguments.getInt("position");
|
int userId = arguments.getInt("user_id");
|
||||||
binding = SwiperefreshRecyclerviewBinding.inflate(getLayoutInflater(), container, false);
|
binding = SwiperefreshRecyclerviewBinding.inflate(getLayoutInflater(), container, false);
|
||||||
adapter = fragment.adapters.get(position);
|
adapter = fragment.adapters.get(userId);
|
||||||
binding.recyclerView.setAdapter(adapter);
|
binding.recyclerView.setAdapter(adapter);
|
||||||
binding.recyclerView.setLayoutManager(new LinearLayoutManager(requireActivity()));
|
binding.recyclerView.setLayoutManager(new LinearLayoutManager(requireActivity()));
|
||||||
binding.swipeRefreshLayout.setOnRefreshListener(adapter::fullRefresh);
|
binding.swipeRefreshLayout.setOnRefreshListener(adapter::fullRefresh);
|
||||||
|
|
@ -430,7 +448,7 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi
|
||||||
@Override
|
@Override
|
||||||
public Fragment createFragment(int position) {
|
public Fragment createFragment(int position) {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putInt("position", position);
|
bundle.putInt("user_id", adapters.keyAt(position));
|
||||||
Fragment fragment = new ModuleListFragment();
|
Fragment fragment = new ModuleListFragment();
|
||||||
fragment.setArguments(bundle);
|
fragment.setArguments(bundle);
|
||||||
return fragment;
|
return fragment;
|
||||||
|
|
@ -443,7 +461,12 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getItemId(int position) {
|
public long getItemId(int position) {
|
||||||
return position;
|
return adapters.keyAt(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsItem(long itemId) {
|
||||||
|
return adapters.indexOfKey((int) itemId) >= 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,13 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.util.Pair;
|
import androidx.core.util.Pair;
|
||||||
|
|
||||||
|
import org.lsposed.lspd.models.UserInfo;
|
||||||
import org.lsposed.manager.App;
|
import org.lsposed.manager.App;
|
||||||
import org.lsposed.manager.ConfigManager;
|
import org.lsposed.manager.ConfigManager;
|
||||||
import org.lsposed.manager.repo.RepoLoader;
|
import org.lsposed.manager.repo.RepoLoader;
|
||||||
import org.lsposed.manager.repo.model.OnlineModule;
|
import org.lsposed.manager.repo.model.OnlineModule;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
@ -50,6 +52,7 @@ public final class ModuleUtil {
|
||||||
private final PackageManager pm;
|
private final PackageManager pm;
|
||||||
private final Set<ModuleListener> listeners = ConcurrentHashMap.newKeySet();
|
private final Set<ModuleListener> listeners = ConcurrentHashMap.newKeySet();
|
||||||
private HashSet<String> enabledModules = new HashSet<>();
|
private HashSet<String> enabledModules = new HashSet<>();
|
||||||
|
private List<UserInfo> users = new ArrayList<>();
|
||||||
private Map<Pair<String, Integer>, InstalledModule> installedModules = new HashMap<>();
|
private Map<Pair<String, Integer>, InstalledModule> installedModules = new HashMap<>();
|
||||||
private boolean modulesLoaded = false;
|
private boolean modulesLoaded = false;
|
||||||
|
|
||||||
|
|
@ -89,6 +92,7 @@ public final class ModuleUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Pair<String, Integer>, InstalledModule> modules = new HashMap<>();
|
Map<Pair<String, Integer>, InstalledModule> modules = new HashMap<>();
|
||||||
|
var users = ConfigManager.getUsers();
|
||||||
for (PackageInfo pkg : ConfigManager.getInstalledPackagesFromAllUsers(PackageManager.GET_META_DATA, false)) {
|
for (PackageInfo pkg : ConfigManager.getInstalledPackagesFromAllUsers(PackageManager.GET_META_DATA, false)) {
|
||||||
ApplicationInfo app = pkg.applicationInfo;
|
ApplicationInfo app = pkg.applicationInfo;
|
||||||
|
|
||||||
|
|
@ -100,11 +104,18 @@ public final class ModuleUtil {
|
||||||
|
|
||||||
installedModules = modules;
|
installedModules = modules;
|
||||||
|
|
||||||
|
this.users = users;
|
||||||
|
|
||||||
enabledModules = new HashSet<>(Arrays.asList(ConfigManager.getEnabledModules()));
|
enabledModules = new HashSet<>(Arrays.asList(ConfigManager.getEnabledModules()));
|
||||||
modulesLoaded = true;
|
modulesLoaded = true;
|
||||||
listeners.forEach(ModuleListener::onModulesReloaded);
|
listeners.forEach(ModuleListener::onModulesReloaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public List<UserInfo> getUsers() {
|
||||||
|
return modulesLoaded ? users : null;
|
||||||
|
}
|
||||||
|
|
||||||
public InstalledModule reloadSingleModule(String packageName, int userId) {
|
public InstalledModule reloadSingleModule(String packageName, int userId) {
|
||||||
return reloadSingleModule(packageName, userId, false);
|
return reloadSingleModule(packageName, userId, false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ package org.lsposed.lspd.service;
|
||||||
|
|
||||||
import static org.lsposed.lspd.service.ServiceManager.TAG;
|
import static org.lsposed.lspd.service.ServiceManager.TAG;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.app.IActivityManager;
|
import android.app.IActivityManager;
|
||||||
import android.app.IApplicationThread;
|
import android.app.IApplicationThread;
|
||||||
import android.app.IServiceConnection;
|
import android.app.IServiceConnection;
|
||||||
|
|
@ -101,17 +102,16 @@ public class ActivityManagerService {
|
||||||
return am.startUserInBackground(userId);
|
return am.startUserInBackground(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NewApi")
|
||||||
public static Intent registerReceiver(String callerPackage,
|
public static Intent registerReceiver(String callerPackage,
|
||||||
String callingFeatureId, IIntentReceiver receiver, IntentFilter filter,
|
String callingFeatureId, IIntentReceiver receiver, IntentFilter filter,
|
||||||
String requiredPermission, int userId, int flags) throws RemoteException {
|
String requiredPermission, int userId, int flags) throws RemoteException {
|
||||||
IActivityManager am = getActivityManager();
|
IActivityManager am = getActivityManager();
|
||||||
if (am == null || thread == null) return null;
|
if (am == null || thread == null) return null;
|
||||||
try {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S || (Build.VERSION.SDK_INT == Build.VERSION_CODES.R && Build.VERSION.PREVIEW_SDK_INT != 0))
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && Build.VERSION.PREVIEW_SDK_INT != 0))
|
||||||
return am.registerReceiverWithFeature(thread, callerPackage, callingFeatureId, "null", receiver, filter, requiredPermission, userId, flags);
|
return am.registerReceiverWithFeature(thread, callerPackage, callingFeatureId, "null", receiver, filter, requiredPermission, userId, flags);
|
||||||
} catch (Throwable ignored) {
|
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
}
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
||||||
return am.registerReceiverWithFeature(thread, callerPackage, callingFeatureId, receiver, filter, requiredPermission, userId, flags);
|
return am.registerReceiverWithFeature(thread, callerPackage, callingFeatureId, receiver, filter, requiredPermission, userId, flags);
|
||||||
} else {
|
} else {
|
||||||
return am.registerReceiver(thread, callerPackage, receiver, filter, requiredPermission, userId, flags);
|
return am.registerReceiver(thread, callerPackage, receiver, filter, requiredPermission, userId, flags);
|
||||||
|
|
|
||||||
|
|
@ -252,13 +252,11 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("WrongConstant")
|
@SuppressLint("WrongConstant")
|
||||||
public static void broadcastIntent(String modulePackageName, int moduleUserId, boolean packageFullyRemoved) {
|
public static void broadcastIntent(Intent inIntent) {
|
||||||
Intent intent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
|
var intent = new Intent("org.lsposed.manager.NOTIFICATION");
|
||||||
|
intent.putExtra(Intent.EXTRA_INTENT, inIntent);
|
||||||
intent.addFlags(0x01000000); //Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
|
intent.addFlags(0x01000000); //Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
|
||||||
intent.addFlags(0x00400000); //Intent.FLAG_RECEIVER_FROM_SHELL
|
intent.addFlags(0x00400000); //Intent.FLAG_RECEIVER_FROM_SHELL
|
||||||
intent.putExtra("android.intent.extra.PACKAGES", modulePackageName);
|
|
||||||
intent.putExtra(Intent.EXTRA_USER, moduleUserId);
|
|
||||||
intent.putExtra(Intent.ACTION_PACKAGE_FULLY_REMOVED, packageFullyRemoved);
|
|
||||||
intent.setPackage(BuildConfig.MANAGER_INJECTED_PKG_NAME);
|
intent.setPackage(BuildConfig.MANAGER_INJECTED_PKG_NAME);
|
||||||
try {
|
try {
|
||||||
ActivityManagerService.broadcastIntentWithFeature(null, intent,
|
ActivityManagerService.broadcastIntentWithFeature(null, intent,
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,10 @@ import java.util.Arrays;
|
||||||
public class LSPosedService extends ILSPosedService.Stub {
|
public class LSPosedService extends ILSPosedService.Stub {
|
||||||
private static final int AID_NOBODY = 9999;
|
private static final int AID_NOBODY = 9999;
|
||||||
private static final int USER_NULL = -10000;
|
private static final int USER_NULL = -10000;
|
||||||
|
private static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
|
||||||
|
public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
|
||||||
|
private static final String EXTRA_USER_HANDLE = "android.intent.extra.user_handle";
|
||||||
|
private static final String EXTRA_REMOVED_FOR_ALL_USERS = "android.intent.extra.REMOVED_FOR_ALL_USERS";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ILSPApplicationService requestApplicationService(int uid, int pid, String processName, IBinder heartBeat) {
|
public ILSPApplicationService requestApplicationService(int uid, int pid, String processName, IBinder heartBeat) {
|
||||||
|
|
@ -79,7 +83,7 @@ public class LSPosedService extends ILSPosedService.Stub {
|
||||||
if (uid == AID_NOBODY || uid <= 0) return;
|
if (uid == AID_NOBODY || uid <= 0) return;
|
||||||
int userId = intent.getIntExtra("android.intent.extra.user_handle", USER_NULL);
|
int userId = intent.getIntExtra("android.intent.extra.user_handle", USER_NULL);
|
||||||
var intentAction = intent.getAction();
|
var intentAction = intent.getAction();
|
||||||
var allUsers = intent.getBooleanExtra("android.intent.extra.REMOVED_FOR_ALL_USERS", false);
|
var allUsers = intent.getBooleanExtra(EXTRA_REMOVED_FOR_ALL_USERS, false);
|
||||||
if (userId == USER_NULL) userId = uid % PER_USER_RANGE;
|
if (userId == USER_NULL) userId = uid % PER_USER_RANGE;
|
||||||
Uri uri = intent.getData();
|
Uri uri = intent.getData();
|
||||||
String moduleName = (uri != null) ? uri.getSchemeSpecificPart() : ConfigManager.getInstance().getModule(uid);
|
String moduleName = (uri != null) ? uri.getSchemeSpecificPart() : ConfigManager.getInstance().getModule(uid);
|
||||||
|
|
@ -102,8 +106,8 @@ public class LSPosedService extends ILSPosedService.Stub {
|
||||||
// because we only care about when the apk is gone
|
// because we only care about when the apk is gone
|
||||||
if (moduleName != null && allUsers)
|
if (moduleName != null && allUsers)
|
||||||
if (ConfigManager.getInstance().removeModule(moduleName)) {
|
if (ConfigManager.getInstance().removeModule(moduleName)) {
|
||||||
broadcastOrShowNotification(moduleName, userId, intent);
|
|
||||||
isXposedModule = true;
|
isXposedModule = true;
|
||||||
|
broadcastAndShowNotification(moduleName, userId, intent, true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -115,8 +119,8 @@ public class LSPosedService extends ILSPosedService.Stub {
|
||||||
if (components != null && !Arrays.stream(components).reduce(false, (p, c) -> p || c.equals(moduleName), Boolean::logicalOr)) {
|
if (components != null && !Arrays.stream(components).reduce(false, (p, c) -> p || c.equals(moduleName), Boolean::logicalOr)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
broadcastAndShowNotification(moduleName, userId, intent, isXposedModule);
|
||||||
if (isXposedModule) {
|
if (isXposedModule) {
|
||||||
broadcastOrShowNotification(moduleName, userId, intent);
|
|
||||||
// When installing a new Xposed module, we update the apk path to mark it as a
|
// When installing a new Xposed module, we update the apk path to mark it as a
|
||||||
// module to send a broadcast when modules that have not been activated are
|
// module to send a broadcast when modules that have not been activated are
|
||||||
// uninstalled.
|
// uninstalled.
|
||||||
|
|
@ -132,8 +136,8 @@ public class LSPosedService extends ILSPosedService.Stub {
|
||||||
case Intent.ACTION_UID_REMOVED: {
|
case Intent.ACTION_UID_REMOVED: {
|
||||||
// when a package is removed (rather than hide) for a single user
|
// when a package is removed (rather than hide) for a single user
|
||||||
// (apk may still be there because of multi-user)
|
// (apk may still be there because of multi-user)
|
||||||
|
broadcastAndShowNotification(moduleName, userId, intent, isXposedModule);
|
||||||
if (isXposedModule) {
|
if (isXposedModule) {
|
||||||
broadcastOrShowNotification(moduleName, userId, intent);
|
|
||||||
// it will automatically remove obsolete scope from database
|
// it will automatically remove obsolete scope from database
|
||||||
ConfigManager.getInstance().updateCache();
|
ConfigManager.getInstance().updateCache();
|
||||||
} else if (ConfigManager.getInstance().isUidHooked(uid)) {
|
} else if (ConfigManager.getInstance().isUidHooked(uid)) {
|
||||||
|
|
@ -159,18 +163,34 @@ public class LSPosedService extends ILSPosedService.Stub {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void broadcastOrShowNotification(String moduleName, int userId, Intent intent) {
|
private void broadcastAndShowNotification(String packageName, int userId, Intent intent, boolean isXposedModule) {
|
||||||
Log.d(TAG, "module " + moduleName + " changed, dispatching to manager");
|
Log.d(TAG, "package " + packageName + " changed, dispatching to manager");
|
||||||
var internAction = intent.getAction();
|
var action = intent.getAction();
|
||||||
var allUsers = intent.getBooleanExtra("android.intent.extra.REMOVED_FOR_ALL_USERS", false);
|
var allUsers = intent.getBooleanExtra(EXTRA_REMOVED_FOR_ALL_USERS, false);
|
||||||
LSPManagerService.broadcastIntent(moduleName, userId, allUsers);
|
intent.putExtra("android.intent.extra.PACKAGES", packageName);
|
||||||
var enabledModules = ConfigManager.getInstance().enabledModules();
|
intent.putExtra(Intent.EXTRA_USER, userId);
|
||||||
var scope = ConfigManager.getInstance().getModuleScope(moduleName);
|
intent.putExtra("isXposedModule", isXposedModule);
|
||||||
boolean systemModule = scope != null &&
|
LSPManagerService.broadcastIntent(intent);
|
||||||
scope.parallelStream().anyMatch(app -> app.packageName.equals("android"));
|
if (isXposedModule) {
|
||||||
boolean enabled = Arrays.asList(enabledModules).contains(moduleName);
|
var enabledModules = ConfigManager.getInstance().enabledModules();
|
||||||
if (!(Intent.ACTION_UID_REMOVED.equals(internAction) || Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(internAction) || allUsers))
|
var scope = ConfigManager.getInstance().getModuleScope(packageName);
|
||||||
LSPManagerService.showNotification(moduleName, userId, enabled, systemModule);
|
boolean systemModule = scope != null &&
|
||||||
|
scope.parallelStream().anyMatch(app -> app.packageName.equals("android"));
|
||||||
|
boolean enabled = Arrays.asList(enabledModules).contains(packageName);
|
||||||
|
if (!(Intent.ACTION_UID_REMOVED.equals(action) || Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action) || allUsers))
|
||||||
|
LSPManagerService.showNotification(packageName, userId, enabled, systemModule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized public void dispatchUserChanged(Intent intent) {
|
||||||
|
if (intent == null) return;
|
||||||
|
int uid = intent.getIntExtra(EXTRA_USER_HANDLE, AID_NOBODY);
|
||||||
|
if (uid == AID_NOBODY || uid <= 0) return;
|
||||||
|
try {
|
||||||
|
LSPManagerService.broadcastIntent(intent);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.e(TAG, "dispatch user info changed", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized public void dispatchUserUnlocked(Intent intent) {
|
synchronized public void dispatchUserUnlocked(Intent intent) {
|
||||||
|
|
@ -335,6 +355,31 @@ public class LSPosedService extends ILSPosedService.Stub {
|
||||||
Log.d(TAG, "registered boot receiver");
|
Log.d(TAG, "registered boot receiver");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void registerUserChangeReceiver() {
|
||||||
|
try {
|
||||||
|
IntentFilter userFilter = new IntentFilter();
|
||||||
|
userFilter.addAction(ACTION_USER_ADDED);
|
||||||
|
userFilter.addAction(ACTION_USER_REMOVED);
|
||||||
|
|
||||||
|
var receiver = new IIntentReceiver.Stub() {
|
||||||
|
@Override
|
||||||
|
public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
|
||||||
|
getExecutorService().submit(() -> dispatchUserChanged(intent));
|
||||||
|
try {
|
||||||
|
ActivityManagerService.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.e(TAG, "finish receiver", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ActivityManagerService.registerReceiver("android", null, receiver, userFilter, null, -1, 0);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.e(TAG, "register user info change receiver", e);
|
||||||
|
}
|
||||||
|
Log.d(TAG, "registered user info change receiver");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispatchSystemServerContext(IBinder activityThread, IBinder activityToken, String api) {
|
public void dispatchSystemServerContext(IBinder activityThread, IBinder activityToken, String api) {
|
||||||
Log.d(TAG, "received system context");
|
Log.d(TAG, "received system context");
|
||||||
|
|
@ -345,6 +390,7 @@ public class LSPosedService extends ILSPosedService.Stub {
|
||||||
registerConfigurationReceiver();
|
registerConfigurationReceiver();
|
||||||
registerSecretCodeReceiver();
|
registerSecretCodeReceiver();
|
||||||
registerBootCompleteReceiver();
|
registerBootCompleteReceiver();
|
||||||
|
registerUserChangeReceiver();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue