From e724a517a8d46cfaf244a551b490186b726a4650 Mon Sep 17 00:00:00 2001 From: Epic Studios <79816069+epicstudios856@users.noreply.github.com> Date: Sat, 20 Jan 2024 02:19:58 +0200 Subject: [PATCH] PHANTOM V2.5 PH2.5 (9) --- .idea/deploymentTargetDropDown.xml | 12 + app/build.gradle | 4 +- app/src/main/AndroidManifest.xml | 23 +- app/src/main/assets/roms/vgabios-virtio.bin | Bin 39424 -> 39936 bytes app/src/main/assets/roms/vgabios-vmware.bin | Bin 39424 -> 39936 bytes .../com/vectras/qemu/MainApplication.java | 20 - .../com/vectras/qemu/MainSDLActivity.java | 241 +++++----- .../com/vectras/qemu/MainSettingsManager.java | 161 ++++++- .../com/vectras/qemu/MainVNCActivity.java | 33 +- .../java/com/vectras/qemu/jni/StartVM.java | 46 +- .../main/java/com/vectras/vm/AppConfig.java | 51 +- .../com/vectras/vm/CustomRomActivity.java | 454 ++++++++++++++++++ .../com/vectras/vm/Fragment/HomeFragment.java | 207 ++++---- .../vm/Fragment/LoggerDialogFragment.java | 91 ++++ .../java/com/vectras/vm/MainActivity.java | 158 +++++- .../vectras/vm/MainRoms/AdapterMainRoms.java | 96 +++- .../com/vectras/vm/MainRoms/DataMainRoms.java | 1 + .../java/com/vectras/vm/Roms/AdapterRoms.java | 22 +- .../com/vectras/vm/RomsManagerActivity.java | 204 +++++--- .../java/com/vectras/vm/SetArchActivity.java | 76 +++ .../java/com/vectras/vm/StoreActivity.java | 118 ++++- .../com/vectras/vm/data/LoginDataSource.java | 29 -- .../com/vectras/vm/data/LoginRepository.java | 54 --- .../main/java/com/vectras/vm/data/Result.java | 48 -- .../vectras/vm/data/model/LoggedInUser.java | 23 - app/src/main/res/drawable/ic_arch.xml | 6 + app/src/main/res/drawable/ic_kvm.xml | 6 + app/src/main/res/drawable/input_circle.xml | 10 + app/src/main/res/drawable/round_adb_24.xml | 10 + app/src/main/res/drawable/round_album_24.xml | 10 + app/src/main/res/drawable/round_image_24.xml | 10 + .../main/res/drawable/round_storage_24.xml | 10 + .../main/res/drawable/round_volume_up_24.xml | 11 + .../main/res/layout/activity_custom_rom.xml | 141 ++++++ app/src/main/res/layout/activity_login.xml | 1 + .../main/res/layout/activity_roms_manager.xml | 39 +- app/src/main/res/layout/activity_set_arch.xml | 87 ++++ app/src/main/res/layout/activity_splash.xml | 17 +- app/src/main/res/layout/activity_store.xml | 7 + .../main/res/layout/container_main_roms.xml | 61 ++- app/src/main/res/layout/content_about.xml | 12 +- app/src/main/res/layout/controls_fragment.xml | 441 +++++++++-------- app/src/main/res/layout/fragment_first.xml | 35 ++ app/src/main/res/layout/fragment_second.xml | 35 ++ app/src/main/res/layout/main_content.xml | 8 + app/src/main/res/layout/no_connection.xml | 20 +- .../main/res/layout/rom_options_dialog.xml | 30 ++ app/src/main/res/menu/home_drawer_menu.xml | 6 +- app/src/main/res/menu/home_toolbar_menu.xml | 6 + app/src/main/res/navigation/nav_graph.xml | 28 ++ app/src/main/res/raw/logo2.json | 1 + app/src/main/res/values-ar/strings.xml | 210 ++++++++ app/src/main/res/values-night/themes.xml | 66 ++- app/src/main/res/values-ru/strings.xml | 210 ++++++++ app/src/main/res/values/array.xml | 24 + app/src/main/res/values/strings.xml | 58 ++- app/src/main/res/values/themes.xml | 69 ++- app/src/main/res/xml/qemu.xml | 42 +- 58 files changed, 3061 insertions(+), 838 deletions(-) delete mode 100644 app/src/main/java/com/vectras/qemu/MainApplication.java create mode 100644 app/src/main/java/com/vectras/vm/CustomRomActivity.java create mode 100644 app/src/main/java/com/vectras/vm/Fragment/LoggerDialogFragment.java create mode 100644 app/src/main/java/com/vectras/vm/SetArchActivity.java delete mode 100644 app/src/main/java/com/vectras/vm/data/LoginDataSource.java delete mode 100644 app/src/main/java/com/vectras/vm/data/LoginRepository.java delete mode 100644 app/src/main/java/com/vectras/vm/data/Result.java delete mode 100644 app/src/main/java/com/vectras/vm/data/model/LoggedInUser.java create mode 100644 app/src/main/res/drawable/ic_arch.xml create mode 100644 app/src/main/res/drawable/ic_kvm.xml create mode 100644 app/src/main/res/drawable/input_circle.xml create mode 100644 app/src/main/res/drawable/round_adb_24.xml create mode 100644 app/src/main/res/drawable/round_album_24.xml create mode 100644 app/src/main/res/drawable/round_image_24.xml create mode 100644 app/src/main/res/drawable/round_storage_24.xml create mode 100644 app/src/main/res/drawable/round_volume_up_24.xml create mode 100644 app/src/main/res/layout/activity_custom_rom.xml create mode 100644 app/src/main/res/layout/activity_set_arch.xml create mode 100644 app/src/main/res/layout/fragment_first.xml create mode 100644 app/src/main/res/layout/fragment_second.xml create mode 100644 app/src/main/res/layout/rom_options_dialog.xml create mode 100644 app/src/main/res/navigation/nav_graph.xml create mode 100644 app/src/main/res/raw/logo2.json create mode 100644 app/src/main/res/values-ar/strings.xml create mode 100644 app/src/main/res/values-ru/strings.xml diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index 646789f..369b62f 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -1,6 +1,18 @@ + + + + + + + + + + + + diff --git a/app/build.gradle b/app/build.gradle index c080641..4b8280a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,8 +20,8 @@ android { applicationId "com.vectras.vm" minSdk 21 targetSdk 34 - versionCode 8 - versionName "2.4" + versionCode 9 + versionName "PHANTOM v2.5" ndk { abiFilters "armeabi-v7a","arm64-v8a", "x86", "x86_64" } testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 91024df..7bfeed7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,6 +12,7 @@ + @@ -25,11 +26,15 @@ android:supportsRtl="false" android:theme="@style/AppTheme" tools:targetApi="31"> + + android:hardwareAccelerated="true"> @@ -112,24 +117,34 @@ android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation|screenSize|smallestScreenSize" android:label="Settings" android:theme="@style/AppTheme" /> + - - + diff --git a/app/src/main/assets/roms/vgabios-virtio.bin b/app/src/main/assets/roms/vgabios-virtio.bin index 60ce81d37c3a6b0fb8d368f2d4c738db0378f427..7c3191b96fbaf3ebba24b031ab899ad1a73ab1b5 100644 GIT binary patch literal 39936 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

JYpi{Usp2@M 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 diff --git a/app/src/main/assets/roms/vgabios-vmware.bin b/app/src/main/assets/roms/vgabios-vmware.bin index b75a3517d11a731276087f1d57cd9fbd9e6cd6e8..8594a2e8b5aede125377650d7e6d952a0b916d91 100644 GIT binary patch literal 39936 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 diff --git a/app/src/main/java/com/vectras/qemu/MainApplication.java b/app/src/main/java/com/vectras/qemu/MainApplication.java deleted file mode 100644 index bcf9d1a..0000000 --- a/app/src/main/java/com/vectras/qemu/MainApplication.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.vectras.qemu; - -import android.app.Application; - -public class MainApplication extends Application { - - @Override - public void onCreate() { - super.onCreate(); - try { - Class.forName("android.os.AsyncTask"); - } catch (Throwable ignore) { - // ignored - } - - - - } - -} diff --git a/app/src/main/java/com/vectras/qemu/MainSDLActivity.java b/app/src/main/java/com/vectras/qemu/MainSDLActivity.java index b3700e8..1316703 100644 --- a/app/src/main/java/com/vectras/qemu/MainSDLActivity.java +++ b/app/src/main/java/com/vectras/qemu/MainSDLActivity.java @@ -2,7 +2,9 @@ import android.annotation.SuppressLint; import android.app.Activity; + import androidx.appcompat.app.AlertDialog; + import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; @@ -35,6 +37,7 @@ import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; +import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.ListView; @@ -55,6 +58,7 @@ import com.vectras.qemu.utils.Machine; import com.vectras.qemu.utils.QmpClient; import com.vectras.vm.Fragment.ControlersOptionsFragment; +import com.vectras.vm.Fragment.LoggerDialogFragment; import com.vectras.vm.MainActivity; import com.vectras.vm.R; import com.vectras.vm.adapter.LogsAdapter; @@ -82,7 +86,7 @@ public class MainSDLActivity extends SDLActivity { public static final String TAG = "MainSDLActivity"; - public static MainSDLActivity activity ; + public static MainSDLActivity activity; public static final int KEYBOARD = 10000; public static final int QUIT = 10001; @@ -172,7 +176,7 @@ public void run() { } catch (InterruptedException ex) { // Log.v("singletap", "Could not sleep"); } - MainActivity.vmexecutor.onVectrasMouse(Config.SDL_MOUSE_LEFT, MotionEvent.ACTION_DOWN, 1,0, 0); + MainActivity.vmexecutor.onVectrasMouse(Config.SDL_MOUSE_LEFT, MotionEvent.ACTION_DOWN, 1, 0, 0); try { Thread.sleep(50); } catch (InterruptedException ex) { @@ -277,13 +281,14 @@ public static void delayKey(int ms) { e.printStackTrace(); } } + public static void sendCtrlAltKey(int code) { delayKey(100); SDLActivity.onNativeKeyDown(KeyEvent.KEYCODE_CTRL_LEFT); delayKey(100); SDLActivity.onNativeKeyDown(KeyEvent.KEYCODE_ALT_LEFT); delayKey(100); - if(code>=0) { + if (code >= 0) { SDLActivity.onNativeKeyDown(code); delayKey(100); SDLActivity.onNativeKeyUp(code); @@ -388,8 +393,7 @@ public void run() { } }, 200); - } - else if (item.getItemId() == R.id.itemMonitor) { + } else if (item.getItemId() == R.id.itemMonitor) { if (this.monitorMode) { this.onVMConsole(); } else { @@ -421,7 +425,7 @@ else if (item.getItemId() == R.id.itemMonitor) { } else if (item.getItemId() == this.QUIT) { } else if (item.getItemId() == R.id.itemHelp) { - } else if (item.getItemId() == R.id.itemHideToolbar) { + } else if (item.getItemId() == R.id.itemHideToolbar) { this.onHideToolbar(); } else if (item.getItemId() == R.id.itemDisplay) { this.onSelectMenuSDLDisplay(); @@ -439,7 +443,7 @@ public void onViewLog() { FileUtils.viewVectrasLog(this); } - public void onHideToolbar(){ + public void onHideToolbar() { ActionBar bar = this.getSupportActionBar(); if (bar != null) { bar.hide(); @@ -449,7 +453,7 @@ public void onHideToolbar(){ private void onMouseMode() { - String [] items = {"Trackpad Mouse (Phone)", + String[] items = {"Trackpad Mouse (Phone)", "Bluetooth/USB Mouse (Desktop mode)", //Physical mouse for Chromebook, Android x86 PC, or Bluetooth Mouse }; final AlertDialog.Builder mBuilder = new AlertDialog.Builder(this); @@ -457,7 +461,7 @@ private void onMouseMode() { mBuilder.setSingleChoiceItems(items, Config.mouseMode.ordinal(), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int i) { - switch(i){ + switch (i) { case 0: setUIModeMobile(true); break; @@ -485,7 +489,7 @@ public boolean checkVMResolutionFits() { height += bar.getHeight(); } - if(vm_width < width && vm_height < height) + if (vm_width < width && vm_height < height) return true; return false; @@ -495,7 +499,7 @@ public void calibration() { //XXX: No need to calibrate for SDL trackpad. } - private void setUIModeMobile(boolean fitToScreen){ + private void setUIModeMobile(boolean fitToScreen) { try { UIUtils.setOrientation(this); @@ -506,16 +510,16 @@ private void setUIModeMobile(boolean fitToScreen){ Config.mouseMode = Config.MouseMode.Trackpad; MainSettingsManager.setDesktopMode(this, false); MainActivity.vmexecutor.setRelativeMouseMode(1); - if(Config.showToast) + if (Config.showToast) UIUtils.toastShort(this.getApplicationContext(), "Trackpad Enabled"); - if(fitToScreen) + if (fitToScreen) onFitToScreen(); else onNormalScreen(); calibration(); invalidateOptionsMenu(); - }catch (Exception ex){ - if(Config.debug) + } catch (Exception ex) { + if (Config.debug) ex.printStackTrace(); } @@ -529,15 +533,15 @@ private void promptSetUIModeDesktop(final boolean mouseMethodAlt) { alertDialog.setTitle("Desktop Mode"); LinearLayout mLayout = new LinearLayout(this); - mLayout.setPadding(20,20,20,20); + mLayout.setPadding(20, 20, 20, 20); mLayout.setOrientation(LinearLayout.VERTICAL); TextView textView = new TextView(activity); textView.setVisibility(View.VISIBLE); String desktopInstructions = this.getString(R.string.desktopInstructions); - if(!checkVMResolutionFits()){ + if (!checkVMResolutionFits()) { String resolutionWarning = "Warning: MainActivity.vmexecutor resolution " - + vm_width+ "x" + vm_height + + + vm_width + "x" + vm_height + " is too high for Desktop Mode. " + "Scaling will be used and Mouse Alignment will not be accurate. " + "Reduce display resolution within the Guest OS for better experience.\n\n"; @@ -584,13 +588,13 @@ private void setUIModeDesktop() { Config.mouseMode = Config.MouseMode.External; MainSettingsManager.setDesktopMode(this, true); MainActivity.vmexecutor.setRelativeMouseMode(0); - if(Config.showToast) + if (Config.showToast) UIUtils.toastShort(MainSDLActivity.this, "External Mouse Enabled"); onNormalScreen(); calibration(); invalidateOptionsMenu(); - }catch (Exception ex){ - if(Config.debug) + } catch (Exception ex) { + if (Config.debug) ex.printStackTrace(); } } @@ -623,7 +627,7 @@ public void run() { Log.d(TAG, "onStretchToScreen"); screenMode = SDLScreenMode.Fullscreen; sendCtrlAltKey(KeyEvent.KEYCODE_F); // not working - if(Config.showToast) + if (Config.showToast) UIUtils.toastShort(activity, "Resizing, Please Wait"); resize(null); @@ -643,14 +647,14 @@ private void onFitToScreen() { public void run() { Log.d(TAG, "onFitToScreen"); screenMode = SDLScreenMode.FitToScreen; - if(Config.showToast) + if (Config.showToast) UIUtils.toastShort(activity, "Resizing, Please Wait"); resize(null); } }).start(); - }catch (Exception ex){ - if(Config.debug) + } catch (Exception ex) { + if (Config.debug) ex.printStackTrace(); } @@ -667,14 +671,14 @@ private void onNormalScreen() { public void run() { Log.d(TAG, "onNormalScreen"); screenMode = SDLScreenMode.Normal; - if(Config.showToast) + if (Config.showToast) UIUtils.toastShort(activity, "Resizing, Please Wait"); resize(null); } }).start(); - }catch (Exception ex){ - if(Config.debug) + } catch (Exception ex) { + if (Config.debug) ex.printStackTrace(); } @@ -751,7 +755,7 @@ public boolean setupMenu(Menu menu) { int maxMenuItemsShown = 4; int actionShow = MenuItemCompat.SHOW_AS_ACTION_IF_ROOM; - if(UIUtils.isLandscapeOrientation(this)) { + if (UIUtils.isLandscapeOrientation(this)) { maxMenuItemsShown = 6; actionShow = MenuItemCompat.SHOW_AS_ACTION_ALWAYS; } @@ -782,13 +786,12 @@ public boolean setupMenu(Menu menu) { maxMenuItemsShown--; } - if (soundcard==null || soundcard.equals("None")) { + if (soundcard == null || soundcard.equals("None")) { menu.removeItem(menu.findItem(R.id.itemVolume).getItemId()); maxMenuItemsShown--; } - for (int i = 0; i < menu.size() && i < maxMenuItemsShown; i++) { MenuItemCompat.setShowAsAction(menu.getItem(i), actionShow); } @@ -825,12 +828,12 @@ public boolean dispatchKeyEvent(KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_MULTIPLE && event.getKeyCode() == KeyEvent.KEYCODE_UNKNOWN) { sendText(event.getCharacters().toString()); return true; - } else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) { + } else /*if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) { this.onBackPressed(); return true; - } if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN) { + }*/ if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN) { // We emulate right click with volume down - if(event.getAction() == KeyEvent.ACTION_DOWN) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { MotionEvent e = MotionEvent.obtain(1000, 1000, MotionEvent.ACTION_DOWN, 0, 0, 0, 0, 0, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0); rightClick(e, 0); @@ -838,7 +841,7 @@ public boolean dispatchKeyEvent(KeyEvent event) { return true; } else if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP) { // We emulate middle click with volume up - if(event.getAction() == KeyEvent.ACTION_DOWN) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { MotionEvent e = MotionEvent.obtain(1000, 1000, MotionEvent.ACTION_DOWN, 0, 0, 0, 0, 0, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0); middleClick(e, 0); @@ -880,6 +883,7 @@ private static void sendText(String string) { private TimerTask t; public boolean ctrlClicked = false; public boolean altClicked = false; + // Setup @SuppressLint("UseCompatLoadingForDrawables") protected void onCreate(Bundle savedInstanceState) { @@ -936,7 +940,10 @@ protected void onCreate(Bundle savedInstanceState) { btnLogs.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - FileUtils.viewVectrasLog(activity); + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + // Create and show the dialog. + LoggerDialogFragment newFragment = new LoggerDialogFragment(); + newFragment.show(ft, "Logger"); } }); shutdownBtn.setOnClickListener(new View.OnClickListener() { @@ -1132,6 +1139,21 @@ public void onItemClick(AdapterView parent, View view, int position, long id) } } });*/ + + ImageButton hideBtn = findViewById(R.id.visibilityButton); + hideBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + FrameLayout l = findViewById(R.id.mainControl); + if (l.getVisibility() == View.VISIBLE) { + l.setVisibility(View.GONE); + hideBtn.setImageResource(R.drawable.round_visibility_24); + } else { + l.setVisibility(View.VISIBLE); + hideBtn.setImageResource(R.drawable.round_visibility_off_24); + } + } + }); } private void createUI(int w, int h) { @@ -1194,7 +1216,7 @@ public void onClick(DialogInterface dialog, int which) { } public LinearLayout createVolumePanel() { - LinearLayout layout = new LinearLayout (this); + LinearLayout layout = new LinearLayout(this); layout.setPadding(20, 20, 20, 20); LinearLayout.LayoutParams volparams = new LinearLayout.LayoutParams( @@ -1277,7 +1299,7 @@ public void run() { file.delete(); } } - if(Config.showToast) + if (Config.showToast) UIUtils.toastShort(getApplicationContext(), "Please wait while saving VM State"); MainActivity.vmexecutor.current_fd = MainActivity.vmexecutor.get_fd(MainActivity.vmexecutor.save_state_name); @@ -1309,8 +1331,8 @@ private void processMigrationResponse(String response) { try { JSONObject object = new JSONObject(response); errorStr = object.getString("error"); - }catch (Exception ex) { - if(Config.debug) + } catch (Exception ex) { + if (Config.debug) ex.printStackTrace(); } if (errorStr != null) { @@ -1319,8 +1341,8 @@ private void processMigrationResponse(String response) { try { JSONObject descObj = new JSONObject(errorStr); descStr = descObj.getString("desc"); - }catch (Exception ex) { - if(Config.debug) + } catch (Exception ex) { + if (Config.debug) ex.printStackTrace(); } final String descStr1 = descStr; @@ -1353,7 +1375,7 @@ protected void onPostExecute(Void test) { @Override public boolean onTouchEvent(MotionEvent event) { boolean res = false; - if(Config.mouseMode == Config.MouseMode.External){ + if (Config.mouseMode == Config.MouseMode.External) { return res; } //TODO: @@ -1363,7 +1385,7 @@ public boolean onTouchEvent(MotionEvent event) { } private void resumeVM() { - if(MainActivity.vmexecutor == null){ + if (MainActivity.vmexecutor == null) { return; } Thread t = new Thread(new Runnable() { @@ -1382,7 +1404,7 @@ public void run() { new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { @Override public void run() { - if(Config.mouseMode == Config.MouseMode.External) + if (Config.mouseMode == Config.MouseMode.External) setUIModeDesktop(); else setUIModeMobile(screenMode == SDLScreenMode.FitToScreen); @@ -1395,13 +1417,6 @@ public void run() { } - public void onBackPressed() { - super.onBackPressed(); - UIUtils.hideKeyboard(this, mSurface); - Machine.stopVM(activity); - return; - } - @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); @@ -1446,7 +1461,7 @@ public LinearLayout createSDLDisplayPanel() { LinearLayout buttonsLayout = new LinearLayout(this); buttonsLayout.setOrientation(LinearLayout.HORIZONTAL); buttonsLayout.setGravity(Gravity.CENTER_HORIZONTAL); - Button displayMode = new Button (this); + Button displayMode = new Button(this); displayMode.setText("Display Mode"); displayMode.setOnClickListener(new View.OnClickListener() { @@ -1458,7 +1473,7 @@ public void onClick(View view) { layout.addView(buttonsLayout); final TextView value = new TextView(this); - value.setText("Idle Refresh Rate: " + currRate+" Hz"); + value.setText("Idle Refresh Rate: " + currRate + " Hz"); layout.addView(value); value.setLayoutParams(params); @@ -1471,7 +1486,7 @@ public void onClick(View view) { ((SeekBar) rate).setOnSeekBarChangeListener(new OnSeekBarChangeListener() { public void onProgressChanged(SeekBar s, int progress, boolean touch) { - value.setText("Idle Refresh Rate: " + (progress+1)+" Hz"); + value.setText("Idle Refresh Rate: " + (progress + 1) + " Hz"); } public void onStartTrackingTouch(SeekBar arg0) { @@ -1479,7 +1494,7 @@ public void onStartTrackingTouch(SeekBar arg0) { } public void onStopTrackingTouch(SeekBar arg0) { - int progress = arg0.getProgress()+1; + int progress = arg0.getProgress() + 1; int refreshMs = 1000 / progress; Log.v(TAG, "Changing idle refresh rate: (ms)" + refreshMs); MainActivity.vmexecutor.setsdlrefreshrate(refreshMs); @@ -1498,18 +1513,17 @@ public int getCurrentSDLRefreshRate() { } - private void onDisplayMode() { - String [] items = { + String[] items = { "Normal (One-To-One)", "Fit To Screen" // ,"Stretch To Screen" //Stretched }; int currentScaleType = 0; - if(screenMode == SDLScreenMode.FitToScreen){ + if (screenMode == SDLScreenMode.FitToScreen) { currentScaleType = 1; - } else if(screenMode == SDLScreenMode.Fullscreen) + } else if (screenMode == SDLScreenMode.Fullscreen) currentScaleType = 2; final AlertDialog.Builder mBuilder = new AlertDialog.Builder(this); @@ -1517,12 +1531,12 @@ private void onDisplayMode() { mBuilder.setSingleChoiceItems(items, currentScaleType, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int i) { - switch(i){ + switch (i) { case 0: onNormalScreen(); break; case 1: - if(Config.mouseMode == Config.MouseMode.External){ + if (Config.mouseMode == Config.MouseMode.External) { UIUtils.toastShort(MainSDLActivity.this, "Fit to Screen Disabled under Desktop Mode"); dialog.dismiss(); return; @@ -1530,7 +1544,7 @@ public void onClick(DialogInterface dialog, int i) { onFitToScreen(); break; case 2: - if(Config.mouseMode == Config.MouseMode.External){ + if (Config.mouseMode == Config.MouseMode.External) { UIUtils.toastShort(MainSDLActivity.this, "Stretch Screen Disabled under Desktop Mode"); dialog.dismiss(); return; @@ -1550,7 +1564,7 @@ public void onClick(DialogInterface dialog, int i) { @Override - protected synchronized void runSDLMain(){ + protected synchronized void runSDLMain() { //We go through the vm executor MainActivity.startvm(this, Config.UI_SDL); @@ -1563,11 +1577,10 @@ protected synchronized void runSDLMain(){ } } - public static void onVMResolutionChanged(int w, int h) - { + public static void onVMResolutionChanged(int w, int h) { boolean refreshDisplay = false; - if(w!=vm_width || h!=vm_height) + if (w != vm_width || h != vm_height) refreshDisplay = true; vm_width = w; vm_height = h; @@ -1575,7 +1588,7 @@ public static void onVMResolutionChanged(int w, int h) Log.v(TAG, "VM resolution changed to " + vm_width + "x" + vm_height); - if(refreshDisplay) { + if (refreshDisplay) { activity.resize(null); } @@ -1594,13 +1607,13 @@ public enum SDLScreenMode { private void setLayout(Configuration newConfig) { boolean isLanscape = - (newConfig!=null && newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) + (newConfig != null && newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) || UIUtils.isLandscapeOrientation(this); View vnc_canvas_layout = (View) this.findViewById(R.id.sdl_layout); RelativeLayout.LayoutParams vnc_canvas_layout_params = null; //normal 1-1 - if(screenMode == SDLScreenMode.Normal) { + if (screenMode == SDLScreenMode.Normal) { if (isLanscape) { vnc_canvas_layout_params = new RelativeLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, @@ -1674,23 +1687,22 @@ public void surfaceCreated(SurfaceHolder holder) { @Override public void run() { - if(Config.mouseMode == Config.MouseMode.External) + if (Config.mouseMode == Config.MouseMode.External) setUIModeDesktop(); else setUIModeMobile(screenMode == SDLScreenMode.FitToScreen); } - },1000); + }, 1000); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - Log.d(TAG,"Configuration changed"); + Log.d(TAG, "Configuration changed"); resize(newConfig); } - public synchronized void doResize(boolean reverse, final Configuration newConfig) { //XXX: notify the UI not to process mouse motion isResizing = true; @@ -1706,18 +1718,18 @@ public synchronized void doResize(boolean reverse, final Configuration newConfig final ActionBar bar = ((SDLActivity) activity).getSupportActionBar(); - if(MainSDLActivity.mLayout != null) { + if (MainSDLActivity.mLayout != null) { width = MainSDLActivity.mLayout.getWidth(); height = MainSDLActivity.mLayout.getHeight(); } //native resolution for use with external mouse - if(screenMode != SDLScreenMode.Fullscreen && screenMode != SDLScreenMode.FitToScreen) { + if (screenMode != SDLScreenMode.Fullscreen && screenMode != SDLScreenMode.FitToScreen) { width = MainSDLActivity.vm_width; height = MainSDLActivity.vm_height; } - if(reverse){ + if (reverse) { int temp = width; width = height; height = temp; @@ -1727,13 +1739,13 @@ public synchronized void doResize(boolean reverse, final Configuration newConfig .getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; if (portrait) { - if(Config.mouseMode != Config.MouseMode.External) { + if (Config.mouseMode != Config.MouseMode.External) { int height_n = (int) (width / (MainSDLActivity.vm_width / (float) MainSDLActivity.vm_height)); Log.d(TAG, "Resizing portrait: " + width + " x " + height_n); getHolder().setFixedSize(width, height_n); } } else { - if ( (screenMode == SDLScreenMode.Fullscreen || screenMode == SDLScreenMode.FitToScreen) + if ((screenMode == SDLScreenMode.Fullscreen || screenMode == SDLScreenMode.FitToScreen) && !MainSettingsManager.getAlwaysShowMenuToolbar(MainSDLActivity.this) && bar != null && bar.isShowing()) { height += bar.getHeight(); @@ -1751,7 +1763,6 @@ public void run() { }, 1000); - } // XXX: SDL is missing some key codes in sdl2-keymap.h @@ -1818,14 +1829,14 @@ public boolean onTouchProcess(View v, MotionEvent event) { float y = event.getY(0); float p = event.getPressure(0); - int relative = Config.mouseMode == Config.MouseMode.External? 0: 1; + int relative = Config.mouseMode == Config.MouseMode.External ? 0 : 1; int sdlMouseButton = 0; - if(event.getButtonState() == MotionEvent.BUTTON_PRIMARY) + if (event.getButtonState() == MotionEvent.BUTTON_PRIMARY) sdlMouseButton = Config.SDL_MOUSE_LEFT; - else if(event.getButtonState() == MotionEvent.BUTTON_SECONDARY) + else if (event.getButtonState() == MotionEvent.BUTTON_SECONDARY) sdlMouseButton = Config.SDL_MOUSE_RIGHT; - else if(event.getButtonState() == MotionEvent.BUTTON_TERTIARY) + else if (event.getButtonState() == MotionEvent.BUTTON_TERTIARY) sdlMouseButton = Config.SDL_MOUSE_MIDDLE; @@ -1837,13 +1848,13 @@ else if(event.getButtonState() == MotionEvent.BUTTON_TERTIARY) mouseUp = false; } if (action == MotionEvent.ACTION_MOVE) { - if(Config.mouseMode == Config.MouseMode.External) { + if (Config.mouseMode == Config.MouseMode.External) { //Log.d("SDL", "onTouch Absolute Move by=" + action + ", X,Y=" + (x) + "," + (y) + " P=" + p); - MainActivity.vmexecutor.onVectrasMouse(0, MotionEvent.ACTION_MOVE,0, x , y ); - }else { + MainActivity.vmexecutor.onVectrasMouse(0, MotionEvent.ACTION_MOVE, 0, x, y); + } else { //Log.d("SDL", "onTouch Relative Moving by=" + action + ", X,Y=" + (x - // old_x) + "," + (y - old_y) + " P=" + p); - MainActivity.vmexecutor.onVectrasMouse(0, MotionEvent.ACTION_MOVE,1, (x - old_x) * sensitivity_mult, (y - old_y) * sensitivity_mult); + MainActivity.vmexecutor.onVectrasMouse(0, MotionEvent.ACTION_MOVE, 1, (x - old_x) * sensitivity_mult, (y - old_y) * sensitivity_mult); } } @@ -1851,13 +1862,12 @@ else if(event.getButtonState() == MotionEvent.BUTTON_TERTIARY) old_x = x; old_y = y; - } - else if (event.getAction() == event.ACTION_UP ) { + } else if (event.getAction() == event.ACTION_UP) { //Log.d("SDL", "onTouch Up: " + sdlMouseButton); //XXX: it seems that the Button state is not available when Button up so // we should release all mouse buttons to be safe since we don't know which one fired the event - if(sdlMouseButton == Config.SDL_MOUSE_MIDDLE - ||sdlMouseButton == Config.SDL_MOUSE_RIGHT + if (sdlMouseButton == Config.SDL_MOUSE_MIDDLE + || sdlMouseButton == Config.SDL_MOUSE_RIGHT ) { MainActivity.vmexecutor.onVectrasMouse(sdlMouseButton, MotionEvent.ACTION_UP, relative, x, y); } else if (sdlMouseButton != 0) { @@ -1866,11 +1876,11 @@ else if (event.getAction() == event.ACTION_UP ) { //Or only the last one pressed if (lastMouseButtonDown > 0) { - if(lastMouseButtonDown == Config.SDL_MOUSE_MIDDLE - ||lastMouseButtonDown == Config.SDL_MOUSE_RIGHT + if (lastMouseButtonDown == Config.SDL_MOUSE_MIDDLE + || lastMouseButtonDown == Config.SDL_MOUSE_RIGHT ) { - MainActivity.vmexecutor.onVectrasMouse(lastMouseButtonDown, MotionEvent.ACTION_UP, relative,x, y); - }else + MainActivity.vmexecutor.onVectrasMouse(lastMouseButtonDown, MotionEvent.ACTION_UP, relative, x, y); + } else MainActivity.vmexecutor.onVectrasMouse(lastMouseButtonDown, MotionEvent.ACTION_UP, relative, x, y); } else { //ALl buttons @@ -1885,13 +1895,12 @@ else if (event.getAction() == event.ACTION_UP ) { } lastMouseButtonDown = -1; mouseUp = true; - } - else if (event.getAction() == event.ACTION_DOWN + } else if (event.getAction() == event.ACTION_DOWN && Config.mouseMode == Config.MouseMode.External ) { //XXX: Some touch events for touchscreen mode are primary so we force left mouse button - if(sdlMouseButton == 0 && MotionEvent.TOOL_TYPE_FINGER == event.getToolType(0)) { + if (sdlMouseButton == 0 && MotionEvent.TOOL_TYPE_FINGER == event.getToolType(0)) { sdlMouseButton = Config.SDL_MOUSE_LEFT; } @@ -1903,8 +1912,8 @@ else if (event.getAction() == event.ACTION_DOWN public boolean onTouch(View v, MotionEvent event) { boolean res = false; - if(Config.mouseMode == Config.MouseMode.External){ - res = onTouchProcess(v,event); + if (Config.mouseMode == Config.MouseMode.External) { + res = onTouchProcess(v, event); res = onTouchEventProcess(event); } return res; @@ -1949,13 +1958,13 @@ protected void setupVolume() { } public void setVolume(int volume) { - if(am!=null) + if (am != null) am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); } protected int getCurrentVolume() { int volumeTmp = 0; - if(am!=null) + if (am != null) volumeTmp = am.getStreamVolume(AudioManager.STREAM_MUSIC); return volumeTmp; } @@ -1989,13 +1998,13 @@ public boolean middleClick(final MotionEvent e, final int i) { Thread t = new Thread(new Runnable() { public void run() { Log.d("SDL", "Mouse Middle Click"); - MainActivity.vmexecutor.onVectrasMouse(Config.SDL_MOUSE_MIDDLE, MotionEvent.ACTION_DOWN, 1,-1, -1); + MainActivity.vmexecutor.onVectrasMouse(Config.SDL_MOUSE_MIDDLE, MotionEvent.ACTION_DOWN, 1, -1, -1); try { Thread.sleep(100); } catch (InterruptedException ex) { // Log.v("SDLSurface", "Interrupted: " + ex); } - MainActivity.vmexecutor.onVectrasMouse(Config.SDL_MOUSE_MIDDLE, MotionEvent.ACTION_UP, 1,-1, -1); + MainActivity.vmexecutor.onVectrasMouse(Config.SDL_MOUSE_MIDDLE, MotionEvent.ACTION_UP, 1, -1, -1); } }); t.start(); @@ -2015,7 +2024,7 @@ public void run() { } catch (InterruptedException ex) { // Log.v("doubletap", "Could not sleep"); } - MainActivity.vmexecutor.onVectrasMouse(Config.SDL_MOUSE_LEFT, MotionEvent.ACTION_UP, 1,0, 0); + MainActivity.vmexecutor.onVectrasMouse(Config.SDL_MOUSE_LEFT, MotionEvent.ACTION_UP, 1, 0, 0); try { Thread.sleep(50); } catch (InterruptedException ex) { @@ -2036,7 +2045,6 @@ public void run() { private boolean firstTouch = false; - private class GestureListener extends GestureDetector.SimpleOnGestureListener { @Override @@ -2051,10 +2059,10 @@ public void onLongPress(MotionEvent event) { // Log.d("SDL", "Long Press Action=" + event.getAction() + ", X,Y=" // + event.getX() + "," + event.getY() + " P=" // + event.getPressure()); - if(Config.mouseMode == Config.MouseMode.External) + if (Config.mouseMode == Config.MouseMode.External) return; - if(Config.enableDragOnLongPress) + if (Config.enableDragOnLongPress) dragPointer(event); } @@ -2062,7 +2070,7 @@ public boolean onSingleTapConfirmed(MotionEvent event) { float x1 = event.getX(); float y1 = event.getY(); - if(Config.mouseMode == Config.MouseMode.External) + if (Config.mouseMode == Config.MouseMode.External) return true; // Log.d("onSingleTapConfirmed", "Tapped at: (" + x1 + "," + y1 + @@ -2090,12 +2098,12 @@ public boolean onSingleTapConfirmed(MotionEvent event) { public boolean onDoubleTap(MotionEvent event) { // Log.d("onDoubleTap", "Tapped at: (" + event.getX() + "," + event.getY() + ")"); - if(Config.mouseMode == Config.MouseMode.External + if (Config.mouseMode == Config.MouseMode.External //&& MotionEvent.TOOL_TYPE_MOUSE == event.getToolType(0) ) return true; - if(!Config.enableDragOnLongPress) + if (!Config.enableDragOnLongPress) processDoubleTap(event); else doubleClick(event, 0); @@ -2152,7 +2160,7 @@ public boolean onGenericMotion(View v, MotionEvent event) { return true; case InputDevice.SOURCE_MOUSE: - if(Config.mouseMode == Config.MouseMode.Trackpad) + if (Config.mouseMode == Config.MouseMode.Trackpad) break; action = event.getActionMasked(); @@ -2166,7 +2174,7 @@ public boolean onGenericMotion(View v, MotionEvent event) { return true; case MotionEvent.ACTION_HOVER_MOVE: - if(Config.processMouseHistoricalEvents) { + if (Config.processMouseHistoricalEvents) { final int historySize = event.getHistorySize(); for (int h = 0; h < historySize; h++) { float ex = event.getHistoricalX(h); @@ -2197,11 +2205,10 @@ public boolean onGenericMotion(View v, MotionEvent event) { return false; } - private void processHoverMouse(float x,float y,float p, int action) { - + private void processHoverMouse(float x, float y, float p, int action) { - if(Config.mouseMode == Config.MouseMode.External) { + if (Config.mouseMode == Config.MouseMode.External) { //Log.d("SDL", "Mouse Hover: " + x + "," + y); MainActivity.vmexecutor.onVectrasMouse(0, action, 0, x, y); } diff --git a/app/src/main/java/com/vectras/qemu/MainSettingsManager.java b/app/src/main/java/com/vectras/qemu/MainSettingsManager.java index 25f6a63..f329eba 100644 --- a/app/src/main/java/com/vectras/qemu/MainSettingsManager.java +++ b/app/src/main/java/com/vectras/qemu/MainSettingsManager.java @@ -39,6 +39,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; import androidx.preference.Preference; @@ -47,8 +48,10 @@ import com.vectras.vm.R; import com.vectras.vm.SplashActivity; +import com.vectras.vm.VectrasApp; import java.util.List; +import java.util.Objects; public class MainSettingsManager extends AppCompatActivity implements PreferenceFragmentCompat.OnPreferenceStartFragmentCallback { @@ -204,19 +207,13 @@ public boolean onPreferenceChange(@NonNull Preference preference, } private void onNightMode() { - mHandler.postDelayed(new Runnable() { - @Override - public void run() { - Intent startActivity = new Intent(getContext(), SplashActivity.class); - int pendingIntentId = 123456; - PendingIntent pendingIntent = PendingIntent.getActivity(getContext(), pendingIntentId, startActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); - - AlarmManager mgr = (AlarmManager) MainSettingsManager.activity.getSystemService(Context.ALARM_SERVICE); - mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 500, pendingIntent); - - System.exit(0); - } - }, 300); + if (MainSettingsManager.getModeNight(activity)) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); + VectrasApp.getApp().setTheme(R.style.AppTheme); + } else { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); + VectrasApp.getApp().setTheme(R.style.AppTheme); + } } @Override @@ -252,8 +249,89 @@ public static class QemuPreferencesFragment extends PreferenceFragmentCompat public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (SDK_INT > 33) - findPreference("sharedFolder").setEnabled(false);/* - Preference pref = findPreference("customMemory"); + findPreference("sharedFolder").setEnabled(false); + + mHandler = new Handler(); + Preference pref = findPreference("vmArch"); + if (pref != null) { + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + + @Override + public boolean onPreferenceChange(@NonNull Preference preference, + Object newValue) { + onArch(); + return true; + } + + }); + } + Preference pref2 = findPreference("kvm"); + if (pref2 != null) { + pref2.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + + @Override + public boolean onPreferenceChange(@NonNull Preference preference, + Object newValue) { + onKvm(); + return true; + } + + private void onKvm() { + if (getKvm(activity)) + setMTTCG(activity, true); + else + setMTTCG(activity, false); + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + Intent startActivity = new Intent(getContext(), SplashActivity.class); + int pendingIntentId = 123456; + PendingIntent pendingIntent = PendingIntent.getActivity(getContext(), pendingIntentId, startActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); + + AlarmManager mgr = (AlarmManager) MainSettingsManager.activity.getSystemService(Context.ALARM_SERVICE); + mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 500, pendingIntent); + + System.exit(0); + } + }, 300); + } + + }); + } + Preference pref3 = findPreference("MTTCG"); + if (pref3 != null) { + pref3.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + + @Override + public boolean onPreferenceChange(@NonNull Preference preference, + Object newValue) { + onMttcg(); + return true; + } + + private void onMttcg() { + if (getMTTCG(activity)) + setKvm(activity, true); + else + setKvm(activity, false); + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + Intent startActivity = new Intent(getContext(), SplashActivity.class); + int pendingIntentId = 123456; + PendingIntent pendingIntent = PendingIntent.getActivity(getContext(), pendingIntentId, startActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); + + AlarmManager mgr = (AlarmManager) MainSettingsManager.activity.getSystemService(Context.ALARM_SERVICE); + mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 500, pendingIntent); + + System.exit(0); + } + }, 300); + } + + }); + } + /*Preference pref = findPreference("customMemory"); if (pref != null) { pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @@ -305,6 +383,21 @@ public void onPause() { findPreference("sharedFolder").setEnabled(false); } + private void onArch() { + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + Intent startActivity = new Intent(getContext(), SplashActivity.class); + int pendingIntentId = 123456; + PendingIntent pendingIntent = PendingIntent.getActivity(getContext(), pendingIntentId, startActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); + + AlarmManager mgr = (AlarmManager) MainSettingsManager.activity.getSystemService(Context.ALARM_SERVICE); + mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 500, pendingIntent); + + System.exit(0); + } + }, 300); + } @Override public void onCreatePreferences(Bundle bundle, String root_key) { @@ -316,7 +409,6 @@ public void onCreatePreferences(Bundle bundle, String root_key) { @Override public boolean onPreferenceChange(Preference pref, Object newValue) { - return true; } @@ -672,7 +764,18 @@ public static void setVmUi(Activity activity, String vmUi) { public static String getVmUi(Activity activity) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - return prefs.getString("vmUi", "VNC"); + return prefs.getString("vmUi", "SDL"); + } + public static void setSoundCard(Activity activity, String soundCard) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); + SharedPreferences.Editor edit = prefs.edit(); + edit.putString("soundCard", soundCard); + edit.apply(); + } + + public static String getSoundCard(Activity activity) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); + return prefs.getString("soundCard", "None"); } public static void setUsbTablet(Activity activity, boolean UsbTablet) { @@ -711,6 +814,30 @@ public static boolean getSharedFolder(Activity activity) { return prefs.getBoolean("sharedFolder", false); } + public static void setArch(Activity activity, String vmArch) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); + SharedPreferences.Editor edit = prefs.edit(); + edit.putString("vmArch", vmArch); + edit.apply(); + } + + public static String getArch(Activity activity) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); + return prefs.getString("vmArch", "X86_64"); + } + + public static void setKvm(Activity activity, boolean kvm) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); + SharedPreferences.Editor edit = prefs.edit(); + edit.putBoolean("kvm", kvm); + edit.apply(); + } + + public static boolean getKvm(Activity activity) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); + return prefs.getBoolean("kvm", false); + } + public static boolean isFirstLaunch(Activity activity) { PackageInfo pInfo = null; diff --git a/app/src/main/java/com/vectras/qemu/MainVNCActivity.java b/app/src/main/java/com/vectras/qemu/MainVNCActivity.java index c6995e0..ba21b87 100644 --- a/app/src/main/java/com/vectras/qemu/MainVNCActivity.java +++ b/app/src/main/java/com/vectras/qemu/MainVNCActivity.java @@ -13,7 +13,9 @@ import android.annotation.SuppressLint; import android.app.Activity; + import androidx.appcompat.app.AlertDialog; + import android.app.Dialog; import android.app.ProgressDialog; import android.content.DialogInterface; @@ -43,6 +45,7 @@ import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; +import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; @@ -55,6 +58,7 @@ import com.vectras.qemu.utils.FileUtils; import com.vectras.vm.Fragment.ControlersOptionsFragment; +import com.vectras.vm.Fragment.LoggerDialogFragment; import com.vectras.vm.MainActivity; import com.vectras.vm.R; import com.vectras.qemu.utils.Machine; @@ -133,7 +137,10 @@ public void onCreate(Bundle b) { btnLogs.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - FileUtils.viewVectrasLog(activity); + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + // Create and show the dialog. + LoggerDialogFragment newFragment = new LoggerDialogFragment(); + newFragment.show(ft, "Logger"); } }); shutdownBtn.setOnClickListener(new View.OnClickListener() { @@ -331,6 +338,21 @@ public void onItemClick(AdapterView parent, View view, int position, long id) } } }); + + ImageButton hideBtn = findViewById(R.id.visibilityButton); + hideBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + FrameLayout l = findViewById(R.id.mainControl); + if (l.getVisibility() == View.VISIBLE) { + l.setVisibility(View.GONE); + hideBtn.setImageResource(R.drawable.round_visibility_24); + } else { + l.setVisibility(View.VISIBLE); + hideBtn.setImageResource(R.drawable.round_visibility_off_24); + } + } + }); } private void keyDownUp(int keyEventCode) { @@ -1197,8 +1219,13 @@ public void onClick(DialogInterface dialog, int which) { public void onBackPressed() { super.onBackPressed(); - Machine.stopVM(activity); - return; + FrameLayout l = findViewById(R.id.mainControl); + if (l != null) { + if (l.getVisibility() == View.VISIBLE) { + l.setVisibility(View.GONE); + } else + l.setVisibility(View.VISIBLE); + } } public void onHideToolbar() { diff --git a/app/src/main/java/com/vectras/qemu/jni/StartVM.java b/app/src/main/java/com/vectras/qemu/jni/StartVM.java index 9bd925c..b7bc624 100644 --- a/app/src/main/java/com/vectras/qemu/jni/StartVM.java +++ b/app/src/main/java/com/vectras/qemu/jni/StartVM.java @@ -16,13 +16,16 @@ import com.vectras.qemu.utils.Machine; import com.vectras.qemu.utils.QmpClient; import com.vectras.qemu.utils.RamInfo; +import com.vectras.vm.AppConfig; import com.vectras.vm.MainActivity; import com.vectras.vm.logger.VectrasStatus; import com.vectras.vm.utils.UIUtils; import java.io.File; +import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Arrays; +import java.util.Objects; import org.json.JSONException; import org.json.JSONObject; @@ -135,24 +138,53 @@ public StartVM(Context context) throws Exception { shared_folder_path = Config.sharedFolder; //extra_params = Config.extra_params; this.context = context; - this.libqemu = FileUtils.getNativeLibDir(context) + "/libqemu-system-x86_64.so"; - this.arch = "x86_64"; + if (Objects.equals(MainSettingsManager.getArch(MainActivity.activity), "ARM")) { + this.libqemu = FileUtils.getNativeLibDir(context) + "/libqemu-system-arm.so"; + File libFile = new File(libqemu); + if (!libFile.exists()) { + this.libqemu = FileUtils.getNativeLibDir(context) + "/libqemu-system-aarch64.so"; + libFile = new File(libqemu); + } + this.arch = "arm"; + this.machine_type = "virt"; + this.disablehpet = 0; + this.disableacpi = 0; + this.disabletsc = 0; + this.cpu = "cortex-a57"; + } else if (Objects.equals(MainSettingsManager.getArch(MainActivity.activity), "X86_64")) { + this.libqemu = FileUtils.getNativeLibDir(context) + "/libqemu-system-x86_64.so"; + this.arch = "x86_64"; + this.machine_type = "pc"; + this.cpu = "qemu64"; + } + this.sound_card = MainSettingsManager.getSoundCard(MainActivity.activity); this.cpuNum = MainSettingsManager.getCpuNum(MainActivity.activity); - this.cpu = "qemu64"; - if (MainSettingsManager.getMTTCG(MainActivity.activity)) - this.enable_mttcg = 1; - else - this.enable_mttcg = 0; + this.vnc_allow_external = 0; this.enablleAvx = MainSettingsManager.getAvx(MainActivity.activity); this.tbSize = MainSettingsManager.getTbSize(MainActivity.activity); + if (MainSettingsManager.getKvm(MainActivity.activity)) { + this.enablekvm = 1; + this.cpu = "host"; + this.enable_mttcg = 0; + } else { + if (MainSettingsManager.getMTTCG(MainActivity.activity)) { + this.enable_mttcg = 1; + } + } + if (MainSettingsManager.getUsbTablet(MainActivity.activity)) this.mouse = "usb-tablet"; else this.mouse = "ps2"; + + if (new File(AppConfig.maindirpath + "/drive.iso").exists()) + cd_iso_path = AppConfig.maindirpath + "/drive.iso"; + else + cd_iso_path = null; } public static void onVMResolutionChanged(int width, int height) { diff --git a/app/src/main/java/com/vectras/vm/AppConfig.java b/app/src/main/java/com/vectras/vm/AppConfig.java index 7a71305..42771ac 100644 --- a/app/src/main/java/com/vectras/vm/AppConfig.java +++ b/app/src/main/java/com/vectras/vm/AppConfig.java @@ -1,34 +1,47 @@ package com.vectras.vm; +import android.app.Activity; +import android.content.Context; import android.os.Environment; import android.widget.ImageView.ScaleType; +import com.vectras.qemu.MainSettingsManager; + import java.io.File; import java.util.Hashtable; +import java.util.Objects; /** - * * @author dev */ public class AppConfig { - // App Config - public static final String vectrasWebsite = "https://vectras.netlify.com/"; - public static final String vectrasRaw = "https://raw.githubusercontent.com/epicstudios856/Vectras-windows-emulator/main/"; - public static final String vectrasLicense = vectrasRaw + "LICENSE.md"; - public static final String vectrasPrivacy = vectrasRaw + "PRIVACYANDPOLICY.md"; - public static final String vectrasTerms = vectrasRaw + "TERMSOFSERVICE.md"; - public static final String vectrasInfo = vectrasRaw + "info.md"; - public static final String vectrasRepo = "https://github.com/epicstudios856/Vectras-windows-emulator/tree/main/"; - public static final String updateJson = vectrasRaw + "UpdateConfig.json"; - public static final String blogJson = vectrasRaw + "news_list.json"; - public static final String storeJson = vectrasRaw + "store_list.json"; - public static final String romsJson = vectrasRaw + "roms.json"; - - // App config - public static final String datadirpath = SplashActivity.activity.getExternalFilesDir("data")+"/"; - public static final String sharedFolder = datadirpath + "Vectras/ProgramFiles/"; - public static final String basefiledir = datadirpath + "Vectras/.qemu/"; - public static final String maindirpath = datadirpath + "Vectras/"; + // App Config + public static final String vectrasWebsite = "https://vectras.netlify.com/"; + public static final String vectrasRaw = "https://raw.githubusercontent.com/epicstudios856/Vectras-windows-emulator/main/"; + public static final String vectrasLicense = vectrasRaw + "LICENSE.md"; + public static final String vectrasPrivacy = vectrasRaw + "PRIVACYANDPOLICY.md"; + public static final String vectrasTerms = vectrasRaw + "TERMSOFSERVICE.md"; + public static final String vectrasInfo = vectrasRaw + "info.md"; + public static final String vectrasRepo = "https://github.com/epicstudios856/Vectras-windows-emulator/tree/main/"; + public static final String updateJson = vectrasRaw + "UpdateConfig.json"; + public static final String blogJson = vectrasRaw + "news_list.json"; + public static final String storeJson = vectrasRaw + "store_list.json"; + + public static final String romsJson(Activity activity) { + if (Objects.equals(MainSettingsManager.getArch(activity), "X86_64")) { + return vectrasRaw + "roms.json"; + } else if (Objects.equals(MainSettingsManager.getArch(activity), "ARM")) { + return vectrasRaw + "roms-arm.json"; + } else { + return null; + } + } + + // App config + public static final String datadirpath = SplashActivity.activity.getExternalFilesDir("data") + "/"; + public static final String sharedFolder = datadirpath + "Vectras/ProgramFiles/"; + public static final String basefiledir = datadirpath + "Vectras/.qemu/"; + public static final String maindirpath = datadirpath + "Vectras/"; } diff --git a/app/src/main/java/com/vectras/vm/CustomRomActivity.java b/app/src/main/java/com/vectras/vm/CustomRomActivity.java new file mode 100644 index 0000000..1427357 --- /dev/null +++ b/app/src/main/java/com/vectras/vm/CustomRomActivity.java @@ -0,0 +1,454 @@ +package com.vectras.vm; + +import static android.content.Intent.ACTION_OPEN_DOCUMENT; + +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; + +import com.google.android.gms.ads.AdRequest; +import com.google.android.gms.ads.AdView; +import com.google.android.gms.ads.MobileAds; +import com.google.android.gms.ads.initialization.InitializationStatus; +import com.google.android.gms.ads.initialization.OnInitializationCompleteListener; +import com.google.android.material.textfield.TextInputEditText; +import com.google.android.material.textfield.TextInputLayout; +import com.vectras.qemu.MainSettingsManager; +import com.vectras.vm.MainRoms.DataMainRoms; +import com.vectras.vm.logger.VectrasStatus; +import com.vectras.vm.utils.FileUtils; +import com.vectras.vm.utils.UIUtils; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; + +import android.os.Environment; +import android.provider.DocumentsContract; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; + +public class CustomRomActivity extends AppCompatActivity { + + public TextInputEditText title, icon, drive, qemu; + public Button addRomBtn; + + public ProgressBar loadingPb; + + public static CustomRomActivity activity; + + private boolean isFilled(TextInputEditText TXT) { + if (TXT.getText().toString().trim().length() > 0) + return true; + else + return false; + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + + menu.add(0, 0, 0, "arch").setShortcut('3', 'c').setIcon(R.drawable.ic_arch).setShowAsAction(1); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case 0: + startActivity(new Intent(activity, SetArchActivity.class)); + return true; + case android.R.id.home: + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_custom_rom); + activity = this; + loadingPb = findViewById(R.id.loadingPb); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setDisplayShowHomeEnabled(true); + toolbar.setTitle("Custom Rom"); + AdView mAdView = findViewById(R.id.adView); + AdRequest adRequest = new AdRequest.Builder().build(); + mAdView.loadAd(adRequest); + MobileAds.initialize(this, new OnInitializationCompleteListener() { + @Override + public void onInitializationComplete(InitializationStatus initializationStatus) { + } + }); + title = findViewById(R.id.title); + icon = findViewById(R.id.icon); + drive = findViewById(R.id.drive); + qemu = findViewById(R.id.qemu); + TextInputLayout iconLayout = findViewById(R.id.iconField); + TextInputLayout driveLayout = findViewById(R.id.driveField); + TextView arch = findViewById(R.id.textArch); + arch.setText(MainSettingsManager.getArch(this)); + icon.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("image/jpeg"); + + // Optionally, specify a URI for the file that should appear in the + // system file picker when it loads. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.DIRECTORY_DOWNLOADS); + } + + startActivityForResult(intent, 1001); + } + }); + iconLayout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("image/jpeg"); + + // Optionally, specify a URI for the file that should appear in the + // system file picker when it loads. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.DIRECTORY_DOWNLOADS); + } + + startActivityForResult(intent, 1001); + } + }); + drive.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + + // Optionally, specify a URI for the file that should appear in the + // system file picker when it loads. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.DIRECTORY_DOWNLOADS); + } + + startActivityForResult(intent, 1002); + } + }); + driveLayout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + + // Optionally, specify a URI for the file that should appear in the + // system file picker when it loads. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.DIRECTORY_DOWNLOADS); + } + + startActivityForResult(intent, 1002); + } + }); + addRomBtn = findViewById(R.id.addRomBtn); + addRomBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + loadingPb.setVisibility(View.VISIBLE); + final File jsonFile = new File(AppConfig.maindirpath + "roms-data" + ".json"); + RomsJso obj = new RomsJso(); + if (jsonFile.exists()) { + try { + List data = new ArrayList<>(); + JSONArray jArray = null; + jArray = new JSONArray(FileUtils.readFromFile(MainActivity.activity, jsonFile)); + + try { + // 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"); + romsMainData.itemPath = json_data.getString("imgPath"); + romsMainData.itemExtra = json_data.getString("imgExtra"); + data.add(romsMainData); + } + + } catch (JSONException e) { + Toast.makeText(MainActivity.activity, e.toString(), Toast.LENGTH_LONG).show(); + } + + JSONObject jsonObject = obj.makeJSONObject(title.getText().toString(), icon.getText().toString(), MainSettingsManager.getArch(activity), drive.getText().toString(), qemu.getText().toString()); + jArray.put(jsonObject); + try { + Writer output = null; + output = new BufferedWriter(new FileWriter(jsonFile)); + output.write(jArray.toString().replace("\\", "").replace("//", "/")); + output.close(); + } catch (Exception e) { + UIUtils.toastLong(activity, e.toString()); + loadingPb.setVisibility(View.GONE); + } + } catch (JSONException e) { + loadingPb.setVisibility(View.GONE); + throw new RuntimeException(e); + } + } else { + JSONObject jsonObject = obj.makeJSONObject(title.getText().toString(), icon.getText().toString(), MainSettingsManager.getArch(activity), drive.getText().toString(), qemu.getText().toString()); + JSONArray jsonArray = new JSONArray(); + jsonArray.put(jsonObject); + try { + Writer output = null; + output = new BufferedWriter(new FileWriter(jsonFile)); + output.write(jsonArray.toString().replace("\\", "").replace("//", "/")); + output.close(); + } catch (Exception e) { + UIUtils.toastLong(activity, e.toString()); + } + VectrasStatus.logInfo("Welcome to Vectras ♡"); + } + MainActivity.activity.finish(); + finish(); + activity.startActivity(new Intent(activity, SplashActivity.class)); + } + }); + TextView textName = findViewById(R.id.textName); + TextWatcher afterTextChangedListener = new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // ignore + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + textName.setText(title.getText().toString()); + + if (isFilled(title) && isFilled(icon) && isFilled(drive)) + addRomBtn.setEnabled(true); + else + addRomBtn.setEnabled(false); + } + + @Override + public void afterTextChanged(Editable s) { + if (isFilled(title) && isFilled(icon) && isFilled(drive)) + addRomBtn.setEnabled(true); + else + addRomBtn.setEnabled(false); + } + }; + title.addTextChangedListener(afterTextChangedListener); + icon.addTextChangedListener(afterTextChangedListener); + drive.addTextChangedListener(afterTextChangedListener); + qemu.addTextChangedListener(afterTextChangedListener); + } + + public static class RomsJso extends JSONObject { + + public JSONObject makeJSONObject(String imgName, String imgIcon, String imgArch, String imgPath, String imgExtra) { + + JSONObject obj = new JSONObject(); + + try { + obj.put("imgName", imgName); + obj.put("imgIcon", imgIcon); + obj.put("imgArch", imgArch); + obj.put("imgPath", imgPath); + obj.put("imgExtra", imgExtra); + } catch (JSONException e) { + e.printStackTrace(); + } + + return obj; + } + } + + byte[] data; + + public String getPath(Uri uri) { + return FileUtils.getPath(this, uri); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent ReturnedIntent) { + super.onActivityResult(requestCode, resultCode, ReturnedIntent); + + LinearLayout custom = findViewById(R.id.custom); + if (requestCode == 1001 && resultCode == RESULT_OK) { + Uri content_describer = ReturnedIntent.getData(); + TextInputEditText icon = findViewById(R.id.icon); + File selectedFilePath = new File(getPath(content_describer)); + ImageView ivIcon = findViewById(R.id.ivIcon); + loadingPb.setVisibility(View.VISIBLE); + new Thread(new Runnable() { + @Override + public void run() { + FileInputStream File = null; + Bitmap selectedImage = null; + try { + File = (FileInputStream) getContentResolver().openInputStream(content_describer); + selectedImage = BitmapFactory.decodeStream(File); + Bitmap finalSelectedImage = selectedImage; + Runnable runnable = new Runnable() { + @Override + public void run() { + ivIcon.setImageBitmap(finalSelectedImage); + } + }; + activity.runOnUiThread(runnable); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } finally { + try { + try { + SaveImage(selectedImage, new File(AppConfig.maindirpath + "/icons/"), selectedFilePath.getName()); + } finally { + Runnable runnable = new Runnable() { + @Override + public void run() { + loadingPb.setVisibility(View.GONE); + icon.setText(AppConfig.maindirpath + "/icons/" + selectedFilePath.getName()); + } + }; + activity.runOnUiThread(runnable); + File.close(); + } + } catch (IOException e) { + Runnable runnable = new Runnable() { + @Override + public void run() { + loadingPb.setVisibility(View.GONE); + } + }; + activity.runOnUiThread(runnable); + MainActivity.UIAlert("error", e.toString(), activity); + } + + } + } + }).start(); + } else if (requestCode == 1002 && resultCode == RESULT_OK) { + Uri content_describer = ReturnedIntent.getData(); + File selectedFilePath = new File(getPath(content_describer)); + drive.setText(AppConfig.maindirpath + selectedFilePath.getName()); + loadingPb.setVisibility(View.VISIBLE); + custom.setVisibility(View.GONE); + new Thread(new Runnable() { + @Override + public void run() { + FileInputStream File = null; + try { + File = (FileInputStream) getContentResolver().openInputStream(content_describer); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + try { + try { + OutputStream out = new FileOutputStream(new File(AppConfig.maindirpath + selectedFilePath.getName())); + try { + // Transfer bytes from in to out + byte[] buf = new byte[1024]; + int len; + while ((len = File.read(buf)) > 0) { + out.write(buf, 0, len); + } + } finally { + out.close(); + } + } finally { + Runnable runnable = new Runnable() { + @Override + public void run() { + loadingPb.setVisibility(View.GONE); + custom.setVisibility(View.VISIBLE); + addRomBtn.setEnabled(isFilled(title) && isFilled(icon) && isFilled(drive)); + } + }; + activity.runOnUiThread(runnable); + File.close(); + } + } catch (IOException e) { + Runnable runnable = new Runnable() { + @Override + public void run() { + loadingPb.setVisibility(View.GONE); + custom.setVisibility(View.VISIBLE); + addRomBtn.setEnabled(isFilled(title) && isFilled(icon) && isFilled(drive)); + } + }; + activity.runOnUiThread(runnable); + MainActivity.UIAlert("error", e.toString(), activity); + } + } + }).start(); + } + if (requestCode == 1000 && resultCode == RESULT_CANCELED) { + finish(); + } + } + + private static void SaveImage(Bitmap finalBitmap, File imgDir, String name) { + File myDir = imgDir; + myDir.mkdirs(); + + String fname = name; + File file = new File(myDir, fname); + if (file.exists()) file.delete(); + try { + FileOutputStream out = new FileOutputStream(file); + finalBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out); + out.flush(); + out.close(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void onDestroy() { + super.onDestroy(); + File lol = new File(AppConfig.maindirpath + drive.getText().toString()); + try { + lol.delete(); + } catch (Exception e) { + } + } +} diff --git a/app/src/main/java/com/vectras/vm/Fragment/HomeFragment.java b/app/src/main/java/com/vectras/vm/Fragment/HomeFragment.java index 332fc79..d6fbcf4 100644 --- a/app/src/main/java/com/vectras/vm/Fragment/HomeFragment.java +++ b/app/src/main/java/com/vectras/vm/Fragment/HomeFragment.java @@ -17,12 +17,14 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.Toast; + import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import com.vectras.qemu.MainSettingsManager; import com.vectras.vm.RomsManagerActivity; import com.vectras.vm.MainRoms.AdapterMainRoms; import com.vectras.vm.MainRoms.DataMainRoms; @@ -33,6 +35,7 @@ import com.vectras.vm.MainActivity; import com.vectras.vm.utils.FileUtils; import com.vectras.vm.utils.UIUtils; + import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -44,120 +47,110 @@ import java.net.URL; import java.util.ArrayList; import java.util.List; + import javax.net.ssl.HttpsURLConnection; + import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; public class HomeFragment extends Fragment { - View view; - public static RecyclerView mRVMainRoms; - public static LinearLayout romsLayout; + public static View view; + public static RecyclerView mRVMainRoms; + public static LinearLayout romsLayout; public static AdapterMainRoms mMainAdapter; - public MainActivity activity; - public static JSONArray jArray; - public static List data; - - /*private ImageButton mStop; - private ImageButton mRestart;*/ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // TODO Auto-generated method stub - - activity = MainActivity.activity; - - view = inflater.inflate(R.layout.home_fragment, container, false); - - romsLayout = view.findViewById(R.id.romsLayout); - - SwipeRefreshLayout refreshRoms = view.findViewById(R.id.refreshRoms); - - refreshRoms.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { - @Override - public void onRefresh() { - data=new ArrayList<>(); - - try { - - jArray = new JSONArray(FileUtils.readFromFile(MainActivity.activity, new File(AppConfig.maindirpath - + "roms-data.json"))); - - // Extract data from json and store into ArrayList as class objects - for(int i=0;i(); - - try { - - jArray = new JSONArray(FileUtils.readFromFile(MainActivity.activity, new File(AppConfig.maindirpath - + "roms-data.json"))); - - // Extract data from json and store into ArrayList as class objects - for(int i=0;i data; + + /*private ImageButton mStop; + private ImageButton mRestart;*/ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + // TODO Auto-generated method stub + + activity = MainActivity.activity; + + view = inflater.inflate(R.layout.home_fragment, container, false); + + romsLayout = view.findViewById(R.id.romsLayout); + + SwipeRefreshLayout refreshRoms = view.findViewById(R.id.refreshRoms); + + refreshRoms.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + loadDataVbi(); + mMainAdapter.notifyItemRangeChanged(0, mMainAdapter.data.size()); + refreshRoms.setRefreshing(false); + } + }); + loadDataVbi(); + + return view; + } + + public static void loadDataVbi() { + data = new ArrayList<>(); + + try { + + jArray = new JSONArray(FileUtils.readFromFile(MainActivity.activity, 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"); + romsMainData.itemExtra = json_data.getString("imgExtra"); + try { + if (json_data.getString("imgArch").equals(MainSettingsManager.getArch(MainActivity.activity))) + data.add(romsMainData); + } catch (JSONException ignored) { + data.add(romsMainData); + } + } + + // Setup and Handover data to recyclerview + mRVMainRoms = (RecyclerView) HomeFragment.view.findViewById(R.id.mRVMainRoms); + mMainAdapter = new AdapterMainRoms(MainActivity.activity, data); + mRVMainRoms.setAdapter(mMainAdapter); + mRVMainRoms.setLayoutManager(new GridLayoutManager(MainActivity.activity, 2)); + + } catch (JSONException e) { + Toast.makeText(MainActivity.activity, e.toString(), Toast.LENGTH_LONG).show(); + } + } + + /** + * CHECK WHETHER INTERNET CONNECTION IS AVAILABLE OR NOT + */ + public boolean checkConnection(Context context) { + final ConnectivityManager connMgr = (ConnectivityManager) context + .getSystemService(Context.CONNECTIVITY_SERVICE); + + if (connMgr != null) { + NetworkInfo activeNetworkInfo = connMgr.getActiveNetworkInfo(); + + if (activeNetworkInfo != null) { // connected to the internet + // connected to the mobile provider's data plan + if (activeNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI) { + // connected to wifi + return true; + } else + return activeNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE; + } + } + return false; + } } diff --git a/app/src/main/java/com/vectras/vm/Fragment/LoggerDialogFragment.java b/app/src/main/java/com/vectras/vm/Fragment/LoggerDialogFragment.java new file mode 100644 index 0000000..0a9c61d --- /dev/null +++ b/app/src/main/java/com/vectras/vm/Fragment/LoggerDialogFragment.java @@ -0,0 +1,91 @@ +package com.vectras.vm.Fragment; + +import android.app.Activity; +import android.app.Dialog; +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; + +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.Toast; + +import com.vectras.qemu.MainSettingsManager; +import com.vectras.vm.R; +import com.vectras.vm.VectrasApp; +import com.vectras.vm.adapter.LogsAdapter; +import com.vectras.vm.logger.VectrasStatus; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Timer; +import java.util.TimerTask; + +public class LoggerDialogFragment extends DialogFragment { + + private final String CREDENTIAL_SHARED_PREF = "settings_prefs"; + private LogsAdapter mLogAdapter; + private RecyclerView logList; + private Timer _timer = new Timer(); + private TimerTask t; + Activity activity; + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + activity = getActivity(); + final Dialog alertDialog = new Dialog(getActivity(), R.style.MainDialogTheme); + alertDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + alertDialog.getWindow().setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT)); + alertDialog.setContentView(R.layout.fragment_logs); + LinearLayoutManager layoutManager = new LinearLayoutManager(VectrasApp.getApp()); + mLogAdapter = new LogsAdapter(layoutManager, VectrasApp.getApp()); + logList = (RecyclerView) alertDialog.findViewById(R.id.recyclerLog); + logList.setAdapter(mLogAdapter); + logList.setLayoutManager(layoutManager); + mLogAdapter.scrollToLastPosition(); + try { + Process process = Runtime.getRuntime().exec("logcat -e"); + BufferedReader bufferedReader = new BufferedReader( + new InputStreamReader(process.getInputStream())); + Process process2 = Runtime.getRuntime().exec("logcat -w"); + BufferedReader bufferedReader2 = new BufferedReader( + new InputStreamReader(process2.getInputStream())); + + t = new TimerTask() { + @Override + public void run() { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + try { + if (bufferedReader.readLine() != null || bufferedReader2.readLine() != null) { + String logLine = bufferedReader.readLine(); + String logLine2 = bufferedReader2.readLine(); + VectrasStatus.logError("[E] "+logLine+""); + VectrasStatus.logError("[W] "+logLine2+""); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }); + } + }; + _timer.scheduleAtFixedRate(t, (int) (0), (int) (100)); + } catch (IOException e) { + Toast.makeText(activity, "There was an error: " + Log.getStackTraceString(e), Toast.LENGTH_LONG).show(); + e.printStackTrace(); + } + alertDialog.show(); + return alertDialog; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/vectras/vm/MainActivity.java b/app/src/main/java/com/vectras/vm/MainActivity.java index 225bf2e..ca0ee3f 100644 --- a/app/src/main/java/com/vectras/vm/MainActivity.java +++ b/app/src/main/java/com/vectras/vm/MainActivity.java @@ -1,5 +1,6 @@ package com.vectras.vm; +import static android.content.Intent.ACTION_OPEN_DOCUMENT; import static android.os.Build.VERSION.SDK_INT; import android.androidVNC.RfbProto; @@ -18,7 +19,10 @@ import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; @@ -28,12 +32,15 @@ import android.os.Looper; import android.os.StrictMode; import android.preference.PreferenceManager; +import android.provider.DocumentsContract; import android.text.Html; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.view.Window; +import android.view.WindowManager; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; @@ -61,6 +68,8 @@ import com.google.android.gms.ads.interstitial.InterstitialAd; import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback; import com.google.android.material.button.MaterialButton; +import com.google.android.material.elevation.SurfaceColors; +import com.google.android.material.textfield.TextInputEditText; import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseUser; import com.vectras.qemu.Config; @@ -88,7 +97,14 @@ import org.json.JSONException; import org.json.JSONObject; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; @@ -136,6 +152,8 @@ public void onClick(DialogInterface dialog, int which) { ad.show(); } + public static ProgressBar loadingPbb; + /** * Called when the activity is first created. */ @@ -143,6 +161,7 @@ public void onClick(DialogInterface dialog, int which) { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + loadingPbb = findViewById(R.id.loadingPb); activity = this; clearNotifications(); setupFolders(); @@ -154,7 +173,7 @@ public void onCreate(Bundle savedInstanceState) { this.setupWidgets(); initNavigationMenu(); FileInstaller.installFiles(activity, false); - + getWindow().setNavigationBarColor(SurfaceColors.SURFACE_2.getColor(this)); //updateApp(true); //mAuth = FirebaseAuth.getInstance(); } @@ -231,6 +250,8 @@ public boolean onOptionsItemSelected(MenuItem item) { } else if (id == R.id.installRoms) { startActivity(new Intent(activity, RomsManagerActivity.class)); + } else if (id == R.id.arch) { + startActivity(new Intent(activity, SetArchActivity.class)); } return super.onOptionsItemSelected(item); @@ -371,12 +392,62 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { //Check to see which item was being clicked and perform appropriate action int id = menuItem.getItemId(); if (id == R.id.navigation_item_info) { - startActivity(new Intent(activity, AboutActivity.class)); + if (SDK_INT > 33) + UIAlert("ANDROID 13+", "sorry android 13+ have storage issues and we are working on it", activity); + else + startActivity(new Intent(activity, AboutActivity.class)); } else if (id == R.id.navigation_item_website) { String tw = AppConfig.vectrasWebsite; Intent w = new Intent(Intent.ACTION_VIEW); w.setData(Uri.parse(tw)); startActivity(w); + } else if (id == R.id.navigation_item_import_iso) { + if (new File(AppConfig.maindirpath + "/drive.iso").exists()) { + AlertDialog ad; + ad = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create(); + ad.setTitle("REPLACE ISO"); + ad.setMessage("there is iso imported you want to replace it?"); + ad.setButton(Dialog.BUTTON_POSITIVE, "REPLACE", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + Intent intent = new Intent(ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + + // Optionally, specify a URI for the file that should appear in the + // system file picker when it loads. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.DIRECTORY_DOWNLOADS); + } + + startActivityForResult(intent, 1004); + return; + } + }); + ad.setButton(Dialog.BUTTON_NEGATIVE, "REMOVE", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + File isoFile = new File(AppConfig.maindirpath + "/drive.iso"); + try { + isoFile.delete(); + } catch (Exception e) { + throw new RuntimeException(e); + } + return; + } + }); + ad.show(); + } else { + Intent intent = new Intent(ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + + // Optionally, specify a URI for the file that should appear in the + // system file picker when it loads. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.DIRECTORY_DOWNLOADS); + } + + startActivityForResult(intent, 1004); + } } else if (id == R.id.navigation_item_view_logs) { FileUtils.viewVectrasLog(activity); } else if (id == R.id.navigation_item_settings) { @@ -478,8 +549,8 @@ public void onInitializationComplete(InitializationStatus initializationStatus) AlertDialog alertDialog; alertDialog = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create(); alertDialog.setTitle("JOIN US ON TELEGRAM"); - TextView title = alertDialog.findViewById(R.id.title_text); - ObjectAnimator rgbAnim=ObjectAnimator.ofObject(title,"textColor",new ArgbEvaluator(), Color.RED,Color.GREEN,Color.BLUE); + TextView title = alertDialog.findViewById(android.R.id.title); + ObjectAnimator rgbAnim = ObjectAnimator.ofObject(title, "textColor", new ArgbEvaluator(), Color.RED, Color.GREEN, Color.BLUE); rgbAnim.setDuration(1000); rgbAnim.setRepeatMode(ValueAnimator.REVERSE); rgbAnim.setRepeatCount(ValueAnimator.INFINITE); @@ -545,10 +616,19 @@ public void onDestroy() { @Override public void onStart() { super.onStart(); - if (MainSettingsManager.getVirtio(activity)) { - Config.hd_if_type = "virtio"; - } else { - Config.hd_if_type = "ide"; + try { + HomeFragment.loadDataVbi(); + } catch (Exception ignored) { + + } + if (MainSettingsManager.getArch(activity) == "X86_64") { + if (MainSettingsManager.getVirtio(activity)) { + Config.hd_if_type = "virtio"; + } else { + Config.hd_if_type = "ide"; + } + } else if (MainSettingsManager.getArch(activity) == "ARM") { + Config.hd_if_type = "scsi"; } setupFolders(); Config.ui = MainSettingsManager.getVmUi(activity); @@ -822,10 +902,18 @@ public static void setupNativeLibs() { public static void loadQEMULib() { - try { - System.loadLibrary("qemu-system-i386"); - } catch (Error ex) { - System.loadLibrary("qemu-system-x86_64"); + if (Objects.equals(MainSettingsManager.getArch(activity), "X86_64")) { + try { + System.loadLibrary("qemu-system-i386"); + } catch (Error ex) { + System.loadLibrary("qemu-system-x86_64"); + } + } else if (Objects.equals(MainSettingsManager.getArch(activity), "ARM")) { + try { + System.loadLibrary("qemu-system-arm"); + } catch (Error ex) { + System.loadLibrary("qemu-system-aarch64"); + } } } @@ -993,11 +1081,47 @@ public void run() { t.start(); } - public static void toggleVisibility(View view) { - if (view.getVisibility() == View.VISIBLE) { - view.setVisibility(View.GONE); - } else if (view.getVisibility() == View.GONE || view.getVisibility() == View.INVISIBLE) { - view.setVisibility(View.VISIBLE); + public String getPath(Uri uri) { + return com.vectras.vm.utils.FileUtils.getPath(this, uri); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent ReturnedIntent) { + super.onActivityResult(requestCode, resultCode, ReturnedIntent); + if (requestCode == 1004 && resultCode == RESULT_OK) { + Uri content_describer = ReturnedIntent.getData(); + File selectedFilePath = new File(getPath(content_describer)); + FileInputStream isoFile = null; + try { + isoFile = (FileInputStream) getContentResolver().openInputStream(content_describer); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + if (selectedFilePath.toString().endsWith(".iso")) { + loadingPbb.setVisibility(View.VISIBLE); + try { + try { + OutputStream out = new FileOutputStream(new File(AppConfig.maindirpath + "/drive.iso")); + try { + // Transfer bytes from in to out + byte[] buf = new byte[1024]; + int len; + while ((len = isoFile.read(buf)) > 0) { + out.write(buf, 0, len); + } + } finally { + out.close(); + } + } finally { + loadingPbb.setVisibility(View.GONE); + isoFile.close(); + } + } catch (IOException e) { + loadingPbb.setVisibility(View.GONE); + UIAlert("error", e.toString(), activity); + } + } else + UIAlert("NOT VAILED FILE", "please select iso file", activity); } } diff --git a/app/src/main/java/com/vectras/vm/MainRoms/AdapterMainRoms.java b/app/src/main/java/com/vectras/vm/MainRoms/AdapterMainRoms.java index 85c9ac4..86cdeb8 100644 --- a/app/src/main/java/com/vectras/vm/MainRoms/AdapterMainRoms.java +++ b/app/src/main/java/com/vectras/vm/MainRoms/AdapterMainRoms.java @@ -6,12 +6,15 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; +import android.widget.Button; +import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; @@ -28,9 +31,14 @@ import com.vectras.vm.R; import com.vectras.vm.utils.UIUtils; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; +import java.io.IOException; import java.io.Writer; import java.util.Collections; import java.util.List; @@ -66,8 +74,45 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) final MyHolder myHolder = (MyHolder) holder; final DataMainRoms current = data.get(position); myHolder.textName.setText(current.itemName); + myHolder.textArch.setText(current.itemArch); Bitmap bmImg = BitmapFactory.decodeFile(current.itemIcon); myHolder.ivIcon.setImageBitmap(bmImg); + myHolder.optionsBtn.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + Dialog d; + d = new Dialog(MainActivity.activity); + d.setTitle(current.itemName); + d.setContentView(R.layout.rom_options_dialog); + TextView qemu = d.findViewById(R.id.qemu); + qemu.setText(current.itemExtra); + Button saveRomBtn = d.findViewById(R.id.saveRomBtn); + saveRomBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + final File jsonFile = new File(AppConfig.maindirpath + "roms-data" + ".json"); + current.itemExtra = qemu.getText().toString(); + try { + JSONObject jObj = HomeFragment.jArray.getJSONObject(position); + jObj.put("imgExtra", qemu.getText().toString()); + HomeFragment.jArray.put(position, jObj); + } catch (JSONException e) { + throw new RuntimeException(e); + } + try { + Writer output = null; + output = new BufferedWriter(new FileWriter(jsonFile)); + output.write(HomeFragment.jArray.toString()); + output.close(); + } catch (Exception e) { + UIUtils.toastLong(MainActivity.activity, e.toString()); + } finally { + d.dismiss(); + } + } + }); + d.show(); + } + }); myHolder.cdRoms.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { @@ -90,7 +135,43 @@ public void run() { myHolder.cdRoms.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { - showDialog(current.itemName, current.itemPath, current.itemIcon); + AlertDialog ad; + ad = new AlertDialog.Builder(MainActivity.activity, R.style.MainDialogTheme).create(); + ad.setTitle("Remove " + current.itemName); + ad.setMessage("Are you sure?"); + ad.setButton(Dialog.BUTTON_NEGATIVE, "REMOVE " + current.itemName, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + File file = new File(current.itemPath); + try { + file.delete(); + } catch (Exception e) { + UIUtils.toastLong(MainActivity.activity, e.toString()); + } finally { + } + HomeFragment.mMainAdapter = new AdapterMainRoms(MainActivity.activity, HomeFragment.data); + HomeFragment.data.remove(position); + HomeFragment.mRVMainRoms.setAdapter(HomeFragment.mMainAdapter); + HomeFragment.mRVMainRoms.setLayoutManager(new GridLayoutManager(MainActivity.activity, 2)); + HomeFragment.jArray.remove(position); + try { + Writer output = null; + File jsonFile = new File(AppConfig.maindirpath + "roms-data" + ".json"); + output = new BufferedWriter(new FileWriter(jsonFile)); + output.write(HomeFragment.jArray.toString()); + output.close(); + } catch (Exception e) { + UIUtils.toastLong(MainActivity.activity, e.toString()); + } + UIUtils.toastLong(MainActivity.activity, current.itemName + " are removed successfully!"); + return; + } + }); + ad.setButton(Dialog.BUTTON_POSITIVE, "CANCEL", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + return; + } + }); + ad.show(); return false; } }); @@ -114,9 +195,11 @@ public void onClick(View v) { ad.setButton(Dialog.BUTTON_NEGATIVE, "REMOVE " + title, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { File file = new File(path); - file.delete(); - File fileIcon = new File(pathIcon); - fileIcon.delete(); + try { + file.delete(); + } catch (Exception e) { + throw new RuntimeException(e); + } HomeFragment.mMainAdapter = new AdapterMainRoms(MainActivity.activity, HomeFragment.data); HomeFragment.data.remove(currentPos); @@ -163,15 +246,18 @@ public int getItemCount() { class MyHolder extends RecyclerView.ViewHolder { CardView cdRoms; - TextView textName; + TextView textName, textArch; ImageView ivIcon; + ImageButton optionsBtn; // create constructor to get widget reference public MyHolder(View itemView) { super(itemView); cdRoms = (CardView) itemView.findViewById(R.id.cdItem); textName = (TextView) itemView.findViewById(R.id.textName); + textArch = (TextView) itemView.findViewById(R.id.textArch); ivIcon = (ImageView) itemView.findViewById(R.id.ivIcon); + optionsBtn = (ImageButton) itemView.findViewById(R.id.optionsButton); } } diff --git a/app/src/main/java/com/vectras/vm/MainRoms/DataMainRoms.java b/app/src/main/java/com/vectras/vm/MainRoms/DataMainRoms.java index 75506cd..cc6a4fd 100644 --- a/app/src/main/java/com/vectras/vm/MainRoms/DataMainRoms.java +++ b/app/src/main/java/com/vectras/vm/MainRoms/DataMainRoms.java @@ -2,6 +2,7 @@ public class DataMainRoms { public String itemIcon; public String itemName; + public String itemArch; public String itemPath; public String itemExtra; } diff --git a/app/src/main/java/com/vectras/vm/Roms/AdapterRoms.java b/app/src/main/java/com/vectras/vm/Roms/AdapterRoms.java index 854ae2d..f3a8a55 100644 --- a/app/src/main/java/com/vectras/vm/Roms/AdapterRoms.java +++ b/app/src/main/java/com/vectras/vm/Roms/AdapterRoms.java @@ -75,14 +75,15 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) myHolder.textSize.setText(current.itemSize); myHolder.checkBox.setChecked(position == mSelectedItem); if (current.itemAvail) { - myHolder.textAvail.setText("availability: available"); - myHolder.textAvail.setTextColor(Color.GREEN); - } else if (!current.itemAvail) { - myHolder.textAvail.setText("availability: unavailable"); - myHolder.textAvail.setTextColor(Color.RED); - myHolder.checkBox.setEnabled(false); - } - if (current.itemAvail) + if (FileUtils.fileValid(RomsManagerActivity.activity, AppConfig.maindirpath + current.itemPath)) { + myHolder.checkBox.setEnabled(false); + myHolder.textAvail.setTextColor(Color.BLUE); + myHolder.textAvail.setText("(installed)"); + } else { + myHolder.checkBox.setEnabled(true); + myHolder.textAvail.setTextColor(Color.GREEN); + myHolder.textAvail.setText("availability: available"); + } myHolder.checkBox.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -96,6 +97,11 @@ public void onClick(View v) { RomsManagerActivity.selectedIcon = current.itemIcon; } }); + } else { + myHolder.textAvail.setText("availability: unavailable"); + myHolder.textAvail.setTextColor(Color.RED); + myHolder.checkBox.setEnabled(false); + } } diff --git a/app/src/main/java/com/vectras/vm/RomsManagerActivity.java b/app/src/main/java/com/vectras/vm/RomsManagerActivity.java index 4c8a2ff..8daa6a9 100644 --- a/app/src/main/java/com/vectras/vm/RomsManagerActivity.java +++ b/app/src/main/java/com/vectras/vm/RomsManagerActivity.java @@ -23,22 +23,29 @@ import android.provider.MediaStore; import android.text.Html; import android.util.Log; +import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.webkit.URLUtil; import android.widget.CheckBox; import android.widget.CompoundButton; +import android.widget.ProgressBar; +import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; +import androidx.cardview.widget.CardView; +import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.button.MaterialButtonToggleGroup; +import com.vectras.qemu.MainSettingsManager; import com.vectras.vm.AppConfig; +import com.vectras.vm.Fragment.HomeFragment; import com.vectras.vm.MainRoms.AdapterMainRoms; import com.vectras.vm.MainRoms.DataMainRoms; import com.vectras.vm.Roms.AdapterRoms; @@ -110,6 +117,31 @@ public class RomsManagerActivity extends AppCompatActivity { public MaterialButton androidToggle; public MaterialButton otherToggle; + public ProgressBar loadingPb; + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + + menu.add(0, 0, 0, "arch").setShortcut('3', 'c').setIcon(R.drawable.ic_arch).setShowAsAction(1); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case 0: + startActivity(new Intent(activity, SetArchActivity.class)); + return true; + case android.R.id.home: + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } + /** * Called when the activity is first created. */ @@ -119,6 +151,7 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); activity = this; setContentView(R.layout.activity_roms_manager); + loadingPb = findViewById(R.id.loadingPb); filterToggle = findViewById(R.id.filterToggle); windowsToggle = findViewById(R.id.windowsToggle); linuxToggle = findViewById(R.id.linuxToggle); @@ -163,6 +196,7 @@ public void onButtonChecked(MaterialButtonToggleGroup group, int checkedId, bool setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); + setTitle("Roms Manager " + MainSettingsManager.getArch(activity)); new RomsManagerActivity.AsyncLogin().execute(); new Thread(new Runnable() { @@ -189,7 +223,7 @@ public void run() { in.close(); } catch (Exception e) { acceptLiceneseChkBox.setEnabled(false); - UIUtils.toastLong(activity, "no internet connection "+e.toString()); + UIUtils.toastLong(activity, "no internet connection " + e.toString()); } //since we are in background thread, to post results we have to go back to ui thread. do the following for that @@ -227,6 +261,15 @@ public void onClick(View view) { } }); + CardView custom = (CardView) findViewById(R.id.cdCustom); + + custom.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View view) { + startActivity(new Intent(activity, CustomRomActivity.class)); + } + }); } public static void UIAlertLicense(String title, String html, final Activity activity) { @@ -270,7 +313,7 @@ protected void onPreExecute() { protected String doInBackground(String... params) { HttpsURLConnection con = null; try { - URL u = new URL(AppConfig.romsJson); + URL u = new URL(AppConfig.romsJson(activity)); con = (HttpsURLConnection) u.openConnection(); con.connect(); @@ -349,23 +392,36 @@ protected void onPostExecute(String result) { } - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - if (item.getItemId() == android.R.id.home) { - finish(); + public static class RomsJso extends JSONObject { + + public JSONObject makeJSONObject(String imgName, String imgIcon, String imgArch, String imgPath, String imgExtra) { + + JSONObject obj = new JSONObject(); + + try { + obj.put("imgName", imgName); + obj.put("imgIcon", imgIcon); + obj.put("imgArch", imgArch); + obj.put("imgPath", imgPath); + obj.put("imgExtra", imgExtra); + } catch (JSONException e) { + e.printStackTrace(); + } + + return obj; } - return super.onOptionsItemSelected(item); } - public class RomsJso extends JSONObject { + public static class RomsJso2 extends JSONObject { - public JSONObject makeJSONObject(String imgName, String imgIcon, String imgPath, String imgExtra) { + public JSONObject makeJSONObject(String imgName, String imgIcon, String imgArch, String imgPath, String imgExtra) { JSONObject obj = new JSONObject(); try { obj.put("imgName", imgName); obj.put("imgIcon", imgIcon); + obj.put("imgArch", imgArch); obj.put("imgPath", imgPath); obj.put("imgExtra", imgExtra); } catch (JSONException e) { @@ -384,7 +440,7 @@ private void startIconDownload() { public void onFirstStartup() { if (selected) { - if (FileUtils.fileValid(activity, AppConfig.maindirpath + selectedPath) && !FileUtils.fileValid(activity, AppConfig.maindirpath + "icons/" + selectedPath.replace(".IMG", ".jpg"))) { + if (FileUtils.fileValid(activity, AppConfig.maindirpath + selectedPath)) { SharedPreferences credentials = activity.getSharedPreferences(CREDENTIAL_SHARED_PREF, Context.MODE_PRIVATE); ProgressDialog mProgressDialog = new ProgressDialog(this, R.style.MainDialogTheme); mProgressDialog.setTitle("Data Setup"); @@ -396,64 +452,78 @@ public void onFirstStartup() { editor.putBoolean("isFirstLaunch", Boolean.TRUE); editor.apply(); RomsJso obj = new RomsJso(); - startIconDownload(); - final File jsonFile = new File(AppConfig.maindirpath + "roms-data" + ".json"); - - if (jsonFile.exists()) { + try { + startIconDownload(); + } catch (Exception e) { + File file = new File(selectedPath); try { - List data = new ArrayList<>(); - JSONArray jArray = new JSONArray(FileUtils.readFromFile(MainActivity.activity, jsonFile)); + file.delete(); + } catch (Exception er) { + throw new RuntimeException(er); + } + throw new RuntimeException(e); + } finally { + mProgressDialog.dismiss(); + final File jsonFile = new File(AppConfig.maindirpath + "roms-data" + ".json"); + if (jsonFile.exists()) { try { - // 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"); - romsMainData.itemPath = json_data.getString("imgPath"); - romsMainData.itemExtra = json_data.getString("imgExtra"); - data.add(romsMainData); + List data = new ArrayList<>(); + JSONArray jArray = new JSONArray(FileUtils.readFromFile(MainActivity.activity, jsonFile)); + + try { + // 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"); + romsMainData.itemPath = json_data.getString("imgPath"); + romsMainData.itemExtra = json_data.getString("imgExtra"); + data.add(romsMainData); + } + + } catch (JSONException e) { + Toast.makeText(MainActivity.activity, e.toString(), Toast.LENGTH_LONG).show(); } + JSONObject jsonObject = obj.makeJSONObject(selectedName, AppConfig.maindirpath + "icons/" + selectedPath.replace(".IMG", ".jpg"), MainSettingsManager.getArch(activity), AppConfig.maindirpath + selectedPath, selectedExtra); + jArray.put(jsonObject); + try { + Writer output = null; + output = new BufferedWriter(new FileWriter(jsonFile)); + output.write(jArray.toString().replace("\\", "").replace("//", "/")); + output.close(); + } catch (Exception e) { + UIUtils.toastLong(activity, e.toString()); + } } catch (JSONException e) { - Toast.makeText(MainActivity.activity, e.toString(), Toast.LENGTH_LONG).show(); + UIUtils.toastLong(activity, e.toString()); } - - JSONObject jsonObject = obj.makeJSONObject(selectedName, AppConfig.maindirpath + "icons/" + selectedPath.replace(".IMG", ".jpg"), AppConfig.maindirpath + selectedPath, selectedExtra); - jArray.put(jsonObject); + MainActivity.activity.finish(); + } else { + JSONObject jsonObject = obj.makeJSONObject(selectedName, AppConfig.maindirpath + "icons/" + selectedPath.replace(".IMG", ".jpg"), MainSettingsManager.getArch(activity), AppConfig.maindirpath + selectedPath, selectedExtra); + JSONArray jsonArray = new JSONArray(); + jsonArray.put(jsonObject); try { Writer output = null; output = new BufferedWriter(new FileWriter(jsonFile)); - output.write(jArray.toString().replace("\\", "").replace("//", "/")); + output.write(jsonArray.toString().replace("\\", "").replace("//", "/")); output.close(); } catch (Exception e) { UIUtils.toastLong(activity, e.toString()); } - } catch (JSONException e) { - UIUtils.toastLong(activity, e.toString()); - } - } else { - JSONObject jsonObject = obj.makeJSONObject(selectedName, AppConfig.maindirpath + "icons/" + selectedPath.replace(".IMG", ".jpg"), AppConfig.maindirpath + selectedPath, selectedExtra); - JSONArray jsonArray = new JSONArray(); - jsonArray.put(jsonObject); - try { - Writer output = null; - output = new BufferedWriter(new FileWriter(jsonFile)); - output.write(jsonArray.toString().replace("\\", "").replace("//", "/")); - output.close(); - } catch (Exception e) { - UIUtils.toastLong(activity, e.toString()); + VectrasStatus.logInfo("Welcome to Vectras ♡"); } - VectrasStatus.logInfo("Welcome to Vectras ♡"); - } - activity.startActivity(new Intent(activity, MainActivity.class)); /*new Timer().schedule(new TimerTask() { @Override public void run() { mProgressDialog.dismiss(); } }, 3000);*/ + finish(); + startActivity(new Intent(activity, SplashActivity.class)); + } } else { AlertDialog ad; ad = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create(); @@ -501,8 +571,6 @@ public String getPath(Uri uri) { return FileUtils.getPath(activity, uri); } - public ProgressDialog progressDialog = null; - @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); @@ -512,12 +580,10 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (selectedFilePath.getName().equals(selectedPath.replace(".IMG", ".vbi"))) { try { - progressDialog = new ProgressDialog(activity, - R.style.MainDialogTheme); - progressDialog.setTitle("Extracting"); - progressDialog.setMessage("Please wait..."); - progressDialog.setCancelable(false); - progressDialog.show(); // Showing Progress Dialog + loadingPb.setVisibility(View.VISIBLE); + goBtn.setEnabled(false); + acceptLiceneseChkBox.setEnabled(false); + mRVRoms.setVisibility(View.GONE); Thread t = new Thread() { public void run() { FileInputStream zipFile = null; @@ -556,7 +622,17 @@ public void run() { UIUtils.toastLong(activity, e.toString()); throw new RuntimeException(e); } finally { - progressDialog.cancel(); // cancelling Dialog. + Runnable runnable = new Runnable() { + @Override + public void run() { + loadingPb.setVisibility(View.GONE); + goBtn.setEnabled(true); + acceptLiceneseChkBox.setEnabled(true); + mRVRoms.setVisibility(View.VISIBLE); + onFirstStartup(); + } + }; + activity.runOnUiThread(runnable); try { zis.close(); } catch (IOException e) { @@ -568,7 +644,10 @@ public void run() { }; t.start(); } catch (Exception e) { - progressDialog.dismiss(); // Close Progress Dialog + loadingPb.setVisibility(View.GONE); + goBtn.setEnabled(true); + acceptLiceneseChkBox.setEnabled(true); + mRVRoms.setVisibility(View.VISIBLE); UIUtils.toastLong(activity, e.toString()); throw new RuntimeException(e); } @@ -580,8 +659,6 @@ public void run() { } } - public static final int DIALOG_DOWNLOAD_PROGRESS = 0; - static class DownloadsImage extends AsyncTask { @Override @@ -643,4 +720,13 @@ public void onBackPressed() { finish(); } + @Override + public void onStart() { + super.onStart(); + if (MainSettingsManager.getArch(activity) == null) { + startActivity(new Intent(this, SetArchActivity.class)); + } + activity = this; + } + } diff --git a/app/src/main/java/com/vectras/vm/SetArchActivity.java b/app/src/main/java/com/vectras/vm/SetArchActivity.java new file mode 100644 index 0000000..9503f96 --- /dev/null +++ b/app/src/main/java/com/vectras/vm/SetArchActivity.java @@ -0,0 +1,76 @@ +package com.vectras.vm; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.view.View; +import android.widget.Button; + +import androidx.appcompat.app.AppCompatActivity; + +import com.vectras.qemu.MainSettingsManager; + +public class SetArchActivity extends AppCompatActivity implements View.OnClickListener { + + SetArchActivity activity; + private static Handler mHandler; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mHandler = new Handler(); + setContentView(R.layout.activity_set_arch); + activity = this; + Button arch86 = findViewById(R.id.archx86); + Button archarm = findViewById(R.id.archarm); + Button web = findViewById(R.id.webBtn); + arch86.setOnClickListener(this); + archarm.setOnClickListener(this); + web.setOnClickListener(this); + } + + public void onClick(View v) { + int id = v.getId(); + if (id == R.id.archx86) { + MainSettingsManager.setArch(this, "X86_64"); + + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + Intent startActivity = new Intent(activity, SplashActivity.class); + int pendingIntentId = 123456; + PendingIntent pendingIntent = PendingIntent.getActivity(activity, pendingIntentId, startActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); + + AlarmManager mgr = (AlarmManager) activity.getSystemService(Context.ALARM_SERVICE); + mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 500, pendingIntent); + + System.exit(0); + } + }, 300); + } else if (id == R.id.archarm) { + MainSettingsManager.setArch(this, "ARM"); + + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + Intent startActivity = new Intent(activity, SplashActivity.class); + int pendingIntentId = 123456; + PendingIntent pendingIntent = PendingIntent.getActivity(activity, pendingIntentId, startActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); + + AlarmManager mgr = (AlarmManager) activity.getSystemService(Context.ALARM_SERVICE); + mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 500, pendingIntent); + + System.exit(0); + } + }, 300); + } else if (id == R.id.webBtn) { + String qe = "https://www.qemu.org/"; + Intent q = new Intent(Intent.ACTION_VIEW); + q.setData(Uri.parse(qe)); + startActivity(q); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/vectras/vm/StoreActivity.java b/app/src/main/java/com/vectras/vm/StoreActivity.java index 02cd874..c1a1d57 100644 --- a/app/src/main/java/com/vectras/vm/StoreActivity.java +++ b/app/src/main/java/com/vectras/vm/StoreActivity.java @@ -1,12 +1,19 @@ package com.vectras.vm; +import static android.content.Intent.ACTION_OPEN_DOCUMENT; + import android.content.Context; +import android.content.Intent; import android.net.ConnectivityManager; import android.net.NetworkInfo; +import android.net.Uri; import android.os.*; +import android.provider.DocumentsContract; +import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.LinearLayout; +import android.widget.ProgressBar; import android.widget.Toast; import androidx.annotation.NonNull; @@ -24,38 +31,50 @@ import com.vectras.vm.Fragment.HomeFragment; import com.vectras.vm.Store.AdapterStore; import com.vectras.vm.Store.DataStore; +import com.vectras.vm.utils.FileUtils; +import com.vectras.vm.utils.UIUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.io.BufferedInputStream; import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; +import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; import javax.net.ssl.HttpsURLConnection; -public class StoreActivity extends AppCompatActivity{ +public class StoreActivity extends AppCompatActivity { private RecyclerView mRVStore; private AdapterStore mAdapter; public static LinearLayout noConnectionLayout; public SwipeRefreshLayout pullToRefresh; public static StoreActivity activity; public String Data; + @Override protected void onCreate(Bundle bundle) { super.onCreate(bundle); setContentView(R.layout.activity_store); + loadingPb = findViewById(R.id.loadingPb); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); toolbar.setTitle(getString(R.string.app_name)); - + activity = this; AdView mAdView = findViewById(R.id.adView); @@ -86,6 +105,7 @@ public void onRefresh() { } }); } + public boolean checkConnection(Context context) { final ConnectivityManager connMgr = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); @@ -194,11 +214,99 @@ protected void onPostExecute(String result) { } } + @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - if(item.getItemId()== android.R.id.home){ - finish(); + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + + menu.add(0, 0, 0, "IMPORT FILES").setShortcut('3', 'c').setIcon(R.drawable.input_circle).setShowAsAction(1); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case 0: + Intent intent = new Intent(ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + + // Optionally, specify a URI for the file that should appear in the + // system file picker when it loads. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Environment.DIRECTORY_DOWNLOADS); + } + + startActivityForResult(intent, 69); + return true; + case android.R.id.home: + finish(); + return true; } return super.onOptionsItemSelected(item); } + + public ProgressBar loadingPb; + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == 69 && resultCode == RESULT_OK) { + Uri content_describer = data.getData(); + File selectedFilePath = new File(getPath(content_describer)); + loadingPb.setVisibility(View.VISIBLE); + + new Thread(new Runnable() { + @Override + public void run() { + FileInputStream File = null; + try { + File = (FileInputStream) getContentResolver().openInputStream(content_describer); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + try { + try { + OutputStream out = new FileOutputStream(new File(AppConfig.sharedFolder + "/" + selectedFilePath.getName())); + try { + // Transfer bytes from in to out + byte[] buf = new byte[1024]; + int len; + while ((len = File.read(buf)) > 0) { + out.write(buf, 0, len); + } + } finally { + out.close(); + } + } finally { + Runnable runnable = new Runnable() { + @Override + public void run() { + loadingPb.setVisibility(View.GONE); + } + }; + activity.runOnUiThread(runnable); + File.close(); + } + } catch (IOException e) { + Runnable runnable = new Runnable() { + @Override + public void run() { + loadingPb.setVisibility(View.GONE); + } + }; + activity.runOnUiThread(runnable); + MainActivity.UIAlert("error", e.toString(), activity); + } + } + }).start(); + } + } + + public String getPath(Uri uri) { + return FileUtils.getPath(activity, uri); + } + } diff --git a/app/src/main/java/com/vectras/vm/data/LoginDataSource.java b/app/src/main/java/com/vectras/vm/data/LoginDataSource.java deleted file mode 100644 index 11d98c2..0000000 --- a/app/src/main/java/com/vectras/vm/data/LoginDataSource.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.vectras.vm.data; - -import com.vectras.vm.data.model.LoggedInUser; - -import java.io.IOException; - -/** - * Class that handles authentication w/ login credentials and retrieves user information. - */ -public class LoginDataSource { - - public Result login(String username, String password) { - - try { - // TODO: handle loggedInUser authentication - LoggedInUser fakeUser = - new LoggedInUser( - java.util.UUID.randomUUID().toString(), - "Jane Doe"); - return new Result.Success<>(fakeUser); - } catch (Exception e) { - return new Result.Error(new IOException("Error logging in", e)); - } - } - - public void logout() { - // TODO: revoke authentication - } -} \ No newline at end of file diff --git a/app/src/main/java/com/vectras/vm/data/LoginRepository.java b/app/src/main/java/com/vectras/vm/data/LoginRepository.java deleted file mode 100644 index fffde4e..0000000 --- a/app/src/main/java/com/vectras/vm/data/LoginRepository.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.vectras.vm.data; - -import com.vectras.vm.data.model.LoggedInUser; - -/** - * Class that requests authentication and user information from the remote data source and - * maintains an in-memory cache of login status and user credentials information. - */ -public class LoginRepository { - - private static volatile LoginRepository instance; - - private LoginDataSource dataSource; - - // If user credentials will be cached in local storage, it is recommended it be encrypted - // @see https://developer.android.com/training/articles/keystore - private LoggedInUser user = null; - - // private constructor : singleton access - private LoginRepository(LoginDataSource dataSource) { - this.dataSource = dataSource; - } - - public static LoginRepository getInstance(LoginDataSource dataSource) { - if (instance == null) { - instance = new LoginRepository(dataSource); - } - return instance; - } - - public boolean isLoggedIn() { - return user != null; - } - - public void logout() { - user = null; - dataSource.logout(); - } - - private void setLoggedInUser(LoggedInUser user) { - this.user = user; - // If user credentials will be cached in local storage, it is recommended it be encrypted - // @see https://developer.android.com/training/articles/keystore - } - - public Result login(String username, String password) { - // handle login - Result result = dataSource.login(username, password); - if (result instanceof Result.Success) { - setLoggedInUser(((Result.Success) result).getData()); - } - return result; - } -} \ No newline at end of file diff --git a/app/src/main/java/com/vectras/vm/data/Result.java b/app/src/main/java/com/vectras/vm/data/Result.java deleted file mode 100644 index 2585d54..0000000 --- a/app/src/main/java/com/vectras/vm/data/Result.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.vectras.vm.data; - -/** - * A generic class that holds a result success w/ data or an error exception. - */ -public class Result { - // hide the private constructor to limit subclass types (Success, Error) - private Result() { - } - - @Override - public String toString() { - if (this instanceof Result.Success) { - Result.Success success = (Result.Success) this; - return "Success[data=" + success.getData().toString() + "]"; - } else if (this instanceof Result.Error) { - Result.Error error = (Result.Error) this; - return "Error[exception=" + error.getError().toString() + "]"; - } - return ""; - } - - // Success sub-class - public final static class Success extends Result { - private T data; - - public Success(T data) { - this.data = data; - } - - public T getData() { - return this.data; - } - } - - // Error sub-class - public final static class Error extends Result { - private Exception error; - - public Error(Exception error) { - this.error = error; - } - - public Exception getError() { - return this.error; - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/vectras/vm/data/model/LoggedInUser.java b/app/src/main/java/com/vectras/vm/data/model/LoggedInUser.java deleted file mode 100644 index 0b647a5..0000000 --- a/app/src/main/java/com/vectras/vm/data/model/LoggedInUser.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.vectras.vm.data.model; - -/** - * Data class that captures user information for logged in users retrieved from LoginRepository - */ -public class LoggedInUser { - - private String userId; - private String displayName; - - public LoggedInUser(String userId, String displayName) { - this.userId = userId; - this.displayName = displayName; - } - - public String getUserId() { - return userId; - } - - public String getDisplayName() { - return displayName; - } -} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_arch.xml b/app/src/main/res/drawable/ic_arch.xml new file mode 100644 index 0000000..4b67352 --- /dev/null +++ b/app/src/main/res/drawable/ic_arch.xml @@ -0,0 +1,6 @@ + + + diff --git a/app/src/main/res/drawable/ic_kvm.xml b/app/src/main/res/drawable/ic_kvm.xml new file mode 100644 index 0000000..c3d121f --- /dev/null +++ b/app/src/main/res/drawable/ic_kvm.xml @@ -0,0 +1,6 @@ + + + diff --git a/app/src/main/res/drawable/input_circle.xml b/app/src/main/res/drawable/input_circle.xml new file mode 100644 index 0000000..5528441 --- /dev/null +++ b/app/src/main/res/drawable/input_circle.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/round_adb_24.xml b/app/src/main/res/drawable/round_adb_24.xml new file mode 100644 index 0000000..c96346e --- /dev/null +++ b/app/src/main/res/drawable/round_adb_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/round_album_24.xml b/app/src/main/res/drawable/round_album_24.xml new file mode 100644 index 0000000..58c0ba6 --- /dev/null +++ b/app/src/main/res/drawable/round_album_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/round_image_24.xml b/app/src/main/res/drawable/round_image_24.xml new file mode 100644 index 0000000..08f6184 --- /dev/null +++ b/app/src/main/res/drawable/round_image_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/round_storage_24.xml b/app/src/main/res/drawable/round_storage_24.xml new file mode 100644 index 0000000..46a63e2 --- /dev/null +++ b/app/src/main/res/drawable/round_storage_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/round_volume_up_24.xml b/app/src/main/res/drawable/round_volume_up_24.xml new file mode 100644 index 0000000..61f5856 --- /dev/null +++ b/app/src/main/res/drawable/round_volume_up_24.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/layout/activity_custom_rom.xml b/app/src/main/res/layout/activity_custom_rom.xml new file mode 100644 index 0000000..1549e40 --- /dev/null +++ b/app/src/main/res/layout/activity_custom_rom.xml @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +