Skip to content

Commit

Permalink
Reload ModulesFragment after user removed or added (LSPosed#1465)
Browse files Browse the repository at this point in the history
Co-authored-by: LoveSy <shana@zju.edu.cn>
  • Loading branch information
Howard20181 and yujincheng08 authored Dec 4, 2021
1 parent 61c4470 commit 6a735d2
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 75 deletions.
42 changes: 35 additions & 7 deletions app/src/main/java/org/lsposed/manager/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

package org.lsposed.manager;

import android.annotation.SuppressLint;
import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.Context;
Expand Down Expand Up @@ -116,6 +117,10 @@ private static String readWebviewHTML(String name) {
}

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 OkHttpClient okHttpClient;
private static Cache okHttpCache;
Expand All @@ -138,6 +143,7 @@ public static boolean isParasitic() {
return !Process.isApplicationUid(Process.myUid());
}

@SuppressLint("WrongConstant")
private void setCrashReport() {
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {

Expand Down Expand Up @@ -182,17 +188,39 @@ public void onCreate() {
DayNightDelegate.setDefaultNightMode(ThemeUtil.getDarkTheme());
LocaleDelegate.setDefaultLocale(getLocale());

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("org.lsposed.manager.NOTIFICATION");
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int userId = intent.getIntExtra(Intent.EXTRA_USER, 0);
String packageName = intent.getStringExtra("android.intent.extra.PACKAGES");
boolean packageFullyRemoved = intent.getBooleanExtra(Intent.ACTION_PACKAGE_FULLY_REMOVED, false);
if (packageName != null) {
ModuleUtil.getInstance().reloadSingleModule(packageName, userId, packageFullyRemoved);
public void onReceive(Context context, Intent inIntent) {
var intent = (Intent) inIntent.getParcelableExtra(Intent.EXTRA_INTENT);
Log.d(TAG, "onReceive: " + intent);
switch (intent.getAction()) {
case Intent.ACTION_PACKAGE_ADDED:
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();

Expand Down
3 changes: 3 additions & 0 deletions app/src/main/java/org/lsposed/manager/adapters/AppHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

package org.lsposed.manager.adapters;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
Expand All @@ -44,6 +45,7 @@ public class AppHelper {
private static List<PackageInfo> appList;
private static final ConcurrentHashMap<PackageInfo, CharSequence> appLabel = new ConcurrentHashMap<>();

@SuppressLint("WrongConstant")
public static Intent getSettingsIntent(String packageName, int userId) {
Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
intentToResolve.addCategory(SETTINGS_CATEGORY);
Expand All @@ -63,6 +65,7 @@ public static Intent getSettingsIntent(String packageName, int userId) {
return intent;
}

@SuppressLint("WrongConstant")
public static Intent getLaunchIntentForPackage(String packageName, int userId) {
Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
intentToResolve.addCategory(Intent.CATEGORY_INFO);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ public boolean onOptionsItemSelected(MenuItem item) {
return true;
}

@SuppressLint("WrongConstant")
public boolean onContextItemSelected(@NonNull MenuItem item) {
ApplicationInfo info = selectedInfo;
if (info == null) {
Expand Down
105 changes: 64 additions & 41 deletions app/src/main/java/org/lsposed/manager/ui/fragment/ModulesFragment.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
package org.lsposed.manager.ui.fragment;

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.content.Intent;
Expand All @@ -37,6 +36,7 @@
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
Expand Down Expand Up @@ -72,9 +72,9 @@
import org.lsposed.manager.ConfigManager;
import org.lsposed.manager.R;
import org.lsposed.manager.adapters.AppHelper;
import org.lsposed.manager.databinding.SwiperefreshRecyclerviewBinding;
import org.lsposed.manager.databinding.FragmentPagerBinding;
import org.lsposed.manager.databinding.ItemModuleBinding;
import org.lsposed.manager.databinding.SwiperefreshRecyclerviewBinding;
import org.lsposed.manager.repo.RepoLoader;
import org.lsposed.manager.ui.dialog.BlurBehindDialogBuilder;
import org.lsposed.manager.ui.widget.EmptyStateRecyclerView;
Expand All @@ -95,12 +95,12 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi
private static final PackageManager pm = App.getInstance().getPackageManager();
private static final ModuleUtil moduleUtil = ModuleUtil.getInstance();
private static final RepoLoader repoLoader = RepoLoader.getInstance();
private static final List<UserInfo> users = ConfigManager.getUsers();
protected FragmentPagerBinding binding;
protected SearchView searchView;
private SearchView.OnQueryTextListener searchListener;

final ArrayList<ModuleAdapter> adapters = new ArrayList<>();
SparseArray<ModuleAdapter> adapters = new SparseArray<>();
PagerAdapter pagerAdapter = null;

private ModuleUtil.InstalledModule selectedModule;

Expand All @@ -110,24 +110,22 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
searchListener = new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
adapters.forEach(adapter -> adapter.getFilter().filter(query));
forEachAdaptor(adapter -> adapter.getFilter().filter(query));
return false;
}

@Override
public boolean onQueryTextChange(String query) {
adapters.forEach(adapter -> adapter.getFilter().filter(query));
forEachAdaptor(adapter -> adapter.getFilter().filter(query));
return false;
}
};
}

if (users != null) {
for (var user : users) {
var adapter = new ModuleAdapter(user);
adapter.setHasStableIds(true);
adapter.setStateRestorationPolicy(PREVENT_WHEN_EMPTY);
adapters.add(adapter);
}
private void forEachAdaptor(Consumer<? super ModuleAdapter> action) {
var snapshot = adapters;
for (var i = 0; i < snapshot.size(); ++i) {
action.accept(snapshot.valueAt(i));
}
}

Expand All @@ -149,7 +147,8 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
binding = FragmentPagerBinding.inflate(inflater, container, false);
binding.appBar.setLiftable(true);
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() {
@Override
public void onPageSelected(int position) {
Expand All @@ -159,29 +158,22 @@ public void onPageSelected(int position) {

new TabLayoutMediator(binding.tabLayout, binding.viewPager, (tab, position) -> {
if (position < adapters.size()) {
tab.setText(adapters.get(position).getUser().name);
tab.setText(adapters.valueAt(position).getUser().name);
}
}).attach();

if (users != null && users.size() != 1) {
binding.viewPager.setUserInputEnabled(true);
binding.tabLayout.setVisibility(View.VISIBLE);
binding.tabLayout.addOnLayoutChangeListener((view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
ViewGroup vg = (ViewGroup) binding.tabLayout.getChildAt(0);
int tabLayoutWidth = IntStream.range(0, binding.tabLayout.getTabCount()).map(i -> vg.getChildAt(i).getWidth()).sum();
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.tabLayout.addOnLayoutChangeListener((view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
ViewGroup vg = (ViewGroup) binding.tabLayout.getChildAt(0);
int tabLayoutWidth = IntStream.range(0, binding.tabLayout.getTabCount()).map(i -> vg.getChildAt(i).getWidth()).sum();
if (tabLayoutWidth <= binding.getRoot().getWidth()) {
binding.tabLayout.setTabMode(TabLayout.MODE_FIXED);
binding.tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
}
});

binding.fab.setOnClickListener(v -> {
var bundle = new Bundle();
var user = adapters.get(binding.viewPager.getCurrentItem()).getUser();
var user = adapters.valueAt(binding.viewPager.getCurrentItem()).getUser();
bundle.putParcelable("userInfo", user);
var f = new RecyclerViewDialogFragment();
f.setArguments(bundle);
Expand All @@ -190,7 +182,7 @@ public void onPageSelected(int position) {

moduleUtil.addListener(this);
repoLoader.addListener(this);
updateModuleSummary();
onModulesReloaded();

return binding.getRoot();
}
Expand All @@ -214,23 +206,48 @@ public void onViewDetachedFromWindow(View v) {
@Override
public void onResume() {
super.onResume();
adapters.forEach(ModuleAdapter::refresh);
forEachAdaptor(ModuleAdapter::refresh);
}

@Override
public void onSingleModuleReloaded(ModuleUtil.InstalledModule module) {
adapters.forEach(ModuleAdapter::refresh);
forEachAdaptor(ModuleAdapter::refresh);
}

@Override
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();
}

@Override
public void onRepoLoaded() {
adapters.forEach(ModuleAdapter::refresh);
forEachAdaptor(ModuleAdapter::refresh);
}

private void updateModuleSummary() {
Expand Down Expand Up @@ -261,6 +278,7 @@ void installModuleToUser(ModuleUtil.InstalledModule module, UserInfo user) {
.show();
}

@SuppressLint("WrongConstant")
@Override
public boolean onContextItemSelected(@NonNull MenuItem item) {
if (selectedModule == null) {
Expand Down Expand Up @@ -348,9 +366,9 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
if (fragment == null || arguments == null) {
return null;
}
int position = arguments.getInt("position");
int userId = arguments.getInt("user_id");
binding = SwiperefreshRecyclerviewBinding.inflate(getLayoutInflater(), container, false);
adapter = fragment.adapters.get(position);
adapter = fragment.adapters.get(userId);
binding.recyclerView.setAdapter(adapter);
binding.recyclerView.setLayoutManager(new LinearLayoutManager(requireActivity()));
binding.swipeRefreshLayout.setOnRefreshListener(adapter::fullRefresh);
Expand Down Expand Up @@ -430,7 +448,7 @@ public PagerAdapter(@NonNull Fragment fragment) {
@Override
public Fragment createFragment(int position) {
Bundle bundle = new Bundle();
bundle.putInt("position", position);
bundle.putInt("user_id", adapters.keyAt(position));
Fragment fragment = new ModuleListFragment();
fragment.setArguments(bundle);
return fragment;
Expand All @@ -443,7 +461,12 @@ public int getItemCount() {

@Override
public long getItemId(int position) {
return position;
return adapters.keyAt(position);
}

@Override
public boolean containsItem(long itemId) {
return adapters.indexOfKey((int) itemId) >= 0;
}
}

Expand Down
11 changes: 11 additions & 0 deletions app/src/main/java/org/lsposed/manager/util/ModuleUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@
import androidx.annotation.Nullable;
import androidx.core.util.Pair;

import org.lsposed.lspd.models.UserInfo;
import org.lsposed.manager.App;
import org.lsposed.manager.ConfigManager;
import org.lsposed.manager.repo.RepoLoader;
import org.lsposed.manager.repo.model.OnlineModule;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -50,6 +52,7 @@ public final class ModuleUtil {
private final PackageManager pm;
private final Set<ModuleListener> listeners = ConcurrentHashMap.newKeySet();
private HashSet<String> enabledModules = new HashSet<>();
private List<UserInfo> users = new ArrayList<>();
private Map<Pair<String, Integer>, InstalledModule> installedModules = new HashMap<>();
private boolean modulesLoaded = false;

Expand Down Expand Up @@ -89,6 +92,7 @@ synchronized public void reloadInstalledModules() {
}

Map<Pair<String, Integer>, InstalledModule> modules = new HashMap<>();
var users = ConfigManager.getUsers();
for (PackageInfo pkg : ConfigManager.getInstalledPackagesFromAllUsers(PackageManager.GET_META_DATA, false)) {
ApplicationInfo app = pkg.applicationInfo;

Expand All @@ -100,11 +104,18 @@ synchronized public void reloadInstalledModules() {

installedModules = modules;

this.users = users;

enabledModules = new HashSet<>(Arrays.asList(ConfigManager.getEnabledModules()));
modulesLoaded = true;
listeners.forEach(ModuleListener::onModulesReloaded);
}

@Nullable
public List<UserInfo> getUsers() {
return modulesLoaded ? users : null;
}

public InstalledModule reloadSingleModule(String packageName, int userId) {
return reloadSingleModule(packageName, userId, false);
}
Expand Down
Loading

0 comments on commit 6a735d2

Please sign in to comment.