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 60ce81d..7c3191b 100644 Binary files a/app/src/main/assets/roms/vgabios-virtio.bin and b/app/src/main/assets/roms/vgabios-virtio.bin differ diff --git a/app/src/main/assets/roms/vgabios-vmware.bin b/app/src/main/assets/roms/vgabios-vmware.bin index b75a351..8594a2e 100644 Binary files a/app/src/main/assets/roms/vgabios-vmware.bin and b/app/src/main/assets/roms/vgabios-vmware.bin differ 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +