v2.9.5.7-3dfx

- Fixed VNC Server could fail to connect from another device.
- Added no more update prompts for an updated version.
- Fixed missing package check could give wrong results for 32bit only devices.
- Fixed VM not being created when ignoring warnings.
- Termux:X11 startup command now only runs for Android 13 and below.
- Added check if cache folder was created successfully when running VM.
- Added VNC Server running notification dialog after successful VM launch.
- New Home interface.
- Rom store has been integrated in Home.
- New System monitor.
- Updated update notification dialog interface.
- New update checker.
- Reduced time on startup screen.
- Fixed issue with virtual machine list data fixer.
This commit is contained in:
An Bui 2025-09-16 23:40:13 +07:00
parent d351766486
commit 149e3c29ea
81 changed files with 4108 additions and 1093 deletions

View file

@ -135,10 +135,6 @@ public class AboutActivity extends AppCompatActivity implements View.OnClickList
} else {
Log.d("TAG", "The interstitial ad wasn't ready yet.");
}
//TextView textversionname = findViewById(R.id.versionname);
//PackageInfo pinfo = MainActivity.activity.getAppInfo(getApplicationContext());
//textversionname.setText(pinfo.versionName);
RecyclerView recyclerView = findViewById(R.id.github_users_recycler_view);
String[] usernames = {"vectras-team", "xoureldeen", "ahmedbarakat2007", "anbui2004"};

View file

@ -76,10 +76,14 @@ public class AppConfig {
public static String pendingCommand = "";
public static String neededPkgs = "aria2 tar dwm xfce4-terminal libslirp libslirp-dev pulseaudio-dev glib-dev pixman-dev zlib-dev spice-dev" +
" libusbredirparser usbredir-dev libiscsi-dev sdl2 sdl2-dev libepoxy-dev virglrenderer-dev rdma-core" +
" libusbredirparser usbredir-dev libiscsi-dev sdl2 sdl2-dev libepoxy-dev virglrenderer-dev rdma-core" +
" libusb ncurses-libs curl libnfs sdl2 gtk+3.0 fuse libpulse libseccomp jack pipewire liburing" +
" mesa-dri-gallium mesa-vulkan-swrast vulkan-loader mesa-utils mesa-egl mesa-gbm mesa-vulkan-ati mesa-vulkan-broadcom mesa-vulkan-freedreno mesa-vulkan-panfrost";
public static String neededPkgs32bit = "aria2 tar dwm xfce4-terminal libslirp libslirp-dev pulseaudio-dev glib-dev pixman-dev zlib-dev spice-dev" +
" libusbredirparser usbredir-dev libiscsi-dev sdl2 sdl2-dev libepoxy-dev virglrenderer-dev rdma-core" +
" libusb ncurses-libs curl libnfs sdl2 gtk+3.0 fuse libpulse libseccomp jack pipewire liburing";
public static boolean needreinstallsystem = false;
public static String temporaryLastedTerminalOutput = "";

View file

@ -10,15 +10,12 @@ import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.cardview.widget.CardView;
import androidx.fragment.app.FragmentTransaction;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.vectras.vm.R;
import com.vectras.vm.Fragment.HomeFragment;
import com.vectras.vm.PostActivity;
import java.util.Collections;
import java.util.List;
import com.vectras.vm.MainActivity;
public class AdapterBlog extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
@ -27,8 +24,7 @@ public class AdapterBlog extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
List<DataBlog> data = Collections.emptyList();
DataBlog current;
int currentPos = 0;
// create constructor to innitilize context and data sent from MainActivity
public AdapterBlog(Context context, List<DataBlog> data) {
this.context = context;
inflater = LayoutInflater.from(context);
@ -52,9 +48,9 @@ public class AdapterBlog extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
final DataBlog current = data.get(position);
myHolder.textTitle.setText(current.postTitle);
myHolder.textDate.setText("Date: " + current.postDate);
Glide.with(MainActivity.activity).load(current.postThumb).into(myHolder.ivThumb);
Glide.with(context).load(current.postThumb).into(myHolder.ivThumb);
Animation animation;
animation = AnimationUtils.loadAnimation(MainActivity.activity, android.R.anim.slide_in_left);
animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
animation.setDuration(300);
myHolder.cdPost.startAnimation(animation);
@ -66,7 +62,7 @@ public class AdapterBlog extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
PostActivity.content = current.postContent;
PostActivity.date = current.postDate;
PostActivity.thumb = current.postThumb;
MainActivity.activity.startActivity(new Intent(MainActivity.activity, PostActivity.class));
context.startActivity(new Intent(context, PostActivity.class));
}
});
}

View file

@ -6,7 +6,6 @@ import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
@ -15,12 +14,12 @@ import androidx.core.app.ActivityCompat;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.vectras.vm.home.HomeActivity;
import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.utils.JSONUtils;
import com.vectras.vm.utils.PermissionUtils;
import com.vectras.vm.utils.UIUtils;
import java.io.File;
import java.util.HashMap;
import java.util.Objects;
@ -86,7 +85,7 @@ public class CqcmActivity extends AppCompatActivity {
} else {
_map = Objects.requireNonNull(getIntent().getStringExtra("content"));
}
if (JSONUtils.isMapValidFromString(_map)) {
if (JSONUtils.isValidFromString(_map)) {
mapForCreateNewVM = new Gson().fromJson(_map, new TypeToken<HashMap<String, Object>>(){}.getType());
if (mapForCreateNewVM.containsKey("imgName")) {
imgName = Objects.requireNonNull(mapForCreateNewVM.get("imgName")).toString();
@ -116,7 +115,7 @@ public class CqcmActivity extends AppCompatActivity {
} else {
Toast.makeText(getApplicationContext(), "The virtual machine list data is corrupted and new virtual machines cannot be added right now.", Toast.LENGTH_LONG).show();
}
if(!MainActivity.isActivate) {
if(!HomeActivity.isActivate) {
Log.i("CqcmActivity", "Vectras VM is not opening.");
gotoActivity.setClass(getApplicationContext(), SplashActivity.class);
startActivity(gotoActivity);
@ -134,7 +133,7 @@ public class CqcmActivity extends AppCompatActivity {
private void runCommand(String _command) {
AppConfig.pendingCommand = _command;
if(!MainActivity.isActivate) {
if(!HomeActivity.isActivate) {
Log.i("CqcmActivity", "Vectras VM is not opening.");
gotoActivity.setClass(getApplicationContext(), SplashActivity.class);
startActivity(gotoActivity);

View file

@ -21,6 +21,7 @@ import com.vectras.qemu.MainSettingsManager;
import com.vectras.vm.Fragment.CreateImageDialogFragment;
import com.vectras.vm.MainRoms.DataMainRoms;
import com.vectras.vm.databinding.ActivityCustomRomBinding;
import com.vectras.vm.home.HomeActivity;
import com.vectras.vm.utils.DeviceUtils;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.FileUtils;
@ -525,10 +526,7 @@ public class CustomRomActivity extends AppCompatActivity {
createNewVM();
} else {
DialogUtils.twoDialog(this, getString(R.string.problem_has_been_detected), _contentDialog, getString(R.string.continuetext), getString(R.string.cancel), true, R.drawable.warning_48px, true,
() -> {
createNewVM();
finish();
}, null, null);
this::createNewVM, null, null);
}
}
}
@ -567,7 +565,7 @@ public class CustomRomActivity extends AppCompatActivity {
}
modify = false;
if (!MainActivity.isActivate) {
if (!HomeActivity.isActivate) {
startActivity(new Intent(this, SplashActivity.class));
} else {
Intent openURL = new Intent();

View file

@ -102,7 +102,7 @@ public class CreateImageDialogFragment extends DialogFragment {
}
Terminal vterm = new Terminal(getActivity());
vterm.executeShellCommand("qemu-img create -f qcow2 \"" + folder + imageName.getText().toString() + ".qcow2\" " +
imageSize.getText().toString() + "G", true, getActivity());
imageSize.getText().toString() + "G", true, true, getActivity());
if (customRom) {
if(drive != null)
drive.setText(folder + imageName.getText().toString() + ".qcow2");

View file

@ -1,62 +1,27 @@
package com.vectras.vm.Fragment;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.net.NetworkRequest;
import android.os.Bundle;
import android.telephony.NetworkScanRequest;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.vm.RomsManagerActivity;
import com.vectras.vm.MainRoms.AdapterMainRoms;
import com.vectras.vm.MainRoms.DataMainRoms;
import com.vectras.vm.R;
import com.vectras.vm.Blog.AdapterBlog;
import com.vectras.vm.Blog.DataBlog;
import com.vectras.vm.AppConfig;
import com.vectras.vm.MainActivity;
import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.utils.UIUtils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import javax.net.ssl.HttpsURLConnection;
import org.json.JSONArray;
import org.json.JSONException;
@ -68,7 +33,6 @@ public class HomeFragment extends Fragment {
public static RecyclerView mRVMainRoms;
public static LinearLayout romsLayout;
public static AdapterMainRoms mMainAdapter;
public MainActivity activity;
public static JSONArray jArray;
public static List<DataMainRoms> data;
private SwipeRefreshLayout refreshRoms;
@ -79,33 +43,28 @@ public class HomeFragment extends Fragment {
// TODO show the text view in @layout/home_fragment if list empty
activity = MainActivity.activity;
view = inflater.inflate(R.layout.home_fragment, container, false);
romsLayout = view.findViewById(R.id.romsLayout);
refreshRoms = view.findViewById(R.id.refreshRoms);
refreshRoms.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
loadDataVbi();
mMainAdapter.notifyItemRangeChanged(0, mMainAdapter.data.size());
refreshRoms.setRefreshing(false);
}
refreshRoms.setOnRefreshListener(() -> {
loadDataVbi();
mMainAdapter.notifyItemRangeChanged(0, mMainAdapter.data.size());
refreshRoms.setRefreshing(false);
});
loadDataVbi();
return view;
}
public static void loadDataVbi() {
private void loadDataVbi() {
data = new ArrayList<>();
try {
jArray = new JSONArray(FileUtils.readFromFile(MainActivity.activity, new File(AppConfig.maindirpath
jArray = new JSONArray(FileUtils.readFromFile(requireActivity(), new File(AppConfig.maindirpath
+ "roms-data.json")));
// Extract data from json and store into ArrayList as class objects
@ -127,7 +86,7 @@ public class HomeFragment extends Fragment {
}
romsMainData.itemExtra = json_data.getString("imgExtra");
try {
if (json_data.getString("imgArch").equals(MainSettingsManager.getArch(MainActivity.activity)))
if (json_data.getString("imgArch").equals(MainSettingsManager.getArch(requireActivity())))
data.add(romsMainData);
} catch (JSONException ignored) {
data.add(romsMainData);
@ -135,35 +94,12 @@ public class HomeFragment extends Fragment {
}
// Setup and Handover data to recyclerview
mRVMainRoms = (RecyclerView) HomeFragment.view.findViewById(R.id.mRVMainRoms);
mMainAdapter = new AdapterMainRoms(MainActivity.activity, data);
mRVMainRoms = HomeFragment.view.findViewById(R.id.mRVMainRoms);
mMainAdapter = new AdapterMainRoms(requireActivity(), data);
mRVMainRoms.setAdapter(mMainAdapter);
mRVMainRoms.setLayoutManager(new GridLayoutManager(MainActivity.activity, 2));
mRVMainRoms.setLayoutManager(new GridLayoutManager(getContext(), 2));
} catch (JSONException e) {
Toast.makeText(MainActivity.activity, e.toString(), Toast.LENGTH_LONG).show();
Toast.makeText(requireActivity(), e.toString(), Toast.LENGTH_LONG).show();
}
}
/**
* CHECK WHETHER INTERNET CONNECTION IS AVAILABLE OR NOT
*/
public boolean checkConnection(Context context) {
final ConnectivityManager connMgr = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connMgr != null) {
NetworkInfo activeNetworkInfo = connMgr.getActiveNetworkInfo();
if (activeNetworkInfo != null) { // connected to the internet
// connected to the mobile provider's data plan
if (activeNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
// connected to wifi
return true;
} else
return activeNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE;
}
}
return false;
}
}

View file

@ -1,48 +1,25 @@
package com.vectras.vm.Fragment;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.vectras.vm.R;
import com.vectras.vm.RomsManagerActivity;
import com.vectras.vm.MainActivity;
import com.vectras.vm.VectrasApp;
import com.vectras.vm.adapter.LogsAdapter;
import com.vectras.vm.logger.VectrasStatus;
import com.vectras.vm.utils.UIUtils;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Objects;
import java.util.Timer;
import java.util.TimerTask;

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
package com.vectras.vm.MainRoms;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
@ -29,14 +30,15 @@ import java.util.List;
public class AdapterMainRoms extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final Activity activity;
private final Context context;
private final LayoutInflater inflater;
public List<DataMainRoms> data = Collections.emptyList();
int currentPos = 0;
// create constructor to innitilize context and data sent from MainActivity
public AdapterMainRoms(Context context, List<DataMainRoms> data) {
this.context = context;
public AdapterMainRoms(Activity activity, List<DataMainRoms> data) {
this.activity = activity;
this.context = activity.getApplicationContext();
inflater = LayoutInflater.from(context);
this.data = data;
}
@ -69,15 +71,15 @@ public class AdapterMainRoms extends RecyclerView.Adapter<RecyclerView.ViewHolde
}
myHolder.optionsBtn.setOnClickListener(view -> {
BottomSheetDialog bottomSheetDialog = new BottomSheetDialog(MainActivity.activity);
View v = MainActivity.activity.getLayoutInflater().inflate(R.layout.rom_options_dialog, null);
BottomSheetDialog bottomSheetDialog = new BottomSheetDialog(context);
View v = activity.getLayoutInflater().inflate(R.layout.rom_options_dialog, null);
bottomSheetDialog.setContentView(v);
Button modifyRomBtn = v.findViewById(R.id.modifyRomBtn);
modifyRomBtn.setOnClickListener(v3 -> {
CustomRomActivity.current = data.get(position);
VMManager.setArch(current.itemArch, MainActivity.activity);
MainActivity.activity.startActivity(new Intent(MainActivity.activity, CustomRomActivity.class).putExtra("POS", position).putExtra("MODIFY", true).putExtra("VMID", current.vmID));
VMManager.setArch(current.itemArch, activity);
context.startActivity(new Intent(context, CustomRomActivity.class).putExtra("POS", position).putExtra("MODIFY", true).putExtra("VMID", current.vmID));
bottomSheetDialog.cancel();
});
@ -92,14 +94,14 @@ public class AdapterMainRoms extends RecyclerView.Adapter<RecyclerView.ViewHolde
Button removeRomBtn = v.findViewById(R.id.removeRomBtn);
removeRomBtn.setOnClickListener(v1 -> {
VMManager.deleteVMDialog(current.itemName, position, MainActivity.activity);
VMManager.deleteVMDialog(current.itemName, position, activity);
bottomSheetDialog.cancel();
});
bottomSheetDialog.show();
});
myHolder.cdRoms.setOnClickListener(view -> {
VMManager.setArch(current.itemArch, MainActivity.activity);
VMManager.setArch(current.itemArch, activity);
StartVM.cdrompath = current.imgCdrom;
if (current.qmpPort == 0) {
Config.setDefault();
@ -107,12 +109,12 @@ public class AdapterMainRoms extends RecyclerView.Adapter<RecyclerView.ViewHolde
Config.QMPPort = current.qmpPort;
}
Config.vmID = current.vmID;
String env = StartVM.env(MainActivity.activity, current.itemExtra, current.itemPath, "");
String env = StartVM.env(activity, current.itemExtra, current.itemPath, "");
MainActivity.startVM(current.itemName, env, current.itemExtra, current.itemPath);
});
myHolder.cdRoms.setOnLongClickListener(v -> {
VMManager.deleteVMDialog(current.itemName, position, MainActivity.activity);
VMManager.deleteVMDialog(current.itemName, position, activity);
return false;
});
}

View file

@ -28,6 +28,7 @@ public class MainService extends Service {
public static String env = null;
private String TAG = "MainService";
public static MainService service;
public static Activity activity;
@Override
public void onCreate() {
@ -49,10 +50,9 @@ public class MainService extends Service {
if (env != null) {
if (service != null) {
String filesDir = MainActivity.activity.getFilesDir().getAbsolutePath();
Terminal vterm = new Terminal(this);
vterm.executeShellCommand2("dwm", false, MainActivity.activity);
vterm.executeShellCommand2(env, true, MainActivity.activity);
Terminal vterm = new Terminal(activity);
vterm.executeShellCommand2("dwm", false, activity);
vterm.executeShellCommand2(env, true, activity);
}
} else
Log.e(TAG, "env is null");
@ -69,12 +69,12 @@ public class MainService extends Service {
//TODO: Not Work
//Terminal.killQemuProcess();
VMManager.killallqemuprocesses(MainActivity.activity);
VMManager.killallqemuprocesses(activity);
}
}
});
t.setName("StartVM");
t.setName("HomeStartVM");
t.start();
}
@ -86,7 +86,7 @@ public class MainService extends Service {
//TODO: Not Work
//Terminal.killQemuProcess();
VMManager.killallqemuprocesses(MainActivity.activity);
VMManager.killallqemuprocesses(this);
return START_NOT_STICKY;
}

View file

@ -26,6 +26,7 @@ import androidx.appcompat.widget.Toolbar;
import com.termux.app.TermuxService;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.vm.home.HomeActivity;
import com.vectras.vm.utils.CommandUtils;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.FileUtils;
@ -149,7 +150,7 @@ public class Minitools extends AppCompatActivity {
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,
() -> {
MainActivity.isActivate = false;
HomeActivity.isActivate = false;
AppConfig.needreinstallsystem = true;
VMManager.killallqemuprocesses(Minitools.this);
FileUtils.deleteDirectory(getFilesDir().getAbsolutePath() + "/data");

View file

@ -147,8 +147,8 @@ public class ProfileActivity extends AppCompatActivity {
@Override
public void onClick(View v) {
FirebaseAuth.getInstance().signOut();
MainActivity.activity.finish();
startActivity(new Intent(activity, SplashActivity.class));
finishAffinity();
}
});
}

View file

@ -38,7 +38,6 @@ public class AdapterRomStoreSearch extends RecyclerView.Adapter<RecyclerView.Vie
static List<DataRoms> dataRom = Collections.emptyList();
private final String TAG = "AdapterRomStoreSearch";
// create constructor to innitilize context and data sent from MainActivity
public AdapterRomStoreSearch(Context context, List<DataRoms> data) {
this.context = context;
inflater = LayoutInflater.from(context);

View file

@ -38,7 +38,6 @@ public class AdapterRoms extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
static List<DataRoms> dataRom = Collections.emptyList();
private final String TAG = "AdapterRoms";
// create constructor to innitilize context and data sent from MainActivity
public AdapterRoms(Context context, List<DataRoms> data) {
this.context = context;
inflater = LayoutInflater.from(context);

View file

@ -43,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.home.HomeActivity;
import com.vectras.vm.utils.DeviceUtils;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.JSONUtils;
@ -172,7 +173,7 @@ public class SetupQemuActivity extends AppCompatActivity implements View.OnClick
public void onResponse(String tag, String response, HashMap<String, Object> responseHeaders) {
linearload.setVisibility(GONE);
contentJSON = response;
if (JSONUtils.isMapValidFromString(contentJSON)) {
if (JSONUtils.isValidFromString(contentJSON)) {
mmap.clear();
mmap = new Gson().fromJson(contentJSON, new TypeToken<HashMap<String, Object>>() {
}.getType());
@ -626,7 +627,7 @@ public class SetupQemuActivity extends AppCompatActivity implements View.OnClick
" echo \"Starting setup...\";" +
" apk update;" +
" echo \"Installing packages...\";" +
" apk add " + AppConfig.neededPkgs.replaceAll(" mesa-vulkan-broadcom mesa-vulkan-freedreno mesa-vulkan-panfrost", "") + ";" +
" apk add " + AppConfig.neededPkgs32bit + ";" +
//" tar -xzvf " + tarPath + " -C /;" +
" echo \"Installing Qemu...\";" +
" apk add qemu-system-x86_64 qemu-system-ppc qemu-system-i386 qemu-system-aarch64 qemu-pr-helper qemu-img qemu-audio-sdl pulseaudio mesa-dri-gallium;" +
@ -855,7 +856,7 @@ public class SetupQemuActivity extends AppCompatActivity implements View.OnClick
@Override
public void onClick(View v) {
if (!AppConfig.needreinstallsystem) {
startActivity(new Intent(SetupQemuActivity.this, MainActivity.class));
startActivity(new Intent(SetupQemuActivity.this, HomeActivity.class));
} else {
AppConfig.needreinstallsystem = false;
}

View file

@ -38,6 +38,7 @@ import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.vm.home.HomeActivity;
import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.utils.UIUtils;
@ -64,15 +65,11 @@ public class SplashActivity extends AppCompatActivity implements Runnable {
setContentView(R.layout.activity_splash);
UIUtils.setOnApplyWindowInsetsListener(findViewById(R.id.main));
//TextView textversionname;
//textversionname = findViewById(R.id.versionname);
//PackageInfo pinfo = MainActivity.activity.getAppInfo(getApplicationContext());
//textversionname.setText(pinfo.versionName);
setupFolders();
SharedPreferences prefs = getSharedPreferences(CREDENTIAL_SHARED_PREF, Context.MODE_PRIVATE);
try {
new Handler().postDelayed(activity, 3000);
new Handler().postDelayed(activity, 1000);
} catch (Exception e) {
throw new RuntimeException(e);
}/*
@ -350,7 +347,7 @@ public class SplashActivity extends AppCompatActivity implements Runnable {
String filesDir = activity.getFilesDir().getAbsolutePath();
SharedPreferences prefs = getSharedPreferences(CREDENTIAL_SHARED_PREF, Context.MODE_PRIVATE);
if ((new File(filesDir, "/distro/usr/local/bin/qemu-system-x86_64").exists()) || (new File(filesDir, "/distro/usr/bin/qemu-system-x86_64").exists())) {
startActivity(new Intent(this, MainActivity.class));
startActivity(new Intent(this, HomeActivity.class));
} else {
startActivity(new Intent(this, SetupQemuActivity.class));
//For Android 14+

View file

@ -41,6 +41,9 @@ public class StartVM {
else if (MainSettingsManager.getArch(activity).equals("PPC"))
params.add("qemu-system-ppc");
params.add("-qmp");
params.add("unix:" + Config.getLocalQMPSocketPath() + ",server,nowait");
String ifType;
ifType= MainSettingsManager.getIfType(activity);
@ -150,10 +153,10 @@ public class StartVM {
}
String memoryStr = "-m ";
if (MainSettingsManager.getArch(activity).equals("PPC") && RamInfo.vectrasMemory() > 2048) {
if (MainSettingsManager.getArch(activity).equals("PPC") && RamInfo.vectrasMemory(activity) > 2048) {
memoryStr += 2048;
} else {
memoryStr += RamInfo.vectrasMemory();
memoryStr += RamInfo.vectrasMemory(activity);
}
String boot = "-boot ";
@ -168,7 +171,7 @@ public class StartVM {
//params.add(soundDevice);
if (MainSettingsManager.useDefaultBios(MainActivity.activity)) {
if (MainSettingsManager.useDefaultBios(activity)) {
if (MainSettingsManager.getArch(activity).equals("PPC")) {
bios = "-L ";
bios += "pc-bios";
@ -197,13 +200,13 @@ public class StartVM {
params.add(machine);
}
if (MainSettingsManager.useMemoryOvercommit(MainActivity.activity)) {
if (MainSettingsManager.useMemoryOvercommit(activity)) {
params.add("-overcommit");
params.add("mem-lock=off");
}
if (MainSettingsManager.useLocalTime(MainActivity.activity)) {
if (MainSettingsManager.useLocalTime(activity)) {
params.add("-rtc");
params.add("base=localtime");
}
@ -252,9 +255,9 @@ public class StartVM {
params.add(vncParams);
} else {
String qmpParams = "unix:";
qmpParams += Config.getLocalVNCSocketPath();
params.add(qmpParams);
String vncSocketParams = "unix:";
vncSocketParams += Config.getLocalVNCSocketPath();
params.add(vncSocketParams);
}
//if (!MainSettingsManager.getArch(activity).equals("PPC") || !MainSettingsManager.getArch(activity).equals("ARM64")) {
@ -272,9 +275,6 @@ public class StartVM {
//params.add("-full-screen");
params.add("-qmp");
params.add("unix:" + Config.getLocalQMPSocketPath() + ",server,nowait");
return String.join(" ", params);
}

View file

@ -10,15 +10,11 @@ import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.cardview.widget.CardView;
import androidx.fragment.app.FragmentTransaction;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.vectras.vm.R;
import com.vectras.vm.Fragment.HomeFragment;
import com.vectras.vm.PostActivity;
import java.util.Collections;
import java.util.List;
import com.vectras.vm.MainActivity;
import com.vectras.vm.StoreActivity;
import com.vectras.vm.StoreItemActivity;
@ -29,8 +25,7 @@ public class AdapterStore extends RecyclerView.Adapter<RecyclerView.ViewHolder>
List<DataStore> data = Collections.emptyList();
DataStore current;
int currentPos = 0;
// create constructor to innitilize context and data sent from MainActivity
public AdapterStore(Context context, List<DataStore> data) {
this.context = context;
inflater = LayoutInflater.from(context);
@ -53,10 +48,10 @@ public class AdapterStore extends RecyclerView.Adapter<RecyclerView.ViewHolder>
MyHolder myHolder = (MyHolder) holder;
final DataStore current = data.get(position);
myHolder.textName.setText(current.itemName);
myHolder.textSize.setText(MainActivity.activity.getString(R.string.size) + current.itemSize);
myHolder.textSize.setText(context.getString(R.string.size) + current.itemSize);
Glide.with(StoreActivity.activity).load(current.itemIcon).into(myHolder.ivIcon);
Animation animation;
animation = AnimationUtils.loadAnimation(MainActivity.activity, android.R.anim.slide_in_left);
animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
animation.setDuration(300);
myHolder.cdItem.startAnimation(animation);

View file

@ -26,12 +26,15 @@ import androidx.recyclerview.widget.GridLayoutManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textfield.TextInputLayout;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import com.vectras.qemu.Config;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.qemu.MainVNCActivity;
import com.vectras.qemu.utils.QmpClient;
import com.vectras.vm.MainRoms.AdapterMainRoms;
import com.vectras.vm.home.HomeActivity;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.utils.JSONUtils;
@ -39,6 +42,7 @@ import com.vectras.vm.utils.UIUtils;
import com.vectras.vterm.Terminal;
import org.jetbrains.annotations.Contract;
import org.json.JSONArray;
import java.io.BufferedWriter;
import java.io.File;
@ -142,24 +146,23 @@ public class VMManager {
}
public static void removeInRomsDataJson(Activity _activity, String _vmName, int _position) {
MainActivity.mMainAdapter = new AdapterMainRoms(MainActivity.activity, MainActivity.data);
MainActivity.data.remove(_position);
MainActivity.mRVMainRoms.setAdapter(MainActivity.mMainAdapter);
MainActivity.mRVMainRoms.setLayoutManager(new GridLayoutManager(MainActivity.activity, 2));
MainActivity.jArray.remove(_position);
try {
JSONArray jSONArray = new JSONArray(FileUtils.readFromFile(_activity, new File(AppConfig.maindirpath
+ "roms-data.json")));
jSONArray.remove(_position);
Writer output = null;
File jsonFile = new File(AppConfig.maindirpath + "roms-data" + ".json");
output = new BufferedWriter(new FileWriter(jsonFile));
output.write(MainActivity.jArray.toString());
output.write(jSONArray.toString());
output.close();
} catch (Exception e) {
UIUtils.toastLong(_activity, e.toString());
}
UIUtils.toastLong(_activity, _vmName + _activity.getString(R.string.are_removed_successfully));
if (!FileUtils.readAFile(AppConfig.maindirpath + "roms-data.json").contains("{")) {
MainActivity.mdatasize2();
}
HomeActivity.refeshVMListNow();
}
public static String idGenerator() {
@ -357,7 +360,7 @@ public class VMManager {
if (_startRepeat < _filelist.size()) {
if (!isFileExists(_filelist.get((int)(_startRepeat)) + "/vmID.txt")) {
if (isFileExists(_filelist.get((int)(_startRepeat)) + "/rom-data.json")) {
if (JSONUtils.isMapValidFromString(FileUtils.readAFile(_filelist.get((int)(_startRepeat)) + "/rom-data.json"))) {
if (JSONUtils.isValidFromString(FileUtils.readAFile(_filelist.get((int)(_startRepeat)) + "/rom-data.json"))) {
if (_resulttemp.toString().contains("}")) {
_resulttemp.append(",").append(FileUtils.readAFile(_filelist.get((int) (_startRepeat)) + "/rom-data.json"));
} else {
@ -428,67 +431,28 @@ public class VMManager {
public static void startFixRomsDataJson() {
int _startRepeat = 0;
StringBuilder _resulttemp = new StringBuilder();
StringBuilder _result = new StringBuilder();
String tempRomData;
JsonArray arr = new JsonArray();
restoredVMs = 0;
ArrayList<String> _filelist = new ArrayList<>();
FileUtils.getAListOfAllFilesAndFoldersInADirectory(AppConfig.vmFolder, _filelist);
if (!_filelist.isEmpty()) {
for (int _repeat = 0; _repeat < (int)(_filelist.size()); _repeat++) {
for (int _repeat = 0; _repeat < _filelist.size(); _repeat++) {
if (_startRepeat < _filelist.size()) {
if (isFileExists(_filelist.get((int)(_startRepeat)) + "/vmID.txt")) {
if (isFileExists(_filelist.get((int)(_startRepeat)) + "/rom-data.json")) {
if (JSONUtils.isMapValidFromString(FileUtils.readAFile(_filelist.get((int)(_startRepeat)) + "/rom-data.json"))) {
if (_resulttemp.toString().contains("}")) {
_resulttemp.append(",").append(FileUtils.readAFile(_filelist.get((int) (_startRepeat)) + "/rom-data.json"));
} else {
_resulttemp = new StringBuilder(FileUtils.readAFile(_filelist.get((int) (_startRepeat)) + "/rom-data.json"));
}
if (JSONUtils.isValidFromString(FileUtils.readAFile(AppConfig.maindirpath + "/roms-data.json").replaceAll("]", _resulttemp + "]"))) {
if (_result.toString().contains("}")) {
_result.append(",").append(FileUtils.readAFile(_filelist.get((int) (_startRepeat)) + "/rom-data.json"));
} else {
_result = new StringBuilder(FileUtils.readAFile(_filelist.get((int) (_startRepeat)) + "/rom-data.json"));
}
restoredVMs++;
} else if (JSONUtils.isValidFromString(FileUtils.readAFile(AppConfig.maindirpath + "/roms-data.json").replaceAll("]", "," + _resulttemp + "]"))) {
if (_result.toString().contains("}")) {
_result.append(",").append(FileUtils.readAFile(_filelist.get((int) (_startRepeat)) + "/rom-data.json"));
} else {
_result = new StringBuilder("," + FileUtils.readAFile(_filelist.get((int) (_startRepeat)) + "/rom-data.json"));
}
restoredVMs++;
} else {
Log.i("CqcmActivity", FileUtils.readAFile(AppConfig.maindirpath + "/roms-data.json").replaceAll("]", _resulttemp + "]"));
}
if (isFileExists(_filelist.get(_startRepeat) + "/vmID.txt")) {
if (isFileExists(_filelist.get(_startRepeat) + "/rom-data.json")) {
tempRomData = FileUtils.readAFile(_filelist.get(_startRepeat) + "/rom-data.json");
if (JSONUtils.isValidFromString(tempRomData)) {
arr.add(JsonParser.parseString(tempRomData));
restoredVMs++;
}
}
}
_startRepeat++;
if (_startRepeat == _filelist.size()) {
if (_result.length() > 0) {
if (JSONUtils.isValidFromString("[" + _result + "]")) {
if (isFileExists(AppConfig.romsdatajson)) {
if (JSONUtils.isValidFromFile(AppConfig.romsdatajson)) {
String _JSONcontent = FileUtils.readAFile(AppConfig.romsdatajson);
String _JSONcontentnew = _JSONcontent.replaceAll("]", _result + "]");
if (JSONUtils.isValidFromString(_JSONcontentnew)) {
FileUtils.writeToFile(AppConfig.maindirpath, "roms-data.json", _JSONcontentnew);
} else {
restoredVMs = 0;
}
} else {
restoredVMs = 0;
}
} else {
FileUtils.writeToFile(AppConfig.maindirpath, "roms-data.json", "[" + _result + "]");
}
} else {
restoredVMs = 0;
}
} else {
restoredVMs = 0;
if (restoredVMs > 0) {
FileUtils.writeToFile(AppConfig.maindirpath, "roms-data.json", arr.toString());
}
}
}
@ -624,7 +588,7 @@ public class VMManager {
if (_result.contains("proot\": error=2,")) {
DialogUtils.twoDialog(_activity, _activity.getResources().getString(R.string.problem_has_been_detected), _activity.getResources().getString(R.string.error_PROOT_IS_MISSING_0), _activity.getString(R.string.continuetext), _activity.getString(R.string.cancel), true, R.drawable.build_24px, true,
() -> {
MainActivity.isActivate = false;
HomeActivity.isActivate = false;
FileUtils.deleteDirectory(_activity.getFilesDir().getAbsolutePath() + "/data");
FileUtils.deleteDirectory(_activity.getFilesDir().getAbsolutePath() + "/distro");
FileUtils.deleteDirectory(_activity.getFilesDir().getAbsolutePath() + "/usr");
@ -650,7 +614,7 @@ public class VMManager {
return true;
} else if (_result.contains("No such file or directory")) {
//Error code: NO_SUCH_FILE_OR_DIRECTORY
DialogUtils.oneDialog(_activity, _activity.getString(R.string.problem_has_been_detected), _activity.getString(R.string.error_NO_SUCH_FILE_OR_DIRECTORY), _activity.getString(R.string.ok),true, R.drawable.file_copy_24px, true,null, null);
DialogUtils.oneDialog(_activity, _activity.getString(R.string.problem_has_been_detected), _activity.getString(R.string.error_NO_SUCH_FILE_OR_DIRECTORY) + "\n\n" + _result, _activity.getString(R.string.ok),true, R.drawable.file_copy_24px, true,null, null);
_activity.stopService(new Intent(_activity, MainService.class));
return true;
} else {
@ -687,8 +651,7 @@ public class VMManager {
} else {
DialogUtils.oneDialog(_context, _context.getString(R.string.done), _context.getString(R.string.roms_data_json_fixed_successfully), _context.getString(R.string.ok),true, R.drawable.check_24px, true,null, null);
}
MainActivity.mMainAdapter.notifyDataSetChanged();
MainActivity.mdatasize2();
HomeActivity.refeshVMListNow();
movetoRecycleBin();
}
@ -752,9 +715,9 @@ public class VMManager {
return true;
}
public static boolean isThisVMRunning(String intemExtra, String itemPath) {
Terminal vterm = new Terminal(MainActivity.activity);
vterm.executeShellCommand2("ps -e", false, MainActivity.activity);
public static boolean isThisVMRunning(Activity activity, String intemExtra, String itemPath) {
Terminal vterm = new Terminal(activity);
vterm.executeShellCommand2("ps -e", false, activity);
if (AppConfig.temporaryLastedTerminalOutput.contains(intemExtra) && AppConfig.temporaryLastedTerminalOutput.contains(itemPath)) {
Log.d("VMManager.isThisVMRunning", "Yes");
return true;
@ -764,9 +727,9 @@ public class VMManager {
}
}
public static boolean isQemuRunning() {
Terminal vterm = new Terminal(MainActivity.activity);
vterm.executeShellCommand2("ps -e", false, MainActivity.activity);
public static boolean isQemuRunning(Activity activity) {
Terminal vterm = new Terminal(activity);
vterm.executeShellCommand2("ps -e", false, activity);
if (AppConfig.temporaryLastedTerminalOutput.contains("qemu-system")) {
Log.d("VMManager.isQemuRunning", "Yes");
return true;
@ -795,15 +758,18 @@ public class VMManager {
}
}
public static void requestKillAllQemuProcess(Activity activity) {
public static void requestKillAllQemuProcess(Activity activity, Runnable runnable) {
DialogUtils.twoDialog(activity, activity.getString(R.string.do_you_want_to_kill_all_qemu_processes), activity.getString(R.string.all_running_vms_will_be_forcibly_shut_down), activity.getString(R.string.kill_all), activity.getString(R.string.cancel), true, R.drawable.power_settings_new_24px, true,
() -> killallqemuprocesses(activity), null, null);
() -> {
killallqemuprocesses(activity);
if (runnable != null) runnable.run();
}, null, null);
}
public static void killcurrentqemuprocess(Context context) {
Terminal vterm = new Terminal(context);
public static void killcurrentqemuprocess(Activity activity) {
Terminal vterm = new Terminal(activity);
String env = "killall -9 ";
switch (MainSettingsManager.getArch(MainActivity.activity)) {
switch (MainSettingsManager.getArch(activity)) {
case "ARM64":
env += "qemu-system-aarch64";
break;
@ -817,15 +783,15 @@ public class VMManager {
env += "qemu-system-x86_64";
break;
}
vterm.executeShellCommand2(env, false, MainActivity.activity);
vterm.executeShellCommand2(env, false, null);
}
public static void killallqemuprocesses(Context context) {
Terminal vterm = new Terminal(context);
vterm.executeShellCommand2("killall -9 qemu-system-i386", false, MainActivity.activity);
vterm.executeShellCommand2("killall -9 qemu-system-x86_64", false, MainActivity.activity);
vterm.executeShellCommand2("killall -9 qemu-system-aarch64", false, MainActivity.activity);
vterm.executeShellCommand2("killall -9 qemu-system-ppc", false, MainActivity.activity);
vterm.executeShellCommand2("killall -9 qemu-system-i386", false, null);
vterm.executeShellCommand2("killall -9 qemu-system-x86_64", false, null);
vterm.executeShellCommand2("killall -9 qemu-system-aarch64", false, null);
vterm.executeShellCommand2("killall -9 qemu-system-ppc", false, null);
}
public static void shutdownCurrentVM() {
@ -1195,4 +1161,18 @@ public class VMManager {
|| _qemuCommand.contains("-M pc-q35")
|| _qemuCommand.contains("-machine pc-q35");
}
public static boolean checkSharedFolder() { //TODO: not work idk why
File folder = new File(AppConfig.sharedFolder);
File[] listOfFiles = folder.listFiles();
if (listOfFiles != null) {
for (File file : listOfFiles) {
if (file.isFile() && file.length() > 500 * 1024 * 1024) { // 500MB
return true;
}
}
}
return false;
}
}

View file

@ -86,6 +86,7 @@ public class VectrasApp extends Application {
public void onCreate() {
super.onCreate();
vectrasapp = this;
context = new WeakReference<>(getApplicationContext());
CrashHandler.getInstance().registerGlobal(this);
CrashHandler.getInstance().registerPart(this);
try {

View file

@ -15,6 +15,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.vectras.vm.MainRoms.AdapterMainRoms;
import com.vectras.vm.MainRoms.DataMainRoms;
import com.vectras.vm.home.HomeActivity;
import com.vectras.vm.utils.FileUtils;
import org.json.JSONArray;
@ -33,7 +34,7 @@ public class WidgetProvider extends AppWidgetProvider {
for (int i=0; i < appWidgetIds.length; i++) {
int appWidgetId = appWidgetIds[i];
// Create an Intent to launch ExampleActivity
Intent intent = new Intent(context, MainActivity.class);
Intent intent = new Intent(context, HomeActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
/* context = */ context,
/* requestCode = */ 0,

View file

@ -0,0 +1,844 @@
package com.vectras.vm.home;
import static android.content.Intent.ACTION_OPEN_DOCUMENT;
import static android.content.Intent.ACTION_VIEW;
import static android.os.Build.VERSION.SDK_INT;
import static com.vectras.vm.VectrasApp.getApp;
import static com.vectras.vm.utils.UIUtils.UIAlert;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.StrictMode;
import android.provider.DocumentsContract;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ProgressBar;
import androidx.activity.EdgeToEdge;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.view.GravityCompat;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.google.android.material.button.MaterialButton;
import com.google.gson.Gson;
import com.termux.app.TermuxActivity;
import com.vectras.qemu.Config;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.vm.AboutActivity;
import com.vectras.vm.AppConfig;
import com.vectras.vm.CustomRomActivity;
import com.vectras.vm.DataExplorerActivity;
import com.vectras.vm.Minitools;
import com.vectras.vm.R;
import com.vectras.vm.RequestNetwork;
import com.vectras.vm.RequestNetworkController;
import com.vectras.vm.Roms.AdapterRomStoreSearch;
import com.vectras.vm.Roms.DataRoms;
import com.vectras.vm.RomsManagerActivity;
import com.vectras.vm.SetArchActivity;
import com.vectras.vm.StoreActivity;
import com.vectras.vm.VMManager;
import com.vectras.vm.adapter.LogsAdapter;
import com.vectras.vm.databinding.ActivityHomeBinding;
import com.vectras.vm.databinding.ActivityHomeContentBinding;
import com.vectras.vm.home.core.CallbackInterface;
import com.vectras.vm.home.core.DisplaySystem;
import com.vectras.vm.home.core.PendingCommand;
import com.vectras.vm.home.core.SharedData;
import com.vectras.vm.home.monitor.SystemMonitorFragment;
import com.vectras.vm.home.romstore.RomStoreFragment;
import com.vectras.vm.home.vms.VmsFragment;
import com.vectras.vm.home.vms.VmsHomeAdapter;
import com.vectras.vm.logger.VectrasStatus;
import com.vectras.vm.settings.UpdaterActivity;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.utils.LibraryChecker;
import com.vectras.vm.utils.NotificationUtils;
import com.vectras.vm.utils.PackageUtils;
import com.vectras.vm.utils.UIUtils;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.stream.Collectors;
public class HomeActivity extends AppCompatActivity implements RomStoreFragment.RomStoreCallToHomeListener, VmsFragment.VmsCallToHomeListener {
public static String curRomName;
private final String TAG = "HomeActivity";
public static boolean isActivate = false;
ActivityHomeBinding binding;
ActivityHomeContentBinding bindingContent;
private AdapterRomStoreSearch adapterRomStoreSearch;
private final List<DataRoms> dataRomStoreSearch = new ArrayList<>();
public static CallbackInterface.HomeCallToVmsListener homeCallToVmsListener;
public static void refeshVMListNow() {
homeCallToVmsListener.refeshVMList();
}
@Override
public void updateDataStatus(boolean isReady) {
bindingContent.searchbar.setEnabled(isReady);
}
@Override
public void openRomStore() {
bindingContent.bottomNavigation.setSelectedItemId(R.id.item_romstore);
}
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
VmsFragment.vmsCallToHomeListener = this;
RomStoreFragment.romStoreCallToHomeListener = this;
EdgeToEdge.enable(this);
binding = ActivityHomeBinding.inflate(getLayoutInflater());
bindingContent = binding.maincontent;
setContentView(binding.getRoot());
isActivate = true;
UIUtils.setOnApplyWindowInsetsListener(bindingContent.main);
UIUtils.setOnApplyWindowInsetsListenerLeftOnly(binding.navView);
UIUtils.setOnApplyWindowInsetsListenerBottomOnly(binding.rvRomstoresearch);
UIUtils.setOnApplyWindowInsetsListenerBottomOnly(binding.lnSearchempty);
initialize(bundle);
}
private void initialize(Bundle savedInstanceState) {
bindingContent.efabCreate.setOnClickListener(view -> startActivity(new Intent(this, SetArchActivity.class)));
setSupportActionBar(bindingContent.toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayShowTitleEnabled(false);
}
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, binding.drawerLayout, bindingContent.toolbar,
R.string.navigation_drawer_open, R.string.navigation_drawer_close);
binding.drawerLayout.addDrawerListener(toggle);
toggle.syncState();
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.replace(bindingContent.containerView.getId(), new VmsFragment())
.commit();
}
binding.searchview.setupWithSearchBar(bindingContent.searchbar);
bindingContent.searchbar.inflateMenu(R.menu.searchbar_menu);
bindingContent.searchbar.setOnMenuItemClickListener(
menuItem -> {
if (menuItem.getItemId() == R.id.importrom) {
Intent intent = new Intent();
intent.setClass(getApplicationContext(), CustomRomActivity.class);
intent.putExtra("importcvbinow", "");
startActivity(intent);
} else if (menuItem.getItemId() == R.id.backtothedisplay) {
DisplaySystem.launch(this);
}
return true;
});
bindingContent.searchbar.setEnabled(false);
bindingContent.bottomNavigation.setOnItemSelectedListener(item -> {
Fragment selectedFragment;
int id = item.getItemId();
if (id == R.id.item_home) {
selectedFragment = new VmsFragment();
bindingContent.efabCreate.setVisibility(View.VISIBLE);
bindingContent.searchbar.setHint(getText(R.string.home));
bindingContent.searchbar.setEnabled(false);
} else if (id == R.id.item_romstore) {
selectedFragment = new RomStoreFragment();
bindingContent.efabCreate.setVisibility(View.GONE);
bindingContent.searchbar.setEnabled(true);
bindingContent.searchbar.setHint(getText(R.string.search));
} else if (id == R.id.item_monitor) {
selectedFragment = new SystemMonitorFragment();
bindingContent.efabCreate.setVisibility(View.GONE);
bindingContent.searchbar.setHint(getText(R.string.system_monitor));
bindingContent.searchbar.setEnabled(false);
} else {
selectedFragment = new VmsFragment();
bindingContent.efabCreate.setVisibility(View.VISIBLE);
bindingContent.searchbar.setHint(getText(R.string.home));
bindingContent.searchbar.setEnabled(false);
}
if (selectedFragment != null) {
getSupportFragmentManager().beginTransaction()
.replace(bindingContent.containerView.getId(), selectedFragment)
.commit();
return true;
}
return false;
});
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
binding.drawerLayout.closeDrawer(GravityCompat.START);
} else {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
});
adapterRomStoreSearch = new AdapterRomStoreSearch(this, dataRomStoreSearch);
binding.rvRomstoresearch.setAdapter(adapterRomStoreSearch);
binding.rvRomstoresearch.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
binding.searchview.getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void afterTextChanged(Editable s) {
search(s.toString());
}
@Override
public void onTextChanged(CharSequence newText, int start, int before, int count) {
}
});
new LibraryChecker(this).checkMissingLibraries(this);
setupDrawer();
DisplaySystem.startTermuxX11();
DialogUtils.joinTelegram(this);
NotificationUtils.clearAll(this);
if (MainSettingsManager.getPromptUpdateVersion(this))
updateApp();
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
homeCallToVmsListener.configurationChanged(getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE);
}
@Override
public void onDestroy() {
isActivate = false;
super.onDestroy();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.home_toolbar_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Menu items
int id = item.getItemId();
if (id == R.id.shutdown) {
VMManager.requestKillAllQemuProcess(this, null);
}
return super.onOptionsItemSelected(item);
}
public void onResume() {
super.onResume();
Log.d(TAG, "onResume");
Config.ui = MainSettingsManager.getVmUi(this);
Config.defaultVNCPort = Integer.parseInt(MainSettingsManager.getVncExternalDisplay(this));
if (!MainSettingsManager.getVncExternal(this))
NotificationUtils.clearAll(this);
Config.ui = MainSettingsManager.getVmUi(this);
DisplaySystem.reLaunchVNC(this);
PendingCommand.runNow(this);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent ReturnedIntent) {
super.onActivityResult(requestCode, resultCode, ReturnedIntent);
if (requestCode == 1004 && resultCode == RESULT_OK) {
Uri content_describer = ReturnedIntent.getData();
File selectedFilePath = new File(getPath(content_describer));
ProgressBar loading = findViewById(R.id.loading);
if (selectedFilePath.toString().endsWith(".iso")) {
loading.setVisibility(View.VISIBLE);
new Thread(() -> {
FileInputStream File;
try {
assert content_describer != null;
File = (FileInputStream) getContentResolver().openInputStream(content_describer);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
try {
try {
try (OutputStream out = new FileOutputStream(AppConfig.maindirpath + "/drive.iso")) {
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while (true) {
assert File != null;
if (!((len = File.read(buf)) > 0)) break;
out.write(buf, 0, len);
}
}
} finally {
Runnable runnable = () -> loading.setVisibility(View.GONE);
runOnUiThread(runnable);
assert File != null;
File.close();
}
} catch (IOException e) {
Runnable runnable = () -> {
loading.setVisibility(View.GONE);
UIAlert(this, e.toString(), "error");
};
runOnUiThread(runnable);
}
}).start();
} else
UIAlert(this, "please select iso file", "INVALID FILE");
} else if (requestCode == 1005 && resultCode == RESULT_OK) {
Uri content_describer = ReturnedIntent.getData();
ProgressBar loading = findViewById(R.id.loading);
loading.setVisibility(View.VISIBLE);
new Thread(() -> {
FileInputStream File;
try {
assert content_describer != null;
File = (FileInputStream) getContentResolver().openInputStream(content_describer);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
try {
try {
try (OutputStream out = new FileOutputStream(AppConfig.maindirpath + "/hdd1.qcow2")) {
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while (true) {
assert File != null;
if (!((len = File.read(buf)) > 0)) break;
out.write(buf, 0, len);
}
}
} finally {
Runnable runnable = () -> loading.setVisibility(View.GONE);
runOnUiThread(runnable);
assert File != null;
File.close();
}
} catch (IOException e) {
Runnable runnable = () -> {
loading.setVisibility(View.GONE);
UIAlert(this, e.toString(), "error");
};
runOnUiThread(runnable);
}
}).start();
} else if (requestCode == 1006 && resultCode == RESULT_OK) {
Uri content_describer = ReturnedIntent.getData();
ProgressBar loading = findViewById(R.id.loading);
loading.setVisibility(View.VISIBLE);
new Thread(() -> {
FileInputStream File;
try {
assert content_describer != null;
File = (FileInputStream) getContentResolver().openInputStream(content_describer);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
try {
try {
try (OutputStream out = new FileOutputStream(AppConfig.maindirpath + "/hdd2.qcow2")) {
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while (true) {
assert File != null;
if (!((len = File.read(buf)) > 0)) break;
out.write(buf, 0, len);
}
}
} finally {
Runnable runnable = () -> loading.setVisibility(View.GONE);
runOnUiThread(runnable);
assert File != null;
File.close();
}
} catch (IOException e) {
Runnable runnable = () -> {
loading.setVisibility(View.GONE);
UIAlert(this, e.toString(), "error");
};
runOnUiThread(runnable);
}
}).start();
} else if (requestCode == 122 && resultCode == RESULT_OK) {
Uri content_describer = ReturnedIntent.getData();
File selectedFilePath = new File(getPath(content_describer));
ProgressBar loading = findViewById(R.id.loading);
loading.setVisibility(View.VISIBLE);
new Thread(() -> {
FileInputStream File;
try {
assert content_describer != null;
File = (FileInputStream) getContentResolver().openInputStream(content_describer);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
try {
try {
File romDir = new File(AppConfig.maindirpath + curRomName + "/");
if (!romDir.exists()) {
if(!romDir.mkdirs()) return;
}
try (OutputStream out = new FileOutputStream(AppConfig.maindirpath + curRomName + "/" + "drv1-" + selectedFilePath.getName())) {
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while (true) {
assert File != null;
if (!((len = File.read(buf)) > 0)) break;
out.write(buf, 0, len);
}
}
} finally {
Runnable runnable = () -> loading.setVisibility(View.GONE);
runOnUiThread(runnable);
assert File != null;
File.close();
}
} catch (IOException e) {
Runnable runnable = () -> {
loading.setVisibility(View.GONE);
UIAlert(this, e.toString(), "error");
};
runOnUiThread(runnable);
}
}).start();
}
}
public String getPath(Uri uri) {
return FileUtils.getPath(this, uri);
}
private void setupDrawer() {
binding.drawerLayout.setScrimColor(Color.parseColor("#40000000")); //25%
//Setting Navigation View Item Selected Listener to handle the item click of the navigation menu
// This method will trigger on item Click of navigation menu
binding.navView.setNavigationItemSelectedListener(menuItem -> {
//Closing drawer on item click
binding.drawerLayout.closeDrawers();
//Check to see which item was being clicked and perform appropriate action
int id = menuItem.getItemId();
if (id == R.id.navigation_item_info) {
startActivity(new Intent(this, AboutActivity.class));
}
if (id == R.id.navigation_item_help) {
String tw = AppConfig.vectrasHelp;
Intent w = new Intent(ACTION_VIEW);
w.setData(Uri.parse(tw));
startActivity(w);
} else if (id == R.id.navigation_item_website) {
String tw = AppConfig.vectrasWebsite;
Intent w = new Intent(ACTION_VIEW);
w.setData(Uri.parse(tw));
startActivity(w);
} else if (id == R.id.navigation_item_import_iso) {
if (new File(AppConfig.maindirpath + "/drive.iso").exists()) {
AlertDialog ad;
ad = new AlertDialog.Builder(this, R.style.MainDialogTheme).create();
ad.setTitle("REPLACE ISO");
ad.setMessage("there is iso imported you want to replace it?");
ad.setButton(Dialog.BUTTON_POSITIVE, "REPLACE", (dialog, which) -> {
Intent intent = new Intent(ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
// Optionally, specify a URI for the file that should appear in the
// system file picker when it loads.
if (SDK_INT >= Build.VERSION_CODES.O) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.DIRECTORY_DOWNLOADS);
}
startActivityForResult(intent, 1004);
});
ad.setButton(Dialog.BUTTON_NEGATIVE, "REMOVE", (dialog, which) -> {
File isoFile = new File(AppConfig.maindirpath + "/drive.iso");
try {
if(!isoFile.delete()) Log.e(TAG, "Delete drive.iso failed!");
} catch (Exception e) {
Log.e(TAG, "Delete drive.iso: ", e);
}
});
ad.show();
} else {
Intent intent = new Intent(ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
// Optionally, specify a URI for the file that should appear in the
// system file picker when it loads.
if (SDK_INT >= Build.VERSION_CODES.O) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.DIRECTORY_DOWNLOADS);
}
startActivityForResult(intent, 1004);
}
} else if (id == R.id.navigation_item_hdd1) {
if (new File(AppConfig.maindirpath + "/hdd1.qcow2").exists()) {
AlertDialog ad;
ad = new AlertDialog.Builder(this, R.style.MainDialogTheme).create();
ad.setTitle("REPLACE HDD1");
ad.setMessage("there is hdd1 imported you want to replace it?");
ad.setButton(Dialog.BUTTON_POSITIVE, "REPLACE", (dialog, which) -> {
Intent intent = new Intent(ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
// Optionally, specify a URI for the file that should appear in the
// system file picker when it loads.
if (SDK_INT >= Build.VERSION_CODES.O) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.DIRECTORY_DOWNLOADS);
}
startActivityForResult(intent, 1006);
});
ad.setButton(Dialog.BUTTON_NEGATIVE, "REMOVE", (dialog, which) -> {
File isoFile = new File(AppConfig.maindirpath + "/hdd1.qcow2");
try {
if(!isoFile.delete()) Log.e(TAG, "Delete hdd1.qcow2 failed!");
} catch (Exception e) {
Log.e(TAG, "Delete hdd1.qcow2: ", e);
}
});
ad.setButton(Dialog.BUTTON_NEUTRAL, "SHARE", (dialog, which) -> {
Intent intentShareFile = new Intent(Intent.ACTION_SEND);
File fileWithinMyDir = new File(AppConfig.maindirpath + "/hdd1.qcow2");
if (fileWithinMyDir.exists()) {
intentShareFile.setType("*/*");
intentShareFile.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + AppConfig.maindirpath + "/hdd1.qcow2"));
intentShareFile.putExtra(Intent.EXTRA_SUBJECT,
"Sharing File...");
intentShareFile.putExtra(Intent.EXTRA_TEXT, "Sharing File...");
startActivity(Intent.createChooser(intentShareFile, "Share File"));
}
});
ad.show();
} else {
Intent intent = new Intent(ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
// Optionally, specify a URI for the file that should appear in the
// system file picker when it loads.
if (SDK_INT >= Build.VERSION_CODES.O) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.DIRECTORY_DOWNLOADS);
}
startActivityForResult(intent, 1005);
}
} else if (id == R.id.navigation_item_hdd2) {
if (new File(AppConfig.maindirpath + "/hdd2.qcow2").exists()) {
AlertDialog ad;
ad = new AlertDialog.Builder(this, R.style.MainDialogTheme).create();
ad.setTitle("REPLACE HDD2");
ad.setMessage("there is hdd2 imported you want to replace it?");
ad.setButton(Dialog.BUTTON_POSITIVE, "REPLACE", (dialog, which) -> {
Intent intent = new Intent(ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
// Optionally, specify a URI for the file that should appear in the
// system file picker when it loads.
if (SDK_INT >= Build.VERSION_CODES.O) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.DIRECTORY_DOWNLOADS);
}
startActivityForResult(intent, 1006);
});
ad.setButton(Dialog.BUTTON_NEGATIVE, "REMOVE", (dialog, which) -> {
File isoFile = new File(AppConfig.maindirpath + "/hdd2.qcow2");
try {
if(!isoFile.delete()) Log.e(TAG, "Delete hdd2.qcow2 failed!");
} catch (Exception e) {
Log.e(TAG, "Delete hdd2.qcow2: ", e);
}
});
ad.setButton(Dialog.BUTTON_NEUTRAL, "SHARE", (dialog, which) -> {
Intent intentShareFile = new Intent(Intent.ACTION_SEND);
File fileWithinMyDir = new File(AppConfig.maindirpath + "/hdd2.qcow2");
if (fileWithinMyDir.exists()) {
intentShareFile.setType("*/*");
intentShareFile.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + AppConfig.maindirpath + "/hdd2.qcow2"));
intentShareFile.putExtra(Intent.EXTRA_SUBJECT,
"Sharing File...");
intentShareFile.putExtra(Intent.EXTRA_TEXT, "Sharing File...");
startActivity(Intent.createChooser(intentShareFile, "Share File"));
}
});
ad.show();
} else {
Intent intent = new Intent(ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
// Optionally, specify a URI for the file that should appear in the
// system file picker when it loads.
if (SDK_INT >= Build.VERSION_CODES.O) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.DIRECTORY_DOWNLOADS);
}
startActivityForResult(intent, 1006);
}
} else if (id == R.id.navigation_item_desktop) {
DisplaySystem.launchX11(this, true);
} else if (id == R.id.navigation_item_terminal) {
/*com.vectras.vterm.TerminalBottomSheetDialog VTERM = new com.vectras.vterm.TerminalBottomSheetDialog(activity);
VTERM.showVterm();*/
startActivity(new Intent(this, TermuxActivity.class));
} else if (id == R.id.navigation_item_view_logs) {
BottomSheetDialog bottomSheetDialog = new BottomSheetDialog(this);
View view = getLayoutInflater().inflate(R.layout.bottomsheetdialog_logger, null);
bottomSheetDialog.setContentView(view);
bottomSheetDialog.show();
// final String CREDENTIAL_SHARED_PREF = "settings_prefs";
Timer _timer = new Timer();
TimerTask t;
LinearLayoutManager layoutManager = new LinearLayoutManager(getApp());
LogsAdapter mLogAdapter = new LogsAdapter(layoutManager, getApp());
RecyclerView logList = view.findViewById(R.id.recyclerLog);
logList.setAdapter(mLogAdapter);
logList.setLayoutManager(layoutManager);
mLogAdapter.scrollToLastPosition();
try {
Process process = Runtime.getRuntime().exec("logcat -e");
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
Process process2 = Runtime.getRuntime().exec("logcat -w");
BufferedReader bufferedReader2 = new BufferedReader(
new InputStreamReader(process2.getInputStream()));
t = new TimerTask() {
@Override
public void run() {
runOnUiThread(() -> {
try {
if (bufferedReader.readLine() != null || bufferedReader2.readLine() != null) {
String logLine = bufferedReader.readLine();
String logLine2 = bufferedReader2.readLine();
VectrasStatus.logError("<font color='red'>[E] " + logLine + "</font>");
VectrasStatus.logError("<font color='#FFC107'>[W] " + logLine2 + "</font>");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
};
_timer.scheduleAtFixedRate(t, 0, 100);
} catch (IOException e) {
Log.e(TAG, "Log: ", e);
}
} else if (id == R.id.navigation_item_settings) {
startActivity(new Intent(this, MainSettingsManager.class));
} else if (id == R.id.navigation_item_store) {
startActivity(new Intent(this, StoreActivity.class));
} else if (id == R.id.navigation_data_explorer) {
startActivity(new Intent(this, DataExplorerActivity.class));
} else if (id == R.id.navigation_item_donate) {
String tw = "https://www.patreon.com/VectrasTeam";
Intent w = new Intent(ACTION_VIEW);
w.setData(Uri.parse(tw));
startActivity(w);
} else if (id == R.id.navigation_item_get_rom) {
Intent intent = new Intent();
intent.setClass(getApplicationContext(), RomsManagerActivity.class);
startActivity(intent);
} else if (id == R.id.mini_tools) {
Intent intent = new Intent();
intent.setClass(this, Minitools.class);
startActivity(intent);
}
return false;
});
}
private void updateApp() {
int versionCode = PackageUtils.getThisVersionCode(getApplicationContext());
String versionName = PackageUtils.getThisVersionName(getApplicationContext());
RequestNetwork requestNetwork = new RequestNetwork(this);
RequestNetwork.RequestListener requestNetworkListener = new RequestNetwork.RequestListener() {
@Override
public void onResponse(String tag, String response, HashMap<String, Object> responseHeaders) {
if (!response.isEmpty()) {
try {
final JSONObject obj = new JSONObject(response);
String versionNameonUpdate;
int versionCodeonUpdate;
// String message;
// String size;
if (MainSettingsManager.getcheckforupdatesfromthebetachannel(HomeActivity.this)) {
versionNameonUpdate = obj.getString("versionNameBeta");
versionCodeonUpdate = obj.getInt("versionCodeBeta");
// message = obj.getString("MessageBeta");
// size = obj.getString("sizeBeta");
} else {
versionNameonUpdate = obj.getString("versionName");
versionCodeonUpdate = obj.getInt("versionCode");
// message = obj.getString("Message");
// size = obj.getString("size");
}
if ((versionCode < versionCodeonUpdate ||
!versionNameonUpdate.equals(versionName)) &&
!MainSettingsManager.getSkipVersion(HomeActivity.this).equals(versionNameonUpdate)) {
BottomSheetDialog bottomSheetDialog = new BottomSheetDialog(HomeActivity.this);
View v = getLayoutInflater().inflate(R.layout.update_bottom_dialog_layout, null);
bottomSheetDialog.setContentView(v);
// TextView tvContent = v.findViewById(R.id.tv_content);
MaterialButton skipButton = v.findViewById(R.id.bn_skip);
MaterialButton laterButton = v.findViewById(R.id.bn_later);
MaterialButton updateButton = v.findViewById(R.id.bn_update);
// tvContent.setMovementMethod(LinkMovementMethod.getInstance());
// tvContent.setText(Html.fromHtml(message + "<br><br>Update size:<br>" + size));
skipButton.setOnClickListener(view -> {
MainSettingsManager.setSkipVersion(HomeActivity.this, versionNameonUpdate);
bottomSheetDialog.dismiss();
});
laterButton.setOnClickListener(view -> bottomSheetDialog.dismiss());
updateButton.setOnClickListener(view -> {
startActivity(new Intent(HomeActivity.this, UpdaterActivity.class));
bottomSheetDialog.dismiss();
});
bottomSheetDialog.show();
}
} catch (JSONException e) {
Log.e(TAG, "updateApp: ", e);
}
}
}
@Override
public void onErrorResponse(String tag, String message) {
}
};
requestNetwork.startRequestNetwork(RequestNetworkController.GET,AppConfig.updateJson,"maincheckupdate",requestNetworkListener);
}
@SuppressLint("NotifyDataSetChanged")
private void search(String keyword) {
try {
// Extract data from json and store into ArrayList as class objects
Gson gson = new Gson();
List<DataRoms> filteredData = new ArrayList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
filteredData = SharedData.dataRomStore.stream()
.filter(rom -> {
String romName = (rom.romName != null) ? rom.romName : "";
String romKernel = (rom.romKernel != null) ? rom.romKernel : "";
return romName.toLowerCase().contains(keyword.toLowerCase())
|| romKernel.toLowerCase().contains(keyword.toLowerCase());
})
.collect(Collectors.toList());
} else {
for (DataRoms rom : SharedData.dataRomStore) {
if (rom.romName.toLowerCase().contains(keyword.toLowerCase()) ||
rom.romKernel.toLowerCase().contains(keyword.toLowerCase())) {
filteredData.add(rom);
}
}
}
dataRomStoreSearch.clear();
dataRomStoreSearch.addAll(filteredData);
} catch (Exception e) {
Log.e("RomManagerActivity", "Json parsing error: " + e.getMessage());
}
if (dataRomStoreSearch.isEmpty())
binding.rvRomstoresearch.setVisibility(View.GONE);
else
binding.rvRomstoresearch.setVisibility(View.VISIBLE);
adapterRomStoreSearch.notifyDataSetChanged();
}
}

View file

@ -0,0 +1,9 @@
package com.vectras.vm.home.core;
public class CallbackInterface {
//Fix Cyclic.
public interface HomeCallToVmsListener {
void refeshVMList();
void configurationChanged(boolean isLandscape);
}
}

View file

@ -0,0 +1,105 @@
package com.vectras.vm.home.core;
import static android.os.Build.VERSION.SDK_INT;
import static com.vectras.vm.utils.LibraryChecker.isPackageInstalled2;
import android.app.Activity;
import android.content.Intent;
import android.os.Build;
import com.termux.app.TermuxService;
import com.vectras.qemu.Config;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.qemu.MainVNCActivity;
import com.vectras.vm.R;
import com.vectras.vm.core.ShellExecutor;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.x11.X11Activity;
import com.vectras.vterm.Terminal;
import java.util.HashSet;
import java.util.Set;
public class DisplaySystem {
public static void launch(Activity activity) {
if (MainSettingsManager.getVmUi(activity).equals("VNC")) {
activity.startActivity(new Intent(activity, MainVNCActivity.class));
} else if (MainSettingsManager.getVmUi(activity).equals("X11")) {
DisplaySystem.launchX11(activity, false);
}
}
public static void reLaunchVNC(Activity activity) {
if (MainSettingsManager.getVmUi(activity).equals("VNC") &&
FileUtils.isFileExists(Config.getLocalQMPSocketPath()) &&
!activity.isFinishing() &&
MainVNCActivity.started)
activity.startActivity(new Intent(activity, MainVNCActivity.class));
}
public static void launchX11(Activity activity, boolean isKillXFCE) {
if (SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
DialogUtils.oneDialog(
activity,
activity.getString(R.string.x11_feature_not_supported),
activity.getString(R.string.the_x11_feature_is_currently_not_supported_on_android_14_and_above_please_use_a_device_with_android_13_or_below_for_x11_functionality),
activity.getString(R.string.ok),
true, R.drawable.error_96px,
true,
null,
null
);
} else {
// XFCE4 meta-package
String xfce4Package = "xfce4";
// Check if XFCE4 is installed
isPackageInstalled2(activity, xfce4Package, (output, errors) -> {
boolean isInstalled = false;
// Check if the package exists in the installed packages output
if (output != null) {
Set<String> installedPackages = new HashSet<>();
for (String installedPackage : output.split("\n")) {
installedPackages.add(installedPackage.trim());
}
isInstalled = installedPackages.contains(xfce4Package.trim());
}
// If not installed, show a dialog to install it
if (!isInstalled) {
DialogUtils.twoDialog(
activity,
"Install XFCE4",
"XFCE4 is not installed. Would you like to install it?",
activity.getString(R.string.install),
activity.getString(R.string.cancel),
true,
R.drawable.desktop_24px,
true,
() -> {
String installCommand = "apk add " + xfce4Package;
new Terminal(activity).executeShellCommand(installCommand, true, true, activity);
},
null,
null
);
} else {
if (isKillXFCE)
new Terminal(activity).executeShellCommand2("killall xfce4-session", false, activity);
activity.startActivity(new Intent(activity, X11Activity.class));
new Terminal(activity).executeShellCommand2("xfce4-session", false, activity);
}
});
}
}
public static void startTermuxX11() {
if (Build.VERSION.SDK_INT < 34) {
ShellExecutor shellExec = new ShellExecutor();
shellExec.exec(TermuxService.PREFIX_PATH + "/bin/termux-x11 :0");
}
}
}

View file

@ -0,0 +1,184 @@
package com.vectras.vm.home.core;
import static android.os.Build.VERSION.SDK_INT;
import android.app.Activity;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.vectras.qemu.Config;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.qemu.MainVNCActivity;
import com.vectras.vm.AppConfig;
import com.vectras.vm.MainService;
import com.vectras.vm.R;
import com.vectras.vm.VMManager;
import com.vectras.vm.logger.VectrasStatus;
import com.vectras.vm.settings.VNCActivity;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.NetworkUtils;
import com.vectras.vm.utils.ServiceUtils;
import java.io.File;
public class HomeStartVM {
public static AlertDialog progressDialog;
public static boolean skipIDEwithARM64DialogInStartVM = false;
public static void startNow(
Activity activity,
String vmName,
String env,
String itemExtra,
String itemPath,
AppBarLayout appbar,
LinearLayout extVncLayout
) {
File romDir = new File(Config.getCacheDir() + "/" + Config.vmID);
if (!romDir.exists()) {
if (!romDir.mkdirs()) {
DialogUtils.oneDialog(activity, activity.getString(R.string.problem_has_been_detected), activity.getString(R.string.vm_cache_dir_failed_to_create_content), activity.getString(R.string.ok), true, R.drawable.warning_48px, true, null, null);
return;
}
}
if (!VMManager.isthiscommandsafe(env, activity.getApplicationContext())) {
DialogUtils.oneDialog(activity, activity.getString(R.string.problem_has_been_detected), activity.getString(R.string.harmful_command_was_detected) + " " + activity.getResources().getString(R.string.reason) + ": " + VMManager.latestUnsafeCommandReason, activity.getString(R.string.ok), true, R.drawable.verified_user_24px, true, null, null);
return;
}
VMManager.lastQemuCommand = env;
if (VMManager.isThisVMRunning(activity, itemExtra, itemPath)) {
Toast.makeText(activity, "This VM is already running.", Toast.LENGTH_LONG).show();
if (MainSettingsManager.getVmUi(activity).equals("VNC"))
activity.startActivity(new Intent(activity, MainVNCActivity.class));
else if (MainSettingsManager.getVmUi(activity).equals("X11"))
DisplaySystem.launchX11(activity, false);
return;
}
if (AppConfig.getSetupFiles().contains("arm") && !AppConfig.getSetupFiles().contains("arm64")) {
if (env.contains("tcg,thread=multi")) {
DialogUtils.twoDialog(activity, activity.getResources().getString(R.string.problem_has_been_detected), activity.getResources().getString(R.string.can_not_use_mttcg), activity.getString(R.string.ok), activity.getString(R.string.cancel), true, R.drawable.warning_48px, true,
() -> startNow(activity, vmName, env.replace("tcg,thread=multi", "tcg,thread=single"), itemExtra, itemPath, appbar, extVncLayout), null, null);
return;
}
}
if (MainSettingsManager.getArch(activity).equals("ARM64") && MainSettingsManager.getIfType(activity).equals("ide") && skipIDEwithARM64DialogInStartVM) {
DialogUtils.twoDialog(activity, activity.getString(R.string.problem_has_been_detected), activity.getString(R.string.you_cannot_use_IDE_hard_drive_type_with_ARM64), activity.getString(R.string.continuetext), activity.getString(R.string.cancel), true, R.drawable.warning_48px, true,
() -> {
skipIDEwithARM64DialogInStartVM = true;
startNow(activity, vmName, env, itemExtra, itemPath, appbar, extVncLayout);
}, null, null);
return;
} else if (skipIDEwithARM64DialogInStartVM) {
skipIDEwithARM64DialogInStartVM = false;
}
if (MainSettingsManager.getSharedFolder(activity) && MainSettingsManager.getArch(activity).equals("I386")) {
Toast.makeText(activity, R.string.shared_folder_is_not_used_because_i386_does_not_support_it, Toast.LENGTH_LONG).show();
}
if (MainSettingsManager.getVncExternal(activity) &&
NetworkUtils.isPortOpen("localhost", Config.defaultVNCPort + Config.defaultVNCPort, 500)) {
DialogUtils.twoDialog(activity, activity.getString(R.string.problem_has_been_detected),
activity.getString(R.string.the_vnc_server_port_you_set_is_currently_in_use_by_other),
activity.getString(R.string.go_to_settings),
activity.getString(R.string.close),
true, R.drawable.warning_48px, true,
() -> activity.startActivity(new Intent(activity, VNCActivity.class)),
null,
null);
return;
}
showProgressDialog(activity, activity.getString(R.string.booting_up));
Handler handler = new Handler();
handler.postDelayed(
() -> {
if (ServiceUtils.isServiceRunning(activity, MainService.class)) {
MainService.startCommand(env, activity);
} else {
Intent serviceIntent = new Intent(activity, MainService.class);
MainService.env = env;
MainService.CHANNEL_ID = vmName;
MainService.activity = activity;
if (SDK_INT >= Build.VERSION_CODES.O) {
activity.startForegroundService(serviceIntent);
} else {
activity.startService(serviceIntent);
}
}
if (MainSettingsManager.getVmUi(activity).equals("VNC")) {
if (MainSettingsManager.getVncExternal(activity)) {
if (extVncLayout != null) extVncLayout.setVisibility(View.VISIBLE);
if (appbar != null) appbar.setExpanded(true);
progressDialog.dismiss();
} else {
Handler handler1 = new Handler();
handler1.postDelayed(
() -> {
MainVNCActivity.started = true;
activity.startActivity(
new Intent(
activity, MainVNCActivity.class));
progressDialog.dismiss();
},
2000);
}
// } else if (MainSettingsManager.getVmUi(activity).equals("SPICE")) {
// //This feature is not available yet.
} else if (MainSettingsManager.getVmUi(activity).equals("X11")) {
Handler handler1 = new Handler();
handler1.postDelayed(
() -> {
progressDialog.dismiss();
DisplaySystem.launchX11(activity, false);
},
3000);
}
if (MainSettingsManager.getVncExternal(activity)) {
Config.currentVNCServervmID = Config.vmID;
DialogUtils.oneDialog(activity, activity.getString(R.string.vnc_server), activity.getString(R.string.running_vm_with_vnc_server_content) + " " + (Integer.parseInt(MainSettingsManager.getVncExternalDisplay(activity)) + 5900) + ".", activity.getString(R.string.ok), true, R.drawable.cast_24px, true, null, null);
}
},
2000);
String[] params = env.split("\\s+");
VectrasStatus.logInfo("Params:");
Log.d("HomeStartVM", "Params:");
for (int i = 0; i < params.length; i++) {
VectrasStatus.logInfo(i + ": " + params[i]);
Log.d("HomeStartVM", i + ": " + params[i]);
}
}
public static void showProgressDialog(Activity activity, String _content) {
View progressView = LayoutInflater.from(activity).inflate(R.layout.dialog_progress_style, null);
TextView progress_text = progressView.findViewById(R.id.progress_text);
progress_text.setText(_content);
progressDialog = new MaterialAlertDialogBuilder(activity, R.style.CenteredDialogTheme)
.setView(progressView)
.setCancelable(false)
.create();
progressDialog.show();
}
}

View file

@ -0,0 +1,57 @@
package com.vectras.vm.home.core;
import android.app.Activity;
import android.widget.Toast;
import com.vectras.vm.AppConfig;
import com.vectras.vm.R;
import com.vectras.vm.StartVM;
import com.vectras.vm.VMManager;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vterm.Terminal;
public class PendingCommand {
public static void runNow(Activity activity) {
if (!AppConfig.pendingCommand.isEmpty()) {
if (!VMManager.isthiscommandsafe(AppConfig.pendingCommand, activity)) {
AppConfig.pendingCommand = "";
DialogUtils.oneDialog(
activity,
activity.getString(R.string.problem_has_been_detected),
activity.getString(R.string.harmful_command_was_detected) + " " + activity.getResources().getString(R.string.reason) + ": " + VMManager.latestUnsafeCommandReason,
activity.getString(R.string.ok),
true,
R.drawable.verified_user_24px,
true,
null,
null
);
} else {
if (AppConfig.pendingCommand.startsWith("qemu-img")) {
if (!VMManager.isthiscommandsafeimg(AppConfig.pendingCommand, activity)) {
DialogUtils.oneDialog(activity,
activity.getString(R.string.problem_has_been_detected),
activity.getString(R.string.size_too_large_try_qcow2_format),
activity.getString(R.string.ok),
true,
R.drawable.warning_48px,
true,
null,
null
);
} else {
Terminal _vterm = new Terminal(activity);
_vterm.executeShellCommand2(AppConfig.pendingCommand, false, activity);
Toast.makeText(activity, activity.getResources().getString(R.string.done), Toast.LENGTH_LONG).show();
}
} else {
com.vectras.vm.StartVM.cdrompath = "";
String env = StartVM.env(activity, AppConfig.pendingCommand, "", "1");
HomeStartVM.startNow(activity, "Quick run", env, AppConfig.pendingCommand, "", null, null);
VMManager.lastQemuCommand = AppConfig.pendingCommand;
}
}
AppConfig.pendingCommand = "";
}
}
}

View file

@ -0,0 +1,47 @@
package com.vectras.vm.home.core;
import android.app.Activity;
import android.content.Intent;
import android.view.View;
import android.widget.Button;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.vectras.vm.CustomRomActivity;
import com.vectras.vm.ExportRomActivity;
import com.vectras.vm.MainRoms.DataMainRoms;
import com.vectras.vm.R;
import com.vectras.vm.VMManager;
import java.util.List;
public class RomOptionsDialog {
public static void showNow(Activity activity, List<DataMainRoms> data, int position, String vmID, String vmName, String arch) {
BottomSheetDialog bottomSheetDialog = new BottomSheetDialog(activity);
View v = activity.getLayoutInflater().inflate(R.layout.rom_options_dialog, null);
bottomSheetDialog.setContentView(v);
Button modifyRomBtn = v.findViewById(R.id.modifyRomBtn);
modifyRomBtn.setOnClickListener(v3 -> {
CustomRomActivity.current = data.get(position);
VMManager.setArch(arch, activity);
activity.startActivity(new Intent(activity, CustomRomActivity.class).putExtra("POS", position).putExtra("MODIFY", true).putExtra("VMID", vmID));
bottomSheetDialog.cancel();
});
Button exportRomBtn = v.findViewById(R.id.exportRomBtn);
exportRomBtn.setOnClickListener(v2 -> {
ExportRomActivity.pendingPosition = position;
Intent intent = new Intent();
intent.setClass(activity, ExportRomActivity.class);
activity.startActivity(intent);
bottomSheetDialog.cancel();
});
Button removeRomBtn = v.findViewById(R.id.removeRomBtn);
removeRomBtn.setOnClickListener(v1 -> {
VMManager.deleteVMDialog(vmName, position, activity);
bottomSheetDialog.cancel();
});
bottomSheetDialog.show();
}
}

View file

@ -0,0 +1,10 @@
package com.vectras.vm.home.core;
import com.vectras.vm.Roms.DataRoms;
import java.util.ArrayList;
import java.util.List;
public class SharedData {
public static List<DataRoms> dataRomStore = new ArrayList<>();
}

View file

@ -0,0 +1,242 @@
package com.vectras.vm.home.monitor;
import static android.content.Context.ACTIVITY_SERVICE;
import static android.os.Build.VERSION.SDK_INT;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.StatFs;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.android.material.transition.MaterialFadeThrough;
import com.vectras.qemu.Config;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.vm.R;
import com.vectras.vm.VMManager;
import com.vectras.vm.databinding.FragmentHomeSystemMonitorBinding;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vterm.Terminal;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class SystemMonitorFragment extends Fragment {
final String TAG = "SystemMonitorFragment";
FragmentHomeSystemMonitorBinding binding;
ExecutorService executor = Executors.newSingleThreadExecutor();
boolean isStopUpdateMonitor = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setEnterTransition(new MaterialFadeThrough());
setReturnTransition(new MaterialFadeThrough());
setExitTransition(new MaterialFadeThrough());
setReenterTransition(new MaterialFadeThrough());
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
binding = FragmentHomeSystemMonitorBinding.inflate(getLayoutInflater());
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
startMonitor();
getQemuInfo();
initialize();
}
@Override
public void onResume() {
super.onResume();
if(isStopUpdateMonitor) {
startMonitor();
getQemuInfo();
}
}
@Override
public void onPause() {
super.onPause();
stopMonitor();
}
private void initialize() {
binding.btStopqemu.setOnClickListener(v -> VMManager.requestKillAllQemuProcess(requireActivity(), () -> {
ScheduledExecutorService executorUpdate = Executors.newSingleThreadScheduledExecutor();
executorUpdate.schedule(() -> {
if (getContext() != null) {
requireActivity().runOnUiThread(this::getQemuInfo);
}
}, 500, TimeUnit.MILLISECONDS);
}));
binding.btStopvmvnc.setOnClickListener(v -> {
if (Config.currentVNCServervmID.isEmpty()) {
binding.btStopvmvnc.setVisibility(View.GONE);
} else {
DialogUtils.threeDialog(
requireActivity(),
getString(R.string.shutdown),
getString(R.string.shutdown_or_reset_content),
getString(R.string.shutdown),
getString(R.string.cancel),
getString(R.string.reset),
true,
R.drawable.power_settings_new_24px,
true,
() -> {
Config.vmID = Config.currentVNCServervmID;
VMManager.shutdownCurrentVM();
Config.currentVNCServervmID = "";
binding.btStopvmvnc.setVisibility(View.GONE);
getQemuInfo();
},
null,
() -> {
Config.vmID = Config.currentVNCServervmID;
VMManager.resetCurrentVM();
},
null);
}
});
}
private final Handler handler = new Handler(Looper.getMainLooper());
private final Runnable monitorTask = new Runnable() {
@Override
public void run() {
if (isStopUpdateMonitor) return;
updateSystemMonitor();
handler.postDelayed(this, 1000);
}
};
private void startMonitor() {
isStopUpdateMonitor = false;
handler.post(monitorTask);
}
private void stopMonitor() {
isStopUpdateMonitor = true;
handler.removeCallbacks(monitorTask);
}
@SuppressLint("SetTextI18n")
private void updateSystemMonitor() {
ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
ActivityManager activityManager = (ActivityManager) requireActivity().getSystemService(ACTIVITY_SERVICE);
activityManager.getMemoryInfo(mi);
double totalMemory = mi.totalMem * Math.pow(10, -9);
double freeMemory = mi.availMem * Math.pow(10, -9);
double usedMemory = totalMemory - freeMemory;
double percentageOfRam = (100 / totalMemory) * usedMemory;
binding.tvTotalram.setText(getString(R.string.total_memory) + " " + String.format(Locale.US, "%.1f", totalMemory) + " GB");
binding.tvUsedram.setText(getString(R.string.used_memory) + " " + String.format(Locale.US, "%.1f", usedMemory) + " GB");
binding.tvPercentageofram.setText((int) percentageOfRam + "%");
if (SDK_INT >= Build.VERSION_CODES.N) {
binding.cpiMemory.setProgress((int) percentageOfRam, true);
} else {
binding.cpiMemory.setProgress((int) percentageOfRam);
}
StatFs externalStatFs = new StatFs( Environment.getExternalStorageDirectory().getAbsolutePath() );
double totalStorage = (externalStatFs.getBlockCountLong() * externalStatFs.getBlockSizeLong()) * Math.pow(10, -9);
double freeStorage = (externalStatFs.getAvailableBlocksLong() * externalStatFs.getBlockSizeLong()) * Math.pow(10, -9);
double usedStorage = totalStorage - freeStorage;
double percentageOfStorage = (100 / totalStorage) * usedStorage;
binding.tvTotalstorage.setText(getString(R.string.total_memory) + " " + String.format(Locale.US, "%.1f", totalStorage) + " GB");
binding.tvUsedstorage.setText(getString(R.string.used_memory) + " " + String.format(Locale.US, "%.1f", usedStorage) + " GB");
binding.tvPercentageofstorage.setText((int) percentageOfStorage + "%");
if (SDK_INT >= Build.VERSION_CODES.N) {
binding.cpiStorage.setProgress((int) percentageOfStorage, true);
} else {
binding.cpiStorage.setProgress((int) percentageOfStorage);
}
}
@SuppressLint("SetTextI18n")
private void getQemuInfo() {
String currentArch = MainSettingsManager.getArch(requireActivity());
String command = "qemu-system-x86_64 --version";
new Terminal(requireActivity()).extractQemuVersion(command, false, requireActivity(), (output, errors) -> {
if (errors.isEmpty()) {
binding.tvQemuversion.setText(getString(R.string.version) + " " + (output.equals("8.2.1") ? output + " - 3dfx" : getString(R.string.unknow)) + ".");
} else {
Log.e(TAG, "Errors: " + errors);
}
});
binding.tvQemuarch.setText(getString(R.string.arch) + " " + currentArch + ".");
executor.execute(() -> {
String result = Terminal.executeShellCommandWithResult("ps -e", requireActivity());
requireActivity().runOnUiThread(() -> {
if (!result.isEmpty()) {
switch (currentArch) {
case "X86_64" ->
binding.tvQemustatus.setText(getString(R.string.status_qemu) + " " + (result.contains("qemu-system-x86_64 -qmp") ? getString(R.string.running) : getString(R.string.stopped)) + ".");
case "I386" ->
binding.tvQemustatus.setText(getString(R.string.status_qemu) + " " + (result.contains("qemu-system-i386 -qmp") ? getString(R.string.running) : getString(R.string.stopped)) + ".");
case "ARM64" ->
binding.tvQemustatus.setText(getString(R.string.status_qemu) + " " + (result.contains("qemu-system-aarch64 -qmp") ? getString(R.string.running) : getString(R.string.stopped)) + ".");
case "PPC" ->
binding.tvQemustatus.setText(getString(R.string.status_qemu) + " " + (result.contains("qemu-system-ppc -qmp") ? getString(R.string.running) : getString(R.string.stopped)) + ".");
default -> binding.tvQemustatus.setText(getString(R.string.status_qemu) + " " + getString(R.string.stopped) + ".");
}
if (result.contains("qemu-system") && result.contains("-qmp")) {
binding.btStopqemu.setVisibility(View.VISIBLE);
} else {
binding.btStopqemu.setVisibility(View.GONE);
}
} else {
binding.tvQemustatus.setText(getString(R.string.status_qemu) + " " + getString(R.string.stopped) + ".");
binding.btStopqemu.setVisibility(View.GONE);
Log.i(TAG, "Errors: " + result);
}
getVNCServerStatus(result);
});
});
}
@SuppressLint("SetTextI18n")
private void getVNCServerStatus(String resultCommand) {
binding.tvVncport.setText(getString(R.string.port_qemu) + " " + (Integer.parseInt(MainSettingsManager.getVncExternalDisplay(requireActivity())) + 5900) + ".");
if (resultCommand.contains(Config.defaultVNCHost + ":" + Config.defaultVNCPort)) {
binding.tvVncstatus.setText(getString(R.string.status_qemu) + " " + getString(R.string.running) + ".");
binding.btStopvmvnc.setVisibility(View.VISIBLE);
} else {
binding.tvVncstatus.setText(getString(R.string.status_qemu) + " " + getString(R.string.stopped) + ".");
binding.btStopvmvnc.setVisibility(View.GONE);
}
}
}

View file

@ -0,0 +1,23 @@
package com.vectras.vm.home.romstore;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.vectras.vm.Roms.DataRoms;
import java.util.ArrayList;
import java.util.List;
public class HomeRomStoreViewModel extends ViewModel {
private final MutableLiveData<List<DataRoms>> romsList = new MutableLiveData<>(new ArrayList<>());
public LiveData<List<DataRoms>> getRomsList() {
return romsList;
}
public void setRomsList(List<DataRoms> data) {
romsList.setValue(data);
}
}

View file

@ -0,0 +1,131 @@
package com.vectras.vm.home.romstore;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.android.material.transition.MaterialFadeThrough;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.vectras.vm.AppConfig;
import com.vectras.vm.RequestNetwork;
import com.vectras.vm.RequestNetworkController;
import com.vectras.vm.Roms.AdapterRoms;
import com.vectras.vm.Roms.DataRoms;
import com.vectras.vm.databinding.FragmentHomeRomStoreBinding;
import com.vectras.vm.home.HomeActivity;
import com.vectras.vm.home.core.SharedData;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class RomStoreFragment extends Fragment {
FragmentHomeRomStoreBinding binding;
private RequestNetwork net;
private RequestNetwork.RequestListener _net_request_listener;
private String contentJSON = "[]";
HomeRomStoreViewModel homeRomStoreViewModel;
RomStoreHomeAdpater mAdapter;
List<DataRoms> data = new ArrayList<>();
public static RomStoreCallToHomeListener romStoreCallToHomeListener;
public interface RomStoreCallToHomeListener {
void updateDataStatus(boolean isReady);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setEnterTransition(new MaterialFadeThrough());
setReturnTransition(new MaterialFadeThrough());
setExitTransition(new MaterialFadeThrough());
setReenterTransition(new MaterialFadeThrough());
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
binding = FragmentHomeRomStoreBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mAdapter = new RomStoreHomeAdpater(getContext(), data);
binding.rvRomlist.setAdapter(mAdapter);
binding.rvRomlist.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false));
homeRomStoreViewModel = new ViewModelProvider(requireActivity()).get(HomeRomStoreViewModel.class);
homeRomStoreViewModel.getRomsList().observe(getViewLifecycleOwner(), roms -> {
if (roms == null || roms.isEmpty()) {
loadFromServer();
} else {
binding.linearload.setVisibility(View.GONE);
data.clear();
data.addAll(roms);
mAdapter.notifyDataSetChanged();
}
});
}
private void loadFromServer() {
romStoreCallToHomeListener.updateDataStatus(false);
net = new RequestNetwork(requireActivity());
_net_request_listener = new RequestNetwork.RequestListener() {
@Override
public void onResponse(String tag, String response, HashMap<String, Object> responseHeaders) {
if (!response.isEmpty())
contentJSON = response;
loadData();
binding.linearload.setVisibility(View.GONE);
}
@Override
public void onErrorResponse(String tag, String message) {
binding.linearload.setVisibility(View.GONE);
binding.linearnothinghere.setVisibility(View.VISIBLE);
}
};
binding.buttontryagain.setOnClickListener(v -> {
binding.linearload.setVisibility(View.VISIBLE);
net.startRequestNetwork(RequestNetworkController.GET,AppConfig.vectrasRaw + "vroms-store.json","",_net_request_listener);
});
net.startRequestNetwork(RequestNetworkController.GET, AppConfig.vectrasRaw + "vroms-store.json","",_net_request_listener);
}
private void loadData() {
List<DataRoms> dataRoms = new ArrayList<>();
try {
Gson gson = new Gson();
Type listType = new TypeToken<List<DataRoms>>() {}.getType();
dataRoms = gson.fromJson(contentJSON, listType);
} catch (Exception e) {
binding.linearload.setVisibility(View.GONE);
binding.linearnothinghere.setVisibility(View.VISIBLE);
}
homeRomStoreViewModel.setRomsList(dataRoms);
data.clear();
data.addAll(dataRoms);
mAdapter.notifyDataSetChanged();
SharedData.dataRomStore.addAll(dataRoms);
romStoreCallToHomeListener.updateDataStatus(true);
}
}

View file

@ -0,0 +1,116 @@
package com.vectras.vm.home.romstore;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.vectras.vm.R;
import com.vectras.vm.RomInfo;
import com.vectras.vm.Roms.DataRoms;
import java.util.Collections;
import java.util.List;
public class RomStoreHomeAdpater extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
Context context;
private final LayoutInflater inflater;
static List<DataRoms> dataRom = Collections.emptyList();
public RomStoreHomeAdpater(Context context, List<DataRoms> data) {
this.context = context;
inflater = LayoutInflater.from(context);
dataRom = data;
}
// Inflate the layout when viewholder created
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.container_roms, parent, false);
return new MyHolder(view);
}
// Bind data
@SuppressLint("SetTextI18n")
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position) {
// Get current position of item in recyclerview to bind data and assign values from list
final MyHolder myHolder = (MyHolder) holder;
final DataRoms current = dataRom.get(position);
Glide.with(context).load(current.romIcon).placeholder(R.drawable.ic_computer_180dp_with_padding).error(R.drawable.ic_computer_180dp_with_padding).into(myHolder.ivIcon);
myHolder.textName.setText(current.romName);
myHolder.textSize.setText(current.romArch + " - " + current.fileSize);
if (current.romAvail) {
myHolder.linearItem.setOnClickListener(v -> {
notifyItemRangeChanged(0, dataRom.size());
Intent intent = new Intent();
intent.setClass(context, RomInfo.class);
intent.putExtra("title", current.romName);
intent.putExtra("shortdesc", current.romSize);
intent.putExtra("getrom", current.romUrl);
intent.putExtra("desc", current.desc);
intent.putExtra("icon", current.romIcon);
intent.putExtra("filename", current.romPath);
intent.putExtra("finalromfilename", current.finalromfilename);
intent.putExtra("extra", current.romExtra);
intent.putExtra("arch", current.romArch);
intent.putExtra("verified", current.verified);
intent.putExtra("creator", current.creator);
intent.putExtra("size", current.fileSize);
context.startActivity(intent);
});
} else {
myHolder.textAvail.setText(context.getString(R.string.unavailable));
myHolder.textAvail.setTextColor(Color.RED);
}
if (dataRom.size() == 1) {
myHolder.linearItem.setBackground(AppCompatResources.getDrawable(context, R.drawable.object_shape_single));
} else if (position == 0) {
myHolder.linearItem.setBackground(AppCompatResources.getDrawable(context, R.drawable.object_shape_top));
} else if (position == dataRom.size() - 1) {
myHolder.linearItem.setBackground(AppCompatResources.getDrawable(context, R.drawable.object_shape_bottom));
} else {
myHolder.linearItem.setBackground(AppCompatResources.getDrawable(context, R.drawable.object_shape_middle));
}
}
// return total item from List
@Override
public int getItemCount() {
return dataRom == null ? 0 : dataRom.size();
}
static class MyHolder extends RecyclerView.ViewHolder {
TextView textName, textAvail, textSize;
ImageView ivIcon;
LinearLayout linearItem;
// create constructor to get widget reference
public MyHolder(View itemView) {
super(itemView);
textName = itemView.findViewById(R.id.textName);
ivIcon = itemView.findViewById(R.id.ivIcon);
textSize = itemView.findViewById(R.id.textSize);
textAvail = itemView.findViewById(R.id.textAvail);
linearItem = itemView.findViewById(R.id.linearItem);
}
}
}

