From f557d718743dd91747e7d9da65e5463f7921cf96 Mon Sep 17 00:00:00 2001 From: jiqiu2021 Date: Wed, 25 Jun 2025 19:19:57 +0800 Subject: [PATCH] add app manager logic --- configapp/src/main/AndroidManifest.xml | 4 - .../com/jiqiu/configapp/AppListAdapter.java | 16 ++ .../com/jiqiu/configapp/AppListFragment.java | 152 +++++++++++++++- .../jiqiu/configapp/AppSoConfigActivity.java | 164 ------------------ .../com/jiqiu/configapp/ConfigManager.java | 31 ++-- .../src/main/res/layout/dialog_app_config.xml | 96 ++++++++++ 6 files changed, 273 insertions(+), 190 deletions(-) delete mode 100644 configapp/src/main/java/com/jiqiu/configapp/AppSoConfigActivity.java create mode 100644 configapp/src/main/res/layout/dialog_app_config.xml 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