package com.vectras.vm; import static android.content.Intent.ACTION_VIEW; import static android.view.View.GONE; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.LinearLayout; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; import android.widget.BaseAdapter; import androidx.activity.EdgeToEdge; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.termux.app.TermuxService; import com.vectras.qemu.MainSettingsManager; import com.vectras.vm.main.MainActivity; import com.vectras.vm.setupwizard.SetupWizard2Activity; import com.vectras.vm.utils.CommandUtils; import com.vectras.vm.utils.DialogUtils; import com.vectras.vm.utils.FileUtils; import com.vectras.vm.utils.ListUtils; import com.vectras.vm.utils.PackageUtils; import com.vectras.vm.utils.UIUtils; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Objects; public class Minitools extends AppCompatActivity { private final String TAG = "Minitools"; private final ArrayList> listmapForSelectMirrors = new ArrayList<>(); private Spinner spinnerselectmirror; private String selectedMirrorCommand = ""; LinearLayout setupsoundfortermux; LinearLayout cleanup; LinearLayout restore; LinearLayout deleteallvm; LinearLayout reinstallsystem; LinearLayout deleteall; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_minitools); // UIUtils.setOnApplyWindowInsetsListener(findViewById(R.id.main)); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); toolbar.setTitle(getString(R.string.mini_tools)); setupsoundfortermux = findViewById(R.id.setupsoundfortermux); cleanup = findViewById(R.id.cleanup); restore = findViewById(R.id.restore); deleteallvm = findViewById(R.id.deleteallvm); reinstallsystem = findViewById(R.id.reinstallsystem); deleteall = findViewById(R.id.deleteall); spinnerselectmirror = findViewById(R.id.spinnerselectmirror); setupsoundfortermux.setOnClickListener(v -> { if (PackageUtils.isInstalled("com.termux", getApplicationContext())) { DialogUtils.twoDialog(Minitools.this, getString(R.string.setup_sound), getResources().getString(R.string.setup_sound_guide_content), getString(R.string.start_setup), getString(R.string.cancel), true, R.drawable.volume_up_24px, true, () -> { ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); ClipData clip = ClipData.newPlainText("Setup", "curl -o setup.sh https://raw.githubusercontent.com/AnBui2004/termux/refs/heads/main/installpulseaudio.sh && chmod +rwx setup.sh && ./setup.sh && rm setup.sh"); clipboard.setPrimaryClip(clip); Intent intent = new Intent(); intent.setAction(ACTION_VIEW); intent.setData(Uri.parse("android-app://com.termux")); startActivity(intent); Toast.makeText(getApplicationContext(), getResources().getString(R.string.copied), Toast.LENGTH_LONG).show(); }, null, null); } else { DialogUtils.twoDialog(Minitools.this, getString(R.string.termux_is_not_installed), getResources().getString(R.string.you_need_to_install_termux), getString(R.string.install), getString(R.string.cancel), true, R.drawable.arrow_downward_24px, true, () -> { Intent intent = new Intent(); intent.setAction(ACTION_VIEW); intent.setData(Uri.parse("https://github.com/termux/termux-app/releases")); startActivity(intent); }, null, null); } }); cleanup.setOnClickListener(v -> DialogUtils.twoDialog(Minitools.this, getResources().getString(R.string.clean_up), getResources().getString(R.string.clean_up_content), getResources().getString(R.string.clean_up), getResources().getString(R.string.cancel), true, R.drawable.cleaning_services_24px, true, this::cleanUp, null, null)); restore.setOnClickListener(v -> DialogUtils.twoDialog(Minitools.this, getResources().getString(R.string.restore), getResources().getString(R.string.restore_content), getResources().getString(R.string.continuetext), getResources().getString(R.string.cancel), true, R.drawable.settings_backup_restore_24px, true, () -> { VMManager.restoreVMs(); UIUtils.oneDialog(getResources().getString(R.string.done), getResources().getString(R.string.restored) + " " + VMManager.restoredVMs + ".", true, false, Minitools.this); restore.setVisibility(GONE); }, null, null)); deleteallvm.setOnClickListener(v -> DialogUtils.twoDialog(Minitools.this, getResources().getString(R.string.delete_all_vm), getResources().getString(R.string.delete_all_vm_content), getResources().getString(R.string.delete_all), getResources().getString(R.string.cancel), true, R.drawable.delete_24px, true, this::eraserAllVM, null, null)); deleteall.setOnClickListener(v -> DialogUtils.twoDialog(Minitools.this, getResources().getString(R.string.delete_all), getResources().getString(R.string.delete_all_content), getResources().getString(R.string.delete_all), getResources().getString(R.string.cancel), true, R.drawable.delete_forever_24px, true, this::eraserData, null, null)); reinstallsystem.setOnClickListener(v -> DialogUtils.twoDialog(Minitools.this, getResources().getString(R.string.reinstall_system), getResources().getString(R.string.reinstall_system_content), getResources().getString(R.string.continuetext), getResources().getString(R.string.cancel), true, R.drawable.system_update_24px, true, this::eraserSystem, null, null)); spinnerselectmirror.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { selectedMirrorCommand = listmapForSelectMirrors.get(position).get("mirror"); MainSettingsManager.setSelectedMirror(Minitools.this, position); CommandUtils.run(selectedMirrorCommand + "&& exit", false, Minitools.this); } @Override public void onNothingSelected(AdapterView parent) { } }); setupSpiner(); } @Override public boolean onOptionsItemSelected(MenuItem item) { return switch (item.getItemId()) { case 0 -> true; case android.R.id.home -> { finish(); yield true; } default -> super.onOptionsItemSelected(item); }; } private void setupSpiner() { ListUtils.setupMirrorListForListmap(listmapForSelectMirrors); spinnerselectmirror.setAdapter(new SpinnerSelectMirrorAdapter(listmapForSelectMirrors)); spinnerselectmirror.setSelection(MainSettingsManager.getSelectedMirror(Minitools.this)); } public class SpinnerSelectMirrorAdapter extends BaseAdapter { ArrayList> _data; public SpinnerSelectMirrorAdapter(ArrayList> _arr) { _data = _arr; } @Override public int getCount() { return _data.size(); } @Override public HashMap getItem(int _index) { return _data.get(_index); } @Override public long getItemId(int _index) { return _index; } @Override public View getView(final int _position, View _v, ViewGroup _container) { LayoutInflater _inflater = getLayoutInflater(); View _view = _v; if (_view == null) { _view = _inflater.inflate(R.layout.simple_layout_for_spiner, null); } final TextView textViewLocation = _view.findViewById(R.id.textViewLocation); textViewLocation.setText(_data.get(_position).get("location")); return _view; } } private void cleanUp() { View progressView = LayoutInflater.from(this).inflate(R.layout.dialog_progress_style, null); TextView progress_text = progressView.findViewById(R.id.progress_text); progress_text.setText(getString(R.string.just_a_moment)); AlertDialog progressDialog = new MaterialAlertDialogBuilder(this, R.style.CenteredDialogTheme) .setView(progressView) .setCancelable(false) .create(); progressDialog.show(); new Thread(() -> { VMManager.cleanUp(); runOnUiThread(() -> { progressDialog.dismiss(); Toast.makeText(getApplicationContext(), getResources().getString(R.string.done), Toast.LENGTH_LONG).show(); restore.setVisibility(GONE); cleanup.setVisibility(GONE); }); }).start(); } private void eraserAllVM() { View progressView = LayoutInflater.from(this).inflate(R.layout.dialog_progress_style, null); TextView progress_text = progressView.findViewById(R.id.progress_text); progress_text.setText(getString(R.string.just_a_moment)); AlertDialog progressDialog = new MaterialAlertDialogBuilder(this, R.style.CenteredDialogTheme) .setView(progressView) .setCancelable(false) .create(); progressDialog.show(); new Thread(() -> { VMManager.killallqemuprocesses(this); FileUtils.deleteDirectory(AppConfig.vmFolder); FileUtils.deleteDirectory(AppConfig.recyclebin); FileUtils.deleteDirectory(AppConfig.romsdatajson); File vDir = new File(AppConfig.maindirpath); if (!vDir.mkdirs()) Log.e(TAG, "Unable to create folder: " + AppConfig.maindirpath); FileUtils.writeToFile(AppConfig.maindirpath, "roms-data.json", "[]"); runOnUiThread(() -> { progressDialog.dismiss(); cleanup.setVisibility(GONE); restore.setVisibility(GONE); deleteallvm.setVisibility(GONE); Toast.makeText(this, getResources().getString(R.string.done), Toast.LENGTH_LONG).show(); }); }).start(); } private void eraserData() { View progressView = LayoutInflater.from(this).inflate(R.layout.dialog_progress_style, null); TextView progress_text = progressView.findViewById(R.id.progress_text); progress_text.setText(getString(R.string.just_a_moment)); AlertDialog progressDialog = new MaterialAlertDialogBuilder(this, R.style.CenteredDialogTheme) .setView(progressView) .setCancelable(false) .create(); progressDialog.show(); new Thread(() -> { VMManager.killallqemuprocesses(this); FileUtils.deleteDirectory(AppConfig.maindirpath); File vDir = new File(AppConfig.maindirpath); if (!vDir.mkdirs()) Log.e(TAG, "Unable to create folder: " + AppConfig.maindirpath); FileUtils.writeToFile(AppConfig.maindirpath, "roms-data.json", "[]"); runOnUiThread(() -> { progressDialog.dismiss(); cleanup.setVisibility(GONE); restore.setVisibility(GONE); deleteallvm.setVisibility(GONE); deleteall.setVisibility(GONE); Toast.makeText(this, getResources().getString(R.string.done), Toast.LENGTH_LONG).show(); }); }).start(); } private void eraserSystem() { View progressView = LayoutInflater.from(this).inflate(R.layout.dialog_progress_style, null); TextView progress_text = progressView.findViewById(R.id.progress_text); progress_text.setText(getString(R.string.just_a_moment)); AlertDialog progressDialog = new MaterialAlertDialogBuilder(this, R.style.CenteredDialogTheme) .setView(progressView) .setCancelable(false) .create(); progressDialog.show(); new Thread(() -> { MainActivity.isActivate = false; AppConfig.needreinstallsystem = true; VMManager.killallqemuprocesses(this); FileUtils.deleteDirectory(getFilesDir().getAbsolutePath() + "/data"); FileUtils.deleteDirectory(getFilesDir().getAbsolutePath() + "/distro"); FileUtils.deleteDirectory(getFilesDir().getAbsolutePath() + "/usr"); runOnUiThread(() -> { progressDialog.dismiss(); Intent intent = new Intent(); intent.setClass(this, SetupWizard2Activity.class); startActivity(intent); finishAffinity(); }); }).start(); } public void extractLoaderApk() { String apkLoaderAssetPath = "bootstrap/loader.apk"; String apkLoaderextractedFilePath = TermuxService.PREFIX_PATH + "/libexec/termux-x11/loader.apk"; FileUtils.deleteDirectory(apkLoaderextractedFilePath); if (copyAssetToFile(apkLoaderAssetPath, apkLoaderextractedFilePath)) { FileUtils.copyAFile(TermuxService.PREFIX_PATH + "/libexec/termux-x11/loader.apk", AppConfig.maindirpath); } } private boolean copyAssetToFile(String assetPath, String outputPath) { try (InputStream in = getAssets().open(assetPath); OutputStream out = new FileOutputStream(outputPath)) { byte[] buffer = new byte[1024]; int read; while ((read = in.read(buffer)) != -1) { out.write(buffer, 0, read); } out.flush(); return true; } catch (IOException e) { Log.e(TAG, "copyAssetToFile: ", e); return false; } } }