View file

@ -0,0 +1,42 @@
package com.vectras.vm.home.vms;
import androidx.recyclerview.widget.DiffUtil;
import com.vectras.vm.MainRoms.DataMainRoms;
import java.util.List;
public class VmsDiffUtil extends DiffUtil.Callback {
private final List<DataMainRoms> oldList;
private final List<DataMainRoms> newList;
public VmsDiffUtil(List<DataMainRoms> oldList, List<DataMainRoms> newList) {
this.oldList = oldList;
this.newList = newList;
}
@Override
public int getOldListSize() {
return oldList.size();
}
@Override
public int getNewListSize() {
return newList.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
// So sánh bằng khóa duy nhất (vd: vmID)
return oldList.get(oldItemPosition).vmID.equals(newList.get(newItemPosition).vmID);
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
// So sánh nội dung, nếu khác thì update item đó
DataMainRoms oldItem = oldList.get(oldItemPosition);
DataMainRoms newItem = newList.get(newItemPosition);
return oldItem.equals(newItem); // Nếu bạn override equals trong DataMainRoms thì dùng cách này
}
}

View file

@ -0,0 +1,199 @@
package com.vectras.vm.home.vms;
import android.content.res.Configuration;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.android.material.transition.MaterialFadeThrough;
import com.vectras.vm.AppConfig;
import com.vectras.vm.MainRoms.DataMainRoms;
import com.vectras.vm.R;
import com.vectras.vm.VMManager;
import com.vectras.vm.databinding.FragmentHomeVmsBinding;
import com.vectras.vm.home.HomeActivity;
import com.vectras.vm.home.core.CallbackInterface;
import com.vectras.vm.utils.DeviceUtils;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.utils.PermissionUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class VmsFragment extends Fragment implements CallbackInterface.HomeCallToVmsListener {
private final String TAG = "VmsFragment";
FragmentHomeVmsBinding binding;
private JSONArray jArray;
private final List<DataMainRoms> data = new ArrayList<>();
private VmsHomeAdapter vmsHomeAdapter;
private int spanCount = 0;
ExecutorService executor = Executors.newSingleThreadExecutor();
public static VmsCallToHomeListener vmsCallToHomeListener;
public interface VmsCallToHomeListener {
void openRomStore();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setEnterTransition(new MaterialFadeThrough());
setReturnTransition(new MaterialFadeThrough());
setExitTransition(new MaterialFadeThrough());
setReenterTransition(new MaterialFadeThrough());
}
@Override
public void onResume() {
super.onResume();
checkAndLoad();
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
binding = FragmentHomeVmsBinding.inflate(inflater, container, false);
spanCount = requireActivity().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? 3 : 2;
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
HomeActivity.homeCallToVmsListener = this;
binding.rvRomlist.setLayoutManager(new GridLayoutManager(getContext(), spanCount));
vmsHomeAdapter = new VmsHomeAdapter(requireActivity(), data);
binding.rvRomlist.setAdapter(vmsHomeAdapter);
binding.bnRomstore.setOnClickListener(v -> {
vmsCallToHomeListener.openRomStore();
});
binding.bnRepair.setOnClickListener(V -> {
VMManager.startFixRomsDataJson();
VMManager.fixRomsDataJsonResult(requireActivity());
});
binding.wrlRomlist.setOnRefreshListener(() -> {
checkAndLoad();
binding.wrlRomlist.setRefreshing(false);
});
checkAndLoad();
}
private void loadDataVbi() {
if (!FileUtils.isFileExists(AppConfig.romsdatajson))
FileUtils.writeToFile(AppConfig.maindirpath, "roms-data.json", "[]");
executor.execute(() -> {
List<DataMainRoms> tempdata = new ArrayList<>();
try {
jArray = new JSONArray(FileUtils.readFromFile(requireActivity(), new File(AppConfig.maindirpath
+ "roms-data.json")));
// Extract data from json and store into ArrayList as class objects
for (int i = 0; i < jArray.length(); i++) {
JSONObject json_data = jArray.getJSONObject(i);
DataMainRoms romsMainData = new DataMainRoms();
romsMainData.itemName = json_data.getString("imgName");
romsMainData.itemIcon = json_data.getString("imgIcon");
try {
romsMainData.itemArch = json_data.getString("imgArch");
} catch (JSONException ignored) {
romsMainData.itemArch = "unknown";
}
romsMainData.itemPath = json_data.getString("imgPath");
try {
romsMainData.imgCdrom = json_data.getString("imgCdrom");
} catch (JSONException ignored) {
romsMainData.imgCdrom = "";
}
try {
romsMainData.vmID = json_data.getString("vmID");
} catch (JSONException ignored) {
romsMainData.vmID = "";
}
try {
romsMainData.qmpPort = json_data.getInt("qmpPort");
} catch (JSONException ignored) {
romsMainData.qmpPort = 0;
}
try {
romsMainData.itemDrv1 = json_data.getString("imgDrv1");
} catch (JSONException ignored) {
romsMainData.itemDrv1 = "";
}
romsMainData.itemExtra = json_data.getString("imgExtra");
tempdata.add(romsMainData);
}
requireActivity().runOnUiThread(() -> binding.lnError.setVisibility(View.GONE));
} catch (JSONException e) {
requireActivity().runOnUiThread(() -> binding.lnError.setVisibility(View.VISIBLE));
Log.e(TAG, "loadDataVbi: ", e);
}
requireActivity().runOnUiThread(() -> {
binding.lnLoad.setVisibility(View.GONE);
if (tempdata.isEmpty()) {
binding.lnNothinghere.setVisibility(View.VISIBLE);
} else {
binding.lnNothinghere.setVisibility(View.GONE);
vmsHomeAdapter.updateData(tempdata);
}
});
});
}
private void checkAndLoad() {
if (PermissionUtils.storagepermission(requireActivity(), true)) {
loadDataVbi();
if (DeviceUtils.isStorageLow(requireActivity())) {
DialogUtils.oneDialog(requireActivity(),
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(requireActivity()))
requireActivity().finish();
});
}
}
}
@Override
public void refeshVMList() {
requireActivity().runOnUiThread(this::checkAndLoad);
}
@Override
public void configurationChanged(boolean isLandscape) {
spanCount = isLandscape ? 3 : 2;
binding.rvRomlist.setLayoutManager(new GridLayoutManager(getContext(), spanCount));
}
}

