diff --git a/configapp/src/main/AndroidManifest.xml b/configapp/src/main/AndroidManifest.xml index 837a2c2..31c3194 100644 --- a/configapp/src/main/AndroidManifest.xml +++ b/configapp/src/main/AndroidManifest.xml @@ -20,6 +20,14 @@ + + + + \ No newline at end of file diff --git a/configapp/src/main/java/com/jiqiu/configapp/AppSoConfigActivity.java b/configapp/src/main/java/com/jiqiu/configapp/AppSoConfigActivity.java new file mode 100644 index 0000000..9199283 --- /dev/null +++ b/configapp/src/main/java/com/jiqiu/configapp/AppSoConfigActivity.java @@ -0,0 +1,164 @@ +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 9e2b16a..5485e94 100644 --- a/configapp/src/main/java/com/jiqiu/configapp/ConfigManager.java +++ b/configapp/src/main/java/com/jiqiu/configapp/ConfigManager.java @@ -104,6 +104,48 @@ public class ConfigManager { return new ArrayList<>(appConfig.soFiles); } + public List getAllSoFiles() { + if (config.globalSoFiles == null) { + config.globalSoFiles = new ArrayList<>(); + } + return new ArrayList<>(config.globalSoFiles); + } + + public void addGlobalSoFile(String originalPath, boolean deleteOriginal) { + if (config.globalSoFiles == null) { + config.globalSoFiles = new ArrayList<>(); + } + + // 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; + config.globalSoFiles.add(soFile); + + if (deleteOriginal) { + Shell.cmd("rm \"" + originalPath + "\"").exec(); + } + + saveConfig(); + } + } + + public void removeGlobalSoFile(SoFile soFile) { + if (config.globalSoFiles == null) return; + + config.globalSoFiles.remove(soFile); + // Delete the stored file + Shell.cmd("rm \"" + soFile.storedPath + "\"").exec(); + saveConfig(); + } + public void addSoFileToApp(String packageName, String originalPath, boolean deleteOriginal) { AppConfig appConfig = config.perAppConfig.get(packageName); if (appConfig == null) { @@ -116,7 +158,7 @@ public class ConfigManager { String storedPath = SO_STORAGE_DIR + "/" + System.currentTimeMillis() + "_" + fileName; // Copy SO file to our storage - Shell.Result result = Shell.cmd("cp " + originalPath + " " + storedPath).exec(); + Shell.Result result = Shell.cmd("cp \"" + originalPath + "\" \"" + storedPath + "\"").exec(); if (result.isSuccess()) { SoFile soFile = new SoFile(); soFile.name = fileName; @@ -125,7 +167,7 @@ public class ConfigManager { appConfig.soFiles.add(soFile); if (deleteOriginal) { - Shell.cmd("rm " + originalPath).exec(); + Shell.cmd("rm \"" + originalPath + "\"").exec(); } saveConfig(); @@ -155,6 +197,7 @@ public class ConfigManager { public static class ModuleConfig { public boolean enabled = true; public boolean hideInjection = false; + public List globalSoFiles = new ArrayList<>(); public Map perAppConfig = new HashMap<>(); } diff --git a/configapp/src/main/java/com/jiqiu/configapp/FileBrowserActivity.java b/configapp/src/main/java/com/jiqiu/configapp/FileBrowserActivity.java new file mode 100644 index 0000000..725913d --- /dev/null +++ b/configapp/src/main/java/com/jiqiu/configapp/FileBrowserActivity.java @@ -0,0 +1,287 @@ +package com.jiqiu.configapp; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; +import android.util.Log; + +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.topjohnwu.superuser.Shell; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class FileBrowserActivity extends AppCompatActivity { + + private static final String TAG = "FileBrowser"; + public static final String EXTRA_START_PATH = "start_path"; + public static final String EXTRA_FILE_FILTER = "file_filter"; + public static final String EXTRA_SELECTED_PATH = "selected_path"; + + private RecyclerView recyclerView; + private TextView currentPathText; + private View emptyView; + private FileListAdapter adapter; + + private String currentPath; + private String fileFilter = ".so"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_file_browser); + + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setTitle("选择SO文件"); + + currentPathText = findViewById(R.id.currentPath); + recyclerView = findViewById(R.id.recyclerView); + emptyView = findViewById(R.id.emptyView); + + adapter = new FileListAdapter(); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + recyclerView.setAdapter(adapter); + + // Get start path from intent + String startPath = getIntent().getStringExtra(EXTRA_START_PATH); + if (startPath == null) { + startPath = "/data/local/tmp"; + } + fileFilter = getIntent().getStringExtra(EXTRA_FILE_FILTER); + if (fileFilter == null) { + fileFilter = ".so"; + } + + // Check if we have root access + if (!Shell.getShell().isRoot()) { + Toast.makeText(this, "需要Root权限才能浏览文件", Toast.LENGTH_LONG).show(); + Log.e(TAG, "No root access"); + } + + currentPath = startPath; + loadFiles(); + } + + private void loadFiles() { + currentPathText.setText(currentPath); + + List items = new ArrayList<>(); + + // Add parent directory if not root + if (!"/".equals(currentPath)) { + items.add(new FileItem("..", true, true)); + } + + // List files using root + Log.d(TAG, "Loading files from: " + currentPath); + Shell.Result result = Shell.cmd("ls -la " + currentPath + " 2>/dev/null").exec(); + Log.d(TAG, "ls command success: " + result.isSuccess() + ", output lines: " + result.getOut().size()); + + if (result.isSuccess()) { + for (String line : result.getOut()) { + // Skip empty lines, total line, and symbolic links + if (line.trim().isEmpty() || line.startsWith("total") || line.contains("->")) { + continue; + } + + // Try to parse ls output - handle different formats + String name = null; + boolean isDirectory = false; + boolean isReadable = true; + + // Check if line starts with permissions (drwxr-xr-x format) + if (line.matches("^[dlrwxst-]{10}.*")) { + String[] parts = line.split("\\s+", 9); + if (parts.length >= 9) { + String permissions = parts[0]; + name = parts[parts.length - 1]; + isDirectory = permissions.startsWith("d"); + isReadable = permissions.length() > 1 && permissions.charAt(1) == 'r'; + } + } else { + // Simple format, just the filename + name = line.trim(); + // Check if it's a directory by trying to list it + Shell.Result dirCheck = Shell.cmd("test -d \"" + currentPath + "/" + name + "\" && echo 'dir'").exec(); + isDirectory = dirCheck.isSuccess() && !dirCheck.getOut().isEmpty(); + } + + if (name != null && !".".equals(name) && !"..".equals(name)) { + // Filter files by extension + if (!isDirectory && fileFilter != null && !name.endsWith(fileFilter)) { + continue; + } + + items.add(new FileItem(name, isDirectory, isReadable)); + } + } + } else { + // If ls fails, try a simpler approach + Shell.Result simpleResult = Shell.cmd("cd " + currentPath + " && for f in *; do echo \"$f\"; done").exec(); + if (simpleResult.isSuccess()) { + for (String name : simpleResult.getOut()) { + if (!name.trim().isEmpty() && !"*".equals(name)) { + Shell.Result dirCheck = Shell.cmd("test -d \"" + currentPath + "/" + name + "\" && echo 'dir'").exec(); + boolean isDirectory = dirCheck.isSuccess() && !dirCheck.getOut().isEmpty(); + + // Filter files by extension + if (!isDirectory && fileFilter != null && !name.endsWith(fileFilter)) { + continue; + } + + items.add(new FileItem(name, isDirectory, true)); + } + } + } + } + + // If still no items and not root, add some common directories to try + if (items.size() <= 1 && "/data/local/tmp".equals(currentPath)) { + // Try to create a test file to verify access + Shell.cmd("touch /data/local/tmp/test_access.tmp && rm /data/local/tmp/test_access.tmp").exec(); + + // Add any .so files we can find + Shell.Result findResult = Shell.cmd("find " + currentPath + " -maxdepth 1 -name '*.so' -type f 2>/dev/null").exec(); + if (findResult.isSuccess()) { + for (String path : findResult.getOut()) { + if (!path.trim().isEmpty()) { + String name = path.substring(path.lastIndexOf('/') + 1); + items.add(new FileItem(name, false, true)); + } + } + } + } + + Collections.sort(items, (a, b) -> { + if (a.isDirectory != b.isDirectory) { + return a.isDirectory ? -1 : 1; + } + return a.name.compareToIgnoreCase(b.name); + }); + + adapter.setItems(items); + emptyView.setVisibility(items.isEmpty() || (items.size() == 1 && "..".equals(items.get(0).name)) ? View.VISIBLE : View.GONE); + } + + @Override + public boolean onSupportNavigateUp() { + onBackPressed(); + return true; + } + + class FileItem { + String name; + boolean isDirectory; + boolean isReadable; + + FileItem(String name, boolean isDirectory, boolean isReadable) { + this.name = name; + this.isDirectory = isDirectory; + this.isReadable = isReadable; + } + } + + class FileListAdapter extends RecyclerView.Adapter { + private List items = new ArrayList<>(); + + void setItems(List items) { + this.items = items; + notifyDataSetChanged(); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_file, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + holder.bind(items.get(position)); + } + + @Override + public int getItemCount() { + return items.size(); + } + + class ViewHolder extends RecyclerView.ViewHolder { + ImageView icon; + TextView name; + TextView info; + + ViewHolder(@NonNull View itemView) { + super(itemView); + icon = itemView.findViewById(R.id.fileIcon); + name = itemView.findViewById(R.id.fileName); + info = itemView.findViewById(R.id.fileInfo); + } + + void bind(FileItem item) { + name.setText(item.name); + + if (item.isDirectory) { + icon.setImageResource(android.R.drawable.ic_menu_agenda); + info.setText("文件夹"); + } else { + icon.setImageResource(android.R.drawable.ic_menu_save); + info.setText("SO文件"); + } + + if (!item.isReadable) { + itemView.setAlpha(0.5f); + } else { + itemView.setAlpha(1.0f); + } + + itemView.setOnClickListener(v -> { + if ("..".equals(item.name)) { + // Go to parent directory + int lastSlash = currentPath.lastIndexOf('/'); + if (lastSlash > 0) { + currentPath = currentPath.substring(0, lastSlash); + } else { + currentPath = "/"; + } + loadFiles(); + } else if (item.isDirectory) { + if (!item.isReadable) { + Toast.makeText(FileBrowserActivity.this, + "没有权限访问此目录", Toast.LENGTH_SHORT).show(); + return; + } + if ("/".equals(currentPath)) { + currentPath = "/" + item.name; + } else { + currentPath = currentPath + "/" + item.name; + } + loadFiles(); + } else { + // File selected + String selectedPath = currentPath + "/" + item.name; + Intent resultIntent = new Intent(); + resultIntent.putExtra(EXTRA_SELECTED_PATH, selectedPath); + setResult(Activity.RESULT_OK, resultIntent); + finish(); + } + }); + } + } + } +} \ No newline at end of file diff --git a/configapp/src/main/java/com/jiqiu/configapp/SoManagerFragment.java b/configapp/src/main/java/com/jiqiu/configapp/SoManagerFragment.java index 70278e9..e996bbd 100644 --- a/configapp/src/main/java/com/jiqiu/configapp/SoManagerFragment.java +++ b/configapp/src/main/java/com/jiqiu/configapp/SoManagerFragment.java @@ -36,6 +36,7 @@ public class SoManagerFragment extends Fragment { private List globalSoFiles = new ArrayList<>(); private ActivityResultLauncher filePickerLauncher; + private ActivityResultLauncher fileBrowserLauncher; @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -55,6 +56,19 @@ public class SoManagerFragment extends Fragment { } } ); + + // Initialize file browser + fileBrowserLauncher = registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), + result -> { + if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) { + String path = result.getData().getStringExtra(FileBrowserActivity.EXTRA_SELECTED_PATH); + if (path != null) { + showDeleteOriginalDialog(path); + } + } + } + ); } @Nullable @@ -87,32 +101,16 @@ public class SoManagerFragment extends Fragment { Toast.makeText(getContext(), "需要Root权限", Toast.LENGTH_LONG).show(); } else { configManager.ensureModuleDirectories(); + // Also ensure common directories exist + Shell.cmd("mkdir -p /data/local/tmp").exec(); + Shell.cmd("chmod 777 /data/local/tmp").exec(); loadSoFiles(); } } private void loadSoFiles() { - // Load global SO files from the storage directory - Shell.Result result = Shell.cmd("ls -la " + ConfigManager.SO_STORAGE_DIR).exec(); - - globalSoFiles.clear(); - if (result.isSuccess()) { - for (String line : result.getOut()) { - if (line.contains(".so")) { - // Parse file info - String[] parts = line.split("\\s+"); - if (parts.length >= 9) { - String fileName = parts[parts.length - 1]; - ConfigManager.SoFile soFile = new ConfigManager.SoFile(); - soFile.name = fileName; - soFile.storedPath = ConfigManager.SO_STORAGE_DIR + "/" + fileName; - soFile.originalPath = soFile.storedPath; // For display - globalSoFiles.add(soFile); - } - } - } - } - + // Load global SO files from config + globalSoFiles = configManager.getAllSoFiles(); updateUI(); } @@ -128,12 +126,14 @@ public class SoManagerFragment extends Fragment { } private void showAddSoDialog() { - String[] options = {"从文件管理器选择", "输入路径"}; + String[] options = {"浏览文件系统", "从外部文件管理器选择", "手动输入路径"}; new MaterialAlertDialogBuilder(requireContext()) .setTitle("添加SO文件") .setItems(options, (dialog, which) -> { if (which == 0) { + openFileBrowser(); + } else if (which == 1) { openFilePicker(); } else { showPathInputDialog(); @@ -142,6 +142,54 @@ public class SoManagerFragment extends Fragment { .show(); } + private void openFileBrowser() { + // Show path selection dialog first + String[] paths = { + "/data/local/tmp", + "/sdcard", + "/sdcard/Download", + "/storage/emulated/0", + "自定义路径..." + }; + + new MaterialAlertDialogBuilder(requireContext()) + .setTitle("选择起始目录") + .setItems(paths, (dialog, which) -> { + if (which == paths.length - 1) { + // Custom path + showCustomPathDialog(); + } else { + Intent intent = new Intent(getContext(), FileBrowserActivity.class); + intent.putExtra(FileBrowserActivity.EXTRA_START_PATH, paths[which]); + intent.putExtra(FileBrowserActivity.EXTRA_FILE_FILTER, ".so"); + fileBrowserLauncher.launch(intent); + } + }) + .show(); + } + + private void showCustomPathDialog() { + View view = getLayoutInflater().inflate(R.layout.dialog_input, null); + android.widget.EditText editText = view.findViewById(android.R.id.edit); + editText.setText("/"); + editText.setHint("输入起始路径"); + + new MaterialAlertDialogBuilder(requireContext()) + .setTitle("自定义起始路径") + .setView(view) + .setPositiveButton("确定", (dialog, which) -> { + String path = editText.getText().toString().trim(); + if (!path.isEmpty()) { + Intent intent = new Intent(getContext(), FileBrowserActivity.class); + intent.putExtra(FileBrowserActivity.EXTRA_START_PATH, path); + intent.putExtra(FileBrowserActivity.EXTRA_FILE_FILTER, ".so"); + fileBrowserLauncher.launch(intent); + } + }) + .setNegativeButton("取消", null) + .show(); + } + private void openFilePicker() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("*/*"); @@ -152,6 +200,7 @@ public class SoManagerFragment extends Fragment { private void showPathInputDialog() { View view = getLayoutInflater().inflate(R.layout.dialog_input, null); android.widget.EditText editText = view.findViewById(android.R.id.edit); + editText.setText("/data/local/tmp/"); editText.setHint("/data/local/tmp/example.so"); new MaterialAlertDialogBuilder(requireContext()) @@ -160,7 +209,7 @@ public class SoManagerFragment extends Fragment { .setPositiveButton("添加", (dialog, which) -> { String path = editText.getText().toString().trim(); if (!path.isEmpty()) { - addSoFile(path, false); + showDeleteOriginalDialog(path); } }) .setNegativeButton("取消", null) @@ -175,40 +224,38 @@ public class SoManagerFragment extends Fragment { if (path.startsWith("file://")) { path = path.substring(7); } - addSoFile(path, false); + showDeleteOriginalDialog(path); } } + private void showDeleteOriginalDialog(String path) { + new MaterialAlertDialogBuilder(requireContext()) + .setTitle("删除原文件") + .setMessage("是否删除原始SO文件?\n\n文件路径:" + path) + .setPositiveButton("删除原文件", (dialog, which) -> { + addSoFile(path, true); + }) + .setNegativeButton("保留原文件", (dialog, which) -> { + addSoFile(path, false); + }) + .setNeutralButton("取消", null) + .show(); + } + private void addSoFile(String path, boolean deleteOriginal) { // Verify file exists - Shell.Result result = Shell.cmd("test -f " + path + " && echo 'exists'").exec(); + Shell.Result result = Shell.cmd("test -f \"" + path + "\" && echo 'exists'").exec(); if (!result.isSuccess() || result.getOut().isEmpty()) { Toast.makeText(getContext(), "文件不存在: " + path, Toast.LENGTH_SHORT).show(); return; } - // Generate unique filename - String fileName = new File(path).getName(); - String storedPath = ConfigManager.SO_STORAGE_DIR + "/" + System.currentTimeMillis() + "_" + fileName; + // Add to global SO files + configManager.addGlobalSoFile(path, deleteOriginal); - // Copy file - result = Shell.cmd("cp " + path + " " + storedPath).exec(); - if (result.isSuccess()) { - ConfigManager.SoFile soFile = new ConfigManager.SoFile(); - soFile.name = fileName; - soFile.storedPath = storedPath; - soFile.originalPath = path; - globalSoFiles.add(soFile); - - if (deleteOriginal) { - Shell.cmd("rm " + path).exec(); - } - - updateUI(); - Toast.makeText(getContext(), "SO文件已添加", Toast.LENGTH_SHORT).show(); - } else { - Toast.makeText(getContext(), "复制文件失败", Toast.LENGTH_SHORT).show(); - } + // Reload the list + loadSoFiles(); + Toast.makeText(getContext(), "SO文件已添加", Toast.LENGTH_SHORT).show(); } private void showDeleteConfirmation(ConfigManager.SoFile soFile) { @@ -223,9 +270,8 @@ public class SoManagerFragment extends Fragment { } private void deleteSoFile(ConfigManager.SoFile soFile) { - Shell.cmd("rm " + soFile.storedPath).exec(); - globalSoFiles.remove(soFile); - updateUI(); + configManager.removeGlobalSoFile(soFile); + loadSoFiles(); Toast.makeText(getContext(), "SO文件已删除", Toast.LENGTH_SHORT).show(); } } \ No newline at end of file diff --git a/configapp/src/main/res/layout/activity_app_so_config.xml b/configapp/src/main/res/layout/activity_app_so_config.xml new file mode 100644 index 0000000..9b80163 --- /dev/null +++ b/configapp/src/main/res/layout/activity_app_so_config.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/configapp/src/main/res/layout/activity_file_browser.xml b/configapp/src/main/res/layout/activity_file_browser.xml new file mode 100644 index 0000000..a661436 --- /dev/null +++ b/configapp/src/main/res/layout/activity_file_browser.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/configapp/src/main/res/layout/fragment_so_manager.xml b/configapp/src/main/res/layout/fragment_so_manager.xml index 2023503..a8153c3 100644 --- a/configapp/src/main/res/layout/fragment_so_manager.xml +++ b/configapp/src/main/res/layout/fragment_so_manager.xml @@ -1,62 +1,75 @@ - - - - - - - + android:layout_height="match_parent"> + android:orientation="vertical"> - + - + - + + + + + + + + + + + + + + + + + - - - \ No newline at end of file + \ No newline at end of file diff --git a/configapp/src/main/res/layout/item_file.xml b/configapp/src/main/res/layout/item_file.xml new file mode 100644 index 0000000..187d4ce --- /dev/null +++ b/configapp/src/main/res/layout/item_file.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/configapp/src/main/res/layout/item_so_selection.xml b/configapp/src/main/res/layout/item_so_selection.xml new file mode 100644 index 0000000..3c4d5fa --- /dev/null +++ b/configapp/src/main/res/layout/item_so_selection.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + \ No newline at end of file