diff --git a/configapp/src/main/AndroidManifest.xml b/configapp/src/main/AndroidManifest.xml
index 31c3194..0ddd386 100644
--- a/configapp/src/main/AndroidManifest.xml
+++ b/configapp/src/main/AndroidManifest.xml
@@ -24,10 +24,6 @@
-
-
\ No newline at end of file
diff --git a/configapp/src/main/java/com/jiqiu/configapp/AppListAdapter.java b/configapp/src/main/java/com/jiqiu/configapp/AppListAdapter.java
index b85b9d9..45a8f51 100644
--- a/configapp/src/main/java/com/jiqiu/configapp/AppListAdapter.java
+++ b/configapp/src/main/java/com/jiqiu/configapp/AppListAdapter.java
@@ -22,11 +22,16 @@ public class AppListAdapter extends RecyclerView.Adapter appList;
private List filteredAppList;
private OnAppToggleListener onAppToggleListener;
+ private OnAppClickListener onAppClickListener;
public interface OnAppToggleListener {
void onAppToggle(AppInfo appInfo, boolean isEnabled);
}
+ public interface OnAppClickListener {
+ void onAppClick(AppInfo appInfo);
+ }
+
public AppListAdapter() {
this.appList = new ArrayList<>();
this.filteredAppList = new ArrayList<>();
@@ -42,6 +47,10 @@ public class AppListAdapter extends RecyclerView.Adapter {
+ if (onAppClickListener != null) {
+ onAppClickListener.onAppClick(appInfo);
+ }
+ });
}
}
}
diff --git a/configapp/src/main/java/com/jiqiu/configapp/AppListFragment.java b/configapp/src/main/java/com/jiqiu/configapp/AppListFragment.java
index f9e5fbe..0c9927e 100644
--- a/configapp/src/main/java/com/jiqiu/configapp/AppListFragment.java
+++ b/configapp/src/main/java/com/jiqiu/configapp/AppListFragment.java
@@ -10,6 +10,10 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
+import android.app.Dialog;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -18,6 +22,8 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.textfield.TextInputEditText;
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
+import com.google.android.material.switchmaterial.SwitchMaterial;
import java.util.ArrayList;
import java.util.Collections;
@@ -27,7 +33,7 @@ import java.util.List;
/**
* 应用列表Fragment
*/
-public class AppListFragment extends Fragment implements AppListAdapter.OnAppToggleListener {
+public class AppListFragment extends Fragment implements AppListAdapter.OnAppToggleListener, AppListAdapter.OnAppClickListener {
private RecyclerView recyclerView;
private AppListAdapter adapter;
@@ -66,6 +72,7 @@ public class AppListFragment extends Fragment implements AppListAdapter.OnAppTog
private void setupRecyclerView() {
adapter = new AppListAdapter();
adapter.setOnAppToggleListener(this);
+ adapter.setOnAppClickListener(this);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(adapter);
}
@@ -111,6 +118,149 @@ public class AppListFragment extends Fragment implements AppListAdapter.OnAppTog
"App " + appInfo.getAppName() + " toggle: " + isEnabled);
}
+ @Override
+ public void onAppClick(AppInfo appInfo) {
+ showAppConfigDialog(appInfo);
+ }
+
+ private void showAppConfigDialog(AppInfo appInfo) {
+ View dialogView = LayoutInflater.from(getContext()).inflate(R.layout.dialog_app_config, null);
+
+ // Set app info
+ ImageView appIcon = dialogView.findViewById(R.id.appIcon);
+ TextView appName = dialogView.findViewById(R.id.appName);
+ TextView packageName = dialogView.findViewById(R.id.packageName);
+ RecyclerView soListRecyclerView = dialogView.findViewById(R.id.soListRecyclerView);
+ TextView emptyText = dialogView.findViewById(R.id.emptyText);
+ SwitchMaterial switchHideInjection = dialogView.findViewById(R.id.switchHideInjection);
+
+ appIcon.setImageDrawable(appInfo.getAppIcon());
+ appName.setText(appInfo.getAppName());
+ packageName.setText(appInfo.getPackageName());
+
+ // Load current config
+ boolean hideInjection = configManager.getHideInjection();
+ switchHideInjection.setChecked(hideInjection);
+
+ // Setup SO list
+ List globalSoFiles = configManager.getAllSoFiles();
+ List appSoFiles = configManager.getAppSoFiles(appInfo.getPackageName());
+
+ if (globalSoFiles.isEmpty()) {
+ emptyText.setVisibility(View.VISIBLE);
+ soListRecyclerView.setVisibility(View.GONE);
+ } else {
+ emptyText.setVisibility(View.GONE);
+ soListRecyclerView.setVisibility(View.VISIBLE);
+
+ SoSelectionAdapter soAdapter = new SoSelectionAdapter(globalSoFiles, appSoFiles);
+ soListRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+ soListRecyclerView.setAdapter(soAdapter);
+ }
+
+ // Create dialog
+ MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext())
+ .setTitle("配置注入")
+ .setView(dialogView)
+ .setPositiveButton("保存", (dialog, which) -> {
+ // Save hide injection setting
+ configManager.setHideInjection(switchHideInjection.isChecked());
+
+ // Save SO selection
+ if (soListRecyclerView.getAdapter() != null) {
+ SoSelectionAdapter adapter = (SoSelectionAdapter) soListRecyclerView.getAdapter();
+ List selectedSoFiles = adapter.getSelectedSoFiles();
+
+ // Clear existing SO files for this app
+ for (ConfigManager.SoFile existingSo : appSoFiles) {
+ configManager.removeSoFileFromApp(appInfo.getPackageName(), existingSo);
+ }
+
+ // Add selected SO files
+ for (ConfigManager.SoFile soFile : selectedSoFiles) {
+ configManager.addSoFileToApp(appInfo.getPackageName(), soFile);
+ }
+ }
+ })
+ .setNegativeButton("取消", null);
+
+ builder.show();
+ }
+
+ // Inner class for SO selection adapter
+ private static class SoSelectionAdapter extends RecyclerView.Adapter {
+ private List globalSoFiles;
+ private List selectedSoFiles;
+
+ public SoSelectionAdapter(List globalSoFiles, List appSoFiles) {
+ this.globalSoFiles = globalSoFiles;
+ this.selectedSoFiles = new ArrayList<>(appSoFiles);
+ }
+
+ public List getSelectedSoFiles() {
+ return new ArrayList<>(selectedSoFiles);
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_so_selection, parent, false);
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ ConfigManager.SoFile soFile = globalSoFiles.get(position);
+ holder.bind(soFile, selectedSoFiles);
+ }
+
+ @Override
+ public int getItemCount() {
+ return globalSoFiles.size();
+ }
+
+ class ViewHolder extends RecyclerView.ViewHolder {
+ CheckBox checkBox;
+ TextView nameText;
+ TextView pathText;
+
+ ViewHolder(@NonNull View itemView) {
+ super(itemView);
+ checkBox = itemView.findViewById(R.id.checkBox);
+ nameText = itemView.findViewById(R.id.textName);
+ pathText = itemView.findViewById(R.id.textPath);
+ }
+
+ void bind(ConfigManager.SoFile soFile, List selectedList) {
+ nameText.setText(soFile.name);
+ pathText.setText(soFile.originalPath);
+
+ // Check if this SO is selected
+ boolean isSelected = false;
+ for (ConfigManager.SoFile selected : selectedList) {
+ if (selected.storedPath.equals(soFile.storedPath)) {
+ isSelected = true;
+ break;
+ }
+ }
+
+ checkBox.setOnCheckedChangeListener(null);
+ checkBox.setChecked(isSelected);
+
+ checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ if (isChecked) {
+ selectedList.add(soFile);
+ } else {
+ selectedList.removeIf(s -> s.storedPath.equals(soFile.storedPath));
+ }
+ });
+
+ itemView.setOnClickListener(v -> checkBox.toggle());
+ }
+ }
+ }
+
/**
* 异步加载应用列表
*/
diff --git a/configapp/src/main/java/com/jiqiu/configapp/AppSoConfigActivity.java b/configapp/src/main/java/com/jiqiu/configapp/AppSoConfigActivity.java
deleted file mode 100644
index 9199283..0000000
--- a/configapp/src/main/java/com/jiqiu/configapp/AppSoConfigActivity.java
+++ /dev/null
@@ -1,164 +0,0 @@
-package com.jiqiu.configapp;
-
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CheckBox;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class AppSoConfigActivity extends AppCompatActivity {
-
- public static final String EXTRA_PACKAGE_NAME = "package_name";
- public static final String EXTRA_APP_NAME = "app_name";
-
- private RecyclerView recyclerView;
- private TextView emptyView;
- private SoSelectionAdapter adapter;
- private ConfigManager configManager;
-
- private String packageName;
- private String appName;
- private List appSoFiles;
- private List globalSoFiles;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_app_so_config);
-
- packageName = getIntent().getStringExtra(EXTRA_PACKAGE_NAME);
- appName = getIntent().getStringExtra(EXTRA_APP_NAME);
-
- if (packageName == null) {
- finish();
- return;
- }
-
- configManager = new ConfigManager(this);
-
- Toolbar toolbar = findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setTitle(appName != null ? appName : packageName);
- getSupportActionBar().setSubtitle("配置SO文件");
-
- recyclerView = findViewById(R.id.recyclerView);
- emptyView = findViewById(R.id.emptyView);
-
- adapter = new SoSelectionAdapter();
- recyclerView.setLayoutManager(new LinearLayoutManager(this));
- recyclerView.setAdapter(adapter);
-
- loadData();
- }
-
- private void loadData() {
- // Load app-specific SO files
- appSoFiles = configManager.getAppSoFiles(packageName);
-
- // Load global SO files
- globalSoFiles = configManager.getAllSoFiles();
-
- if (globalSoFiles.isEmpty()) {
- emptyView.setVisibility(View.VISIBLE);
- recyclerView.setVisibility(View.GONE);
- } else {
- emptyView.setVisibility(View.GONE);
- recyclerView.setVisibility(View.VISIBLE);
- adapter.setData(globalSoFiles, appSoFiles);
- }
- }
-
- @Override
- public boolean onSupportNavigateUp() {
- onBackPressed();
- return true;
- }
-
- class SoSelectionAdapter extends RecyclerView.Adapter {
- private List availableSoFiles = new ArrayList<>();
- private List selectedSoFiles = new ArrayList<>();
-
- void setData(List available, List selected) {
- this.availableSoFiles = available;
- this.selectedSoFiles = selected;
- notifyDataSetChanged();
- }
-
- @NonNull
- @Override
- public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- View view = LayoutInflater.from(parent.getContext())
- .inflate(R.layout.item_so_selection, parent, false);
- return new ViewHolder(view);
- }
-
- @Override
- public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
- holder.bind(availableSoFiles.get(position));
- }
-
- @Override
- public int getItemCount() {
- return availableSoFiles.size();
- }
-
- class ViewHolder extends RecyclerView.ViewHolder {
- CheckBox checkBox;
- TextView nameText;
- TextView pathText;
-
- ViewHolder(@NonNull View itemView) {
- super(itemView);
- checkBox = itemView.findViewById(R.id.checkBox);
- nameText = itemView.findViewById(R.id.textName);
- pathText = itemView.findViewById(R.id.textPath);
- }
-
- void bind(ConfigManager.SoFile soFile) {
- nameText.setText(soFile.name);
- pathText.setText(soFile.originalPath);
-
- // Check if this SO is selected for the app
- boolean isSelected = false;
- for (ConfigManager.SoFile selected : selectedSoFiles) {
- if (selected.storedPath.equals(soFile.storedPath)) {
- isSelected = true;
- break;
- }
- }
-
- checkBox.setOnCheckedChangeListener(null);
- checkBox.setChecked(isSelected);
-
- checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
- if (isChecked) {
- // Add SO to app
- configManager.addSoFileToApp(packageName, soFile.originalPath, false);
- } else {
- // Remove SO from app
- configManager.removeSoFileFromApp(packageName, soFile);
- }
-
- // Reload data
- loadData();
- });
-
- itemView.setOnClickListener(v -> checkBox.toggle());
- }
- }
- }
-}
\ No newline at end of file
diff --git a/configapp/src/main/java/com/jiqiu/configapp/ConfigManager.java b/configapp/src/main/java/com/jiqiu/configapp/ConfigManager.java
index 5485e94..7483825 100644
--- a/configapp/src/main/java/com/jiqiu/configapp/ConfigManager.java
+++ b/configapp/src/main/java/com/jiqiu/configapp/ConfigManager.java
@@ -146,41 +146,30 @@ public class ConfigManager {
saveConfig();
}
- public void addSoFileToApp(String packageName, String originalPath, boolean deleteOriginal) {
+ public void addSoFileToApp(String packageName, SoFile globalSoFile) {
AppConfig appConfig = config.perAppConfig.get(packageName);
if (appConfig == null) {
appConfig = new AppConfig();
config.perAppConfig.put(packageName, appConfig);
}
- // Generate unique filename
- String fileName = new File(originalPath).getName();
- String storedPath = SO_STORAGE_DIR + "/" + System.currentTimeMillis() + "_" + fileName;
-
- // Copy SO file to our storage
- Shell.Result result = Shell.cmd("cp \"" + originalPath + "\" \"" + storedPath + "\"").exec();
- if (result.isSuccess()) {
- SoFile soFile = new SoFile();
- soFile.name = fileName;
- soFile.storedPath = storedPath;
- soFile.originalPath = originalPath;
- appConfig.soFiles.add(soFile);
-
- if (deleteOriginal) {
- Shell.cmd("rm \"" + originalPath + "\"").exec();
+ // Check if already added
+ for (SoFile existing : appConfig.soFiles) {
+ if (existing.storedPath.equals(globalSoFile.storedPath)) {
+ return; // Already added
}
-
- saveConfig();
}
+
+ // Add reference to the global SO file
+ appConfig.soFiles.add(globalSoFile);
+ saveConfig();
}
public void removeSoFileFromApp(String packageName, SoFile soFile) {
AppConfig appConfig = config.perAppConfig.get(packageName);
if (appConfig == null) return;
- appConfig.soFiles.remove(soFile);
- // Delete the stored file
- Shell.cmd("rm " + soFile.storedPath).exec();
+ appConfig.soFiles.removeIf(s -> s.storedPath.equals(soFile.storedPath));
saveConfig();
}
diff --git a/configapp/src/main/res/layout/dialog_app_config.xml b/configapp/src/main/res/layout/dialog_app_config.xml
new file mode 100644
index 0000000..bc1df5e
--- /dev/null
+++ b/configapp/src/main/res/layout/dialog_app_config.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file