[app] Add fab to add modules to other users (#603)

This commit is contained in:
LoveSy 2021-05-17 12:58:21 +08:00 committed by GitHub
parent dc8deae175
commit ce0a095a7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 210 additions and 114 deletions

View File

@ -240,7 +240,7 @@ public class ConfigManager {
}
public static boolean installExistingPackageAsUser(String packageName, int userId) {
int INSTALL_SUCCEEDED = 1;
final int INSTALL_SUCCEEDED = 1;
try {
var ret = LSPManagerServiceClient.installExistingPackageAsUser(packageName, userId);
return ret == INSTALL_SUCCEEDED;

View File

@ -43,7 +43,6 @@ import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@ -60,6 +59,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.request.target.CustomTarget;
@ -72,6 +72,7 @@ import org.lsposed.manager.App;
import org.lsposed.manager.BuildConfig;
import org.lsposed.manager.ConfigManager;
import org.lsposed.manager.R;
import org.lsposed.manager.databinding.ItemModuleBinding;
import org.lsposed.manager.ui.activity.AppListActivity;
import org.lsposed.manager.ui.fragment.CompileDialogFragment;
import org.lsposed.manager.util.GlideApp;
@ -149,8 +150,7 @@ public class ScopeAdapter extends RecyclerView.Adapter<ScopeAdapter.ViewHolder>
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(activity).inflate(R.layout.item_module, parent, false);
return new ViewHolder(v);
return new ViewHolder(ItemModuleBinding.inflate(activity.getLayoutInflater(), parent, false));
}
private boolean shouldHideApp(PackageInfo info, ApplicationWithEquals app) {
@ -559,20 +559,19 @@ public class ScopeAdapter extends RecyclerView.Adapter<ScopeAdapter.ViewHolder>
}
static class ViewHolder extends RecyclerView.ViewHolder {
View root;
ConstraintLayout root;
ImageView appIcon;
TextView appName;
TextView appDescription;
MaterialCheckBox checkbox;
ViewHolder(View itemView) {
super(itemView);
root = itemView.findViewById(R.id.item_root);
appIcon = itemView.findViewById(R.id.app_icon);
appName = itemView.findViewById(R.id.app_name);
appDescription = itemView.findViewById(R.id.description);
checkbox = itemView.findViewById(R.id.checkbox);
ViewHolder(ItemModuleBinding binding) {
super(binding.getRoot());
root = binding.itemRoot;
appIcon = binding.appIcon;
appName = binding.appName;
appDescription = binding.description;
checkbox = binding.checkbox;
checkbox.setVisibility(View.VISIBLE);
}
}

View File

@ -41,7 +41,6 @@ import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@ -57,6 +56,7 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.Lifecycle;
import androidx.recyclerview.widget.RecyclerView;
@ -64,6 +64,8 @@ import androidx.viewpager2.widget.ViewPager2;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;
import com.google.android.material.checkbox.MaterialCheckBox;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.tabs.TabLayoutMediator;
@ -71,6 +73,7 @@ import org.lsposed.manager.ConfigManager;
import org.lsposed.manager.R;
import org.lsposed.manager.adapters.AppHelper;
import org.lsposed.manager.databinding.ActivityModuleDetailBinding;
import org.lsposed.manager.databinding.ItemModuleBinding;
import org.lsposed.manager.databinding.ItemRepoRecyclerviewBinding;
import org.lsposed.manager.repo.RepoLoader;
import org.lsposed.manager.ui.activity.base.BaseActivity;
@ -83,6 +86,7 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import rikka.core.res.ResourcesKt;
@ -133,8 +137,31 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
if (recyclerView != null) {
binding.appBar.setRaised(!recyclerView.getBorderViewDelegate().isShowingTopBorder());
}
if (position > 0) binding.fab.show();
else binding.fab.hide();
}
});
binding.fab.setOnClickListener(view -> {
var pickAdaptor = new ModuleAdapter(0, null, true);
var position = binding.viewPager.getCurrentItem();
var snapshot = adapters.get(position).snapshot().stream().map(m -> m.packageName).collect(Collectors.toSet());
var userId = adapters.get(position).getUserId();
pickAdaptor.setFilter(m -> !snapshot.contains(m.packageName));
pickAdaptor.refresh();
var v = new RecyclerView(ModulesActivity.this);
v.setAdapter(pickAdaptor);
v.setLayoutManager(new LinearLayoutManagerFix(ModulesActivity.this));
var dialog = new AlertDialog.Builder(ModulesActivity.this)
.setTitle(getString(R.string.install_to_user, userId))
.setView(v)
.setNegativeButton(android.R.string.cancel, null)
.show();
pickAdaptor.setOnPickListener(picked -> {
var module = (ModuleUtil.InstalledModule) picked.getTag();
installModuleToUser(module, userId);
dialog.dismiss();
});
});
mSearchListener = new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
@ -191,12 +218,6 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
ArrayList<String> titles = new ArrayList<>();
for (int userId : userIds) {
var adapter = new ModuleAdapter(userId, handles.get(userId));
if (userId == 0) {
adapter.setProfiles(users.stream()
.filter(u -> u.hashCode() != 0)
.mapToInt(UserHandle::hashCode)
.toArray());
}
adapter.setHasStableIds(true);
adapters.add(adapter);
titles.add(getString(R.string.user_title, userId));
@ -244,6 +265,28 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
return super.onOptionsItemSelected(item);
}
private void installModuleToUser(ModuleUtil.InstalledModule module, int userId) {
new AlertDialog.Builder(this)
.setTitle(getString(R.string.install_to_user, userId))
.setMessage(getString(R.string.install_to_user_message, module.getAppName(), userId))
.setPositiveButton(android.R.string.ok, (dialog, which) ->
workHandler.post(() -> {
var success = ConfigManager.installExistingPackageAsUser(module.packageName, userId);
runOnUiThread(() -> {
String text = success ? getString(R.string.module_installed, module.getAppName(), userId) : getString(R.string.module_install_failed);
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
Snackbar.make(binding.snackbar, text, Snackbar.LENGTH_SHORT).show();
} else {
Toast.makeText(ModulesActivity.this, text, Toast.LENGTH_SHORT).show();
}
});
if (success)
moduleUtil.reloadSingleModule(module.packageName, userId);
}))
.setNegativeButton(android.R.string.cancel, null)
.show();
}
@Override
public boolean onContextItemSelected(@NonNull MenuItem item) {
ModuleUtil.InstalledModule module = ModuleUtil.getInstance().getModule(selectedModule.packageName, selectedModule.userId);
@ -305,25 +348,7 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
startActivity(intent);
return true;
} else if (item.getGroupId() == 1) {
new AlertDialog.Builder(this)
.setTitle(getString(R.string.install_to_user, itemId))
.setMessage(getString(R.string.install_to_user_message, module.getAppName(), itemId))
.setPositiveButton(android.R.string.ok, (dialog, which) ->
workHandler.post(() -> {
var success = ConfigManager.installExistingPackageAsUser(module.packageName, itemId);
runOnUiThread(() -> {
String text = success ? getString(R.string.module_installed, module.getAppName()) : getString(R.string.module_install_failed);
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
Snackbar.make(binding.snackbar, text, Snackbar.LENGTH_SHORT).show();
} else {
Toast.makeText(ModulesActivity.this, text, Toast.LENGTH_SHORT).show();
}
});
if (success)
moduleUtil.reloadSingleModule(module.packageName, itemId);
}))
.setNegativeButton(android.R.string.cancel, null)
.show();
installModuleToUser(module, itemId);
return true;
}
return super.onContextItemSelected(item);
@ -334,7 +359,7 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
@NonNull
@Override
public PagerAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new PagerAdapter.ViewHolder(ItemRepoRecyclerviewBinding.inflate(getLayoutInflater(), parent, false).getRoot());
return new PagerAdapter.ViewHolder(ItemRepoRecyclerviewBinding.inflate(getLayoutInflater(), parent, false));
}
@Override
@ -357,10 +382,11 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
class ViewHolder extends RecyclerView.ViewHolder {
BorderRecyclerView recyclerView;
FloatingActionButton btn;
public ViewHolder(@NonNull View itemView) {
super(itemView);
recyclerView = itemView.findViewById(R.id.recyclerView);
public ViewHolder(@NonNull ItemRepoRecyclerviewBinding binding) {
super(binding.getRoot());
recyclerView = binding.recyclerView;
}
}
}
@ -370,29 +396,35 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
private final List<ModuleUtil.InstalledModule> showList = new ArrayList<>();
private final int userId;
private final UserHandle userHandle;
private final boolean isPick;
private boolean isLoaded;
private int[] profiles = new int[0];
private View.OnClickListener onPickListener;
private Predicate<ModuleUtil.InstalledModule> customFilter = m -> true;
ModuleAdapter(int userId, UserHandle userHandle) {
this.userId = userId;
this.userHandle = userHandle;
this(userId, userHandle, false);
}
public void setProfiles(int[] profiles) {
this.profiles = profiles;
ModuleAdapter(int userId, UserHandle userHandle, boolean isPick) {
this.userId = userId;
this.userHandle = userHandle;
this.isPick = isPick;
}
public int getUserId() {
return userId;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_module, parent, false);
return new ViewHolder(v);
return new ViewHolder(ItemModuleBinding.inflate(getLayoutInflater(), parent, false));
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
ModuleUtil.InstalledModule item = showList.get(position);
holder.root.setAlpha(moduleUtil.isModuleEnabled(item.packageName) ? 1.0f : .5f);
String appName;
if (item.userId != 0) {
appName = String.format("%s (%s)", item.getAppName(), item.userId);
@ -460,7 +492,7 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
menu.removeItem(R.id.menu_app_info);
}
if (item.userId == 0) {
for (int profile : profiles) {
for (int profile : ConfigManager.getUsers()) {
if (ModuleUtil.getInstance().getModule(item.packageName, profile) == null) {
menu.add(1, profile, 0, getString(R.string.install_to_user, profile));
}
@ -468,23 +500,30 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
}
});
holder.itemView.setOnClickListener(v -> {
Intent intent = new Intent(ModulesActivity.this, AppListActivity.class);
intent.putExtra("modulePackageName", item.packageName);
intent.putExtra("moduleUserId", item.userId);
intent.putExtra("userHandle", userHandle);
startActivity(intent);
});
if (!isPick) {
holder.root.setAlpha(moduleUtil.isModuleEnabled(item.packageName) ? 1.0f : .5f);
holder.itemView.setOnClickListener(v -> {
Intent intent = new Intent(ModulesActivity.this, AppListActivity.class);
intent.putExtra("modulePackageName", item.packageName);
intent.putExtra("moduleUserId", item.userId);
intent.putExtra("userHandle", userHandle);
startActivity(intent);
});
holder.itemView.setOnLongClickListener(v -> {
selectedModule = item;
selectedModuleUser = userHandle;
return false;
});
holder.appVersion.setVisibility(View.VISIBLE);
holder.appVersion.setText(item.versionName);
holder.appVersion.setSelected(true);
holder.itemView.setOnLongClickListener(v -> {
selectedModule = item;
selectedModuleUser = userHandle;
return false;
});
holder.appVersion.setVisibility(View.VISIBLE);
holder.appVersion.setText(item.versionName);
holder.appVersion.setSelected(true);
} else {
holder.itemView.setTag(item);
holder.itemView.setOnClickListener(v -> {
if (onPickListener != null) onPickListener.onClick(v);
});
}
}
@Override
@ -503,6 +542,20 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
return new ApplicationFilter();
}
public void setFilter(@NonNull Predicate<ModuleUtil.InstalledModule> filter) {
this.customFilter = filter;
}
public void setOnPickListener(View.OnClickListener onPickListener) {
this.onPickListener = onPickListener;
}
public List<ModuleUtil.InstalledModule> snapshot() {
List<ModuleUtil.InstalledModule> list = new ArrayList<>();
list.addAll(searchList);
return list;
}
public void refresh() {
refresh(false);
}
@ -515,7 +568,7 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
private final Runnable reloadModules = new Runnable() {
public void run() {
searchList.clear();
searchList.addAll(moduleUtil.getModules().values().stream().filter(module -> module.userId == userId).collect(Collectors.toList()));
searchList.addAll(moduleUtil.getModules().values().stream().filter(module -> module.userId == userId).filter(customFilter).collect(Collectors.toList()));
Comparator<PackageInfo> cmp = AppHelper.getAppListComparator(0, pm);
searchList.sort((a, b) -> {
boolean aChecked = moduleUtil.isModuleEnabled(a.packageName);
@ -539,21 +592,21 @@ public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleLi
}
class ViewHolder extends RecyclerView.ViewHolder {
View root;
ConstraintLayout root;
ImageView appIcon;
TextView appName;
TextView appDescription;
TextView appVersion;
TextView warningText;
MaterialCheckBox checkBox;
ViewHolder(View itemView) {
super(itemView);
root = itemView.findViewById(R.id.item_root);
appIcon = itemView.findViewById(R.id.app_icon);
appName = itemView.findViewById(R.id.app_name);
appDescription = itemView.findViewById(R.id.description);
appVersion = itemView.findViewById(R.id.version_name);
warningText = itemView.findViewById(R.id.warning);
ViewHolder(ItemModuleBinding binding) {
super(binding.getRoot());
root = binding.itemRoot;
appIcon = binding.appIcon;
appName = binding.appName;
appDescription = binding.description;
appVersion = binding.versionName;
checkBox = binding.checkbox;
}
}

View File

@ -24,10 +24,8 @@ import android.content.Intent;
import android.os.Bundle;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.TextView;
@ -35,6 +33,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.lifecycle.Lifecycle;
import androidx.recyclerview.widget.RecyclerView;
@ -42,6 +41,7 @@ import com.google.android.material.snackbar.Snackbar;
import org.lsposed.manager.ConfigManager;
import org.lsposed.manager.R;
import org.lsposed.manager.databinding.ItemOnlinemoduleBinding;
import org.lsposed.manager.repo.RepoLoader;
import org.lsposed.manager.repo.model.OnlineModule;
import org.lsposed.manager.ui.activity.base.ListActivity;
@ -148,8 +148,7 @@ public class RepoActivity extends ListActivity implements RepoLoader.Listener {
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_onlinemodule, parent, false);
return new ViewHolder(v);
return new ViewHolder(ItemOnlinemoduleBinding.inflate(getLayoutInflater(), parent, false));
}
@Override
@ -211,15 +210,15 @@ public class RepoActivity extends ListActivity implements RepoLoader.Listener {
}
class ViewHolder extends RecyclerView.ViewHolder {
View root;
ConstraintLayout root;
TextView appName;
TextView appDescription;
ViewHolder(View itemView) {
super(itemView);
root = itemView.findViewById(R.id.item_root);
appName = itemView.findViewById(R.id.app_name);
appDescription = itemView.findViewById(R.id.description);
ViewHolder(ItemOnlinemoduleBinding binding) {
super(binding.getRoot());
root = binding.itemRoot;
appName = binding.appName;
appDescription = binding.description;
}
}

View File

@ -40,6 +40,7 @@ import androidx.lifecycle.Lifecycle;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.progressindicator.CircularProgressIndicator;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.tabs.TabLayoutMediator;
@ -282,9 +283,9 @@ public class RepoItemActivity extends BaseActivity implements RepoLoader.Listene
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == 0) {
return new ViewHolder(ItemRepoReleaseBinding.inflate(getLayoutInflater(), parent, false).getRoot());
return new ReleaseViewHolder(ItemRepoReleaseBinding.inflate(getLayoutInflater(), parent, false));
} else {
return new ViewHolder(ItemRepoLoadmoreBinding.inflate(getLayoutInflater(), parent, false).getRoot());
return new LoadmoreViewHolder(ItemRepoLoadmoreBinding.inflate(getLayoutInflater(), parent, false));
}
}
@ -344,17 +345,30 @@ public class RepoItemActivity extends BaseActivity implements RepoLoader.Listene
class ViewHolder extends RecyclerView.ViewHolder {
TextView title;
LinkifyTextView description;
View openInBrowser;
View viewAssets;
MaterialButton openInBrowser;
MaterialButton viewAssets;
CircularProgressIndicator progress;
public ViewHolder(View view) {
super(view);
title = view.findViewById(R.id.title);
description = view.findViewById(R.id.description);
openInBrowser = view.findViewById(R.id.open_in_browser);
viewAssets = view.findViewById(R.id.view_assets);
progress = view.findViewById(R.id.progress);
public ViewHolder(@NonNull View itemView) {
super(itemView);
}
}
class ReleaseViewHolder extends ViewHolder {
public ReleaseViewHolder(ItemRepoReleaseBinding binding) {
super(binding.getRoot());
title = binding.title;
description = binding.description;
openInBrowser = binding.openInBrowser;
viewAssets = binding.viewAssets;
}
}
class LoadmoreViewHolder extends ViewHolder {
public LoadmoreViewHolder(ItemRepoLoadmoreBinding binding) {
super(binding.getRoot());
title = binding.title;
progress = binding.progress;
}
}
}
@ -365,9 +379,9 @@ public class RepoItemActivity extends BaseActivity implements RepoLoader.Listene
@Override
public PagerAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == 0) {
return new ViewHolder(ItemRepoReadmeBinding.inflate(getLayoutInflater(), parent, false).getRoot(), viewType);
return new ReadmeViewHolder(ItemRepoReadmeBinding.inflate(getLayoutInflater(), parent, false));
} else {
return new ViewHolder(ItemRepoRecyclerviewBinding.inflate(getLayoutInflater(), parent, false).getRoot(), viewType);
return new RecyclerviewBinding(ItemRepoRecyclerviewBinding.inflate(getLayoutInflater(), parent, false));
}
}
@ -411,14 +425,23 @@ public class RepoItemActivity extends BaseActivity implements RepoLoader.Listene
BorderNestedScrollView scrollView;
BorderRecyclerView recyclerView;
public ViewHolder(@NonNull View itemView, int viewType) {
public ViewHolder(@NonNull View itemView) {
super(itemView);
if (viewType == 0) {
textView = itemView.findViewById(R.id.readme);
scrollView = itemView.findViewById(R.id.scrollView);
} else {
recyclerView = itemView.findViewById(R.id.recyclerView);
}
}
}
class ReadmeViewHolder extends ViewHolder {
public ReadmeViewHolder(ItemRepoReadmeBinding binding) {
super(binding.getRoot());
textView = binding.readme;
scrollView = binding.scrollView;
}
}
class RecyclerviewBinding extends ViewHolder {
public RecyclerviewBinding(ItemRepoRecyclerviewBinding binding) {
super(binding.getRoot());
recyclerView = binding.recyclerView;
}
}
}

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

View File

@ -59,4 +59,15 @@
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:src="@drawable/ic_baseline_add_24"
android:layout_margin="16dp"
android:visibility="invisible"
app:layout_fitSystemWindowsInsets="bottom"
android:contentDescription="@string/add_module_to_user" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -17,8 +17,8 @@
~ Copyright (C) 2020 EdXposed Contributors
~ Copyright (C) 2021 LSPosed Contributors
-->
<org.lsposed.manager.ui.widget.EmptyStateRecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
<org.lsposed.manager.ui.widget.EmptyStateRecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/recyclerView"
android:layout_width="match_parent"

View File

@ -88,7 +88,7 @@
<string name="user_title">用户 %d</string>
<string name="install_to_user">安装到用户 %d</string>
<string name="install_to_user_message">要安装 %1$s 到用户 %2$d 吗?建议手动安装或多开,通过 LSPosed 强制安装可能会出现问题。</string>
<string name="module_installed">已安装 %1$s</string>
<string name="module_installed">已安装 %1$s 到用户 %2$d</string>
<string name="module_install_failed">安装失败</string>
<!-- AppListActivity -->

View File

@ -85,11 +85,11 @@
<string name="module_uninstall_message">Do you want to uninstall this module?</string>
<string name="module_uninstalled">Uninstalled %1$s</string>
<string name="module_uninstall_failed">Uninstall unsuccessful</string>
<string name="module_installed">Added %1$s to user %2$d</string>
<string name="module_install_failed">Adding module failed</string>
<string name="user_title">User %d</string>
<string name="install_to_user">Install to user %d</string>
<string name="install_to_user_message">Want to install %1$s to user %2$d? It is recommended to install manually, forcing installation via LSPosed may cause problems.</string>
<string name="module_installed">%1$s installed</string>
<string name="module_install_failed">install failed</string>
<!-- AppListActivity -->
<string name="compile_speed">Re-optimize</string>
@ -185,4 +185,5 @@
<string name="translators"><![CDATA[<a href="https://github.com/LSPosed/LSPosed">LSPosed</a>]]></string>
<string name="copy_toast_msg">Copied</string>
<string name="list_empty">¯\\\\_(ツ)_\/¯\nNothing here</string>
<string name="add_module_to_user">Add module to user</string>
</resources>