Merge pull request #503 from AnBui2004/master

3.5.6
This commit is contained in:
An Bui 2025-12-29 23:42:18 +07:00 committed by GitHub
commit e3dc8ff5b6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 55 additions and 2014 deletions

View file

@ -15,8 +15,8 @@ android {
applicationId "com.vectras.vm"
minSdk minApi
targetSdk targetApi
versionCode 59
versionName "3.5.5"
versionCode 60
versionName "3.5.6"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true

View file

@ -146,19 +146,6 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".setupwizard.SetupWizardActivity"
android:configChanges="orientation|screenSize|keyboardHidden|smallestScreenSize|screenLayout"
android:exported="false"
android:hardwareAccelerated="true"
tools:ignore="DiscouragedApi" /> <!-- stupid android issue -->
<activity
android:name=".RomStoreActivity"
android:configChanges="orientation|screenSize|keyboardHidden|smallestScreenSize|screenLayout"
android:exported="false"
android:hardwareAccelerated="true"
android:label="Roms"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".AboutActivity"
android:exported="true"
@ -168,11 +155,6 @@
android:name=".ImagePrvActivity"
android:exported="true"
android:hardwareAccelerated="true" />
<activity
android:name=".VectrasApp$CrashActivity"
android:exported="true"
android:hardwareAccelerated="true"
android:label="App Crash" />
<activity
android:name="com.vectras.qemu.MainVNCActivity"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation|screenSize|smallestScreenSize"

View file

@ -1,5 +1,6 @@
package com.vectras.vm.Fragment;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.os.Bundle;
@ -39,12 +40,13 @@ public class CreateImageDialogFragment extends DialogFragment {
}
// If you want to style the dialog to have no title or to adjust the width, etc., override onCreateDialog.
@SuppressLint("SetTextI18n")
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(Objects.requireNonNull(getActivity()), R.style.MainDialogTheme);
AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity(), R.style.MainDialogTheme);
View view = getActivity().getLayoutInflater().inflate(R.layout.create_vhd, null);
View view = requireActivity().getLayoutInflater().inflate(R.layout.create_vhd, null);
TextInputEditText imageSize = view.findViewById(R.id.size);

View file

@ -1,105 +0,0 @@
package com.vectras.vm.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.Toast;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.vm.MainRoms.AdapterMainRoms;
import com.vectras.vm.MainRoms.DataMainRoms;
import com.vectras.vm.R;
import com.vectras.vm.AppConfig;
import com.vectras.vm.utils.FileUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class HomeFragment extends Fragment {
public static View view;
public static RecyclerView mRVMainRoms;
public static LinearLayout romsLayout;
public static AdapterMainRoms mMainAdapter;
public static JSONArray jArray;
public static List<DataMainRoms> data;
private SwipeRefreshLayout refreshRoms;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// TODO Auto-generated method stub
// TODO show the text view in @layout/home_fragment if list empty
view = inflater.inflate(R.layout.home_fragment, container, false);
romsLayout = view.findViewById(R.id.romsLayout);
refreshRoms = view.findViewById(R.id.refreshRoms);
refreshRoms.setOnRefreshListener(() -> {
loadDataVbi();
mMainAdapter.notifyItemRangeChanged(0, mMainAdapter.data.size());
refreshRoms.setRefreshing(false);
});
loadDataVbi();
return view;
}
private void loadDataVbi() {
data = 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.itemDrv1 = json_data.getString("imgDrv1");
} catch (JSONException ignored) {
romsMainData.itemDrv1 = "";
}
romsMainData.itemExtra = json_data.getString("imgExtra");
try {
if (json_data.getString("imgArch").equals(MainSettingsManager.getArch(requireActivity())))
data.add(romsMainData);
} catch (JSONException ignored) {
data.add(romsMainData);
}
}
// Setup and Handover data to recyclerview
mRVMainRoms = HomeFragment.view.findViewById(R.id.mRVMainRoms);
mMainAdapter = new AdapterMainRoms(requireActivity(), data);
mRVMainRoms.setAdapter(mMainAdapter);
mRVMainRoms.setLayoutManager(new GridLayoutManager(getContext(), 2));
} catch (JSONException e) {
Toast.makeText(requireActivity(), e.toString(), Toast.LENGTH_LONG).show();
}
}
}

View file

@ -1,146 +0,0 @@
package com.vectras.vm.MainRoms;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
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.RecyclerView;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.vectras.qemu.Config;
import com.vectras.vm.VMCreatorActivity;
import com.vectras.vm.ExportRomActivity;
import com.vectras.vm.R;
import com.vectras.vm.StartVM;
import com.vectras.vm.VMManager;
import java.util.Collections;
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;
public AdapterMainRoms(Activity activity, List<DataMainRoms> data) {
this.activity = activity;
this.context = activity.getApplicationContext();
inflater = LayoutInflater.from(context);
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 -> {
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 -> {
VMCreatorActivity.current = data.get(position);
VMManager.setArch(current.itemArch, activity);
context.startActivity(new Intent(context, VMCreatorActivity.class).putExtra("POS", position).putExtra("MODIFY", true).putExtra("VMID", current.vmID));
bottomSheetDialog.cancel();
});
Button exportRomBtn = v.findViewById(R.id.exportRomBtn);
exportRomBtn.setOnClickListener(v2 -> {
ExportRomActivity.pendingPosition = position;
Intent intent = new Intent();
intent.setClass(context.getApplicationContext(), ExportRomActivity.class);
context.startActivity(intent);
bottomSheetDialog.cancel();
});
Button removeRomBtn = v.findViewById(R.id.removeRomBtn);
removeRomBtn.setOnClickListener(v1 -> {
VMManager.deleteVMDialog(current.itemName, position, activity);
bottomSheetDialog.cancel();
});
bottomSheetDialog.show();
});
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, false);
//MainActivity.startVM(current.itemName, env, current.itemExtra, current.itemPath);
});
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);
}
}
}

View file

@ -1,6 +1,5 @@
package com.vectras.vm;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@ -14,13 +13,8 @@ import android.util.Log;
import androidx.core.app.NotificationCompat;
import com.vectras.qemu.MainVNCActivity;
import com.vectras.vm.core.PulseAudio;
import com.vectras.vterm.Terminal;
import java.io.File;
import java.util.Objects;
public class MainService extends Service {
public static String CHANNEL_ID = "Vectras VM Service";
private static final int NOTIFICATION_ID = 1;
@ -28,6 +22,7 @@ public class MainService extends Service {
public static String env = null;
private String TAG = "MainService";
public static MainService service;
public static Context activityContext;
@Override
public void onCreate() {
@ -49,8 +44,8 @@ public class MainService extends Service {
if (env != null) {
if (service != null) {
Terminal vterm = new Terminal(this);
vterm.executeShellCommand2(env, true, this);
Terminal vterm = new Terminal(activityContext);
vterm.executeShellCommand2(env, true, activityContext);
}
} else
Log.e(TAG, "env is null");

View file

@ -1,207 +0,0 @@
package com.vectras.vm;
import android.annotation.SuppressLint;
import android.os.Build;
import android.os.Bundle;
import android.os.StrictMode;
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 androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.vectras.vm.databinding.ActivityRomStoreBinding;
import com.vectras.vm.main.romstore.RomStoreHomeAdapterSearch;
import com.vectras.vm.Roms.AdapterRoms;
import com.vectras.vm.Roms.DataRoms;
import com.vectras.vm.network.RequestNetwork;
import com.vectras.vm.network.RequestNetworkController;
import com.vectras.vm.utils.UIUtils;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
public class RomStoreActivity extends AppCompatActivity {
ActivityRomStoreBinding binding;
private RequestNetwork net;
private RequestNetwork.RequestListener _net_request_listener;
private String contentJSON = "[]";
public static String license;
private RomStoreHomeAdapterSearch mAdapterSearch;
private List<DataRoms> data = new ArrayList<>();
private List<DataRoms> dataSearch = new ArrayList<>();
public static boolean isFinishNow = false;
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
super.onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
UIUtils.edgeToEdge(this);
binding = ActivityRomStoreBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
UIUtils.setOnApplyWindowInsetsListenerTop(findViewById(R.id.appbar));
UIUtils.setOnApplyWindowInsetsListenerBottom(findViewById(R.id.romsRv));
UIUtils.setOnApplyWindowInsetsListenerBottom(findViewById(R.id.romsSearch));
UIUtils.setOnApplyWindowInsetsListenerBottom(findViewById(R.id.linear_search_emty));
binding.searchBar.setNavigationOnClickListener(v -> onBackPressed());
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) {
}
});
net = new RequestNetwork(this);
_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);
}
public void onResume() {
super.onResume();
if (isFinishNow)
finish();
isFinishNow = false;
}
@Override
public void onBackPressed() {
if (binding.searchView.isShowing())
binding.searchView.hide();
else
super.onBackPressed();
}
@SuppressLint("NotifyDataSetChanged")
private void loadData() {
AdapterRoms mAdapter = new AdapterRoms(this, data);
binding.romsRv.setAdapter(mAdapter);
binding.romsRv.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
mAdapterSearch = new RomStoreHomeAdapterSearch(this, dataSearch);
binding.romsSearch.setAdapter(mAdapterSearch);
binding.romsSearch.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
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);
}
data.clear();
data.addAll(dataRoms);
mAdapter.notifyDataSetChanged();
dataSearch.clear();
dataSearch.addAll(dataRoms);
mAdapterSearch.notifyDataSetChanged();
}
@SuppressLint("NotifyDataSetChanged")
private void search(String keyword) {
try {
// Extract data from json and store into ArrayList as class objects
Gson gson = new Gson();
Type listType = new TypeToken<List<DataRoms>>() {}.getType();
List<DataRoms> allData = gson.fromJson(contentJSON, listType);
List<DataRoms> filteredData = new ArrayList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
filteredData = allData.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 : allData) {
if (rom.romName.toLowerCase().contains(keyword.toLowerCase()) ||
rom.romKernel.toLowerCase().contains(keyword.toLowerCase())) {
filteredData.add(rom);
}
}
}
dataSearch.clear();
dataSearch.addAll(filteredData);
} catch (Exception e) {
Log.e("RomManagerActivity", "Json parsing error: " + e.getMessage());
}
if (dataSearch.isEmpty())
binding.romsSearch.setVisibility(View.GONE);
else
binding.romsSearch.setVisibility(View.VISIBLE);
mAdapterSearch.notifyDataSetChanged();
}
}

View file

@ -1,127 +0,0 @@
package com.vectras.vm.Roms;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.util.Log;
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.AppConfig;
import com.vectras.vm.RomInfo;
import com.vectras.vm.R;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
public class AdapterRoms extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
Context context;
private final LayoutInflater inflater;
static List<DataRoms> dataRom = Collections.emptyList();
private final String TAG = "AdapterRoms";
public AdapterRoms(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.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

@ -26,7 +26,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textfield.TextInputLayout;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.vm.Fragment.CreateImageDialogFragment;
import com.vectras.vm.MainRoms.DataMainRoms;
import com.vectras.vm.main.vms.DataMainRoms;
import com.vectras.vm.databinding.ActivityVmCreatorBinding;
import com.vectras.vm.databinding.DialogProgressStyleBinding;
import com.vectras.vm.main.MainActivity;

View file

@ -1,38 +0,0 @@
package com.vectras.vm;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.widget.RemoteViews;
import com.vectras.vm.main.MainActivity;
public class WidgetProvider extends AppWidgetProvider {
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// Perform this loop procedure for each widget that belongs to this
// provider.
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);
PendingIntent pendingIntent = PendingIntent.getActivity(
/* context = */ context,
/* requestCode = */ 0,
/* intent = */ intent,
/* flags = */ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
// Get the layout for the widget and attach an onClick listener to
// the button.
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widgetlayout);
views.setOnClickPendingIntent(R.id.button, pendingIntent);
views.setRemoteAdapter(R.id.mRVMainRoms, intent);
// Tell the AppWidgetManager to perform an update on the current app
// widget.
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
}

View file

@ -37,7 +37,6 @@ import com.google.android.material.floatingactionbutton.ExtendedFloatingActionBu
import com.termux.app.TermuxActivity;
import com.vectras.qemu.Config;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.qemu.MainVNCActivity;
import com.vectras.vm.AboutActivity;
import com.vectras.vm.AppConfig;
import com.vectras.vm.VMCreatorActivity;
@ -53,7 +52,7 @@ import com.vectras.vm.network.RequestNetworkController;
import com.vectras.vm.databinding.BottomsheetdialogLoggerBinding;
import com.vectras.vm.databinding.UpdateBottomDialogLayoutBinding;
import com.vectras.vm.main.romstore.RomStoreHomeAdapterSearch;
import com.vectras.vm.Roms.DataRoms;
import com.vectras.vm.main.romstore.DataRoms;
import com.vectras.vm.SetArchActivity;
import com.vectras.vm.VMManager;
import com.vectras.vm.adapter.LogsAdapter;
@ -565,7 +564,7 @@ public class MainActivity extends AppCompatActivity implements RomStoreFragment.
@SuppressLint("NotifyDataSetChanged")
private void search(String keyword) {
try {
// Extract data from json and store into ArrayList as class objects
// Extract data from JSON and store into ArrayList as class objects
List<DataRoms> filteredData = new ArrayList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
@ -598,7 +597,11 @@ public class MainActivity extends AppCompatActivity implements RomStoreFragment.
else
binding.rvRomstoresearch.setVisibility(View.VISIBLE);
adapterRomStoreSearch.notifyDataSetChanged();
if (currentSearchMode == SEARCH_ROM_STORE ) {
if (adapterRomStoreSearch != null) adapterRomStoreSearch.notifyDataSetChanged();
} else {
if (adapterSoftwareStoreSearch != null) adapterSoftwareStoreSearch.notifyDataSetChanged();
}
}
private void showLogsDialog() {

View file

@ -206,6 +206,7 @@ public class MainStartVM {
MainService.startCommand(finalCommand, context);
} else {
Intent serviceIntent = new Intent(context, MainService.class);
MainService.activityContext = context;
MainService.env = finalCommand;
MainService.CHANNEL_ID = vmName;
if (SDK_INT >= Build.VERSION_CODES.O) {

View file

@ -8,7 +8,7 @@ import android.widget.Button;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.vectras.vm.VMCreatorActivity;
import com.vectras.vm.ExportRomActivity;
import com.vectras.vm.MainRoms.DataMainRoms;
import com.vectras.vm.main.vms.DataMainRoms;
import com.vectras.vm.R;
import com.vectras.vm.VMManager;

View file

@ -1,6 +1,6 @@
package com.vectras.vm.main.core;
import com.vectras.vm.Roms.DataRoms;
import com.vectras.vm.main.romstore.DataRoms;
import java.util.ArrayList;
import java.util.List;

View file

@ -1,4 +1,4 @@
package com.vectras.vm.Roms;
package com.vectras.vm.main.romstore;
import com.google.gson.annotations.SerializedName;

View file

@ -4,8 +4,6 @@ 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;

View file

@ -18,7 +18,6 @@ import com.google.gson.Gson;
import com.vectras.vm.AppConfig;
import com.vectras.vm.network.RequestNetwork;
import com.vectras.vm.network.RequestNetworkController;
import com.vectras.vm.Roms.DataRoms;
import com.vectras.vm.databinding.FragmentHomeRomStoreBinding;
import com.vectras.vm.main.core.SharedData;

View file

@ -19,7 +19,6 @@ import com.bumptech.glide.Glide;
import com.vectras.vm.RomInfo;
import com.vectras.vm.R;
import com.vectras.vm.Roms.DataRoms;
import java.util.Collections;
import java.util.List;

View file

@ -18,7 +18,6 @@ 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;

View file

@ -16,11 +16,10 @@ 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.Roms.DataRoms;
import com.vectras.vm.main.romstore.DataRoms;
import com.vectras.vm.databinding.FragmentHomeSoftwareStoreBinding;
import com.vectras.vm.main.core.SharedData;
import com.vectras.vm.main.romstore.RomStoreFragment;
import com.vectras.vm.main.romstore.RomStoreHomeAdpater;
import com.vectras.vm.network.RequestNetwork;
import com.vectras.vm.network.RequestNetworkController;

View file

@ -18,7 +18,7 @@ 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 com.vectras.vm.main.romstore.DataRoms;
import java.util.Collections;
import java.util.List;

View file

@ -19,7 +19,7 @@ import com.bumptech.glide.Glide;
import com.vectras.vm.RomInfo;
import com.vectras.vm.R;
import com.vectras.vm.Roms.DataRoms;
import com.vectras.vm.main.romstore.DataRoms;
import java.util.Collections;
import java.util.List;

View file

@ -4,7 +4,7 @@ import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.vectras.vm.Roms.DataRoms;
import com.vectras.vm.main.romstore.DataRoms;
import java.util.ArrayList;
import java.util.List;

View file

@ -1,4 +1,4 @@
package com.vectras.vm.MainRoms;
package com.vectras.vm.main.vms;
public class DataMainRoms {
public String itemIcon;
public String itemName;

View file

@ -2,8 +2,6 @@ package com.vectras.vm.main.vms;
import androidx.recyclerview.widget.DiffUtil;
import com.vectras.vm.MainRoms.DataMainRoms;
import java.util.List;
public class VmsDiffUtil extends DiffUtil.Callback {

View file

@ -15,7 +15,6 @@ 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;

View file

@ -16,7 +16,6 @@ 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;

View file

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

View file

@ -2,6 +2,7 @@ package com.vectras.vm.utils;
import static android.content.Intent.ACTION_VIEW;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
@ -31,6 +32,8 @@ public class DialogUtils {
}
public static void oneDialog(Context context, String _title, String _message, String _textPositiveButton, boolean _isicon, int _iconid, boolean _cancel, Runnable _onPositive, Runnable _onDismiss) {
if (!isAllowShow(context)) return;
View buttonsView = LayoutInflater.from(context).inflate(R.layout.dialog_layout, null);
AlertDialog dialog = new AlertDialog.Builder(context).create();
@ -89,6 +92,8 @@ public class DialogUtils {
}
public static void twoDialog(Context context, String _title, String _message, String _textPositiveButton, String _textNegativeButton, boolean _isicon, int _iconid, boolean _cancel, Runnable _onPositive, Runnable _onNegative, Runnable _onDismiss) {
if (!isAllowShow(context)) return;
View buttonsView = LayoutInflater.from(context).inflate(R.layout.dialog_layout, null);
AlertDialog dialog = new AlertDialog.Builder(context).create();
@ -156,6 +161,8 @@ public class DialogUtils {
}
public static void threeDialog(Context context, String _title, String _message, String _textPositiveButton, String _textNegativeButton, String _textNeutralButton, boolean _isicon, int _iconid, boolean _cancel, Runnable _onPositive, Runnable _onNegative, Runnable _onNeutral, Runnable _onDismiss) {
if (!isAllowShow(context)) return;
View buttonsView = LayoutInflater.from(context).inflate(R.layout.dialog_layout, null);
AlertDialog dialog = new AlertDialog.Builder(context).create();
@ -232,6 +239,10 @@ public class DialogUtils {
dialog.show();
}
public static boolean isAllowShow(Context context) {
return context instanceof Activity;
}
public static void joinTelegram(Context _context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(_context);
if (!prefs.getBoolean("tgDialog", false)) {

View file

@ -59,7 +59,7 @@ public class LibraryChecker {
.setMessage("The following libraries are missing:\n\n" + missingLibraries)
.setCancelable(false)
.setPositiveButton("Install", (dialog, which) -> {
// Create the install command
// Create the installation command
String installCommand = "apk add " + missingLibraries.replace("\n", " ");
new Terminal(context).executeShellCommand(installCommand, true, true, activity);
})

View file

@ -1,8 +1,6 @@
package com.vectras.vterm;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
@ -26,10 +24,7 @@ import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.vectras.qemu.MainVNCActivity;
import com.vectras.vm.R;
import com.vectras.vm.VMManager;
import com.vectras.vm.AppConfig;
@ -57,8 +52,8 @@ public class Terminal {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress() && inetAddress.getHostAddress().toString().contains(".")) {
return inetAddress.getHostAddress().toString();
if (!inetAddress.isLoopbackAddress() && Objects.requireNonNull(inetAddress.getHostAddress()).contains(".")) {
return inetAddress.getHostAddress();
}
}
}
@ -187,7 +182,7 @@ public class Terminal {
com.vectras.vm.logger.VectrasStatus.logError("<font color='#4db6ac'>VTERM: >" + userCommand + "</font>");
new Thread(() -> {
try {
// Setup the qemuProcess builder to start PRoot with environmental variables and commands
// Set up the qemuProcess builder to start PRoot with environmental variables and commands
ProcessBuilder processBuilder = new ProcessBuilder();
// Adjust these environment variables as necessary for your app
@ -264,13 +259,11 @@ public class Terminal {
int exitCode = qemuProcess.waitFor(); // Wait for the process to finish
if (exitCode == 0) {
output.append("Execution finished successfully.\n");
output.append(reader.readLine()).append("\n");
Log.i(TAG, reader.readLine());
} else {
output.append("Execution finished with exit code: ").append(exitCode).append("\n");
output.append(reader.readLine()).append("\n");
Log.i(TAG, reader.readLine());
}
output.append(reader.readLine()).append("\n");
Log.i(TAG, reader.readLine());
} catch (IOException | InterruptedException e) {
output.append(e.getMessage());
errors.append(Log.getStackTraceString(e));
@ -367,109 +360,6 @@ public class Terminal {
return output.toString();
}
public void extractQemuVersion(String userCommand, boolean showResultDialog, Activity dialogActivity, CommandCallback callback) {
StringBuilder output = new StringBuilder();
StringBuilder errors = new StringBuilder();
Log.d(TAG, userCommand);
com.vectras.vm.logger.VectrasStatus.logError("<font color='#4db6ac'>VTERM: >" + userCommand + "</font>");
new Thread(() -> {
try {
// Process setup (same as your original code)
ProcessBuilder processBuilder = new ProcessBuilder();
String filesDir = Objects.requireNonNull(context.getFilesDir().getAbsolutePath());
File tmpDir = new File(Objects.requireNonNull(context.getFilesDir()), "usr/tmp");
processBuilder.environment().put("PROOT_TMP_DIR", tmpDir.getAbsolutePath());
processBuilder.environment().put("HOME", "/root");
processBuilder.environment().put("USER", user);
processBuilder.environment().put("TERM", "xterm-256color");
processBuilder.environment().put("TMPDIR", "/tmp");
processBuilder.environment().put("SHELL", "/bin/sh");
processBuilder.environment().put("DISPLAY", DISPLAY);
processBuilder.environment().put("PULSE_SERVER", "127.0.0.1");
processBuilder.environment().put("XDG_RUNTIME_DIR", "${TMPDIR}");
processBuilder.environment().put("SDL_VIDEODRIVER", "x11");
String[] prootCommand = {
TermuxService.PREFIX_PATH + "/bin/proot",
"--kill-on-exit",
"--link2symlink",
"-0",
"-r", filesDir + "/distro",
"-b", "/dev",
"-b", "/proc",
"-b", "/sys",
"-b", AppConfig.internalDataDirPath + "distro/root:/dev/shm",
"-b", "/sdcard",
"-b", "/storage",
"-b", "/data",
"-b", AppConfig.internalDataDirPath + "usr/tmp:/tmp",
"-w", "/root",
"/bin/sh",
"--login"
};
processBuilder.command(prootCommand);
qemuProcess = processBuilder.start();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(qemuProcess.getOutputStream()));
BufferedReader reader = new BufferedReader(new InputStreamReader(qemuProcess.getInputStream()));
BufferedReader errorReader = new BufferedReader(new InputStreamReader(qemuProcess.getErrorStream()));
writer.write(userCommand);
writer.newLine();
writer.flush();
writer.close();
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
while ((line = errorReader.readLine()) != null) {
errors.append(line).append("\n");
}
reader.close();
errorReader.close();
qemuProcess.waitFor();
} catch (IOException | InterruptedException e) {
output.append(e.getMessage());
errors.append(Log.getStackTraceString(e));
} finally {
String finalOutput = output.toString();
String finalErrors = errors.toString();
// Extract version using regex
String version = extractVersion(finalOutput);
// Run callback on main thread
new Handler(Looper.getMainLooper()).post(() -> {
if (callback != null) {
callback.onCommandCompleted(version != null ? version : finalOutput, finalErrors);
}
if (showResultDialog) {
showDialog(finalOutput.isEmpty() ? finalErrors : finalOutput, dialogActivity, userCommand);
}
});
}
}).start();
}
private String extractVersion(String output) {
String regex = "QEMU emulator version ([\\d.]+)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(output);
if (matcher.find()) {
return matcher.group(1); // Return the version number
}
return null;
}
public interface CommandCallback {
void onCommandCompleted(String output, String errors);
}

View file

@ -1,135 +0,0 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
app:liftOnScrollColor="@android:color/transparent"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$Behavior">
<com.google.android.material.search.SearchBar
android:id="@+id/search_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:hint="@string/search"
app:defaultMarginsEnabled="true"
app:layout_collapseMode="pin"
app:navigationIcon="@drawable/arrow_back_24px" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/romsRv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="8dp"
android:clipToPadding="false"
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<com.google.android.material.search.SearchView
android:id="@+id/searchView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="@string/search"
app:layout_anchor="@id/search_bar">
<LinearLayout
android:id="@+id/linear_search_emty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/search_96px" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:gravity="center"
android:text="@string/no_matching_results_found" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/romsSearch"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurfaceContainerHigh"
android:paddingVertical="8dp"
android:clipToPadding="false"/>
</com.google.android.material.search.SearchView>
<LinearLayout
android:id="@+id/linearload"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface"
android:gravity="center"
android:orientation="vertical"
android:visibility="visible">
<com.google.android.material.loadingindicator.LoadingIndicator
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/textviewsettingup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="8dp"
android:text="@string/just_a_moment" />
</LinearLayout>
<LinearLayout
android:id="@+id/linearnothinghere"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/cloud_off_96px" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:gravity="center"
android:text="@string/oops"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:gravity="center"
android:text="@string/unable_to_connect_to_server" />
<com.google.android.material.button.MaterialButton
android:id="@+id/buttontryagain"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal|center_vertical"
android:padding="8dp"
android:text="@string/try_again" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -1,303 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:fitsSystemWindows="true"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/linearwelcome"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="8dp"
android:visibility="visible">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/vectrasvm200" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="@string/welcome"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="@string/welcome_to_vectras_vm_lets_get_started" />
<com.google.android.material.button.MaterialButton
android:id="@+id/buttonletstart"
style="@style/Widget.Material3Expressive.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal|center_vertical"
android:padding="8dp"
android:text="@string/letstart" />
</LinearLayout>
<LinearLayout
android:id="@+id/linearload"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="visible">
<com.google.android.material.loadingindicator.LoadingIndicator
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/textviewconnecting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="8dp"
android:text="@string/just_a_moment" />
</LinearLayout>
<LinearLayout
android:id="@+id/linearcannotconnecttoserver"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/cloud_off_96px" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:gravity="center"
android:text="@string/oops"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:gravity="center"
android:text="@string/unable_to_connect_to_server" />
<com.google.android.material.button.MaterialButton
android:id="@+id/buttontryconnectagain"
style="@style/Widget.Material3Expressive.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal|center_vertical"
android:padding="8dp"
android:text="@string/try_again" />
</LinearLayout>
<LinearLayout
android:id="@+id/linearsimplesetupui"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="visible">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|center_vertical"
android:layout_weight="1"
android:gravity="center_horizontal|center_vertical"
android:orientation="vertical">
<LinearLayout
android:id="@+id/linearstartsetup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
<TextView
android:id="@+id/textview2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="@string/start_setup"
android:textSize="18sp" />
<TextView
android:id="@+id/textview3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="@string/do_you_want_to_set_it_up_automatically_or_select_the_bootstrap_file_manually" />
<LinearLayout
android:id="@+id/linear5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="8dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/buttonautosetup"
style="@style/Widget.Material3Expressive.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="4dp"
android:gravity="center_horizontal|center_vertical"
android:padding="8dp"
android:text="@string/auto_setup" />
<com.google.android.material.button.MaterialButton
android:id="@+id/buttonmanualsetup"
style="@style/Widget.Material3Expressive.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal|center_vertical"
android:padding="8dp"
android:text="@string/manual" />
</LinearLayout>
<LinearLayout
android:id="@+id/linear6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="vertical"
android:padding="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="20dp"
android:paddingBottom="8dp"
android:text="@string/do_you_want_to_change_the_mirror_before_setting_up_the_current_mirror_is" />
<com.google.android.material.card.MaterialCardView
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Spinner
android:id="@+id/spinnerselectmirror"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:spinnerMode="dialog"
tools:listitem="@layout/simple_layout_for_spiner" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/linearsettingup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal|center_vertical"
android:orientation="vertical"
android:padding="8dp"
android:visibility="gone">
<com.google.android.material.loadingindicator.LoadingIndicator
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/textviewsettingup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="8dp"
android:text="@string/getting_ready_for_you" />
</LinearLayout>
<LinearLayout
android:id="@+id/linearsetupfailed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp"
android:visibility="gone">
<TextView
android:id="@+id/textview5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="@string/failed"
android:textSize="18sp" />
<TextView
android:id="@+id/textview6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="@string/something_went_wrong_during_setup_would_you_like_to_try_again" />
<LinearLayout
android:id="@+id/linear8"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="8dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/buttonsetuptryagain"
style="@style/Widget.Material3Expressive.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="4dp"
android:gravity="center_horizontal|center_vertical"
android:padding="8dp"
android:text="@string/try_again" />
<com.google.android.material.button.MaterialButton
android:id="@+id/buttonsetupshowlog"
style="@style/Widget.Material3Expressive.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal|center_vertical"
android:padding="8dp"
android:text="@string/show_log" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal|center_vertical"
android:orientation="vertical"
android:padding="8dp">
<TextView
android:id="@+id/textviewshowadvancedsetup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="@string/show_advanced_setup" />
</LinearLayout>
</LinearLayout>
<include
android:id="@+id/advancedsetup"
layout="@layout/setup_qemu_advanced" />
<include
android:id="@+id/finalsteps"
layout="@layout/setup_qemu_done" />
</LinearLayout>

View file

@ -1,38 +0,0 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="8dp">
<LinearLayout
android:id="@+id/romsLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/refreshRoms"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/mRVMainRoms"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:clipToPadding="false"
tools:listitem="@layout/container_main_roms" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/imported_roms_will_be_shown_here"
android:gravity="center" />
</LinearLayout>

View file

@ -5,11 +5,11 @@
"url": "https://github.com/xoureldeen/Vectras-VM-Android/releases",
"Message": "<h2>3.5.0</h2>\n3dfx is back!",
"cancellable": true,
"versionCodeBeta":"59",
"versionNameBeta":"3.5.5",
"versionNameBetas":"3.0.0,3.1.0,3.2.1,3.2.2,3.2.3,3.2.4,3.2.5,3.2.6,3.2.7,3.2.8,3.2.9,3.2.10,3.3.1,3.3.2,3.3.3,3.3.4,3.3.5,3.3.6,3.3.7,3.3.8,3.3.9,3.4.1,3.4.2,3.4.3,3.4.4,3.4.5,3.4.6,3.4.7,3.4.8,3.4.9,3.5.1,3.5.2,3.5.3,3.5.4,3.5.5",
"versionCodeBeta":"60",
"versionNameBeta":"3.5.6",
"versionNameBetas":"3.0.0,3.1.0,3.2.1,3.2.2,3.2.3,3.2.4,3.2.5,3.2.6,3.2.7,3.2.8,3.2.9,3.2.10,3.3.1,3.3.2,3.3.3,3.3.4,3.3.5,3.3.6,3.3.7,3.3.8,3.3.9,3.4.1,3.4.2,3.4.3,3.4.4,3.4.5,3.4.6,3.4.7,3.4.8,3.4.9,3.5.1,3.5.2,3.5.3,3.5.4,3.5.5,3.5.6",
"sizeBeta": "46 MB",
"urlBeta": "https://github.com/AnBui2004/Vectras-VM-Emu-Android/releases",
"MessageBeta": "<h2>3.5.5</h2>Bugs fixed.",
"MessageBeta": "<h2>3.5.6</h2>Bugs fixed.",
"cancellableBeta": true
}

View file

@ -126,21 +126,21 @@
"id": ""
},
{
"rom_name": "VMware SVGA 3D",
"rom_icon": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5a/Vmware_workstation_16_icon.svg/2051px-Vmware_workstation_16_icon.svg.png",
"rom_url": "https://archive.org/details/vmware-drivers-x-64-for-video-wddm",
"rom_path": "VMware_Drivers_x64_for_video_wddm.iso",
"rom_name": "Half-Life 2",
"rom_icon": "https://upload.wikimedia.org/wikipedia/en/thumb/2/25/Half-Life_2_cover.jpg/250px-Half-Life_2_cover.jpg",
"rom_url": "https://archive.org/details/half-life-2_202512",
"rom_path": "Half Life 2.iso",
"rom_avail": true,
"rom_size": "8.15.1.50 x64",
"rom_size": "2004",
"rom_arch": "X86_64",
"rom_kernel": "",
"rom_extra": "",
"final_rom_file_name": "",
"desc": "VMware SVGA 3D is a virtual graphics adapter and driver for VMware virtual machines (VMs), enabling 2D/3D acceleration (DirectX 9.0c, OpenGL 2.1+) for better guest OS performance, higher resolutions, and smoother graphics, installed via VMware Tools. It replaces basic VGA, offers dynamic switching between software/hardware rendering, and provides features like increased VRAM (up to 8GB for modern guests).",
"file_size": "33 MB",
"desc": "Half-Life 2 is a 2004 first-person shooter game developed and published by Valve Corporation. It was published for Windows on Valve's digital distribution service, Steam. Like the original Half-Life (1998), Half-Life 2 is played from a first-person perspective, combining combat, puzzles, and storytelling. It adds features such as vehicles and physics-based gameplay. The player controls Gordon Freeman, who joins a resistance effort to liberate Earth from the alien Combine empire.",
"file_size": "2 GB",
"creator": "tokaevUser",
"verified": true,
"vecid": "vmwaredriversx64forvideowddmiso",
"vecid": "halflife2iso",
"id": ""
},
{