v2.9.5.6-3dfx

- Fixed bug when importing rom from Rom store.
- Added not allowing you to use Vectras VM when available storage space is too low.
- Added auto-show keyboard when editing Qemu params.
- Thumbnails in Rom store no longer download automatically, it only downloads when importing valid rom.
This commit is contained in:
An Bui 2025-09-03 23:20:06 +07:00
parent 347d769308
commit 2dfb569cc4
16 changed files with 261 additions and 247 deletions

View file

@ -66,6 +66,7 @@ public class CustomRomActivity extends AppCompatActivity {
int decompressionProgress = 0;
boolean modify;
public static DataMainRoms current;
private boolean isImportingCVBI = false;
@Override
public boolean onCreateOptionsMenu(Menu menu) {
@ -272,7 +273,7 @@ public class CustomRomActivity extends AppCompatActivity {
binding.title.setText(getIntent().getStringExtra("romname"));
if (getIntent().hasExtra("romicon")) {
if (getIntent().hasExtra("romicon") && !Objects.requireNonNull(getIntent().getStringExtra("romicon")).isEmpty()) {
startProcessingThumbnail(Uri.parse(getIntent().getStringExtra("romicon")));
}
@ -361,7 +362,6 @@ public class CustomRomActivity extends AppCompatActivity {
if (MainSettingsManager.copyFile(this)) {
whenProcessing(true);
binding.custom.setVisibility(View.GONE);
executor.execute(() -> {
try {
try {
@ -383,13 +383,11 @@ public class CustomRomActivity extends AppCompatActivity {
} finally {
runOnUiThread(() -> {
whenProcessing(false);
binding.custom.setVisibility(View.VISIBLE);
});
}
} catch (IOException e) {
runOnUiThread(() -> {
whenProcessing(false);
binding.custom.setVisibility(View.VISIBLE);
DialogUtils.oneDialog(this,
getString(R.string.oops),
getString(R.string.unable_to_copy_iso_file_content),
@ -537,7 +535,7 @@ public class CustomRomActivity extends AppCompatActivity {
private void createNewVM() {
if (FileUtils.isFileExists(AppConfig.romsdatajson)) {
if (!VMManager.isRomsDataJsonNormal(true, this)) {
if (!VMManager.isRomsDataJsonValid(true, this)) {
return;
}
} else {
@ -600,7 +598,7 @@ public class CustomRomActivity extends AppCompatActivity {
null));
} finally {
runOnUiThread(() -> {
whenProcessing(false);
if (!isImportingCVBI) whenProcessing(false);
});
}
});
@ -613,7 +611,7 @@ public class CustomRomActivity extends AppCompatActivity {
if (imgFile.exists()) {
Glide.with(this)
.load(new File(AppConfig.vmFolder + vmID + "/thumbnail.png"))
.load(new File(thumbnailPath))
.placeholder(R.drawable.ic_computer_180dp_with_padding)
.error(R.drawable.ic_computer_180dp_with_padding)
.into(binding.ivIcon);
@ -644,7 +642,6 @@ public class CustomRomActivity extends AppCompatActivity {
private void startProcessingHardDriveFile(Uri _content_describer, boolean _addtodrive) {
if (MainSettingsManager.copyFile(this)) {
whenProcessing(true);
binding.custom.setVisibility(View.GONE);
if (!createVMFolder()) {
return;
}
@ -668,13 +665,11 @@ public class CustomRomActivity extends AppCompatActivity {
} finally {
runOnUiThread(() -> {
whenProcessing(false);
binding.custom.setVisibility(View.VISIBLE);
});
}
} catch (IOException e) {
runOnUiThread(() -> {
whenProcessing(false);
binding.custom.setVisibility(View.VISIBLE);
DialogUtils.oneDialog(this,
getString(R.string.oops),
getString(R.string.unable_to_copy_hard_drive_file_content),
@ -742,8 +737,8 @@ public class CustomRomActivity extends AppCompatActivity {
return;
}
isImportingCVBI = true;
whenProcessing(true);
binding.custom.setVisibility(View.GONE);
binding.ivIcon.setEnabled(false);
zipFileSize = FileUtils.getFileSize(_filepath);
@ -857,8 +852,8 @@ public class CustomRomActivity extends AppCompatActivity {
timerTask.cancel();
}
isImportingCVBI = false;
whenProcessing(false);
binding.custom.setVisibility(View.VISIBLE);
binding.ivIcon.setEnabled(true);
try {
if (!FileUtils.isFileExists(AppConfig.vmFolder + vmID + "/rom-data.json")) {
@ -881,12 +876,8 @@ public class CustomRomActivity extends AppCompatActivity {
binding.title.setText(getIntent().getStringExtra("romname"));
if (!Objects.requireNonNull(getIntent().getStringExtra("romicon")).isEmpty()) {
File imgFile = new File(Objects.requireNonNull(getIntent().getStringExtra("romicon")));
if (imgFile.exists()) {
thumbnailPath = getIntent().getStringExtra("romicon");
thumbnailProcessing();
}
if (getIntent().hasExtra("romicon") && !Objects.requireNonNull(getIntent().getStringExtra("romicon")).isEmpty()) {
startProcessingThumbnail(Uri.parse(getIntent().getStringExtra("romicon")));
}
} else {
if (Objects.requireNonNull(binding.qemu.getText()).toString().isEmpty()) {

View file

@ -1,7 +1,10 @@
package com.vectras.vm;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
@ -11,7 +14,6 @@ import com.vectras.vm.utils.UIUtils;
public class EditActivity extends AppCompatActivity {
private Button buttondone;
private EditText editcontent;
public static String result = "";
@ -21,21 +23,23 @@ public class EditActivity extends AppCompatActivity {
UIUtils.edgeToEdge(this);
setContentView(R.layout.activity_edit);
UIUtils.setOnApplyWindowInsetsListener(findViewById(R.id.main));
buttondone = findViewById(R.id.materialbutton1);
Button buttondone = findViewById(R.id.materialbutton1);
editcontent = findViewById(R.id.edittext1);
if (getIntent().hasExtra("content")) {
result = getIntent().getStringExtra("content");
editcontent.setText(result);
}
buttondone.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
result = editcontent.getText().toString();
finish();
}
buttondone.setOnClickListener(v -> {
result = editcontent.getText().toString();
finish();
});
editcontent.requestFocus();
new Handler(Looper.getMainLooper()).postDelayed(() -> {
editcontent.requestFocus();
editcontent.setSelection(editcontent.getText().length());
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
imm.showSoftInput(editcontent, InputMethodManager.SHOW_IMPLICIT);
}, 200);
}
}

View file

@ -8,29 +8,23 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.progressindicator.LinearProgressIndicator;
import com.termux.app.TermuxService;
import static com.vectras.vm.VMManager.startFixRomsDataJson;
import static com.vectras.vm.VectrasApp.getApp;
import static com.vectras.vm.utils.LibraryChecker.isPackageInstalled2;
import static com.vectras.vm.utils.UIUtils.UIAlert;
import com.vectras.vm.settings.VNCActivity;
import com.vectras.vm.utils.CommandUtils;
import com.vectras.vm.utils.DeviceUtils;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.NetworkUtils;
import com.vectras.vm.utils.PermissionUtils;
import android.androidVNC.androidVNC;
import android.app.ActivityManager;
import android.app.Dialog;
import android.app.NotificationManager;
import android.app.ProgressDialog;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
@ -41,7 +35,6 @@ import android.os.Environment;
import android.os.Handler;
import android.os.StrictMode;
import android.provider.DocumentsContract;
import android.provider.Settings;
import android.text.Html;
import android.util.Log;
import android.view.LayoutInflater;
@ -53,25 +46,20 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.Manifest;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdView;
import com.google.android.gms.ads.LoadAdError;
import com.google.android.gms.ads.MobileAds;
import com.google.android.gms.ads.initialization.InitializationStatus;
@ -97,7 +85,6 @@ import com.vectras.vm.utils.AppUpdater;
import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.utils.LibraryChecker;
import com.vectras.vm.utils.PackageUtils;
import com.vectras.vm.utils.UIUtils;
import com.vectras.vterm.Terminal;
import org.json.JSONArray;
@ -113,15 +100,11 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.vectras.vm.core.ShellExecutor;
@ -135,7 +118,6 @@ public class MainActivity extends AppCompatActivity {
public static AdapterMainRoms mMainAdapter;
public static JSONArray jArray;
public static List<DataMainRoms> data;
public AlertDialog ad;
public static MainActivity activity;
private InterstitialAd mInterstitialAd;
private AdRequest adRequest;
@ -149,15 +131,11 @@ public class MainActivity extends AppCompatActivity {
public static LinearLayout linearnothinghere;
private final Timer _timer = new Timer();
private AlertDialog alertDialog;
private boolean doneonstart = false;
public static boolean isActivate = false;
public boolean skipIDEwithARM64DialogInStartVM = false;
BottomAppBar bottomAppBar;
AlertDialog progressDialog;
public static Timer timer = new Timer();
public static TimerTask timerTask;
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
@ -165,7 +143,6 @@ public class MainActivity extends AppCompatActivity {
RamInfo.activity = this;
setContentView(R.layout.activity_main);
isActivate = true;
setupFolders();
NotificationManager notificationManager = (NotificationManager) activity.getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancelAll();
@ -204,7 +181,6 @@ public class MainActivity extends AppCompatActivity {
refreshRoms.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
errorjsondialog();
loadDataVbi();
mMainAdapter.notifyItemRangeChanged(0, mMainAdapter.data.size());
refreshRoms.setRefreshing(false);
@ -598,26 +574,26 @@ public class MainActivity extends AppCompatActivity {
totalRam.setText(
activity.getResources()
.getString(R.string.total_memory)
.getString(R.string.total_memory)
+ " "
+ totalMemory
+ " MB");
usedRam.setText(
activity.getResources()
.getString(R.string.used_memory)
.getString(R.string.used_memory)
+ " "
+ usedMemory
+ " MB");
freeRam.setText(
activity.getResources()
.getString(R.string.free_memory)
.getString(R.string.free_memory)
+ " "
+ freeMemory
+ " MB ("
+ vectrasMemory
+ " "
+ activity.getResources()
.getString(R.string.used)
.getString(R.string.used)
+ ")");
LinearProgressIndicator progressBar = findViewById(R.id.progressBar);
progressBar.setMax((int) totalMemory);
@ -719,19 +695,19 @@ public class MainActivity extends AppCompatActivity {
if (versionCode < obj.getInt("versionCode") || !versionNameonUpdate.equals(versionName)) {
if (showDialog) {
AlertDialog.Builder alert = new AlertDialog.Builder(activity, R.style.MainDialogTheme);
alert.setTitle("Install the latest version")
.setMessage(Html.fromHtml(obj.getString("MessageBeta") + "<br><br>Update size:<br>" + obj.getString("sizeBeta")))
.setCancelable(obj.getBoolean("cancellableBeta"))
.setNegativeButton("Update", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
try {
startActivity(new Intent(ACTION_VIEW, Uri.parse(obj.getString("urlBeta"))));
} catch (JSONException e) {
AlertDialog.Builder alert = new AlertDialog.Builder(activity, R.style.MainDialogTheme);
alert.setTitle("Install the latest version")
.setMessage(Html.fromHtml(obj.getString("MessageBeta") + "<br><br>Update size:<br>" + obj.getString("sizeBeta")))
.setCancelable(obj.getBoolean("cancellableBeta"))
.setNegativeButton("Update", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
try {
startActivity(new Intent(ACTION_VIEW, Uri.parse(obj.getString("urlBeta"))));
} catch (JSONException e) {
}
}
}
}).show();
}).show();
} else {
View _update = findViewById(R.id.update);
_update.setVisibility(View.VISIBLE);
@ -742,19 +718,19 @@ public class MainActivity extends AppCompatActivity {
if (versionCode < obj.getInt("versionCode") || !versionNameonUpdate.contains(versionName)) {
if (showDialog) {
AlertDialog.Builder alert = new AlertDialog.Builder(activity, R.style.MainDialogTheme);
alert.setTitle("Install the latest version")
.setMessage(Html.fromHtml(obj.getString("Message") + "<br><br>Update size:<br>" + obj.getString("size")))
.setCancelable(obj.getBoolean("cancellable"))
.setNegativeButton("Update", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
try {
startActivity(new Intent(ACTION_VIEW, Uri.parse(obj.getString("url"))));
} catch (JSONException e) {
AlertDialog.Builder alert = new AlertDialog.Builder(activity, R.style.MainDialogTheme);
alert.setTitle("Install the latest version")
.setMessage(Html.fromHtml(obj.getString("Message") + "<br><br>Update size:<br>" + obj.getString("size")))
.setCancelable(obj.getBoolean("cancellable"))
.setNegativeButton("Update", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
try {
startActivity(new Intent(ACTION_VIEW, Uri.parse(obj.getString("url"))));
} catch (JSONException e) {
}
}
}
}).show();
}).show();
} else {
View _update = findViewById(R.id.update);
_update.setVisibility(View.VISIBLE);
@ -776,6 +752,29 @@ public class MainActivity extends AppCompatActivity {
}
private void loadDataVbi() {
if (FileUtils.isFileExists(AppConfig.romsdatajson)) {
if (!VMManager.isRomsDataJsonValid(true, MainActivity.this)) {
DialogUtils.twoDialog(this,
getString(R.string.problem_has_been_detected),
getString(R.string.vm_list_data_is_corrupted_content),
getString(R.string.continuetext),
getString(R.string.cancel),
true,
R.drawable.build_24px,
true,
() -> {
FileUtils.moveAFile(AppConfig.maindirpath + "roms-data.json", AppConfig.maindirpath + "roms-data.old.json");
FileUtils.writeToFile(AppConfig.maindirpath, "roms-data.json", "[]");
startFixRomsDataJson();
},
null,
null);
}
} else {
FileUtils.writeToFile(AppConfig.maindirpath, "roms-data.json", "[]");
}
data = new ArrayList<>();
try {
@ -808,7 +807,7 @@ public class MainActivity extends AppCompatActivity {
try {
romsMainData.qmpPort = json_data.getInt("qmpPort");
} catch (JSONException ignored) {
romsMainData.qmpPort= 0;
romsMainData.qmpPort = 0;
}
try {
romsMainData.itemDrv1 = json_data.getString("imgDrv1");
@ -817,10 +816,10 @@ public class MainActivity extends AppCompatActivity {
}
romsMainData.itemExtra = json_data.getString("imgExtra");
//try {
//if (json_data.getString("imgArch").equals(MainSettingsManager.getArch(MainActivity.activity)))
data.add(romsMainData);
//if (json_data.getString("imgArch").equals(MainSettingsManager.getArch(MainActivity.activity)))
data.add(romsMainData);
//} catch (JSONException ignored) {
//data.add(romsMainData);
//data.add(romsMainData);
//}
}
@ -831,7 +830,9 @@ public class MainActivity extends AppCompatActivity {
int spanCount = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? 3 : 2;
mRVMainRoms.setLayoutManager(new GridLayoutManager(this, spanCount));
} catch (JSONException e) {
e.printStackTrace();
}
mdatasize();
}
@Override
@ -947,7 +948,7 @@ public class MainActivity extends AppCompatActivity {
// };
// timer.schedule(timerTask, 5000);
File romDir = new File(Config.getCacheDir()+ "/" + Config.vmID);
File romDir = new File(Config.getCacheDir() + "/" + Config.vmID);
romDir.mkdirs();
if (!VMManager.isthiscommandsafe(env, activity.getApplicationContext())) {
@ -1068,18 +1069,6 @@ public class MainActivity extends AppCompatActivity {
}
private void setupFolders() {
Thread t = new Thread(new Runnable() {
public void run() {
Config.cacheDir = getCacheDir().getAbsolutePath();
Config.storagedir = Environment.getExternalStorageDirectory().toString();
}
});
t.start();
}
public void onResume() {
super.onResume();
checkpermissions();
@ -1121,7 +1110,7 @@ public class MainActivity extends AppCompatActivity {
//TEMPORARY FIX FOR VNC CLOSES
//TODO: FIND FIX FOR CRASHING
//if (MainSettingsManager.getVmUi(activity).equals("VNC") && MainVNCActivity.started)
//startActivity(new Intent(activity, MainVNCActivity.class));
//startActivity(new Intent(activity, MainVNCActivity.class));
InterstitialAd.load(this, "ca-app-pub-3568137780412047/7745973511", adRequest,
new InterstitialAdLoadCallback() {
@ -1145,7 +1134,6 @@ public class MainActivity extends AppCompatActivity {
} else {
Log.d("TAG", "The interstitial ad wasn't ready yet.");
}
doneonstart = true;
if (!AppConfig.pendingCommand.isEmpty()) {
if (!VMManager.isthiscommandsafe(AppConfig.pendingCommand, getApplicationContext())) {
@ -1394,49 +1382,8 @@ public class MainActivity extends AppCompatActivity {
return FileUtils.getPath(this, uri);
}
private void errorjsondialog() {
if (FileUtils.isFileExists(AppConfig.romsdatajson)) {
if (VMManager.isRomsDataJsonNormal(true, MainActivity.this)) {
loadDataVbi();
mdatasize();
}
} else {
FileUtils.writeToFile(AppConfig.maindirpath, "roms-data.json", "[]");
loadDataVbi();
mdatasize();
}
}
private String readFile(String filePath) {
StringBuilder content = new StringBuilder();
try (FileInputStream inputStream = new FileInputStream(filePath);
BufferedReader reader = new BufferedReader(new
InputStreamReader(inputStream))) {
String line;
while ((line = reader.readLine()) != null) {
content.append(line).append("\n");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return content.toString();
}
private void mdatasize() {
if (MainActivity.data.isEmpty()) {
if (!doneonstart) {
alertDialog = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create();
alertDialog.setTitle(getResources().getString(R.string.nothing_here));
alertDialog.setMessage("Do you want to create a new virtual machine now?");
alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, getResources().getString(R.string.create), (dialog, which) -> {
startActivity(new Intent(activity, SetArchActivity.class));
});
alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getResources().getString(R.string.cancel), (dialog, which) -> {
});
alertDialog.show();
}
linearnothinghere.setVisibility(View.VISIBLE);
} else {
linearnothinghere.setVisibility(View.GONE);
@ -1453,7 +1400,20 @@ public class MainActivity extends AppCompatActivity {
private void checkpermissions() {
if (PermissionUtils.storagepermission(activity, true)) {
errorjsondialog();
loadDataVbi();
if (DeviceUtils.isStorageLow(this)) {
DialogUtils.oneDialog(this,
getResources().getString(R.string.oops),
getResources().getString(R.string.very_low_available_storage_space_content),
getResources().getString(R.string.ok),
true,
R.drawable.warning_48px,
true,
null,
() -> {
if (DeviceUtils.isStorageLow(this)) finish();
});
}
}
}

View file

@ -1,21 +1,14 @@
package com.vectras.vm;
import static android.content.Intent.ACTION_OPEN_DOCUMENT;
import static android.content.Intent.ACTION_VIEW;
import static android.view.View.GONE;
import android.app.ProgressDialog;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
@ -28,12 +21,8 @@ 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 androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.termux.app.TermuxService;
import com.vectras.qemu.MainSettingsManager;
@ -44,19 +33,18 @@ import com.vectras.vm.utils.ListUtils;
import com.vectras.vm.utils.PackageUtils;
import com.vectras.vm.utils.UIUtils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Objects;
public class Minitools extends AppCompatActivity {
private ArrayList<HashMap<String, String>> listmapForSelectMirrors = new ArrayList<>();
private final ArrayList<HashMap<String, String>> listmapForSelectMirrors = new ArrayList<>();
private Spinner spinnerselectmirror;
private String selectedMirrorCommand = "";
@ -69,7 +57,7 @@ public class Minitools extends AppCompatActivity {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
toolbar.setTitle(getString(R.string.mini_tools));
@ -194,14 +182,14 @@ public class Minitools extends AppCompatActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case 0:
return true;
case android.R.id.home:
return switch (item.getItemId()) {
case 0 -> true;
case android.R.id.home -> {
finish();
return true;
}
return super.onOptionsItemSelected(item);
yield true;
}
default -> super.onOptionsItemSelected(item);
};
}
private void setupSpiner() {

View file

@ -3,6 +3,8 @@ package com.vectras.vm;
import static android.content.Intent.ACTION_OPEN_DOCUMENT;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@ -12,14 +14,18 @@ import android.view.View;
import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.vm.databinding.ActivityRomInfoBinding;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.utils.ImageUtils;
import java.io.File;
import java.util.Objects;
@ -92,6 +98,32 @@ public class RomInfo extends AppCompatActivity {
File selectedFilePath = new File(getPath(content_describer));
if (selectedFilePath.getName().equals(getIntent().getStringExtra("filename")) || (selectedFilePath.getName().endsWith(".cvbi.zip") && selectedFilePath.getName().equals(getIntent().getStringExtra("filename") + ".zip"))) {
Intent intent = new Intent();
if (getIntent().hasExtra("icon") &&
!Objects.requireNonNull(getIntent().getStringExtra("icon")).isEmpty() &&
(!selectedFilePath.getName().endsWith(".cvbi")
|| !selectedFilePath.getName().endsWith(".cvbi.zip"))) {
Glide.with(this)
.asBitmap()
.load(getIntent().getStringExtra("icon"))
.into(new CustomTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource,
@Nullable Transition<? super Bitmap> transition) {
ImageUtils.saveBitmapToPNGFile(resource, Objects.requireNonNull(getExternalCacheDir()).getAbsolutePath(), "thumbnail.png");
intent.putExtra("romicon", getExternalCacheDir().getAbsolutePath() + "/thumbnail.png");
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
intent.putExtra("romicon", "");
}
});
} else {
intent.putExtra("romicon", "");
}
intent.setClass(getApplicationContext(), CustomRomActivity.class);
intent.putExtra("addromnow", "");
intent.putExtra("romname", getIntent().getStringExtra("title"));
@ -105,7 +137,6 @@ public class RomInfo extends AppCompatActivity {
intent.putExtra("addtodrive", "1");
intent.putExtra("romextra", getIntent().getStringExtra("extra"));
}
intent.putExtra("romicon", AppConfig.maindirpath + "icons/" + getIntent().getStringExtra("filename") + ".png");
switch (Objects.requireNonNull(getIntent().getStringExtra("arch"))) {
case "X86_64":
MainSettingsManager.setArch(this, "X86_64");

View file

@ -83,33 +83,6 @@ public class AdapterRomStoreSearch extends RecyclerView.Adapter<RecyclerView.Vie
intent.putExtra("creator", current.creator);
intent.putExtra("size", current.fileSize);
context.startActivity(intent);
if (!current.romPath.endsWith(".cvbi")) {
//Save image to icon folder
myHolder.ivIcon.buildDrawingCache();
Bitmap bm = myHolder.ivIcon.getDrawingCache();
OutputStream fOut = null;
try {
File root = new File(AppConfig.maindirpath + "/icons/");
if(root.mkdirs()) {
File sdImageMainDirectory = new File(root, current.romPath + ".png");
fOut = new FileOutputStream(sdImageMainDirectory);
} else {
Log.e(TAG, "Directory: " + AppConfig.maindirpath + "/icons/");
}
} catch (FileNotFoundException e) {
Log.e(TAG, "File: " + Objects.requireNonNull(e.getMessage()));
}
try {
assert fOut != null;
bm.compress(Bitmap.CompressFormat.PNG, 100, fOut);
fOut.flush();
fOut.close();
} catch (Exception e) {
Log.e(TAG, "Bitmap: " + Objects.requireNonNull(e.getMessage()));
}
}
});
} else {
myHolder.textAvail.setText(context.getString(R.string.unavailable));

View file

@ -83,33 +83,6 @@ public class AdapterRoms extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
intent.putExtra("creator", current.creator);
intent.putExtra("size", current.fileSize);
context.startActivity(intent);
if (!current.romPath.endsWith(".cvbi")) {
//Save image to icon folder
myHolder.ivIcon.buildDrawingCache();
Bitmap bm = myHolder.ivIcon.getDrawingCache();
OutputStream fOut = null;
try {
File root = new File(AppConfig.maindirpath + "/icons/");
if(root.mkdirs()) {
File sdImageMainDirectory = new File(root, current.romPath + ".png");
fOut = new FileOutputStream(sdImageMainDirectory);
} else {
Log.e(TAG, "Directory: " + AppConfig.maindirpath + "/icons/");
}
} catch (FileNotFoundException e) {
Log.e(TAG, "File: " + Objects.requireNonNull(e.getMessage()));
}
try {
assert fOut != null;
bm.compress(Bitmap.CompressFormat.PNG, 100, fOut);
fOut.flush();
fOut.close();
} catch (Exception e) {
Log.e(TAG, "Bitmap: " + Objects.requireNonNull(e.getMessage()));
}
}
});
} else {
myHolder.textAvail.setText(context.getString(R.string.unavailable));

View file

@ -5,6 +5,7 @@ import static android.content.Intent.ACTION_VIEW;
import static android.view.View.GONE;
import com.termux.app.TermuxService;
import static com.vectras.vm.utils.UIUtils.UIAlert;
import android.annotation.SuppressLint;
@ -42,6 +43,7 @@ import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.vm.databinding.ActivitySetupQemuBinding;
import com.vectras.vm.utils.DeviceUtils;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.JSONUtils;
import com.vectras.vm.utils.ListUtils;
@ -172,8 +174,9 @@ public class SetupQemuActivity extends AppCompatActivity implements View.OnClick
contentJSON = response;
if (JSONUtils.isMapValidFromString(contentJSON)) {
mmap.clear();
mmap= new Gson().fromJson(contentJSON, new TypeToken<HashMap<String, Object>>(){}.getType());
if(mmap.containsKey("arm64") && mmap.containsKey("x86_64")) {
mmap = new Gson().fromJson(contentJSON, new TypeToken<HashMap<String, Object>>() {
}.getType());
if (mmap.containsKey("arm64") && mmap.containsKey("x86_64")) {
if (Build.SUPPORTED_ABIS[0].contains("arm64")) {
bootstrapfilelink = mmap.get("arm64").toString();
} else {
@ -204,7 +207,7 @@ public class SetupQemuActivity extends AppCompatActivity implements View.OnClick
linearload.setVisibility(GONE);
}
};
String filesDir = activity.getFilesDir().getAbsolutePath();
File distroDir = new File(filesDir + "/distro");
File binDir = new File(distroDir + "/bin");
@ -276,7 +279,7 @@ public class SetupQemuActivity extends AppCompatActivity implements View.OnClick
}
//FileUtils.deleteDirectory(apkLoaderextractedFilePath);
//if (!copyAssetToFile(apkLoaderAssetPath, apkLoaderextractedFilePath)) {
//errorMessage = "Failed to copy loader.apk file.";
//errorMessage = "Failed to copy loader.apk file.";
//}
}
}
@ -301,10 +304,12 @@ public class SetupQemuActivity extends AppCompatActivity implements View.OnClick
}.execute();
}
/** Copies the specified asset to the given file path. */
/**
* Copies the specified asset to the given file path.
*/
private boolean copyAssetToFile(String assetPath, String outputPath) {
try (InputStream in = getAssets().open(assetPath);
OutputStream out = new FileOutputStream(outputPath)) {
OutputStream out = new FileOutputStream(outputPath)) {
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
@ -318,7 +323,9 @@ public class SetupQemuActivity extends AppCompatActivity implements View.OnClick
}
}
/** Determines the ABI of the device. */
/**
* Determines the ABI of the device.
*/
private String getDeviceAbi() {
return Build.SUPPORTED_ABIS[0];
}
@ -367,10 +374,10 @@ public class SetupQemuActivity extends AppCompatActivity implements View.OnClick
}
} else if (id == R.id.buttontryconnectagain) {
linearload.setVisibility(View.VISIBLE);
net.startRequestNetwork(RequestNetworkController.GET,AppConfig.bootstrapfileslink,"",_net_request_listener);
net.startRequestNetwork(RequestNetworkController.GET, AppConfig.bootstrapfileslink, "", _net_request_listener);
} else if (id == R.id.buttonletstart) {
linearwelcome.setVisibility(GONE);
net.startRequestNetwork(RequestNetworkController.GET,AppConfig.bootstrapfileslink,"",_net_request_listener);
net.startRequestNetwork(RequestNetworkController.GET, AppConfig.bootstrapfileslink, "", _net_request_listener);
}
}
@ -388,9 +395,9 @@ public class SetupQemuActivity extends AppCompatActivity implements View.OnClick
isexecutingCommand = false;
linearsimplesetupui.setVisibility(GONE);
binding.advancedsetup.lnAdvancedsetup.setVisibility(GONE);
} else if (textToAdd.contains("libproot.so --help")){
} else if (textToAdd.contains("libproot.so --help")) {
libprooterror = true;
} else if (textToAdd.contains("not complete: /root/setup.tar.gz")){
} else if (textToAdd.contains("not complete: /root/setup.tar.gz")) {
aria2Error = true;
}
@ -458,7 +465,7 @@ public class SetupQemuActivity extends AppCompatActivity implements View.OnClick
// 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");
@ -687,6 +694,20 @@ public class SetupQemuActivity extends AppCompatActivity implements View.OnClick
}
}
}
if (DeviceUtils.isStorageLow(this)) {
DialogUtils.oneDialog(this,
getResources().getString(R.string.oops),
getResources().getString(R.string.very_low_available_storage_space_content),
getResources().getString(R.string.ok),
true,
R.drawable.warning_48px,
true,
null,
() -> {
if (DeviceUtils.isStorageLow(this)) finish();
});
}
}
}
@ -718,7 +739,7 @@ public class SetupQemuActivity extends AppCompatActivity implements View.OnClick
File selectedFilePath = new File(getPath(content_describer));
ProgressBar loading = progressBar;
String abi = Build.SUPPORTED_ABIS[0];
if (selectedFilePath.toString().endsWith(abi+".tar.gz")) {
if (selectedFilePath.toString().endsWith(abi + ".tar.gz")) {
loading.setVisibility(View.VISIBLE);
new Thread(new Runnable() {
@Override
@ -772,8 +793,7 @@ public class SetupQemuActivity extends AppCompatActivity implements View.OnClick
}
UIAlert(activity, "INVALID FILE", "please select vectras-vm-" + abi + ".tar.gz file");
}
} else
if (linearsimplesetupui.getVisibility() == GONE) {
} else if (linearsimplesetupui.getVisibility() == GONE) {
showAdvancedSetupDialog();
}
}

View file

@ -658,7 +658,7 @@ public class VMManager {
}
}
public static boolean isRomsDataJsonNormal(Boolean _needfix, Activity _context) {
public static boolean isRomsDataJsonValid(Boolean _needfix, Activity _context) {
if (isFileExists(AppConfig.romsdatajson)) {
if (!JSONUtils.isValidFromFile(AppConfig.romsdatajson)) {
if (_needfix) {

View file

@ -15,6 +15,7 @@ import android.graphics.Typeface;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
@ -84,7 +85,6 @@ public class VectrasApp extends Application {
@Override
public void onCreate() {
super.onCreate();
setupAppConfig(getApplicationContext());
vectrasapp = this;
CrashHandler.getInstance().registerGlobal(this);
CrashHandler.getInstance().registerPart(this);
@ -104,7 +104,7 @@ public class VectrasApp extends Application {
} else {
overrideFont("DEFAULT", R.font.gilroy);
}
setupAppConfig(getApplicationContext());
}
public void overrideFont(String defaultFontNameToOverride, int customFontResourceId) {
@ -430,7 +430,7 @@ public class VectrasApp extends Application {
}
}
private static void setupAppConfig(Context _context) {
private void setupAppConfig(Context _context) {
AppConfig.vectrasVersion = PackageUtils.getThisVersionName(_context);
AppConfig.vectrasVersionCode = PackageUtils.getThisVersionCode(_context);
AppConfig.basefiledir = AppConfig.datadirpath(_context) + "/.qemu/";
@ -440,5 +440,8 @@ public class VectrasApp extends Application {
AppConfig.romsdatajson = AppConfig.maindirpath + "roms-data.json";
AppConfig.vmFolder = AppConfig.maindirpath + "roms/";
AppConfig.recyclebin = AppConfig.maindirpath + "recyclebin/";
Config.cacheDir = _context.getCacheDir().getAbsolutePath();
Config.storagedir = Environment.getExternalStorageDirectory().toString();
}
}

View file

@ -1,13 +1,62 @@
package com.vectras.vm.utils;
import android.app.ActivityManager;
import android.app.usage.StorageStatsManager;
import android.content.Context;
import android.os.Build;
import android.os.Environment;
import android.os.StatFs;
import android.os.storage.StorageManager;
import android.util.Log;
import androidx.annotation.RequiresApi;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
public class DeviceUtils {
public static String TAG = "DeviceUtils";
public static double totalMemoryCapacity(Context context) {
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
activityManager.getMemoryInfo(memoryInfo);
return memoryInfo.totalMem;
}
public static boolean isStorageLow(Context context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
try {
File path = Environment.getDataDirectory();
StatFs stat = new StatFs(path.getPath());
long blockSize = stat.getBlockSizeLong();
long availableBlocks = stat.getAvailableBlocksLong();
long availableBytes = availableBlocks * blockSize;
long availableMB = availableBytes / (1024 * 1024);
return availableMB < 2048;
} catch (Exception e) {
Log.e(TAG, "Error getting storage stats", e);
}
} else {
StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
StorageStatsManager statsManager = (StorageStatsManager) context.getSystemService(Context.STORAGE_STATS_SERVICE);
try {
UUID uuid = storageManager.getUuidForPath(Environment.getDataDirectory());
long availableBytes = statsManager.getFreeBytes(uuid);
long availableMB = availableBytes / (1024 * 1024);
return availableMB < 2048;
} catch (IOException e) {
Log.e(TAG, "Error getting storage stats", e);
}
}
return false;
}
}

View file

@ -9,6 +9,7 @@ import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Objects;
public class ImageUtils {
public static final String TAG = "ImageUtils";
@ -16,7 +17,7 @@ public class ImageUtils {
public static void convertToPng(Context context, Uri sourceUri, String outputFullPath) throws IOException {
File outputFile = new File(outputFullPath);
if (!outputFile.getParentFile().exists()) {
if (!Objects.requireNonNull(outputFile.getParentFile()).exists()) {
outputFile.getParentFile().mkdirs();
}
@ -33,4 +34,24 @@ public class ImageUtils {
}
public static void saveBitmapToPNGFile(Bitmap bitmap, String saveTo, String fileName) {
try {
File folder = new File(saveTo);
if (!folder.exists()) {
if (!folder.mkdirs()) {
Log.e(TAG, "Failed to create folder: " + saveTo);
return;
}
}
File file = new File(saveTo, fileName);
FileOutputStream out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
out.flush();
out.close();
} catch (IOException e) {
Log.e(TAG, "Failed to save bitmap to file: " + e.getMessage());
}
}
}