mirror of
https://github.com/xoureldeen/Vectras-VM-Android.git
synced 2026-05-22 03:19:23 +00:00
734 lines
32 KiB
Java
734 lines
32 KiB
Java
package com.vectras.vm.setupwizard;
|
|
|
|
import static android.content.Intent.ACTION_VIEW;
|
|
|
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
|
import com.termux.app.TermuxActivity;
|
|
import com.termux.app.TermuxService;
|
|
|
|
import android.annotation.SuppressLint;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.SharedPreferences;
|
|
import android.net.Uri;
|
|
import android.os.Build;
|
|
import android.os.Bundle;
|
|
import android.util.Log;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.widget.AdapterView;
|
|
import android.widget.ScrollView;
|
|
import android.widget.TextView;
|
|
import android.widget.Toast;
|
|
import android.widget.BaseAdapter;
|
|
|
|
import androidx.activity.OnBackPressedCallback;
|
|
import androidx.activity.result.ActivityResultLauncher;
|
|
import androidx.activity.result.contract.ActivityResultContracts;
|
|
import androidx.appcompat.app.AlertDialog;
|
|
import androidx.appcompat.app.AppCompatActivity;
|
|
import androidx.preference.PreferenceManager;
|
|
|
|
import com.google.gson.Gson;
|
|
import com.google.gson.reflect.TypeToken;
|
|
import com.vectras.qemu.MainSettingsManager;
|
|
import com.vectras.vm.AppConfig;
|
|
import com.vectras.vm.R;
|
|
import com.vectras.vm.RequestNetwork;
|
|
import com.vectras.vm.RequestNetworkController;
|
|
import com.vectras.vm.databinding.ActivitySetupWizardBinding;
|
|
import com.vectras.vm.databinding.SetupQemuAdvancedBinding;
|
|
import com.vectras.vm.databinding.SetupQemuDoneBinding;
|
|
import com.vectras.vm.databinding.SimpleLayoutForSpinerBinding;
|
|
import com.vectras.vm.home.HomeActivity;
|
|
import com.vectras.vm.utils.ClipboardUltils;
|
|
import com.vectras.vm.utils.DeviceUtils;
|
|
import com.vectras.vm.utils.DialogUtils;
|
|
import com.vectras.vm.utils.FileUtils;
|
|
import com.vectras.vm.utils.JSONUtils;
|
|
import com.vectras.vm.utils.ListUtils;
|
|
import com.vectras.vm.utils.UIUtils;
|
|
import com.vectras.vm.utils.PermissionUtils;
|
|
import com.vectras.vterm.TerminalBottomSheetDialog;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.BufferedWriter;
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.io.InputStreamReader;
|
|
import java.io.OutputStreamWriter;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.Objects;
|
|
|
|
public class SetupWizardActivity extends AppCompatActivity {
|
|
ActivitySetupWizardBinding binding;
|
|
SetupQemuAdvancedBinding bindingAdvancedSetup;
|
|
SetupQemuDoneBinding bindingFinalSteps;
|
|
private final String TAG = "SetupWizardActivity";
|
|
private boolean isFirstLaunch = false;
|
|
private boolean libprooterror = false;
|
|
private RequestNetwork net;
|
|
private RequestNetwork.RequestListener _net_request_listener;
|
|
private String contentJSON = "";
|
|
private HashMap<String, Object> mmap = new HashMap<>();
|
|
private String bootstrapfilelink = "";
|
|
private final ArrayList<HashMap<String, String>> listmapForSelectMirrors = new ArrayList<>();
|
|
private String selectedMirrorCommand = "echo ";
|
|
private String selectedMirrorLocation = "";
|
|
private String downloadBootstrapsCommand = "";
|
|
private boolean aria2Error = false;
|
|
private boolean isexecutingCommand = false;
|
|
private boolean isServerError = false;
|
|
private boolean isManualMode = false;
|
|
private boolean isAllowCheckPermissions = false;
|
|
String tarPath;
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
UIUtils.edgeToEdge(this);
|
|
binding = ActivitySetupWizardBinding.inflate(getLayoutInflater());
|
|
bindingAdvancedSetup = binding.advancedsetup;
|
|
bindingFinalSteps = binding.finalsteps;
|
|
setContentView(binding.getRoot());
|
|
UIUtils.setOnApplyWindowInsetsListener(findViewById(R.id.main));
|
|
|
|
setupSpiner();
|
|
|
|
tarPath = getExternalFilesDir("data") + "/data.tar.gz";
|
|
|
|
binding.spinnerselectmirror.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
|
@Override
|
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
|
selectedMirrorCommand = Objects.requireNonNull(listmapForSelectMirrors.get(position).get("mirror"));
|
|
selectedMirrorLocation = Objects.requireNonNull(listmapForSelectMirrors.get(position).get("location"));
|
|
MainSettingsManager.setSelectedMirror(SetupWizardActivity.this, position);
|
|
}
|
|
|
|
@Override
|
|
public void onNothingSelected(AdapterView<?> parent) {
|
|
|
|
}
|
|
});
|
|
|
|
net = new RequestNetwork(this);
|
|
_net_request_listener = new RequestNetwork.RequestListener() {
|
|
@Override
|
|
public void onResponse(String tag, String response, HashMap<String, Object> responseHeaders) {
|
|
binding.linearload.setVisibility(View.GONE);
|
|
contentJSON = response;
|
|
if (JSONUtils.isValidFromString(contentJSON)) {
|
|
mmap.clear();
|
|
mmap = new Gson().fromJson(contentJSON, new TypeToken<HashMap<String, Object>>() {
|
|
}.getType());
|
|
if (mmap.containsKey("arm64-v8a") && mmap.containsKey("amd64")) {
|
|
if (Build.SUPPORTED_ABIS[0].contains("arm64")) {
|
|
bootstrapfilelink = Objects.requireNonNull(mmap.get("arm64-v8a")).toString();
|
|
} else {
|
|
bootstrapfilelink = Objects.requireNonNull(mmap.get("amd64")).toString();
|
|
}
|
|
downloadBootstrapsCommand = " aria2c -x 4 --async-dns=false --disable-ipv6 --check-certificate=false -o setup.tar.gz " + bootstrapfilelink;
|
|
if (!bootstrapfilelink.isEmpty()) {
|
|
binding.linearcannotconnecttoserver.setVisibility(View.GONE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onErrorResponse(String tag, String message) {
|
|
binding.linearload.setVisibility(View.GONE);
|
|
}
|
|
};
|
|
|
|
setupOnClick();
|
|
|
|
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
|
|
@Override
|
|
public void handleOnBackPressed() {
|
|
if (isexecutingCommand) {
|
|
if (binding.linearsimplesetupui.getVisibility() == View.GONE)
|
|
binding.linearsimplesetupui.setVisibility(View.VISIBLE);
|
|
} else if (bindingAdvancedSetup.lnAdvancedsetup.getVisibility() == View.GONE) {
|
|
onBackInFinalSteps();
|
|
} else {
|
|
setEnabled(false);
|
|
getOnBackPressedDispatcher().onBackPressed();
|
|
}
|
|
}
|
|
});
|
|
|
|
extractSystemFiles();
|
|
}
|
|
|
|
private void extractSystemFiles() {
|
|
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.installing));
|
|
AlertDialog progressDialog = new MaterialAlertDialogBuilder(this, R.style.CenteredDialogTheme)
|
|
.setView(progressView)
|
|
.setCancelable(false)
|
|
.create();
|
|
progressDialog.show();
|
|
|
|
new Thread(() -> {
|
|
boolean result = SetupFeatureCore.startExtractSystemFiles(this);
|
|
|
|
runOnUiThread(() -> {
|
|
progressDialog.dismiss();
|
|
|
|
if (!result) DialogUtils.oneDialog(
|
|
this,
|
|
getString(R.string.oops),
|
|
getString(R.string.system_files_installation_failed_content),
|
|
getString(R.string.try_again),
|
|
true,
|
|
R.drawable.error_96px,
|
|
false,
|
|
this::extractSystemFiles,
|
|
null);
|
|
});
|
|
}).start();
|
|
}
|
|
|
|
@Override
|
|
protected void onResume() {
|
|
super.onResume();
|
|
checkpermissions();
|
|
}
|
|
|
|
// Function to append text and automatically scroll to bottom
|
|
@SuppressLint("SetTextI18n")
|
|
private void appendTextAndScroll(String textToAdd) {
|
|
// Update the text
|
|
bindingAdvancedSetup.tvTerminalOutput.append(textToAdd);
|
|
|
|
if (textToAdd.contains("xssFjnj58Id")) {
|
|
isexecutingCommand = false;
|
|
binding.linearsimplesetupui.setVisibility(View.GONE);
|
|
bindingAdvancedSetup.lnAdvancedsetup.setVisibility(View.GONE);
|
|
} else if (textToAdd.contains("libproot.so --help") || textToAdd.contains("/bin/sh: can't fork:")) {
|
|
libprooterror = true;
|
|
} else if (textToAdd.contains("not complete: /root/setup.tar.gz")) {
|
|
aria2Error = true;
|
|
} else if (textToAdd.contains("temporary error")) {
|
|
isServerError = true;
|
|
}
|
|
|
|
if (textToAdd.contains("Starting setup...")) {
|
|
setTextStatus(getString(R.string.getting_ready_for_you_please_don_t_disconnect_the_network));
|
|
} else if (textToAdd.contains("fetch http")) {
|
|
setTextStatus(getString(R.string.connecting_to_mirror_in) + "\n" + selectedMirrorLocation + "...");
|
|
} else if (textToAdd.contains("Installing packages...")) {
|
|
setTextStatus(getString(R.string.completed_10_it_won_t_take_long));
|
|
} else if (textToAdd.contains("(50/")) {
|
|
setTextStatus(getString(R.string.completed_20_it_won_t_take_long));
|
|
} else if (textToAdd.contains("100/")) {
|
|
setTextStatus(getString(R.string.completed_30_it_won_t_take_long));
|
|
} else if (textToAdd.contains("150/")) {
|
|
setTextStatus(getString(R.string.completed_40_it_won_t_take_long));
|
|
} else if (textToAdd.contains("200/")) {
|
|
setTextStatus(getString(R.string.completed_50_it_won_t_take_long));
|
|
} else if (textToAdd.contains("Downloading Qemu...")) {
|
|
setTextStatus(getString(R.string.completed_75_don_t_disconnect));
|
|
} else if (textToAdd.contains("Installing Qemu...")) {
|
|
setTextStatus(getString(R.string.keep_it_up));
|
|
} else if (textToAdd.contains("qemu-system")) {
|
|
setTextStatus(getString(R.string.completed_95_keep_it_up));
|
|
} else if (textToAdd.contains("Just a sec...")) {
|
|
setTextStatus(getString(R.string.almost_there));
|
|
}
|
|
|
|
// Scroll to the bottom
|
|
bindingAdvancedSetup.scrollView.post(() -> bindingAdvancedSetup.scrollView.fullScroll(ScrollView.FOCUS_DOWN));
|
|
}
|
|
|
|
private void setTextStatus(String content) {
|
|
bindingAdvancedSetup.title.setText(content.replaceAll("\n", ". "));
|
|
binding.textviewsettingup.setText(content);
|
|
}
|
|
|
|
private void startSetup() {
|
|
aria2Error = false;
|
|
isServerError = false;
|
|
simpleSetupUIControler(1);
|
|
uiControllerAdvancedSetup(true);
|
|
String cmd = selectedMirrorCommand + ";" +
|
|
" set -e;" +
|
|
" echo \"Starting setup...\";" +
|
|
" apk update;" +
|
|
" echo \"Installing packages...\";" +
|
|
" apk add " + (DeviceUtils.is64bit() ? AppConfig.neededPkgs : AppConfig.neededPkgs32bit) + ";" +
|
|
" echo \"Downloading Qemu...\";";
|
|
|
|
if (isManualMode) {
|
|
cmd += " tar -xzvf " + tarPath + " -C /;" +
|
|
" rm " + tarPath + ";" +
|
|
" chmod 775 /usr/local/bin/*;";
|
|
} else if (DeviceUtils.is64bit()) {
|
|
cmd += downloadBootstrapsCommand + ";" +
|
|
" echo \"Installing Qemu...\";" +
|
|
" tar -xzvf setup.tar.gz -C /;" +
|
|
" rm setup.tar.gz;" +
|
|
" chmod 775 /usr/local/bin/*;";
|
|
} else {
|
|
cmd += " apk add qemu-system-x86_64 qemu-system-ppc qemu-system-i386 qemu-system-aarch64" +
|
|
" qemu-pr-helper qemu-img mesa-dri-gallium;";
|
|
}
|
|
|
|
cmd += " echo \"Just a sec...\";" +
|
|
" echo export TMPDIR=/tmp >> /etc/profile;" +
|
|
" mkdir -p $TMPDIR/pulse;" +
|
|
" echo export PULSE_SERVER=127.0.0.1 >> /etc/profile;" +
|
|
" mkdir -p ~/.vnc && echo -e \"555555\\n555555\" | vncpasswd -f > ~/.vnc/passwd && chmod 0600 ~/.vnc/passwd;" +
|
|
" echo \"installation successful! xssFjnj58Id\"";
|
|
|
|
bindingAdvancedSetup.tvCommandsetup.setText(cmd);
|
|
executeShellCommand(cmd);
|
|
}
|
|
|
|
private void checkpermissions() {
|
|
if (!isAllowCheckPermissions) return;
|
|
|
|
if (PermissionUtils.storagepermission(this, true)) {
|
|
if (!isFirstLaunch) {
|
|
isFirstLaunch = true;
|
|
SetupFeatureCore.checkabi(this);
|
|
|
|
if (binding.linearsimplesetupui.getVisibility() == View.GONE) {
|
|
showAdvancedSetupDialog();
|
|
}
|
|
}
|
|
|
|
if (DeviceUtils.isStorageLow(this, false)) {
|
|
DialogUtils.oneDialog(this,
|
|
getResources().getString(R.string.oops),
|
|
getResources().getString(R.string.not_enough_storage_to_set_up_content),
|
|
getResources().getString(R.string.ok),
|
|
true,
|
|
R.drawable.warning_48px,
|
|
true,
|
|
null,
|
|
() -> {
|
|
if (DeviceUtils.isStorageLow(this, false)) finish();
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
private void simpleSetupUIControler(int status) {
|
|
if (status == 0) {
|
|
//Before setup.
|
|
binding.linearstartsetup.setVisibility(View.VISIBLE);
|
|
binding.linearsettingup.setVisibility(View.GONE);
|
|
binding.linearsetupfailed.setVisibility(View.GONE);
|
|
|
|
File tarGZ = new File(tarPath);
|
|
if (tarGZ.exists()) {
|
|
if (!tarGZ.delete()) Log.e(TAG, "simpleSetupUIControler: Unable to delete " + tarPath);
|
|
}
|
|
} else if (status == 1) {
|
|
//Setting up.
|
|
binding.linearstartsetup.setVisibility(View.GONE);
|
|
binding.linearsettingup.setVisibility(View.VISIBLE);
|
|
binding.linearsetupfailed.setVisibility(View.GONE);
|
|
} else if (status == 2) {
|
|
//Failed.
|
|
binding.linearstartsetup.setVisibility(View.GONE);
|
|
binding.linearsettingup.setVisibility(View.GONE);
|
|
binding.linearsetupfailed.setVisibility(View.VISIBLE);
|
|
}
|
|
}
|
|
|
|
private final ActivityResultLauncher<String> bootstrapFilePicker =
|
|
registerForActivityResult(new ActivityResultContracts.GetContent(), uri -> {
|
|
if (uri != null) {
|
|
String abi = Build.SUPPORTED_ABIS[0];
|
|
if (FileUtils.getFileNameFromUri(this, uri).endsWith(abi + ".tar.gz")) {
|
|
simpleSetupUIControler(1);
|
|
new Thread(() -> {
|
|
try {
|
|
setTextStatus(getString(R.string.copying_file));
|
|
FileUtils.copyFileFromUri(this, uri, tarPath);
|
|
runOnUiThread(() -> {
|
|
isManualMode = true;
|
|
startSetup();
|
|
MainSettingsManager.setsetUpWithManualSetupBefore(SetupWizardActivity.this, true);
|
|
});
|
|
} catch (Exception e) {
|
|
runOnUiThread(() -> {
|
|
DialogUtils.oneDialog(this,
|
|
getString(R.string.oops),
|
|
getString(R.string.the_file_could_not_be_processed_content),
|
|
getResources().getString(R.string.ok),
|
|
true,
|
|
R.drawable.warning_48px,
|
|
true,
|
|
null,
|
|
() -> {
|
|
if (binding.linearsimplesetupui.getVisibility() == View.GONE) {
|
|
showAdvancedSetupDialog();
|
|
}
|
|
});
|
|
simpleSetupUIControler(0);
|
|
});
|
|
}
|
|
}).start();
|
|
} else {
|
|
DialogUtils.oneDialog(this,
|
|
getString(R.string.invalid_file),
|
|
getString(R.string.please_select) + " vectras-vm-" + abi + ".tar.gz.",
|
|
getResources().getString(R.string.ok),
|
|
true,
|
|
R.drawable.warning_48px,
|
|
true,
|
|
null,
|
|
() -> {
|
|
if (binding.linearsimplesetupui.getVisibility() == View.GONE) {
|
|
showAdvancedSetupDialog();
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
if (binding.linearsimplesetupui.getVisibility() == View.GONE) {
|
|
showAdvancedSetupDialog();
|
|
}
|
|
}
|
|
});
|
|
|
|
private void onBackInFinalSteps() {
|
|
if (bindingFinalSteps.lineardonate.getVisibility() == View.GONE) {
|
|
bindingFinalSteps.lineardonate.setVisibility(View.VISIBLE);
|
|
} else if (bindingFinalSteps.linearcommunity.getVisibility() == View.GONE) {
|
|
bindingFinalSteps.linearcommunity.setVisibility(View.VISIBLE);
|
|
}
|
|
|
|
if (bindingFinalSteps.tvLater.getVisibility() == View.GONE) {
|
|
bindingFinalSteps.tvLater.setVisibility(View.VISIBLE);
|
|
bindingFinalSteps.btnContinue.setText(getString(R.string.join));
|
|
}
|
|
}
|
|
|
|
private void setupOnClick() {
|
|
//Simple setup
|
|
binding.buttonletstart.setOnClickListener(v -> {
|
|
binding.linearwelcome.setVisibility(View.GONE);
|
|
isAllowCheckPermissions = true;
|
|
checkpermissions();
|
|
net.startRequestNetwork(RequestNetworkController.GET, AppConfig.bootstrapfileslink, "", _net_request_listener);
|
|
});
|
|
|
|
binding.buttontryconnectagain.setOnClickListener(v -> {
|
|
binding.linearload.setVisibility(View.VISIBLE);
|
|
net.startRequestNetwork(RequestNetworkController.GET, AppConfig.bootstrapfileslink, "", _net_request_listener);
|
|
});
|
|
|
|
binding.buttonautosetup.setOnClickListener(v -> {
|
|
isManualMode = false;
|
|
startSetup();
|
|
simpleSetupUIControler(1);
|
|
});
|
|
|
|
binding.buttonmanualsetup.setOnClickListener(v -> bootstrapFilePicker.launch("*/*"));
|
|
|
|
binding.buttonsetuptryagain.setOnClickListener(v -> simpleSetupUIControler(0));
|
|
|
|
binding.buttonsetupshowlog.setOnClickListener(v -> binding.linearsimplesetupui.setVisibility(View.GONE));
|
|
|
|
binding.textviewshowadvancedsetup.setOnClickListener(v -> {
|
|
binding.linearsimplesetupui.setVisibility(View.GONE);
|
|
if (binding.linearstartsetup.getVisibility() == View.VISIBLE) {
|
|
showAdvancedSetupDialog();
|
|
}
|
|
});
|
|
|
|
|
|
//Advanced setup
|
|
bindingAdvancedSetup.ivClose.setOnClickListener(v -> binding.linearsimplesetupui.setVisibility(View.VISIBLE));
|
|
|
|
bindingAdvancedSetup.ivOpenterminal.setOnClickListener(v -> {
|
|
if (DeviceUtils.is64bit()) {
|
|
startActivity(new Intent(this, TermuxActivity.class));
|
|
} else {
|
|
TerminalBottomSheetDialog VTERM = new TerminalBottomSheetDialog(this);
|
|
VTERM.showVterm();
|
|
}
|
|
});
|
|
|
|
bindingAdvancedSetup.btnInstall.setOnClickListener(v -> {
|
|
File tarGZ = new File(tarPath);
|
|
if (tarGZ.exists()) {
|
|
if (!tarGZ.delete()) Log.e(TAG, "btnInstall: Unable to delete " + tarPath);
|
|
}
|
|
showAdvancedSetupDialog();
|
|
});
|
|
|
|
bindingAdvancedSetup.ivCopycommandsetup.setOnClickListener(v -> ClipboardUltils.copyToClipboard(SetupWizardActivity.this, bindingAdvancedSetup.tvCommandsetup.getText().toString()));
|
|
|
|
|
|
//Final steps
|
|
bindingFinalSteps.tvLater.setOnClickListener(v -> {
|
|
if (bindingFinalSteps.linearcommunity.getVisibility() == View.VISIBLE) {
|
|
bindingFinalSteps.linearcommunity.setVisibility(View.GONE);
|
|
} else if (bindingFinalSteps.lineardonate.getVisibility() == View.VISIBLE) {
|
|
bindingFinalSteps.tvLater.setVisibility(View.GONE);
|
|
bindingFinalSteps.lineardonate.setVisibility(View.GONE);
|
|
bindingFinalSteps.btnContinue.setText(getString(R.string.done));
|
|
}
|
|
});
|
|
|
|
bindingFinalSteps.btnContinue.setOnClickListener(v -> {
|
|
if (bindingFinalSteps.linearcommunity.getVisibility() == View.VISIBLE) {
|
|
bindingFinalSteps.linearcommunity.setVisibility(View.GONE);
|
|
Intent intent = new Intent(ACTION_VIEW, Uri.parse(AppConfig.telegramLink));
|
|
startActivity(intent);
|
|
//Don't show join Telegram dialog again
|
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
|
SharedPreferences.Editor edit = prefs.edit();
|
|
edit.putBoolean("tgDialog", true);
|
|
edit.apply();
|
|
} else if (bindingFinalSteps.lineardonate.getVisibility() == View.VISIBLE) {
|
|
bindingFinalSteps.tvLater.setVisibility(View.GONE);
|
|
bindingFinalSteps.lineardonate.setVisibility(View.GONE);
|
|
bindingFinalSteps.btnContinue.setText(getString(R.string.done));
|
|
Intent intent = new Intent(ACTION_VIEW, Uri.parse(AppConfig.patreonLink));
|
|
startActivity(intent);
|
|
} else {
|
|
startActivity(new Intent(SetupWizardActivity.this, HomeActivity.class));
|
|
finish();
|
|
}
|
|
});
|
|
}
|
|
|
|
private void uiControllerAdvancedSetup(boolean isStartSetup) {
|
|
bindingAdvancedSetup.lnBntinstall.setVisibility(isStartSetup ? View.GONE : View.VISIBLE);
|
|
bindingAdvancedSetup.progressBar.setVisibility(isStartSetup ? View.VISIBLE : View.GONE);
|
|
}
|
|
|
|
private void showAdvancedSetupDialog() {
|
|
DialogUtils.twoDialog(
|
|
this,
|
|
getResources().getString(R.string.bootstrap_required),
|
|
getResources().getString(R.string.you_can_choose_between_auto_download_and_setup_or_manual_setup_by_choosing_bootstrap_file),
|
|
getString(R.string.auto_setup),
|
|
getString(R.string.manual_setup),
|
|
true, R.drawable.system_update_24px,
|
|
false,
|
|
() -> {
|
|
//startDownload();
|
|
isManualMode = false;
|
|
startSetup();
|
|
simpleSetupUIControler(1);
|
|
},
|
|
() -> bootstrapFilePicker.launch("*/*"),
|
|
null);
|
|
}
|
|
|
|
private void setupSpiner() {
|
|
ListUtils.setupMirrorListForListmap(listmapForSelectMirrors);
|
|
|
|
binding.spinnerselectmirror.setAdapter(new SpinnerSelectMirrorAdapter(getApplicationContext(), listmapForSelectMirrors));
|
|
binding.spinnerselectmirror.setSelection(MainSettingsManager.getSelectedMirror(this));
|
|
}
|
|
|
|
public static class SpinnerSelectMirrorAdapter extends BaseAdapter {
|
|
|
|
private final ArrayList<HashMap<String, String>> data;
|
|
private final LayoutInflater inflater;
|
|
|
|
public SpinnerSelectMirrorAdapter(Context context, ArrayList<HashMap<String, String>> arr) {
|
|
this.data = arr;
|
|
this.inflater = LayoutInflater.from(context);
|
|
}
|
|
|
|
@Override
|
|
public int getCount() {
|
|
return data.size();
|
|
}
|
|
|
|
@Override
|
|
public HashMap<String, String> getItem(int position) {
|
|
return data.get(position);
|
|
}
|
|
|
|
@Override
|
|
public long getItemId(int position) {
|
|
return position;
|
|
}
|
|
|
|
@Override
|
|
public View getView(int position, View convertView, ViewGroup parent) {
|
|
ViewHolder holder;
|
|
|
|
if (convertView == null) {
|
|
// Inflate binding only once for each new item
|
|
SimpleLayoutForSpinerBinding binding =
|
|
SimpleLayoutForSpinerBinding.inflate(inflater, parent, false);
|
|
|
|
// Create ViewHolder to hold binding
|
|
holder = new ViewHolder(binding);
|
|
convertView = binding.getRoot();
|
|
convertView.setTag(holder);
|
|
} else {
|
|
// Get back the saved ViewHolder
|
|
holder = (ViewHolder) convertView.getTag();
|
|
}
|
|
|
|
// Assign data
|
|
HashMap<String, String> item = data.get(position);
|
|
holder.binding.textViewLocation.setText(item.get("location"));
|
|
|
|
return convertView;
|
|
}
|
|
|
|
// ViewHolder holds binding for reuse
|
|
private record ViewHolder(SimpleLayoutForSpinerBinding binding) {
|
|
}
|
|
}
|
|
|
|
// Method to execute the shell command
|
|
public void executeShellCommand(String userCommand) {
|
|
isexecutingCommand = true;
|
|
new Thread(() -> {
|
|
try {
|
|
// Setup the process builder to start PRoot with environmental variables and commands
|
|
ProcessBuilder processBuilder = new ProcessBuilder();
|
|
|
|
// Adjust these environment variables as necessary for your app
|
|
String filesDir = getFilesDir().getAbsolutePath();
|
|
|
|
File tmpDir = new File(getFilesDir(), "usr/tmp");
|
|
|
|
// Setup environment for the PRoot process
|
|
processBuilder.environment().put("PROOT_TMP_DIR", tmpDir.getAbsolutePath());
|
|
|
|
processBuilder.environment().put("HOME", "/root");
|
|
processBuilder.environment().put("USER", "root");
|
|
processBuilder.environment().put("PATH", "/bin:/usr/bin:/sbin:/usr/sbin");
|
|
processBuilder.environment().put("TERM", "xterm-256color");
|
|
processBuilder.environment().put("TMPDIR", tmpDir.getAbsolutePath());
|
|
processBuilder.environment().put("SHELL", "/bin/sh");
|
|
|
|
String[] prootCommand = {
|
|
TermuxService.PREFIX_PATH + "/bin/proot", // PRoot binary path
|
|
"--kill-on-exit",
|
|
"--link2symlink",
|
|
"-0",
|
|
"-r", filesDir + "/distro", // Path to the rootfs
|
|
"-b", "/dev",
|
|
"-b", "/proc",
|
|
"-b", "/sys",
|
|
"-b", "/sdcard",
|
|
"-b", "/storage",
|
|
"-b", "/data",
|
|
"-w", "/root",
|
|
"/bin/sh",
|
|
"--login"// The shell to execute inside PRoot
|
|
};
|
|
|
|
processBuilder.command(prootCommand);
|
|
Process process = processBuilder.start();
|
|
// Get the input and output streams of the process
|
|
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
|
|
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
|
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
|
|
|
|
// Send user command to PRoot
|
|
writer.write(userCommand);
|
|
writer.newLine();
|
|
writer.flush();
|
|
writer.close();
|
|
|
|
// Read the input stream for the output of the command
|
|
String line;
|
|
while ((line = reader.readLine()) != null) {
|
|
final String outputLine = line;
|
|
runOnUiThread(() -> appendTextAndScroll(outputLine + "\n"));
|
|
}
|
|
|
|
// Read any errors from the error stream
|
|
while ((line = errorReader.readLine()) != null) {
|
|
final String errorLine = line;
|
|
runOnUiThread(() -> appendTextAndScroll(errorLine + "\n"));
|
|
}
|
|
|
|
// Clean up
|
|
reader.close();
|
|
errorReader.close();
|
|
|
|
// Wait for the process to finish
|
|
process.waitFor();
|
|
|
|
// Wait for the process to finish
|
|
int exitValue = process.waitFor();
|
|
|
|
// Check if the exit value indicates an error
|
|
if (exitValue != 0) {
|
|
isexecutingCommand = false;
|
|
// If exit value is not zero, display a toast message
|
|
if (!aria2Error) {
|
|
String toastMessage = "Command failed with exit code: " + exitValue;
|
|
runOnUiThread(() -> {
|
|
appendTextAndScroll("Error: " + toastMessage + "\n");
|
|
Toast.makeText(this, toastMessage, Toast.LENGTH_LONG).show();
|
|
uiControllerAdvancedSetup(false);
|
|
bindingAdvancedSetup.title.setText(getString(R.string.failed));
|
|
simpleSetupUIControler(2);
|
|
});
|
|
}
|
|
if (libprooterror) {
|
|
runOnUiThread(() -> DialogUtils.twoDialog(
|
|
this,
|
|
getResources().getString(R.string.oops),
|
|
getResources().getString(R.string.a_serious_problem_has_occurred),
|
|
getString(R.string.join_our_community),
|
|
getString(R.string.close),
|
|
true, R.drawable.system_update_24px,
|
|
true,
|
|
() -> {
|
|
Intent intent = new Intent();
|
|
intent.setAction(ACTION_VIEW);
|
|
intent.setData(Uri.parse(AppConfig.community));
|
|
startActivity(intent);
|
|
},
|
|
null,
|
|
null));
|
|
} else if (aria2Error && downloadBootstrapsCommand.contains("aria2c")) {
|
|
runOnUiThread(() -> {
|
|
downloadBootstrapsCommand = " curl -o setup.tar.gz -L " + bootstrapfilelink;
|
|
startSetup();
|
|
});
|
|
} else if (isServerError) {
|
|
runOnUiThread(() -> DialogUtils.oneDialog(
|
|
this,
|
|
getResources().getString(R.string.oops),
|
|
getResources().getString(R.string.unable_to_connect_to_alpine_linux_server_content),
|
|
getString(R.string.ok),
|
|
true,
|
|
R.drawable.warning_48px,
|
|
true,
|
|
null,
|
|
null));
|
|
}
|
|
}
|
|
} catch (IOException | InterruptedException e) {
|
|
isexecutingCommand = false;
|
|
// Handle exceptions by printing the stack trace in the terminal output
|
|
final String errorMessage = e.getMessage();
|
|
runOnUiThread(() -> {
|
|
appendTextAndScroll("Error: " + errorMessage + "\n");
|
|
Toast.makeText(this, "Error executing command: " + errorMessage, Toast.LENGTH_LONG).show();
|
|
uiControllerAdvancedSetup(false);
|
|
bindingAdvancedSetup.title.setText(getString(R.string.failed));
|
|
simpleSetupUIControler(2);
|
|
});
|
|
}
|
|
}).start(); // Execute the command in a separate thread to prevent blocking the UI thread
|
|
}
|
|
|
|
public String getPath(Uri uri) {
|
|
return FileUtils.getPath(this, uri);
|
|
}
|
|
}
|