View file

@ -0,0 +1,119 @@
package com.vectras.vm.home.vms;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.vectras.qemu.Config;
import com.vectras.vm.MainRoms.DataMainRoms;
import com.vectras.vm.R;
import com.vectras.vm.StartVM;
import com.vectras.vm.VMManager;
import com.vectras.vm.home.core.HomeStartVM;
import com.vectras.vm.home.core.RomOptionsDialog;
import java.util.Collections;
import java.util.List;
public class VmsHomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private final Activity activity;
private final LayoutInflater inflater;
public List<DataMainRoms> data = Collections.emptyList();
public VmsHomeAdapter(Activity activity, List<DataMainRoms> data) {
this.activity = activity;
inflater = LayoutInflater.from(activity);
this.data = data;
}
// Inflate the layout when viewholder created
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.container_main_roms, parent, false);
return new MyHolder(view);
}
// Bind data
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int _position) {
int position = holder.getBindingAdapterPosition();
if (position == RecyclerView.NO_POSITION) return;
// Get current position of item in recyclerview to bind data and assign values from list
final MyHolder myHolder = (MyHolder) holder;
final DataMainRoms current = data.get(position);
myHolder.textName.setText(current.itemName);
myHolder.textArch.setText(current.itemArch);
if (current.itemIcon.isEmpty()){
VMManager.setIconWithName(myHolder.ivIcon, current.itemName);
} else {
Bitmap bmImg = BitmapFactory.decodeFile(current.itemIcon);
myHolder.ivIcon.setImageBitmap(bmImg);
}
myHolder.optionsBtn.setOnClickListener(view -> RomOptionsDialog.showNow(activity, data, position, current.vmID, current.itemName, current.itemArch));
myHolder.cdRoms.setOnClickListener(view -> {
VMManager.setArch(current.itemArch, activity);
StartVM.cdrompath = current.imgCdrom;
if (current.qmpPort == 0) {
Config.setDefault();
} else {
Config.QMPPort = current.qmpPort;
}
Config.vmID = current.vmID;
String env = StartVM.env(activity, current.itemExtra, current.itemPath, "");
HomeStartVM.startNow(activity, current.itemName, env, current.itemExtra, current.itemPath, null, null);
});
myHolder.cdRoms.setOnLongClickListener(v -> {
VMManager.deleteVMDialog(current.itemName, position, activity);
return false;
});
}
// return total item from List
@Override
public int getItemCount() {
return data.size();
}
static class MyHolder extends RecyclerView.ViewHolder {
CardView cdRoms;
TextView textName, textArch;
ImageView ivIcon;
ImageButton optionsBtn;
// create constructor to get widget reference
public MyHolder(View itemView) {
super(itemView);
cdRoms = itemView.findViewById(R.id.cdItem);
textName = itemView.findViewById(R.id.textName);
textArch = itemView.findViewById(R.id.textArch);
ivIcon = itemView.findViewById(R.id.ivIcon);
optionsBtn = itemView.findViewById(R.id.optionsButton);
}
}
public void updateData(List<DataMainRoms> newData) {
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new VmsDiffUtil(this.data, newData));
this.data.clear();
this.data.addAll(newData);
diffResult.dispatchUpdatesTo(this);
}
}

View file

@ -0,0 +1,162 @@
package com.vectras.vm.settings;
import static android.content.Intent.ACTION_VIEW;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.view.View;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
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.ActivityUpdaterBinding;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.PackageUtils;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Objects;
public class UpdaterActivity extends AppCompatActivity {
ActivityUpdaterBinding binding;
String downloadUrl = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_vncactivity);
binding = ActivityUpdaterBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar);
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
binding.toolbar.setNavigationOnClickListener(view -> finish());
initialize();
}
private void initialize() {
binding.bnUpdate.setOnClickListener(v -> {
if (downloadUrl.isEmpty()) {
finish();
} else {
startActivity(new Intent(ACTION_VIEW, Uri.parse(downloadUrl)));
}
});
check();
}
private void whenUpToDate() {
binding.lpiProgressbar.setVisibility(View.GONE);
binding.collapsingToolbarLayout.setTitle(getText(R.string.you_are_up_to_date));
binding.collapsingToolbarLayout.setSubtitle(PackageUtils.getThisVersionName(getApplicationContext()));
binding.bnUpdate.setText(getString(R.string.close));
}
private void checkAgain() {
binding.lpiProgressbar.setVisibility(View.VISIBLE);
binding.mcvWhatsnew.setVisibility(View.GONE);
binding.lnBottombar.setVisibility(View.GONE);
binding.collapsingToolbarLayout.setTitle(getText(R.string.checking_for_updates));
binding.collapsingToolbarLayout.setSubtitle(getText(R.string.just_a_sec));
binding.bnUpdate.setText(getString(R.string.update));
downloadUrl = "";
check();
}
private void check() {
int versionCode = PackageUtils.getThisVersionCode(getApplicationContext());
String versionName = PackageUtils.getThisVersionName(getApplicationContext());
RequestNetwork requestNetwork = new RequestNetwork(this);
RequestNetwork.RequestListener requestNetworkListener = new RequestNetwork.RequestListener() {
@Override
public void onResponse(String tag, String response, HashMap<String, Object> responseHeaders) {
binding.lpiProgressbar.setVisibility(View.GONE);
binding.lnBottombar.setVisibility(View.VISIBLE);
if (!response.isEmpty()) {
try {
final JSONObject obj = new JSONObject(response);
String versionNameonUpdate;
int versionCodeonUpdate;
String whatsnew;
String size;
String url;
if (MainSettingsManager.getcheckforupdatesfromthebetachannel(UpdaterActivity.this)) {
versionNameonUpdate = obj.getString("versionNameBeta");
versionCodeonUpdate = obj.getInt("versionCodeBeta");
whatsnew = obj.getString("MessageBeta");
size = obj.getString("sizeBeta");
url = obj.getString("urlBeta");
} else {
versionNameonUpdate = obj.getString("versionName");
versionCodeonUpdate = obj.getInt("versionCode");
whatsnew = obj.getString("Message");
size = obj.getString("size");
url = obj.getString("url");
}
if (versionCode < versionCodeonUpdate || !versionNameonUpdate.equals(versionName)) {
binding.collapsingToolbarLayout.setTitle(getText(R.string.new_update_available));
binding.collapsingToolbarLayout.setSubtitle(getString(R.string.whats_new));
binding.mcvWhatsnew.setVisibility(View.VISIBLE);
binding.tvWhatsnewcontent.setMovementMethod(LinkMovementMethod.getInstance());
binding.tvWhatsnewcontent.setText(Html.fromHtml(whatsnew + "<br><br>Update size:<br>" + size));
downloadUrl = url;
} else {
whenUpToDate();
}
if (obj.getString("versionNameBetas").contains(versionName)
&& !MainSettingsManager.getcheckforupdatesfromthebetachannel(UpdaterActivity.this)
&& !MainSettingsManager.getDontShowAgainJoinBetaUpdateChannelDialog(UpdaterActivity.this)) {
Activity activity = UpdaterActivity.this;
DialogUtils.threeDialog(activity,
activity.getResources().getString(R.string.you_are_using_beta_version),
activity.getResources().getString(R.string.switch_to_check_for_updates_on_the_Beta_channel_now),
activity.getResources().getString(R.string.ok),
activity.getResources().getString(R.string.cancel),
activity.getResources().getString(R.string.dont_show_again),
true,
R.drawable.science_24px,
true,
() -> {
MainSettingsManager.setcheckforupdatesfromthebetachannel(activity, true);
checkAgain();
},
null,
() -> MainSettingsManager.setDontShowAgainJoinBetaUpdateChannelDialog(activity, true),
null);
}
} catch (JSONException e) {
whenUpToDate();
}
} else {
whenUpToDate();
}
}
@Override
public void onErrorResponse(String tag, String message) {
whenUpToDate();
}
};
requestNetwork.startRequestNetwork(RequestNetworkController.GET,AppConfig.updateJson,"checkupdate",requestNetworkListener);
}
}

View file

@ -59,4 +59,8 @@ public class DeviceUtils {
return false;
}
public static boolean is64bit() {
return Build.SUPPORTED_ABIS[0].contains("arm64");
}
}

View file

@ -3,7 +3,6 @@ package com.vectras.vm.utils;
import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
@ -15,7 +14,6 @@ import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.MimeTypeMap;
import android.widget.Toast;
import java.io.File;
@ -27,31 +25,21 @@ import java.util.LinkedList;
import java.util.List;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import com.vectras.vm.MainActivity;
import com.vectras.vm.AppConfig;
import com.vectras.vm.R;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Objects;
@ -486,11 +474,11 @@ public class FileUtils {
}
public static String getDataDir() {
public static String getDataDir(Context context) {
String dataDir = MainActivity.activity.getApplicationInfo().dataDir;
PackageManager m = MainActivity.activity.getPackageManager();
String packageName = MainActivity.activity.getPackageName();
String dataDir = context.getApplicationInfo().dataDir;
PackageManager m = context.getPackageManager();
String packageName = context.getPackageName();
Log.v("VMExecutor", "Found packageName: " + packageName);
if (dataDir == null) {

View file

@ -1,6 +1,8 @@
package com.vectras.vm.utils;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import java.util.ArrayList;
@ -9,41 +11,17 @@ import java.util.Objects;
public class JSONUtils {
public static boolean isValidFromString(String _content) {
ArrayList<HashMap<String, Objects>> mmap = new ArrayList<>();
try {
mmap.clear();
mmap = new Gson().fromJson(_content, new TypeToken<ArrayList<HashMap<String, Object>>>() {
}.getType());
return true;
} catch (Exception e) {
return false;
}
}
public static boolean isMapValidFromString(String _content) {
HashMap<String, Object> mmap = new HashMap<>();
try {
mmap.clear();
mmap= new Gson().fromJson(_content, new TypeToken<HashMap<String, Object>>(){}.getType());
return true;
JsonElement element = JsonParser.parseString(_content);
return element != null;
} catch (Exception e) {
return false;
}
}
public static boolean isValidFromFile(String _filepath) {
ArrayList<HashMap<String, Objects>> mmap = new ArrayList<>();
String contentjson = "";
if (FileUtils.isFileExists(_filepath)) {
contentjson = FileUtils.readAFile(_filepath);
try {
mmap.clear();
mmap = new Gson().fromJson(contentjson, new TypeToken<ArrayList<HashMap<String, Object>>>() {
}.getType());
return true;
} catch (Exception e) {
return false;
}
return isValidFromString(FileUtils.readAFile(_filepath));
} else {
return false;
}

View file

@ -22,7 +22,7 @@ public class LibraryChecker {
public void checkMissingLibraries(Activity activity) {
// List of required libraries
String[] requiredLibraries = AppConfig.neededPkgs.split(" ");
String[] requiredLibraries = DeviceUtils.is64bit() ? AppConfig.neededPkgs.split(" ") : AppConfig.neededPkgs32bit.split(" ");
// Get the list of installed packages
isPackageInstalled(null, (output, errors) -> {
@ -61,7 +61,7 @@ public class LibraryChecker {
.setPositiveButton("Install", (dialog, which) -> {
// Create the install command
String installCommand = "apk add " + missingLibraries.replace("\n", " ");
new Terminal(context).executeShellCommand(installCommand, true, activity);
new Terminal(context).executeShellCommand(installCommand, true, true, activity);
})
.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss())
.show();
@ -81,7 +81,7 @@ public class LibraryChecker {
String command = "apk info";
Terminal terminal = new Terminal(context);
terminal.executeShellCommand(command, (Activity) context, (output, errors) -> {
terminal.executeShellCommand(command, (Activity) context, false, (output, errors) -> {
if (callback != null) {
callback.onCommandCompleted(output, errors);
}
@ -93,7 +93,7 @@ public class LibraryChecker {
String command = "apk info";
Terminal terminal = new Terminal(activity);
terminal.executeShellCommand(command, activity, (output, errors) -> {
terminal.executeShellCommand(command, activity, false, (output, errors) -> {
if (callback != null) {
callback.onCommandCompleted(output, errors);
}
@ -134,7 +134,7 @@ public class LibraryChecker {
.setCancelable(false)
.setPositiveButton("Install", (dialog, which) -> {
String installCommand = "apk add " + packageName;
new Terminal(context).executeShellCommand(installCommand, true, activity);
new Terminal(context).executeShellCommand(installCommand, true, true, activity);
})
.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss())
.show();

View file

@ -1,5 +1,9 @@
package com.vectras.vm.utils;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
@ -20,4 +24,26 @@ public class NetworkUtils {
}
}
}
/**
* CHECK WHETHER INTERNET CONNECTION IS AVAILABLE OR NOT
*/
public boolean checkConnection(Context context) {
final ConnectivityManager connMgr = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connMgr != null) {
NetworkInfo activeNetworkInfo = connMgr.getActiveNetworkInfo();
if (activeNetworkInfo != null) { // connected to the internet
// connected to the mobile provider's data plan
if (activeNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
// connected to wifi
return true;
} else
return activeNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE;
}
}
return false;
}
}

View file

@ -0,0 +1,11 @@
package com.vectras.vm.utils;
import android.app.NotificationManager;
import android.content.Context;
public class NotificationUtils {
public static void clearAll(Context context) {
NotificationManager notificationManager = (NotificationManager) context.getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancelAll();
}
}

View file

@ -0,0 +1,10 @@
package com.vectras.vm.utils;
public class NumberUtils {
public static int safeLongToInt(long l) {
if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
return 0;
}
return (int) l;
}
}

View file

@ -0,0 +1,18 @@
package com.vectras.vm.utils;
import android.app.ActivityManager;
import android.content.Context;
public class ServiceUtils {
public static boolean isServiceRunning(Context context, Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (manager != null) {
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
}
return false;
}
}

View file

@ -26,7 +26,6 @@ import android.text.Html;
import android.text.InputType;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.view.Display;
@ -41,7 +40,6 @@ import android.widget.Toast;
import com.vectras.qemu.Config;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.qemu.utils.FileUtils;
import com.vectras.vm.MainActivity;
import com.vectras.vm.R;
import com.vectras.vm.logger.VectrasStatus;
@ -461,4 +459,44 @@ public class UIUtils {
return insets;
});
}
public static void setOnApplyWindowInsetsListenerBottomOnly(View _view) {
int originalPaddingLeft = _view.getPaddingLeft();
int originalPaddingTop = _view.getPaddingTop();
int originalPaddingRight = _view.getPaddingRight();
int originalPaddingBottom = _view.getPaddingBottom();
ViewCompat.setOnApplyWindowInsetsListener(_view, (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout() | WindowInsetsCompat.Type.ime());
v.setPadding(originalPaddingLeft, originalPaddingTop, originalPaddingRight, systemBars.bottom + originalPaddingBottom);
return insets;
});
}
public static void setOnApplyWindowInsetsListenerLeftOnly(View _view) {
int originalPaddingLeft = _view.getPaddingLeft();
int originalPaddingTop = _view.getPaddingTop();
int originalPaddingRight = _view.getPaddingRight();
int originalPaddingBottom = _view.getPaddingBottom();
ViewCompat.setOnApplyWindowInsetsListener(_view, (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout() | WindowInsetsCompat.Type.ime());
v.setPadding(systemBars.left + originalPaddingLeft , originalPaddingTop, originalPaddingRight, originalPaddingBottom);
return insets;
});
}
public static void setOnApplyWindowInsetsListenerHorizontalOnly(View _view) {
int originalPaddingLeft = _view.getPaddingLeft();
int originalPaddingTop = _view.getPaddingTop();
int originalPaddingRight = _view.getPaddingRight();
int originalPaddingBottom = _view.getPaddingBottom();
ViewCompat.setOnApplyWindowInsetsListener(_view, (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout() | WindowInsetsCompat.Type.ime());
v.setPadding(systemBars.left + originalPaddingLeft, originalPaddingTop, systemBars.right + originalPaddingRight, originalPaddingBottom);
return insets;
});
}
}

View file

@ -28,7 +28,7 @@ public class JoystickView extends View
/**
* Interface definition for a callback to be invoked when a
* CallbackInterface definition for a callback to be invoked when a
* JoystickView's button is moved
*/
public interface OnMoveListener {
@ -43,7 +43,7 @@ public class JoystickView extends View
/**
* Interface definition for a callback to be invoked when a JoystickView
* CallbackInterface definition for a callback to be invoked when a JoystickView
* is touched and held by multiple pointers.
*/
public interface OnMultipleLongPressListener {

View file

@ -269,7 +269,7 @@ public class RadioGroupPlus extends LinearLayout {
}
/**
* <p>Interface definition for a callback to be invoked when the checked
* <p>CallbackInterface definition for a callback to be invoked when the checked
* radio button changed in this group.</p>
*/
public interface OnCheckedChangeListener {

View file

@ -599,7 +599,7 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
// Stop the service
MainService.stopService();
//Terminal.killQemuProcess();
VMManager.killcurrentqemuprocess(getApplicationContext());
VMManager.killcurrentqemuprocess(X11Activity.this);
finish();
})
.setNegativeButton(getString(R.string.no), null)

View file

@ -757,7 +757,7 @@ public class TouchInputHandler {
/**
* Interface with a set of functions to control the behavior of the remote host renderer.
* CallbackInterface with a set of functions to control the behavior of the remote host renderer.
*/
public interface RenderStub {
/**