diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 8d479ce..46a251e 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -68,7 +68,7 @@ jobs: "-F chat_id=${{ secrets.TELEGRAM_CHAT_ID_VEC }}"; do if curl -s -o /dev/null -F document=@"$file" $target \ - -F $'caption=Done! Note that this is a version that is automatically built when there are changes in the GitHub repository, not the official version. Please only install it if you really want to see what is new.\nRun ID: '${GITHUB_RUN_ID} \ + -F $'caption=Done! Note that this is a version that is automatically built when there are changes in the GitHub repository, not the official version. Please only install it if you really want to see what is new.\nExplore beta releases here: https://github.com/AnBui2004/Vectras-VM-Emu-Android/releases\nRun ID: '${GITHUB_RUN_ID} \ https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendDocument; then success_targets=true else @@ -96,7 +96,7 @@ jobs: "-d chat_id=${{ secrets.TELEGRAM_CHAT_ID_VEC }}"; do curl -s -o /dev/null -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \ $chat \ - -d $'text=Something went wrong and the APK file could not be uploaded.\nRun ID: '${GITHUB_RUN_ID} + -d $'text=Something went wrong and the APK file could not be uploaded.\nExplore beta releases here: https://github.com/AnBui2004/Vectras-VM-Emu-Android/releases\nRun ID: '${GITHUB_RUN_ID} done fi fi diff --git a/app/build.gradle b/app/build.gradle index 79546d7..8cce1c5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "com.vectras.vm" minSdk minApi targetSdk targetApi - versionCode 47 - versionName "3.4.3" + versionCode 48 + versionName "3.4.4" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true } @@ -113,7 +113,6 @@ dependencies { implementation 'androidx.activity:activity-ktx:1.12.1' compileOnly project(':shell-loader:stub') implementation project(":terminal-view") - implementation project(":library") // Retrofit implementation 'com.squareup.retrofit2:retrofit:3.0.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c625705..769c325 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -248,25 +248,6 @@ - - - - - - - - - - - diff --git a/app/src/main/java/au/com/darkside/xdemo/AccessControlEditor.java b/app/src/main/java/au/com/darkside/xdemo/AccessControlEditor.java deleted file mode 100644 index 10d2cdd..0000000 --- a/app/src/main/java/au/com/darkside/xdemo/AccessControlEditor.java +++ /dev/null @@ -1,205 +0,0 @@ -package au.com.darkside.xdemo; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.ListActivity; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.view.View; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.EditText; -import android.widget.Toast; - -import com.vectras.vm.R; - -import java.util.LinkedList; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -/** - * Editor for the list of hosts allowed to access the X server. - * - * Written by Matthew Kwan - March 2012 - * - * @author mkwan - */ -public class AccessControlEditor extends ListActivity implements OnItemClickListener { - private ArrayAdapter _adapter; - private EditText _hostField; - private int _deletePosition = -1; - - /** - * Called when the activity is first created. - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_access_control_editor_x_server); - - _hostField = findViewById(R.id.host_field); - - Button button; - - button = findViewById(R.id.add_button); - button.setOnClickListener(v -> addHost()); - - button = findViewById(R.id.cancel_button); - button.setOnClickListener(v -> { - setResult(RESULT_CANCELED, null); - finish(); - }); - - button = findViewById(R.id.apply_button); - button.setOnClickListener(v -> { - saveAccessList(); - setResult(RESULT_OK, null); - finish(); - }); - - getListView().setOnItemClickListener(this); - loadAccessList(); - } - - /** - * Called when a list item is selected. - */ - @Override - public void onItemClick(AdapterView parent, View v, int position, long id) { - _deletePosition = position; - getDeleteHostDialog().show(); - } - - /** - * @return The Dialog to delete a host. - */ - private Dialog getDeleteHostDialog() { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - - builder.setMessage("Delete the IP address?").setPositiveButton("OK", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - deleteSelectedHost(); - } - }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }); - - return builder.create(); - } - - /** - * Delete the host that was selected by the user. - */ - private void deleteSelectedHost() { - if (_deletePosition < 0) return; - - _adapter.remove(_adapter.getItem(_deletePosition)); - } - - /** - * Convert a hexadecimal IP address into a human-readable dot-separated - * format. - * - * @param host The host IP address, in hexadecimal format. - * @return The host IP address in dot-separated format. - */ - private static String hostToString(String host) { - int n; - - try { - n = (int) Long.parseLong(host, 16); - } catch (Exception e) { - return "Error"; - } - - int b1 = (n >> 24) & 0xff; - int b2 = (n >> 16) & 0xff; - int b3 = (n >> 8) & 0xff; - int b4 = n & 0xff; - - return b1 + "." + b2 + "." + b3 + "." + b4; - } - - /** - * Load the list of hosts that are allowed to access the X server. - */ - private void loadAccessList() { - SharedPreferences prefs = getSharedPreferences("AccessControlHosts", MODE_PRIVATE); - Map map = prefs.getAll(); - Set set = map.keySet(); - LinkedList hosts = new LinkedList(); - - for (String s : set) - hosts.add(hostToString(s)); - - _adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, hosts); - setListAdapter(_adapter); - } - - /** - * Convert a human-readable dot-separated IP address into hexadecimal. - * Return null if there's a parse error. - * - * @param s The host IP address, in dot-separated format. - * @return The host IP address in hexadecimal format, or null. - */ - private static String stringToHost(String s) { - String[] sa = s.split("\\."); - - if (sa.length != 4) return null; - - int n = 0; - - for (int i = 0; i < 4; i++) { - int b; - - try { - b = Integer.parseInt(sa[i]); - } catch (Exception e) { - return null; - } - - if (b < 0 || b > 255) return null; - - n = (n << 8) | b; - } - - return Integer.toHexString(n); - } - - /** - * Save the list of hosts that are allowed to access the X server. - */ - private void saveAccessList() { - SharedPreferences prefs = getSharedPreferences("AccessControlHosts", MODE_PRIVATE); - SharedPreferences.Editor editor = prefs.edit(); - - editor.clear(); - - int n = _adapter.getCount(); - - for (int i = 0; i < n; i++) { - String host = stringToHost(Objects.requireNonNull(_adapter.getItem(i))); - - if (host != null) editor.putBoolean(host, true); - } - - editor.commit(); - } - - /** - * Parse the IP address in the host field and add it to the list. - */ - private void addHost() { - String s = _hostField.getText().toString(); - - if (stringToHost(s) != null) _adapter.add(s); - else Toast.makeText(this, "Bad IP address '" + s + "'", Toast.LENGTH_LONG).show(); - } -} \ No newline at end of file diff --git a/app/src/main/java/au/com/darkside/xdemo/XServerActivity.java b/app/src/main/java/au/com/darkside/xdemo/XServerActivity.java deleted file mode 100644 index 2abc195..0000000 --- a/app/src/main/java/au/com/darkside/xdemo/XServerActivity.java +++ /dev/null @@ -1,624 +0,0 @@ -package au.com.darkside.xdemo; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.Service; -import android.app.NotificationManager; -import android.app.Notification; -import android.app.PendingIntent; -import android.content.ActivityNotFoundException; -import android.content.ComponentName; -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.os.PowerManager; -import android.os.PowerManager.WakeLock; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.WindowManager; -import android.view.inputmethod.InputMethodManager; -import android.widget.Toast; - -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -import au.com.darkside.xserver.ScreenView; -import au.com.darkside.xserver.XServer; - -import java.io.File; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.ProcessBuilder; -import java.lang.Class; - -import android.util.Log; - -import java.io.FileOutputStream; -import android.content.res.AssetManager; - -import java.io.IOException; -import android.content.pm.ActivityInfo; -import android.content.res.Configuration; - -import androidx.activity.OnBackPressedCallback; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; - -import com.vectras.qemu.Config; -import com.vectras.vm.MainService; -import com.vectras.vm.R; -import com.vectras.vm.VMManager; -import com.vectras.vm.databinding.ActivityMainXServerBinding; -import com.vectras.vm.home.core.HomeStartVM; -import com.vectras.vm.utils.DialogUtils; -import com.vectras.vm.utils.FileUtils; -import com.vectras.vm.utils.SimulateKeyEvent; - -/** - * This activity launches an X server and provides a screen for it. - * - * @author Matthew Kwan - */ -public class XServerActivity extends AppCompatActivity { - private final String TAG = "XServerActivity"; - ActivityMainXServerBinding binding; - private XServer _xServer; - private ScreenView _screenView; - private WakeLock _wakeLock; - - private static final String NOTIFICATION_CHANNEL_DEFAULT = "default"; - - private static final int MENU_KEYBOARD = 1; - private static final int MENU_IP_ADDRESS = 2; - private static final int MENU_ACCESS_CONTROL = 3; - private static final int MENU_REMOTE_LOGIN = 4; - private static final int MENU_TOGGLE_ARROWS = 5; - private static final int MENU_TOGGLE_BACKBUTTON = 6; - private static final int MENU_TOGGLE_TOUCHCLICKS = 7; - private static final int MENU_TOGGLE_WINDOWMANAGER = 8; - private static final int MENU_TOGGLE_ORIENTATION = 9; - private static final int MENU_TOGGLE_SHARED_CLIPBOARD = 10; - private static final int ACTIVITY_ACCESS_CONTROL = 1; - - private static final int DEFAULT_PORT = 6000; - private static final String PORT_DESC_PRE = "Listening on port "; - - private int _port = DEFAULT_PORT; - private String _portDescription = PORT_DESC_PRE + DEFAULT_PORT; - private Process _windowManager; - - /** - * Called when the activity is first created. - * - * @param savedInstanceState Saved state. - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - binding = ActivityMainXServerBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - -// copyAssetData(""); // copy all assets to getApplicationInfo().dataDir directory - - int port = DEFAULT_PORT; - Intent intent = getIntent(); - - // If it was launched from an intent, get the port number. - if (intent != null) { - Uri uri = intent.getData(); - - if (uri != null) { - int p = uri.getPort(); - - if (p >= 0) { - if (p < 10) // Using ports 0-9 is bad juju. - port = p + DEFAULT_PORT; - else port = p; - } - } - } - - _port = port; - if (_port != DEFAULT_PORT) _portDescription = PORT_DESC_PRE + _port; - - _xServer = new XServer(this, port, null); - - // execute binary on start (if there was any packed into the assets folder) - _xServer.setOnStartListener(new XServer.OnXSeverStartListener() { - @Override - public void onStart() { - // execute our program - try { - File file = new File(getApplicationInfo().nativeLibraryDir + "/libbinary.so"); - if(file.exists()){ - if (!file.setExecutable(true)) Log.e(TAG, "Execution of libbinary.so failed."); // make program executable - ProcessBuilder pb = new ProcessBuilder(file.getPath()); - Map env = pb.environment(); - env.put("DISPLAY", "127.0.0.1:0"); - pb.directory(new File(getApplicationInfo().dataDir)); // execute within data-dir - Process process = pb.start(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - }); - - setAccessControl(); - _screenView = _xServer.getScreen(); - - if (_screenView != null) { -// FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(1024, 768); -// _screenView.setLayoutParams(params); - binding.frame.addView(_screenView); - } - - PowerManager pm; - - pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - _wakeLock = pm.newWakeLock(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, getPackageName() + ":XServer"); - - // make window fullscreen - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); - - startService(new Intent(this, XServerService.class)); - - /* - * Create notification channel as it required for notifications on Android >= 8 - * Use reflection to stay backward compatible with sdk provided by debian - */ - if (Build.VERSION.SDK_INT >= 26) { - CharSequence name = "Default channel"; - String description = "Default notification channel of XServer demo"; - int importance = 3; // default importance - - try{ - Class nc = Class.forName("android.app.NotificationChannel"); - Object ncObj = nc.getConstructor(new Class[] {String.class, CharSequence.class, int.class}).newInstance(NOTIFICATION_CHANNEL_DEFAULT, name, importance); - nc.getMethod("setDescription", String.class).invoke(ncObj, description); - nc.getMethod("setVibrationPattern", long[].class).invoke(ncObj, (Object) new long[]{ 0 }); // enableVibration is bugged, use this as workaround - nc.getMethod("enableVibration", boolean.class).invoke(ncObj, true); - nc.getMethod("enableLights", boolean.class).invoke(ncObj, false); - NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - manager.getClass().getMethod("createNotificationChannel", nc).invoke(manager, ncObj); - } - catch(Exception e){ - Log.e("FATAL", "Could not reflect Android SDK >= 26", e); - } - } - - getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) { - @Override - public void handleOnBackPressed() { - binding.lnTools.setVisibility(binding.lnTools.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE); - } - }); - - initializeToolBar(); - HomeStartVM.startPending(this); - } - - /** - * Called when the activity resumes. - */ - @Override - public void onResume() { - super.onResume(); - NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - manager.cancel(1); - _wakeLock.acquire(); - } - - /** - * Called when the activity pauses. - */ - @Override - public void onPause() { - super.onPause(); - PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, getIntent(), PendingIntent.FLAG_UPDATE_CURRENT); - Notification.Builder nb = new Notification.Builder(this) - .setSmallIcon(android.R.drawable.ic_menu_view) - .setContentTitle("Running!") - .setContentText("XServer running in background.") - .setContentIntent(pendingIntent) - .setOngoing(true); - - /* - * Set notification channel as it required for notifications on Android >= 8 - * Use reflection to stay backward compatible with sdk provided by debian - */ - if (Build.VERSION.SDK_INT >= 26) { - try{ - nb.getClass().getMethod("setChannelId", String.class).invoke(nb, NOTIFICATION_CHANNEL_DEFAULT); - } - catch(Exception e){ - Log.e("FATAL", "Could not reflect Android SDK >= 26", e); - } - } - - - NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - manager.notify(1, nb.build()); - - _wakeLock.release(); - } - - /** - * Called when the activity is destroyed. - */ - @Override - public void onDestroy() { - _xServer.stop(); - super.onDestroy(); - - NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - manager.cancel(1); - } - - /** - * Called the first time a menu is needed. - * - * @param menu The options menu in which you place your items. - * @return True for the menu to be displayed. - */ - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuItem item; - - item = menu.add(0, MENU_KEYBOARD, 0, "Keyboard"); - item.setIcon(android.R.drawable.ic_menu_add); - - item = menu.add(0, MENU_IP_ADDRESS, 0, "IP address"); - item.setIcon(android.R.drawable.ic_menu_info_details); - - item = menu.add(0, MENU_ACCESS_CONTROL, 0, "Access control"); - item.setIcon(android.R.drawable.ic_menu_edit); - - item = menu.add(0, MENU_REMOTE_LOGIN, 0, "Remote login"); - item.setIcon(android.R.drawable.ic_menu_upload); - - item = menu.add(0, MENU_TOGGLE_ARROWS, 0, "Arrows as Mouseclicks (off)"); - item.setIcon(android.R.drawable.star_off); - - item = menu.add(0, MENU_TOGGLE_BACKBUTTON, 0, "Inhibit back button (off)"); - item.setIcon(android.R.drawable.star_off); - - item = menu.add(0, MENU_TOGGLE_TOUCHCLICKS, 0, "Touch Mouseclicks (on)"); - item.setIcon(android.R.drawable.star_on); - - item = menu.add(0, MENU_TOGGLE_WINDOWMANAGER, 0, "Window Manager (off)"); - item.setIcon(android.R.drawable.star_on); - - item = menu.add(0, MENU_TOGGLE_SHARED_CLIPBOARD, 0, "Shared Clipboard (on)"); - item.setIcon(android.R.drawable.star_on); - - item = menu.add(0, MENU_TOGGLE_ORIENTATION, 0, "Screen Orientation (H)"); - - return true; - } - - /** - * Called when a menu selection has been made. - * - * @param item The menu item that was selected. - * @return True if the menu selection has been handled. - */ - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - super.onOptionsItemSelected(item); - - switch (item.getItemId()) { - case MENU_KEYBOARD: - InputMethodManager imm = (InputMethodManager) getSystemService(Service.INPUT_METHOD_SERVICE); - - // If anyone knows a better way to bring up the soft - // keyboard, I'd love to hear about it. - _screenView.requestFocus(); - imm.hideSoftInputFromWindow(_screenView.getWindowToken(), 0); - imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); - return true; - case MENU_IP_ADDRESS: - getMenuIpAdressDialog().show(); - return true; - case MENU_ACCESS_CONTROL: - launchAccessControlEditor(); - return true; - case MENU_REMOTE_LOGIN: - launchSshApp(); - return true; - case MENU_TOGGLE_ARROWS: - if (_xServer.getScreen().toggleArrowsAsButtons()) { - item.setIcon(android.R.drawable.star_on); - item.setTitle("Arrows as Mouseclicks (on)"); - } else { - item.setIcon(android.R.drawable.star_off); - item.setTitle("Arrows as Mouseclicks (off)"); - } - return true; - case MENU_TOGGLE_BACKBUTTON: - if (_xServer.getScreen().toggleInhibitBackButton()) { - item.setIcon(android.R.drawable.star_on); - item.setTitle("Inhibit back button (on)"); - } else { - item.setIcon(android.R.drawable.star_off); - item.setTitle("Inhibit back button (off)"); - } - return true; - case MENU_TOGGLE_TOUCHCLICKS: - if (_xServer.getScreen().toggleEnableTouchClicks()) { - item.setIcon(android.R.drawable.star_on); - item.setTitle("Touch Mouseclicks (on)"); - } else { - item.setIcon(android.R.drawable.star_off); - item.setTitle("Touch Mouseclicks (off)"); - } - return true; - case MENU_TOGGLE_SHARED_CLIPBOARD: - if (_xServer.getScreen().toggleSharedClipboard()) { - item.setIcon(android.R.drawable.star_on); - item.setTitle("Shared Clipboard (on)"); - } else { - item.setIcon(android.R.drawable.star_off); - item.setTitle("Shared Clipboard (off)"); - } - return true; - case MENU_TOGGLE_WINDOWMANAGER: - if (_windowManager == null) { - try { - File file = new File(getApplicationInfo().nativeLibraryDir + "/libwm.so"); - if (!file.setExecutable(true)) Log.e(TAG, "Execution of libwm.so failed."); // make program executable - ProcessBuilder pb = new ProcessBuilder(file.getPath()); - Map env = pb.environment(); - env.put("DISPLAY", "127.0.0.1:0"); - pb.directory(new File(getApplicationInfo().dataDir)); // execute within dataDir - _windowManager = pb.start(); - item.setIcon(android.R.drawable.star_on); - item.setTitle("Window Manager (on)"); - } catch (IOException e) { - throw new RuntimeException(e); - } - } else { - _windowManager.destroy(); - _windowManager = null; - item.setIcon(android.R.drawable.star_off); - item.setTitle("Window Manager (off)"); - } - return true; - case MENU_TOGGLE_ORIENTATION: - if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - item.setTitle("Screen Orientation (V)"); - } else { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - item.setTitle("Screen Orientation (H)"); - } - return true; - } - - return false; - } - - /** - * Return a string describing the IP address(es) of this device. - * - * @return A string describing the IP address(es) of this device. - */ - private String getAddressInfo() { - String s = _portDescription; - - try { - for (Enumeration nie = NetworkInterface.getNetworkInterfaces(); nie.hasMoreElements(); ) { - NetworkInterface ni = nie.nextElement(); - - for (Enumeration iae = ni.getInetAddresses(); iae.hasMoreElements(); ) { - InetAddress ia = iae.nextElement(); - - if (ia.isLoopbackAddress()) continue; - - s += "\n" + ni.getDisplayName() + ": " + ia.getHostAddress(); - } - } - } catch (Exception e) { - s += "\nError: " + e.getMessage(); - } - - return s; - } - - /** - * @return The Dialog to enter the server IP Adress. - */ - private Dialog getMenuIpAdressDialog() { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle("IP address").setMessage(getAddressInfo()).setPositiveButton("OK", (dialog, id) -> dialog.cancel()); - return builder.create(); - } - - - /** - * Load the access control hosts from persistent storage. - */ - private void setAccessControl() { - SharedPreferences prefs = getSharedPreferences("AccessControlHosts", MODE_PRIVATE); - Map map = prefs.getAll(); - HashSet hosts = _xServer.getAccessControlHosts(); - - hosts.clear(); - if (!map.isEmpty()) { - Set set = map.keySet(); - - for (String s : set) { - try { - int host = (int) Long.parseLong(s, 16); - - hosts.add(host); - } catch (Exception e) { - Log.e(TAG, "setAccessControl: ", e); - } - } - } - - _xServer.setAccessControl(!hosts.isEmpty()); - } - - /** - * Called when an activity returns a result. - */ - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (resultCode == RESULT_OK) { - if (requestCode == ACTIVITY_ACCESS_CONTROL) { - setAccessControl(); - } else { - Uri content_describer = data.getData(); - File selectedFilePath = new File(getPath(content_describer)); - - switch (requestCode) { - case 120: - VMManager.changeCDROM(selectedFilePath.getAbsolutePath(), this); - break; - case 889: - VMManager.changeFloppyDriveA(selectedFilePath.getAbsolutePath(), this); - break; - case 13335: - VMManager.changeFloppyDriveB(selectedFilePath.getAbsolutePath(), this); - break; - case 32: - VMManager.changeSDCard(selectedFilePath.getAbsolutePath(), this); - break; - case 1996: - VMManager.changeRemovableDevice(VMManager.pendingDeviceID, selectedFilePath.getAbsolutePath(), this); - break; - } - } - } - } - - /** - * Launch the access control list editor. - */ - private void launchAccessControlEditor() { - Intent intent = new Intent(this, AccessControlEditor.class); - - startActivityForResult(intent, ACTIVITY_ACCESS_CONTROL); - } - - /** - * Launch an application. - */ - private boolean launchApp(String pkg, String cls) { - Intent intent = new Intent(Intent.ACTION_MAIN); - - intent.setComponent(new ComponentName(pkg, cls)); - try { - startActivity(intent); - } catch (ActivityNotFoundException e) { - return false; - } - - return true; - } - - /** - * Launch an application that will allow an SSH login. - */ - private void launchSshApp() { - if (launchApp("org.connectbot", "org.connectbot.HostListActivity")) return; - if (launchApp("com.madgag.ssh.agent", "com.madgag.ssh.agent.HostListActivity")) return; - if (launchApp("sk.vx.connectbot", "sk.vx.connectbot.HostListActivity")) return; - - Toast.makeText(this, "The ConnectBot application needs to be installed", Toast.LENGTH_LONG).show(); - } - - private void copyAssetData(String path) { - AssetManager assetManager = this.getAssets(); - String[] assets; - try { - assets = assetManager.list(path); - assert assets != null; - if (assets.length == 0) { - copyFile(path); - } else { - String fullPath = getApplicationInfo().dataDir + "/" + path; - File dir = new File(fullPath); - if (!dir.exists()) - if (!dir.mkdir()) Log.e(TAG, "Unable to copy asset: " + fullPath); - for (String asset : assets) { - Log.i(asset, "Info"); - if (path.isEmpty()) - copyAssetData(asset); - else - copyAssetData(path + "/" + asset); - } - } - } catch (IOException ex) { - Log.e("tag", "I/O Exception", ex); - } - } - - private void copyFile(String filename) { - AssetManager assetManager = this.getAssets(); - InputStream in; - OutputStream out; - try { - in = assetManager.open(filename); - String newFileName = getApplicationInfo().dataDir + "/" + filename; - out = new FileOutputStream(newFileName); - - byte[] buffer = new byte[1024]; - int read; - while ((read = in.read(buffer)) != -1) { - out.write(buffer, 0, read); - } - in.close(); - out.flush(); - out.close(); - } catch (Exception e) { - Log.e("tag", Objects.requireNonNull(e.getMessage())); - } - } - - private void initializeToolBar() { - binding.btnPower.setOnClickListener(v -> DialogUtils.threeDialog(this, getString(R.string.power), getString(R.string.shutdown_or_reset_content), getString(R.string.shutdown), getString(R.string.reset), getString(R.string.close), true, R.drawable.power_settings_new_24px, true, - this::shutdownthisvm, VMManager::resetCurrentVM, null, null)); - - binding.btnDevices.setOnClickListener(v -> VMManager.showChangeRemovableDevicesDialog(this, null)); - - binding.btnKeyboard.setOnClickListener(v -> { - InputMethodManager imm = (InputMethodManager) getSystemService(Service.INPUT_METHOD_SERVICE); - - // If anyone knows a better way to bring up the soft - // keyboard, I'd love to hear about it. - _screenView.requestFocus(); - imm.hideSoftInputFromWindow(_screenView.getWindowToken(), 0); - imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); - }); - - binding.btnForcustoqemu.setOnClickListener(v -> SimulateKeyEvent.qemuForcus(this)); - - binding.btnZoomin.setOnClickListener(v -> SimulateKeyEvent.qemuZoomIn(this)); - - binding.btnZoomout.setOnClickListener(v -> SimulateKeyEvent.qemuZoomOut(this)); - } - - private void shutdownthisvm() { - VMManager.shutdownCurrentVM(); - Config.setDefault(); - MainService.stopService(); - finish(); - } - - public String getPath(Uri uri) { - return FileUtils.getPath(this, uri); - } -} \ No newline at end of file diff --git a/app/src/main/java/au/com/darkside/xdemo/XServerService.java b/app/src/main/java/au/com/darkside/xdemo/XServerService.java deleted file mode 100644 index b55e599..0000000 --- a/app/src/main/java/au/com/darkside/xdemo/XServerService.java +++ /dev/null @@ -1,33 +0,0 @@ -// only needed to capture kill of app, here used to get rid off all notifications -package au.com.darkside.xdemo; - -import android.app.Service; -import android.app.NotificationManager; -import android.content.Intent; -import android.os.IBinder; - -import android.content.Context; - -public class XServerService extends Service { - - @Override - public IBinder onBind(Intent intent) { - return null; - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - return START_NOT_STICKY; - } - - @Override - public void onDestroy() { - super.onDestroy(); - } - - @Override - public void onTaskRemoved(Intent rootIntent) { - NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - manager.cancelAll(); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/vectras/vm/MainService.java b/app/src/main/java/com/vectras/vm/MainService.java index 6e79f15..e03ee11 100644 --- a/app/src/main/java/com/vectras/vm/MainService.java +++ b/app/src/main/java/com/vectras/vm/MainService.java @@ -111,9 +111,6 @@ public class MainService extends Service { public static void startCommand(String _env, Context _context) { Terminal vterm = new Terminal(_context); - if (Build.VERSION.SDK_INT < 34) { - vterm.executeShellCommand2("dwm", false, _context); - } vterm.executeShellCommand2(_env, true, _context); } } \ No newline at end of file diff --git a/app/src/main/java/com/vectras/vm/home/core/DisplaySystem.java b/app/src/main/java/com/vectras/vm/home/core/DisplaySystem.java index c7b2074..80ee87d 100644 --- a/app/src/main/java/com/vectras/vm/home/core/DisplaySystem.java +++ b/app/src/main/java/com/vectras/vm/home/core/DisplaySystem.java @@ -102,22 +102,18 @@ public class DisplaySystem { } else { if (SDK_INT >= 34) { Log.d(TAG, "launchX11: Opened: com.termux.x11.MainActivity."); - // activity.startActivity(new Intent(activity, XServerActivity.class)); Intent intent = new Intent(); intent.setClassName("com.termux.x11", "com.termux.x11.MainActivity"); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); context.startActivity(intent); - try { - TermuxX11.main(new String[]{":0"}); - } catch (Exception e) { - Log.e(TAG, "TermuxX11.main: ", e); - } + + startTermuxX11(context); } else { context.startActivity(new Intent(context, X11Activity.class)); } if (isKill) { - new Terminal(context).executeShellCommand2("killall fluxbox && " + (SDK_INT >= 34 ? "export DISPLAY=:0 && sleep 5 && " : "") + "fluxbox > /dev/null", false, context); + new Terminal(context).executeShellCommand2((SDK_INT >= 34 ? "export DISPLAY=:0 && " : "killall fluxbox && ") + "fluxbox > /dev/null", false, context); } } }); diff --git a/app/src/main/java/com/vectras/vm/home/core/HomeStartVM.java b/app/src/main/java/com/vectras/vm/home/core/HomeStartVM.java index f19e566..a9ea88a 100644 --- a/app/src/main/java/com/vectras/vm/home/core/HomeStartVM.java +++ b/app/src/main/java/com/vectras/vm/home/core/HomeStartVM.java @@ -32,6 +32,7 @@ import com.vectras.vm.utils.FileUtils; import com.vectras.vm.utils.NetworkUtils; import com.vectras.vm.utils.PackageUtils; import com.vectras.vm.utils.ServiceUtils; +import com.vectras.vterm.Terminal; import java.io.File; @@ -197,10 +198,13 @@ public class HomeStartVM { String finalCommand = VMManager.addAudioDevSdl(String.format(runCommandFormat, env)); if (MainSettingsManager.getVmUi(context).equals("X11") && SDK_INT >= 34) { - finalCommand = "export DISPLAY=:0 && sleep 5\nfluxbox > /dev/null &\n" + finalCommand; + finalCommand = "export DISPLAY=:0 &&" + finalCommand; } Log.i(TAG, finalCommand); + Terminal vterm = new Terminal(context); + vterm.executeShellCommand2("export DISPLAY=:0 && fluxbox > /dev/null", false, context); + if (ServiceUtils.isServiceRunning(context, MainService.class)) { MainService.startCommand(finalCommand, context); } else { diff --git a/app/src/main/java/com/vectras/vm/home/vms/VmsDiffUtil.java b/app/src/main/java/com/vectras/vm/home/vms/VmsDiffUtil.java index b5aa7e2..069ed60 100644 --- a/app/src/main/java/com/vectras/vm/home/vms/VmsDiffUtil.java +++ b/app/src/main/java/com/vectras/vm/home/vms/VmsDiffUtil.java @@ -27,16 +27,16 @@ public class VmsDiffUtil extends DiffUtil.Callback { @Override public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { - // So sánh bằng khóa duy nhất (vd: vmID) + // Compare using a unique key (e.g., 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 đó + // Compare the content, if it's different, update that 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 + return oldItem.equals(newItem); // If you override equals in DataMainRoms, use this method. } } diff --git a/app/src/main/java/com/vectras/vm/home/vms/VmsFragment.java b/app/src/main/java/com/vectras/vm/home/vms/VmsFragment.java index 80b027b..883c11c 100644 --- a/app/src/main/java/com/vectras/vm/home/vms/VmsFragment.java +++ b/app/src/main/java/com/vectras/vm/home/vms/VmsFragment.java @@ -84,9 +84,7 @@ public class VmsFragment extends Fragment implements CallbackInterface.HomeCallT vmsHomeAdapter = new VmsHomeAdapter(requireActivity(), data); binding.rvRomlist.setAdapter(vmsHomeAdapter); - binding.bnRomstore.setOnClickListener(v -> { - vmsCallToHomeListener.openRomStore(); - }); + binding.bnRomstore.setOnClickListener(v -> vmsCallToHomeListener.openRomStore()); binding.bnRepair.setOnClickListener(V -> { VMManager.startFixRomsDataJson(); diff --git a/app/src/main/jniLibs/arm64-v8a/libwm.so b/app/src/main/jniLibs/arm64-v8a/libwm.so deleted file mode 100755 index 20a4734..0000000 Binary files a/app/src/main/jniLibs/arm64-v8a/libwm.so and /dev/null differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libwm.so b/app/src/main/jniLibs/armeabi-v7a/libwm.so deleted file mode 100755 index 6072a54..0000000 Binary files a/app/src/main/jniLibs/armeabi-v7a/libwm.so and /dev/null differ diff --git a/app/src/main/jniLibs/x86/libwm.so b/app/src/main/jniLibs/x86/libwm.so deleted file mode 100755 index 5884ce3..0000000 Binary files a/app/src/main/jniLibs/x86/libwm.so and /dev/null differ diff --git a/app/src/main/jniLibs/x86_64/libwm.so b/app/src/main/jniLibs/x86_64/libwm.so deleted file mode 100755 index 4e4e5d9..0000000 Binary files a/app/src/main/jniLibs/x86_64/libwm.so and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/syncglass96.png b/app/src/main/res/drawable-xhdpi/syncglass96.png deleted file mode 100644 index ce599a3..0000000 Binary files a/app/src/main/res/drawable-xhdpi/syncglass96.png and /dev/null differ diff --git a/app/src/main/res/layout/activity_access_control_editor_x_server.xml b/app/src/main/res/layout/activity_access_control_editor_x_server.xml deleted file mode 100644 index 32d12eb..0000000 --- a/app/src/main/res/layout/activity_access_control_editor_x_server.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - -