diff --git a/.gitignore b/.gitignore index ea17e28c8..a92ad9efe 100644 --- a/.gitignore +++ b/.gitignore @@ -60,6 +60,7 @@ gradle /gradlew.bat obj/ jniLibs/ +QEMU_VERSION jnilibs limbotest @@ -69,6 +70,5 @@ limbotest sync.sh gitsync.sh -/limbo-android-lib/src/main/jni/qemu.5.1.0 -/limbo-android-lib/src/main/jni/qemu.2.9.1 - +/limbo-android-lib/src/main/jni/qemu-2.9.1/ +/limbo-android-lib/src/main/jni/qemu-5.1.0/ diff --git a/README.developers b/README.developers index 89c64e7c7..8e0d44605 100644 --- a/README.developers +++ b/README.developers @@ -14,15 +14,15 @@ https://github.com/limboemu/limbo 2. Requirements: Android SDK - Android NDK: r14b/gcc or r23/clang - Android Studio (3.1.1 prefered) - Android device with Android OS 5.0 (Lollipop) and above - Linux Desktop pc (Ubuntu prefered) + Android NDK: r14b/gcc or r23b/clang + Android Studio (3.1.1 preferred) + Android device with Android OS 8.0 (Oreo) and above + Linux Desktop pc (Ubuntu preferred) Make sure you have the following packages installed, if not run: sudo apt-get install make autoconf automake git python binutils - sudo apt-get install libtool-bin pkg-config flex bison gettext texinfo - For developement you can use your own editors Geany is highly - recommended for the native code + sudo apt-get install libtool-bin pkg-config flex bison gettext texinfo rsync + For development you can use your own editors Geany is highly + recommended for editing the native code =============================================================================== @@ -30,27 +30,23 @@ https://github.com/limboemu/limbo https://github.com/limboemu/limbo/issues =============================================================================== + 4. Update Configuration - a. Update the path to the ndk directory in file limbo-android-lib/src/main/jni/android-limbo-config.mak - If you use gcc set: - USE_GCC ?= true - otherwise use: - USE_GCC ?= false + If you build the supported QEMU version then the only thing you need to do is edit file: + limbo-android-lib/src/main/jni/android-limbo-config.mak + In all other cases you have to look at the config files under: + limbo-android-lib/src/main/jni/android-config/ - b. Add the NDK directory to your PATH variable so makefile can find ndk-build - Examples: - For bash if you use gcc: - export PATH=$PATH:/home/dev/tools/ndk/android-ndk-r14b - or if you use clang: - export PATH=$PATH:/home/dev/tools/ndk/android-ndk-r23 + For more information see the Building section. -5. Get and Patch libraries +5. Get and Compile external libraries #Make sure you're under the jni directory cd ./limbo-android-lib/src/main/jni - + + # Now download the source code for the external libraries and unzip them under the jni directory #Note: if some of these file links don't download with wget use your browser to download them ##### Get QEMU @@ -60,6 +56,10 @@ https://github.com/limboemu/limbo wget http://download.qemu-project.org/qemu-5.1.0.tar.xz -P /tmp/ tar -xJf /tmp/qemu-5.1.0.tar.xz mv qemu-5.1.0 qemu + # For QEMU version 2.9.1: + wget http://download.qemu-project.org/qemu-2.9.1.tar.xz -P /tmp/ + tar -xJf /tmp/qemu-2.9.1.tar.xz + mv qemu-2.9.1 qemu ##### GET glib wget https://ftp.gnome.org/pub/GNOME/sources/glib/2.56/glib-2.56.1.tar.xz -P /tmp/ @@ -93,19 +93,16 @@ https://github.com/limboemu/limbo qemu/ SDL2/ +=============================================================================== + +6. Apply patches ### Apply patch for QEMU: # example for 5.1.0: - Make sure the following line is uncommented in android-qemu-config.mak - include android-qemu-config-5.1.0.mak - Apply the patch on the terminal: cd ./limbo-android-lib/src/main/jni/qemu/ patch -p1 < ../patches/qemu-5.1.0.patch - - # for QEMU versions 4 and above you can enable MTTCG on the ui - # this will only work if your android device is 64bit - modify Config.java: - set to true: public static boolean enableMTTCG = true; + # for 2.9.1: + patch -p1 < ../patches/qemu-2.9.1.patch ### Apply glib patch for Limbo: cd ./limbo-android-lib/src/main/jni/glib/ @@ -116,42 +113,54 @@ https://github.com/limboemu/limbo patch -p1 < ../patches/sdl2-2.0.8.patch ### Other QEMU versions: - # If you want to distribute Limbo build with other QEMU versions, create your own patch like this: + # If you want to redistribute Limbo build with other QEMU versions, create your own patch like this: cd /limbo-android-lib/src/main/jni/qemu/ diff -ru --no-dereference /tmp/qemu-x.x.x . | grep -v '^Only in' > ../patches/qemu-x.x.x.patch Don't forget to create your android-qemu-config-x.x.x.mak file and include it in android-qemu-config.mak =============================================================================== -5. Build - a. To build the NDK part of the app make sure you're under the jni directory: - cd limbo-android-lib/src/main/jni +7. Build - Make sure you update the android-config/android-limbo-config.mak file with the NDK path you have installed: + a. To build the native part of the app make sure you're under the jni directory: + cd limbo-android-lib/src/main/jni + + Make sure you update file android-limbo-config.mak with the NDK folder to your installation folder + keep in mind the last NDK version that supported gcc was r14b. The alternative is to use clang + with ndk version r23. For example: NDK_ROOT = /home/dev/tools/ndk/android-ndk-r14b + The rest of the configuration is for specifying the right QEMU version: USE_QEMU_VERSION + the host architecture (android device): BUILD_HOST + and the emulator guest architecture: BUILD_GUEST + b. From Android Studio import BOTH the Android library limbo-android-lib AND the module for the guest architecture you need (x86,arm,ppc,sparc) ie limbo-android-x86. c. Build the native libraries: - cd limbo-android-lib/src/main/jni: + # Make sure you're still under the jni directory: + cd limbo-android-lib/src/main/jni - To build Limbo Emulator: + # Instead of editing android-limbo-config.mak you can also supply the variables on the command line. + # For example: export BUILD_HOST= export BUILD_GUEST= export NDK_DEBUG= - make limbo where: EABI is the Android device type (host arch): armeabi-v7a, arm64-v8a, x86, x86_64 GUEST_ARCH is the Emulator type: x86_64-softmmu,aarch64-softmmu,sparc64-softmmu,ppc64-softmmu ENABLE_DEBUG is 1 (optional) + # To start the build type on your terminal: + make limbo + + Notes: If you want to remove ALL previously compiled native objects and libraries: make clean - If you're building apk for multiple host architectures you need to do in betweeen builds: + If you're building apk for multiple host architectures you need to do in between builds: make distclean If you're building apk for multiple guest architectures you can specify them with commas: @@ -194,6 +203,16 @@ https://github.com/limboemu/limbo export BUILD_GUEST=x86_64-softmmu,aarch64-softmmu make limbo + 8) To build limbo for older devices with gcc set the following options: + export NDK_ROOT=/home/dev/tools/ndk/android-ndk-r14b + export USE_GCC=true + export BUILD_HOST=armeabi-v7a + export BUILD_GUEST=x86_64-softmmu + export USE_QEMU_VERSION=2.9.1 + export USE_AAUDIO=false + + For more options see android-limbo-config.mak + You should now have the following libraries in these 2 folders: limbo-android-lib/src/main/jniLibs// @@ -233,8 +252,11 @@ https://github.com/limboemu/limbo Important: From Android studio click Build> Rebuild Project and Run > Debug - - g. To debug the native code for a particular guest: + +=============================================================================== + +8. Debugging + To debug the native code for a particular guest: # for x86 guest and ARM64 phone: export BUILD_HOST=arm64-v8a @@ -261,7 +283,8 @@ https://github.com/limboemu/limbo To see stack (backtrace): bt =============================================================================== -6. Development Notes + +9. Development Notes a. Codes Changes for Android compatibility are in patch files marked with __ANDROID__ @@ -274,7 +297,7 @@ https://github.com/limboemu/limbo limbo-android-lib/src/main/jni/android-config/android-qemu-config.mak d. Advanced Device Configuration files: - limbo-android-lib/src/main/jni/android-config/android-device-config/*.mak + limbo-android-lib/src/main/jni/android-config/android-config/*.mak e. Important Makefiles: limbo-android-lib/src/main/jni/Makefile @@ -284,9 +307,16 @@ https://github.com/limboemu/limbo limbo-android-lib/src/main/jni/android-qemu-build.mak f. Frontend UI options configuration see: Config.java + + g. When using git apply the following setting to your config file if you want to use + return new lines for windows: + [core] + eol = true + autocrlf = input + fileMode = false =============================================================================== -7. Run +10. Run a. Installing a full Qwerty keyboard for Android like Hacker's keyboard from the Google Android store. Make sure you use Transparent theme @@ -299,19 +329,15 @@ https://github.com/limboemu/limbo f. Have fun! =============================================================================== -8. Changelog +10. Changelog See limbo-android-lib/src/main/assets/CHANGELOG for release notes =============================================================================== -9. License +11. License Limbo PC Emulator is released under GPL v2 License. -All icons unders /res are from Gnome Project (GPL v2 License) +All icons under /res are from Gnome Project (GPL v2 License) See file COPYING under root directory and LICENSE under limbo-android-lib/src/main/assets Other source included are released under their own license please view Licenses under each subdirectory - -=============================================================================== - -Endofdoc diff --git a/README.md b/README.md index 6b176adb4..4874d7a8b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Limbo Emulator (QEMU) for Android # -# For APK Downloads, Guides, and Help visit the Limbo Wiki: -# https://github.com/limboemu/limbo/wiki +# For APK Downloads, Guides, and Help visit: +# https://virtualmachinery.weebly.com Limbo is a QEMU-based emulator for Android supports emulation for these architectures: x86/x86_64 diff --git a/VERSION b/VERSION index 01fdd2d8f..6f68e2ea5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -501.00 +600.01 diff --git a/gradle.properties b/gradle.properties index 26dc65176..fefd06fef 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,15 +15,9 @@ org.gradle.jvmargs=-Xmx2000m # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects org.gradle.parallel=true - org.gradle.daemon=true - org.gradle.configureondemand=true - android.enableBuildCache=true - -android.useDeprecatedNdk=true - android.useAndroidX=true android.enableJetifier=true -org.gradle.jvmargs=-Xmx2000m + diff --git a/limbo-android-arm/src/main/AndroidManifest.xml b/limbo-android-arm/src/main/AndroidManifest.xml index 6f87599e0..0823c2de1 100644 --- a/limbo-android-arm/src/main/AndroidManifest.xml +++ b/limbo-android-arm/src/main/AndroidManifest.xml @@ -1,83 +1,24 @@ - + - - - - - - - - - - - - - + android:versionCode="60001" + android:versionName="6.0.1-arm"> + > - - - - - - - - - - - - - - - - - - diff --git a/limbo-android-arm/src/main/java/com/limbo/emu/main/arm/LimboEmuActivity.java b/limbo-android-arm/src/main/java/com/limbo/emu/main/arm/LimboEmuActivity.java index aaacd8a34..144a62718 100644 --- a/limbo-android-arm/src/main/java/com/limbo/emu/main/arm/LimboEmuActivity.java +++ b/limbo-android-arm/src/main/java/com/limbo/emu/main/arm/LimboEmuActivity.java @@ -2,42 +2,34 @@ import android.os.Bundle; +import com.max2idea.android.limbo.log.Logger; import com.max2idea.android.limbo.main.Config; import com.max2idea.android.limbo.main.LimboActivity; -import com.max2idea.android.limbo.utils.LinksManager; -import com.max2idea.android.limbo.utils.Machine; +import com.max2idea.android.limbo.links.LinksManager; +import com.max2idea.android.limbo.main.LimboApplication; public class LimboEmuActivity extends LimboActivity { public void onCreate(Bundle bundle){ - Config.enable_ARM = true; - Config.enable_ARM64 = true; - - Config.enable_KVM = true; - + LimboApplication.arch = Config.Arch.arm64; + Config.clientClass = this.getClass(); + Config.enableKVM = true; Config.enableEmulatedFloppy = false; Config.enableEmulatedSDCard = true; - //XXX; only for 64bit hosts, also make sure you have qemu 2.9.1 arm-softmmu and above compiled - if(Machine.isHost64Bit() && Config.enableMTTCG) + if(LimboApplication.isHost64Bit() && Config.enableMTTCG) Config.enableMTTCG = true; else Config.enableMTTCG = false; - Config.machineFolder = Config.machineFolder + "other/arm_machines/"; - - Config.osImages.put("Debian ARM Linux", new LinksManager.LinkInfo("Debian ARM Linux", - "A Linux-based light weight OS with Desktop Manager, network, and package manager", + Config.osImages.put(getString(R.string.DebianArmLinux), new LinksManager.LinkInfo(getString(R.string.DebianArmLinux), + getString(R.string.DebianArmLinuxDescr), "https://github.com/limboemu/limbo/wiki/Debian-ARM-Linux", LinksManager.LinkType.ISO)); - - Config.hd_if_type = "scsi"; - super.onCreate(bundle); - //TODO: change location to something that the user will have access outside of limbo // like internal storage - Config.logFilePath = Config.cacheDir + "/limbo/limbo-arm-log.txt"; + Logger.setupLogFile("/limbo/limbo-arm-log.txt"); } protected void loadQEMULib(){ diff --git a/limbo-android-arm/src/main/res/values/strings.xml b/limbo-android-arm/src/main/res/values/strings.xml index 2ac8d99c3..91b029bc3 100644 --- a/limbo-android-arm/src/main/res/values/strings.xml +++ b/limbo-android-arm/src/main/res/values/strings.xml @@ -3,5 +3,7 @@ Limbo ARM Emulator Limbo ARM - + Debian ARM Linux + A Linux-based light weight OS with Desktop Manager, network, and package manager + \ No newline at end of file diff --git a/limbo-android-lib/src/main/AndroidManifest.xml b/limbo-android-lib/src/main/AndroidManifest.xml index 66775b5a0..85fbc7c67 100644 --- a/limbo-android-lib/src/main/AndroidManifest.xml +++ b/limbo-android-lib/src/main/AndroidManifest.xml @@ -1,11 +1,12 @@ + > + @@ -15,8 +16,39 @@ - - - \ No newline at end of file + + + + + + + + + + + + diff --git a/limbo-android-lib/src/main/assets/CHANGELOG b/limbo-android-lib/src/main/assets/CHANGELOG index dd3050001..cc584ebff 100644 --- a/limbo-android-lib/src/main/assets/CHANGELOG +++ b/limbo-android-lib/src/main/assets/CHANGELOG @@ -1,3 +1,42 @@ +* Limbo Legacy v6.0.1 +Added qemu disk cache option +Added qemu hdd interfaces (tap on the disk icon) +Added ignore breakpoint tb invalidation option +Apply acceleration option after extra params +Added option for importing custom BIOS +Fixed update checker dialog box +Fixed crash when no machine is loaded +Fixed problem not saving edit text when machine starts +Fixed legacy file manager +Fixed old ndk/gcc support + +* Limbo Legacy v6.0.0 +Built both variants for QEMU 5.1.0 for stability and 2.9.1 for better performance. +WARNING: QEMU 2.9.1 version is much faster than 5.1.0 but very old and +behind many security patches! Install only virtual images and software you trust! +Fixed SDL resizing after rotating and resuming application. +SDL user interface can now disconnect and resume. +Added support for changin idle refresh rate for SDL. +Created KeyMapper for mapping keys and mouse buttons for gaming, see Wiki for info. +Removed internal VNC client you should check the Wiki for alternative VNC clients. +VNC Password and Allow External VNC clients are now under settings. +New Pause button for pausing when started VNC user interface. +Mouse enhancements for Touchscreen and External Mouse. +Absolute mouse devices support only guests that have usb-tablet drivers installed. +Added new out of bounds prevention option for mouse. +Added new Key and Mouse delay options. +Global screen options are now moved to Menu Settings. +Machine architecture was not needed so it is now removed. +Removed Shared Folder because it is buggy. +Added Android Aaudio native audio interface for lower latency (Oreo and above only). +Using 22050 sample rate instead of 44199 for better audio with lesser clicking. +Fixed crashing when resuming due to audio buffer writing. +Internal redesign of the Android user interface. +Better build environment for development. +Building with ndk r23 clang by default, r14b gcc is also supported. +Upgraded libraries: ffi 3.3 and pixman 0.40.0 + + * Limbo v5.1.0 Fixed issue with importing machines No signalfd for better compatibility diff --git a/limbo-android-lib/src/main/java/android/androidVNC/AbstractBitmapData.java b/limbo-android-lib/src/main/java/android/androidVNC/AbstractBitmapData.java deleted file mode 100644 index d18f6345c..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/AbstractBitmapData.java +++ /dev/null @@ -1,203 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package android.androidVNC; - -import java.io.IOException; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.widget.ImageView; - -/** - * Abstract interface between the VncCanvas and the bitmap and pixel data - * buffers that actually contain the data. This allows for implementations that - * use smaller bitmaps or buffers to save memory. - * - * @author Michael A. MacDonald - * - */ -abstract class AbstractBitmapData { - int framebufferwidth; - int framebufferheight; - int bitmapwidth; - int bitmapheight; - RfbProto rfb; - Bitmap mbitmap; - int bitmapPixels[]; - Canvas memGraphics; - boolean waitingForInput; - VncCanvas vncCanvas; - private AbstractBitmapDrawable drawable; - - AbstractBitmapData(RfbProto p, VncCanvas c) { - rfb = p; - vncCanvas = c; - framebufferwidth = rfb.framebufferWidth; - framebufferheight = rfb.framebufferHeight; - } - - synchronized void doneWaiting() { - waitingForInput = false; - } - - final void invalidateMousePosition() { - if (vncCanvas.connection.getUseLocalCursor()) { - if (drawable == null) - drawable = createDrawable(); - drawable.setCursorRect(vncCanvas.mouseX, vncCanvas.mouseY); - vncCanvas.invalidate(drawable.cursorRect); - } - } - - /** - * - * @return The smallest scale supported by the implementation; the scale at - * which the bitmap would be smaller than the screen - */ - float getMinimumScale() { - double scale = 0.75; - int displayWidth = vncCanvas.getWidth(); - int displayHeight = vncCanvas.getHeight(); - for (; scale >= 0; scale -= 0.25) { - if (scale * bitmapwidth < displayWidth - || scale * bitmapheight < displayHeight) - break; - } - return (float) (scale + 0.25); - } - - /** - * Send a request through the protocol to get the data for the currently - * held bitmap - * - * @param incremental - * True if we want incremental update; false for full update - */ - abstract void writeFullUpdateRequest(boolean incremental) - throws IOException; - - /** - * Determine if a rectangle in full-frame coordinates can be drawn in the - * existing buffer - * - * @param x - * Top left x - * @param y - * Top left y - * @param w - * width (pixels) - * @param h - * height (pixels) - * @return True if entire rectangle fits into current screen buffer, false - * otherwise - */ - abstract boolean validDraw(int x, int y, int w, int h); - - /** - * Return an offset in the bitmapPixels array of a point in full-frame - * coordinates - * - * @param x - * @param y - * @return Offset in bitmapPixels array of color data for that point - */ - abstract int offset(int x, int y); - - /** - * Update pixels in the bitmap with data from the bitmapPixels array, - * positioned in full-frame coordinates - * - * @param x - * Top left x - * @param y - * Top left y - * @param w - * width (pixels) - * @param h - * height (pixels) - */ - abstract void updateBitmap(int x, int y, int w, int h); - - /** - * Create drawable appropriate for this data - * - * @return drawable - */ - abstract AbstractBitmapDrawable createDrawable(); - - /** - * Call in UI thread; tell ImageView we've changed - * - * @param v - * ImageView displaying bitmap data - */ - void updateView(ImageView v) { - if (drawable == null) - drawable = createDrawable(); - v.setImageDrawable(drawable); - v.invalidate(); - } - - /** - * Copy a rectangle from one part of the bitmap to another - * - * @param src - * Rectangle in full-frame coordinates to be copied - * @param dest - * Destination rectangle in full-frame coordinates - * @param paint - * Paint specifier - */ - abstract void copyRect(Rect src, Rect dest, Paint paint); - - /** - * Draw a rectangle in the bitmap with coordinates given in full frame - * - * @param x - * Top left x - * @param y - * Top left y - * @param w - * width (pixels) - * @param h - * height (pixels) - * @param paint - * How to draw - */ - abstract void drawRect(int x, int y, int w, int h, Paint paint); - - /** - * Scroll position has changed. - *

- * This method is called in the UI thread-- it updates internal status, but - * does not change the bitmap data or send a network request until - * syncScroll is called - * - * @param newx - * Position of left edge of visible part in full-frame - * coordinates - * @param newy - * Position of top edge of visible part in full-frame coordinates - */ - abstract void scrollChanged(int newx, int newy); - - /** - * Sync scroll -- called from network thread; copies scroll changes from UI - * to network state - */ - abstract void syncScroll(); - - /** - * Release resources - */ - void dispose() { - if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) - if (mbitmap != null) - mbitmap.recycle(); - memGraphics = null; - bitmapPixels = null; - } -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/AbstractBitmapDrawable.java b/limbo-android-lib/src/main/java/android/androidVNC/AbstractBitmapDrawable.java deleted file mode 100644 index a311cce8a..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/AbstractBitmapDrawable.java +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package android.androidVNC; - -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.graphics.drawable.DrawableContainer; - -/** - * @author Michael A. MacDonald - * - */ -public class AbstractBitmapDrawable extends DrawableContainer { - Rect cursorRect; - Rect clipRect; - - AbstractBitmapData data; - - static final Paint _defaultPaint; - static final Paint _whitePaint; - static final Paint _blackPaint; - - static { - _defaultPaint = new Paint(); - _whitePaint = new Paint(); - _whitePaint.setColor(0xffffffff); - _blackPaint = new Paint(); - _blackPaint.setColor(0xff000000); - } - - AbstractBitmapDrawable(AbstractBitmapData data) - { - this.data = data; - cursorRect = new Rect(); - clipRect = new Rect(); - } - - void draw(Canvas canvas, int xoff, int yoff) - { - canvas.drawBitmap(data.mbitmap, xoff, yoff, _defaultPaint); - if(data.vncCanvas.connection.getUseLocalCursor()) - { - setCursorRect(data.vncCanvas.mouseX, data.vncCanvas.mouseY); - clipRect.set(cursorRect); - if (canvas.clipRect(cursorRect)) - { - drawCursor(canvas); - } - } - } - - void drawCursor(Canvas canvas) - { - canvas.drawRect(cursorRect,_whitePaint); - canvas.drawRect((float)cursorRect.left + 1, (float)cursorRect.top + 1, (float)cursorRect.right - 1, (float)cursorRect.bottom - 1, _blackPaint); - } - - void setCursorRect(int mouseX, int mouseY) - { - cursorRect.left = mouseX - 2; - cursorRect.right = cursorRect.left + 4; - cursorRect.top = mouseY - 2; - cursorRect.bottom = cursorRect.top + 4; - } - - /* (non-Javadoc) - * @see android.graphics.drawable.DrawableContainer#getIntrinsicHeight() - */ - @Override - public int getIntrinsicHeight() { - return data.framebufferheight; - } - - /* (non-Javadoc) - * @see android.graphics.drawable.DrawableContainer#getIntrinsicWidth() - */ - @Override - public int getIntrinsicWidth() { - return data.framebufferwidth; - } - - /* (non-Javadoc) - * @see android.graphics.drawable.DrawableContainer#getOpacity() - */ - @Override - public int getOpacity() { - return PixelFormat.OPAQUE; - } - - /* (non-Javadoc) - * @see android.graphics.drawable.DrawableContainer#isStateful() - */ - @Override - public boolean isStateful() { - return false; - } -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/AbstractGestureInputHandler.java b/limbo-android-lib/src/main/java/android/androidVNC/AbstractGestureInputHandler.java deleted file mode 100644 index 8ed2e70e9..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/AbstractGestureInputHandler.java +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package android.androidVNC; - -import android.view.GestureDetector; -import android.view.MotionEvent; - -/** - * An AbstractInputHandler that uses GestureDetector to detect standard gestures in touch events - * - * @author Michael A. MacDonald - */ -abstract class AbstractGestureInputHandler extends GestureDetector.SimpleOnGestureListener implements AbstractInputHandler { - protected GestureDetector gestures = new GestureDetector(this); - private VncCanvasActivity activity; - - float xInitialFocus; - float yInitialFocus; - boolean inScaling; - - private static final String TAG = "AbstractGestureInputHandler"; - - AbstractGestureInputHandler(VncCanvasActivity c) - { - activity = c; - gestures.setOnDoubleTapListener(this); - } - - @Override - public boolean onTouchEvent(MotionEvent evt) { - //MK - if(evt.getAction()==MotionEvent.ACTION_CANCEL) - return true; - - return gestures.onTouchEvent(evt); - } - - /* (non-Javadoc) - * @see com.antlersoft.android.bc.OnScaleGestureListener#onScale(com.antlersoft.android.bc.IBCScaleGestureDetector) - */ -// @Override -// public boolean onScale(IBCScaleGestureDetector detector) { -// boolean consumed = true; -// //if (detector.) -// //Log.i(TAG,"Focus("+detector.getFocusX()+","+detector.getFocusY()+") scaleFactor = "+detector.getScaleFactor()); -// // Calculate focus shift -// float fx = detector.getFocusX(); -// float fy = detector.getFocusY(); -// double xfs = fx - xInitialFocus; -// double yfs = fy - yInitialFocus; -// double fs = Math.sqrt(xfs * xfs + yfs * yfs); -// if (Math.abs(1.0 - detector.getScaleFactor())<0.02) -// consumed = false; -// if (fs * 2< Math.abs(detector.getCurrentSpan() - detector.getPreviousSpan())) -// { -// inScaling = true; -// if (consumed) -// { -// //Log.i(TAG,"Adjust scaling "+detector.getScaleFactor()); -// if (activity.vncCanvas != null && activity.vncCanvas.scaling != null) -// activity.vncCanvas.scaling.adjust(activity, detector.getScaleFactor(), fx, fy); -// } -// } -// return consumed; -// } -// -// /* (non-Javadoc) -// * @see com.antlersoft.android.bc.OnScaleGestureListener#onScaleBegin(com.antlersoft.android.bc.IBCScaleGestureDetector) -// */ -// @Override -// public boolean onScaleBegin(IBCScaleGestureDetector detector) { -// xInitialFocus = detector.getFocusX(); -// yInitialFocus = detector.getFocusY(); -// inScaling = false; -// //Log.i(TAG,"scale begin ("+xInitialFocus+","+yInitialFocus+")"); -// return true; -// } -// -// /* (non-Javadoc) -// * @see com.antlersoft.android.bc.OnScaleGestureListener#onScaleEnd(com.antlersoft.android.bc.IBCScaleGestureDetector) -// */ -// @Override -// public void onScaleEnd(IBCScaleGestureDetector detector) { -// //Log.i(TAG,"scale end"); -// inScaling = false; -// } -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/AbstractInputHandler.java b/limbo-android-lib/src/main/java/android/androidVNC/AbstractInputHandler.java deleted file mode 100644 index 47e49dc35..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/AbstractInputHandler.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package android.androidVNC; - -import android.view.KeyEvent; -import android.view.MotionEvent; - -/** - * The VncCanvasActivity has several different ways of handling input from the touchscreen, - * keyboard, buttons and trackball. These will be represented by different implementations - * of this interface. Putting the different modes in different classes - * will keep the logic clean. The relevant Activity callbacks in VncCanvasActivity - * are forwarded to methods in AbstractInputHandler. - *

- * It is expected that the implementations will be contained within - * VncCanvasActivity, so they can do things like super.VncCanvasActivity.onXXX to invoke - * default behavior. - * @author Michael A. MacDonald - * - */ -public interface AbstractInputHandler { - /** - * Note: Menu key code is handled before this is called - * @see android.app.Activity#onKeyDown(int keyCode, KeyEvent evt) - */ - boolean onKeyDown(int keyCode, KeyEvent evt); - /** - * Note: Menu key code is handled before this is called - * @see android.app.Activity#onKeyUp(int keyCode, KeyEvent evt) - */ - boolean onKeyUp(int keyCode, KeyEvent evt); - /* (non-Javadoc) - * @see android.app.Activity#onTrackballEvent(android.view.MotionEvent) - */ - boolean onTrackballEvent( MotionEvent evt); - /* (non-Javadoc) - * @see android.app.Activity#onTrackballEvent(android.view.MotionEvent) - */ - boolean onTouchEvent( MotionEvent evt); - - /** - * Return a user-friendly description for this mode; it will be displayed in a toaster - * when changing modes. - * @return - */ - public CharSequence getHandlerDescription(); - - /** - * Return an internal name for this handler; this name will be stable across language - * and version changes - */ - String getName(); -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/AbstractScaling.java b/limbo-android-lib/src/main/java/android/androidVNC/AbstractScaling.java deleted file mode 100644 index 3b4c3ed4b..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/AbstractScaling.java +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package android.androidVNC; - -import com.limbo.emu.lib.R; - -import android.widget.ImageView; -/** - * @author Michael A. MacDonald - * - * A scaling mode for the VncCanvas; based on ImageView.ScaleType - */ -public abstract class AbstractScaling { - private static final int scaleModeIds[] = { R.id.itemFitToScreen, R.id.itemOneToOne, R.id.itemZoomable }; - - private static AbstractScaling[] scalings; - - public static AbstractScaling getById(int id) - { - if ( scalings==null) - { - scalings=new AbstractScaling[scaleModeIds.length]; - } - for ( int i=0; i(64, (float)0.25); - orderedList = new Vector(32, 8); - } - - public void add(CapabilityInfo capinfo) { - Integer key = new Integer(capinfo.getCode()); - infoMap.put(key, capinfo); - } - - public void add(int code, String vendor, String name, String desc) { - Integer key = new Integer(code); - infoMap.put(key, new CapabilityInfo(code, vendor, name, desc)); - } - - public boolean isKnown(int code) { - return infoMap.containsKey(new Integer(code)); - } - - public CapabilityInfo getInfo(int code) { - return (CapabilityInfo)infoMap.get(new Integer(code)); - } - - public String getDescription(int code) { - CapabilityInfo capinfo = (CapabilityInfo)infoMap.get(new Integer(code)); - if (capinfo == null) - return null; - - return capinfo.getDescription(); - } - - public boolean enable(CapabilityInfo other) { - Integer key = new Integer(other.getCode()); - CapabilityInfo capinfo = (CapabilityInfo)infoMap.get(key); - if (capinfo == null) - return false; - - boolean enabled = capinfo.enableIfEquals(other); - if (enabled) - orderedList.addElement(key); - - return enabled; - } - - public boolean isEnabled(int code) { - CapabilityInfo capinfo = (CapabilityInfo)infoMap.get(new Integer(code)); - if (capinfo == null) - return false; - - return capinfo.isEnabled(); - } - - public int numEnabled() { - return orderedList.size(); - } - - public int getByOrder(int idx) { - int code; - try { - code = ((Integer)orderedList.elementAt(idx)).intValue(); - } catch (ArrayIndexOutOfBoundsException e) { - code = 0; - } - return code; - } - - // Protected data - - protected Hashtable infoMap; - protected Vector orderedList; -} - diff --git a/limbo-android-lib/src/main/java/android/androidVNC/ColorModel256.java b/limbo-android-lib/src/main/java/android/androidVNC/ColorModel256.java deleted file mode 100644 index f759fe34a..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/ColorModel256.java +++ /dev/null @@ -1,266 +0,0 @@ -package android.androidVNC; - -public class ColorModel256 { - - public final static int [] colors; - - static { - colors = new int[256]; - colors[0]=0xff000000; - colors[1]=0xff240000; - colors[2]=0xff490000; - colors[3]=0xff6d0000; - colors[4]=0xff920000; - colors[5]=0xffb60000; - colors[6]=0xffdb0000; - colors[7]=0xffff0000; - colors[8]=0xff002400; - colors[9]=0xff242400; - colors[10]=0xff492400; - colors[11]=0xff6d2400; - colors[12]=0xff922400; - colors[13]=0xffb62400; - colors[14]=0xffdb2400; - colors[15]=0xffff2400; - colors[16]=0xff004900; - colors[17]=0xff244900; - colors[18]=0xff494900; - colors[19]=0xff6d4900; - colors[20]=0xff924900; - colors[21]=0xffb64900; - colors[22]=0xffdb4900; - colors[23]=0xffff4900; - colors[24]=0xff006d00; - colors[25]=0xff246d00; - colors[26]=0xff496d00; - colors[27]=0xff6d6d00; - colors[28]=0xff926d00; - colors[29]=0xffb66d00; - colors[30]=0xffdb6d00; - colors[31]=0xffff6d00; - colors[32]=0xff009200; - colors[33]=0xff249200; - colors[34]=0xff499200; - colors[35]=0xff6d9200; - colors[36]=0xff929200; - colors[37]=0xffb69200; - colors[38]=0xffdb9200; - colors[39]=0xffff9200; - colors[40]=0xff00b600; - colors[41]=0xff24b600; - colors[42]=0xff49b600; - colors[43]=0xff6db600; - colors[44]=0xff92b600; - colors[45]=0xffb6b600; - colors[46]=0xffdbb600; - colors[47]=0xffffb600; - colors[48]=0xff00db00; - colors[49]=0xff24db00; - colors[50]=0xff49db00; - colors[51]=0xff6ddb00; - colors[52]=0xff92db00; - colors[53]=0xffb6db00; - colors[54]=0xffdbdb00; - colors[55]=0xffffdb00; - colors[56]=0xff00ff00; - colors[57]=0xff24ff00; - colors[58]=0xff49ff00; - colors[59]=0xff6dff00; - colors[60]=0xff92ff00; - colors[61]=0xffb6ff00; - colors[62]=0xffdbff00; - colors[63]=0xffffff00; - colors[64]=0xff000055; - colors[65]=0xff240055; - colors[66]=0xff490055; - colors[67]=0xff6d0055; - colors[68]=0xff920055; - colors[69]=0xffb60055; - colors[70]=0xffdb0055; - colors[71]=0xffff0055; - colors[72]=0xff002455; - colors[73]=0xff242455; - colors[74]=0xff492455; - colors[75]=0xff6d2455; - colors[76]=0xff922455; - colors[77]=0xffb62455; - colors[78]=0xffdb2455; - colors[79]=0xffff2455; - colors[80]=0xff004955; - colors[81]=0xff244955; - colors[82]=0xff494955; - colors[83]=0xff6d4955; - colors[84]=0xff924955; - colors[85]=0xffb64955; - colors[86]=0xffdb4955; - colors[87]=0xffff4955; - colors[88]=0xff006d55; - colors[89]=0xff246d55; - colors[90]=0xff496d55; - colors[91]=0xff6d6d55; - colors[92]=0xff926d55; - colors[93]=0xffb66d55; - colors[94]=0xffdb6d55; - colors[95]=0xffff6d55; - colors[96]=0xff009255; - colors[97]=0xff249255; - colors[98]=0xff499255; - colors[99]=0xff6d9255; - colors[100]=0xff929255; - colors[101]=0xffb69255; - colors[102]=0xffdb9255; - colors[103]=0xffff9255; - colors[104]=0xff00b655; - colors[105]=0xff24b655; - colors[106]=0xff49b655; - colors[107]=0xff6db655; - colors[108]=0xff92b655; - colors[109]=0xffb6b655; - colors[110]=0xffdbb655; - colors[111]=0xffffb655; - colors[112]=0xff00db55; - colors[113]=0xff24db55; - colors[114]=0xff49db55; - colors[115]=0xff6ddb55; - colors[116]=0xff92db55; - colors[117]=0xffb6db55; - colors[118]=0xffdbdb55; - colors[119]=0xffffdb55; - colors[120]=0xff00ff55; - colors[121]=0xff24ff55; - colors[122]=0xff49ff55; - colors[123]=0xff6dff55; - colors[124]=0xff92ff55; - colors[125]=0xffb6ff55; - colors[126]=0xffdbff55; - colors[127]=0xffffff55; - colors[128]=0xff0000aa; - colors[129]=0xff2400aa; - colors[130]=0xff4900aa; - colors[131]=0xff6d00aa; - colors[132]=0xff9200aa; - colors[133]=0xffb600aa; - colors[134]=0xffdb00aa; - colors[135]=0xffff00aa; - colors[136]=0xff0024aa; - colors[137]=0xff2424aa; - colors[138]=0xff4924aa; - colors[139]=0xff6d24aa; - colors[140]=0xff9224aa; - colors[141]=0xffb624aa; - colors[142]=0xffdb24aa; - colors[143]=0xffff24aa; - colors[144]=0xff0049aa; - colors[145]=0xff2449aa; - colors[146]=0xff4949aa; - colors[147]=0xff6d49aa; - colors[148]=0xff9249aa; - colors[149]=0xffb649aa; - colors[150]=0xffdb49aa; - colors[151]=0xffff49aa; - colors[152]=0xff006daa; - colors[153]=0xff246daa; - colors[154]=0xff496daa; - colors[155]=0xff6d6daa; - colors[156]=0xff926daa; - colors[157]=0xffb66daa; - colors[158]=0xffdb6daa; - colors[159]=0xffff6daa; - colors[160]=0xff0092aa; - colors[161]=0xff2492aa; - colors[162]=0xff4992aa; - colors[163]=0xff6d92aa; - colors[164]=0xff9292aa; - colors[165]=0xffb692aa; - colors[166]=0xffdb92aa; - colors[167]=0xffff92aa; - colors[168]=0xff00b6aa; - colors[169]=0xff24b6aa; - colors[170]=0xff49b6aa; - colors[171]=0xff6db6aa; - colors[172]=0xff92b6aa; - colors[173]=0xffb6b6aa; - colors[174]=0xffdbb6aa; - colors[175]=0xffffb6aa; - colors[176]=0xff00dbaa; - colors[177]=0xff24dbaa; - colors[178]=0xff49dbaa; - colors[179]=0xff6ddbaa; - colors[180]=0xff92dbaa; - colors[181]=0xffb6dbaa; - colors[182]=0xffdbdbaa; - colors[183]=0xffffdbaa; - colors[184]=0xff00ffaa; - colors[185]=0xff24ffaa; - colors[186]=0xff49ffaa; - colors[187]=0xff6dffaa; - colors[188]=0xff92ffaa; - colors[189]=0xffb6ffaa; - colors[190]=0xffdbffaa; - colors[191]=0xffffffaa; - colors[192]=0xff0000ff; - colors[193]=0xff2400ff; - colors[194]=0xff4900ff; - colors[195]=0xff6d00ff; - colors[196]=0xff9200ff; - colors[197]=0xffb600ff; - colors[198]=0xffdb00ff; - colors[199]=0xffff00ff; - colors[200]=0xff0024ff; - colors[201]=0xff2424ff; - colors[202]=0xff4924ff; - colors[203]=0xff6d24ff; - colors[204]=0xff9224ff; - colors[205]=0xffb624ff; - colors[206]=0xffdb24ff; - colors[207]=0xffff24ff; - colors[208]=0xff0049ff; - colors[209]=0xff2449ff; - colors[210]=0xff4949ff; - colors[211]=0xff6d49ff; - colors[212]=0xff9249ff; - colors[213]=0xffb649ff; - colors[214]=0xffdb49ff; - colors[215]=0xffff49ff; - colors[216]=0xff006dff; - colors[217]=0xff246dff; - colors[218]=0xff496dff; - colors[219]=0xff6d6dff; - colors[220]=0xff926dff; - colors[221]=0xffb66dff; - colors[222]=0xffdb6dff; - colors[223]=0xffff6dff; - colors[224]=0xff0092ff; - colors[225]=0xff2492ff; - colors[226]=0xff4992ff; - colors[227]=0xff6d92ff; - colors[228]=0xff9292ff; - colors[229]=0xffb692ff; - colors[230]=0xffdb92ff; - colors[231]=0xffff92ff; - colors[232]=0xff00b6ff; - colors[233]=0xff24b6ff; - colors[234]=0xff49b6ff; - colors[235]=0xff6db6ff; - colors[236]=0xff92b6ff; - colors[237]=0xffb6b6ff; - colors[238]=0xffdbb6ff; - colors[239]=0xffffb6ff; - colors[240]=0xff00dbff; - colors[241]=0xff24dbff; - colors[242]=0xff49dbff; - colors[243]=0xff6ddbff; - colors[244]=0xff92dbff; - colors[245]=0xffb6dbff; - colors[246]=0xffdbdbff; - colors[247]=0xffffdbff; - colors[248]=0xff00ffff; - colors[249]=0xff24ffff; - colors[250]=0xff49ffff; - colors[251]=0xff6dffff; - colors[252]=0xff92ffff; - colors[253]=0xffb6ffff; - colors[254]=0xffdbffff; - colors[255]=0xffffffff; - } -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/ColorModel64.java b/limbo-android-lib/src/main/java/android/androidVNC/ColorModel64.java deleted file mode 100644 index 7236da828..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/ColorModel64.java +++ /dev/null @@ -1,267 +0,0 @@ -package android.androidVNC; - -public class ColorModel64 { - - public final static int [] colors; - - static { - colors = new int[256]; - colors[0]=0xff000000; - colors[1]=0xff000055; - colors[2]=0xff0000aa; - colors[3]=0xff0000ff; - colors[4]=0xff005500; - colors[5]=0xff005555; - colors[6]=0xff0055aa; - colors[7]=0xff0055ff; - colors[8]=0xff00aa00; - colors[9]=0xff00aa55; - colors[10]=0xff00aaaa; - colors[11]=0xff00aaff; - colors[12]=0xff00ff00; - colors[13]=0xff00ff55; - colors[14]=0xff00ffaa; - colors[15]=0xff00ffff; - colors[16]=0xff550000; - colors[17]=0xff550055; - colors[18]=0xff5500aa; - colors[19]=0xff5500ff; - colors[20]=0xff555500; - colors[21]=0xff555555; - colors[22]=0xff5555aa; - colors[23]=0xff5555ff; - colors[24]=0xff55aa00; - colors[25]=0xff55aa55; - colors[26]=0xff55aaaa; - colors[27]=0xff55aaff; - colors[28]=0xff55ff00; - colors[29]=0xff55ff55; - colors[30]=0xff55ffaa; - colors[31]=0xff55ffff; - colors[32]=0xffaa0000; - colors[33]=0xffaa0055; - colors[34]=0xffaa00aa; - colors[35]=0xffaa00ff; - colors[36]=0xffaa5500; - colors[37]=0xffaa5555; - colors[38]=0xffaa55aa; - colors[39]=0xffaa55ff; - colors[40]=0xffaaaa00; - colors[41]=0xffaaaa55; - colors[42]=0xffaaaaaa; - colors[43]=0xffaaaaff; - colors[44]=0xffaaff00; - colors[45]=0xffaaff55; - colors[46]=0xffaaffaa; - colors[47]=0xffaaffff; - colors[48]=0xffff0000; - colors[49]=0xffff0055; - colors[50]=0xffff00aa; - colors[51]=0xffff00ff; - colors[52]=0xffff5500; - colors[53]=0xffff5555; - colors[54]=0xffff55aa; - colors[55]=0xffff55ff; - colors[56]=0xffffaa00; - colors[57]=0xffffaa55; - colors[58]=0xffffaaaa; - colors[59]=0xffffaaff; - colors[60]=0xffffff00; - colors[61]=0xffffff55; - colors[62]=0xffffffaa; - colors[63]=0xffffffff; - colors[64]=0xff000000; - colors[65]=0xff000055; - colors[66]=0xff0000aa; - colors[67]=0xff0000ff; - colors[68]=0xff005500; - colors[69]=0xff005555; - colors[70]=0xff0055aa; - colors[71]=0xff0055ff; - colors[72]=0xff00aa00; - colors[73]=0xff00aa55; - colors[74]=0xff00aaaa; - colors[75]=0xff00aaff; - colors[76]=0xff00ff00; - colors[77]=0xff00ff55; - colors[78]=0xff00ffaa; - colors[79]=0xff00ffff; - colors[80]=0xff550000; - colors[81]=0xff550055; - colors[82]=0xff5500aa; - colors[83]=0xff5500ff; - colors[84]=0xff555500; - colors[85]=0xff555555; - colors[86]=0xff5555aa; - colors[87]=0xff5555ff; - colors[88]=0xff55aa00; - colors[89]=0xff55aa55; - colors[90]=0xff55aaaa; - colors[91]=0xff55aaff; - colors[92]=0xff55ff00; - colors[93]=0xff55ff55; - colors[94]=0xff55ffaa; - colors[95]=0xff55ffff; - colors[96]=0xffaa0000; - colors[97]=0xffaa0055; - colors[98]=0xffaa00aa; - colors[99]=0xffaa00ff; - colors[100]=0xffaa5500; - colors[101]=0xffaa5555; - colors[102]=0xffaa55aa; - colors[103]=0xffaa55ff; - colors[104]=0xffaaaa00; - colors[105]=0xffaaaa55; - colors[106]=0xffaaaaaa; - colors[107]=0xffaaaaff; - colors[108]=0xffaaff00; - colors[109]=0xffaaff55; - colors[110]=0xffaaffaa; - colors[111]=0xffaaffff; - colors[112]=0xffff0000; - colors[113]=0xffff0055; - colors[114]=0xffff00aa; - colors[115]=0xffff00ff; - colors[116]=0xffff5500; - colors[117]=0xffff5555; - colors[118]=0xffff55aa; - colors[119]=0xffff55ff; - colors[120]=0xffffaa00; - colors[121]=0xffffaa55; - colors[122]=0xffffaaaa; - colors[123]=0xffffaaff; - colors[124]=0xffffff00; - colors[125]=0xffffff55; - colors[126]=0xffffffaa; - colors[127]=0xffffffff; - colors[128]=0xff000000; - colors[129]=0xff000055; - colors[130]=0xff0000aa; - colors[131]=0xff0000ff; - colors[132]=0xff005500; - colors[133]=0xff005555; - colors[134]=0xff0055aa; - colors[135]=0xff0055ff; - colors[136]=0xff00aa00; - colors[137]=0xff00aa55; - colors[138]=0xff00aaaa; - colors[139]=0xff00aaff; - colors[140]=0xff00ff00; - colors[141]=0xff00ff55; - colors[142]=0xff00ffaa; - colors[143]=0xff00ffff; - colors[144]=0xff550000; - colors[145]=0xff550055; - colors[146]=0xff5500aa; - colors[147]=0xff5500ff; - colors[148]=0xff555500; - colors[149]=0xff555555; - colors[150]=0xff5555aa; - colors[151]=0xff5555ff; - colors[152]=0xff55aa00; - colors[153]=0xff55aa55; - colors[154]=0xff55aaaa; - colors[155]=0xff55aaff; - colors[156]=0xff55ff00; - colors[157]=0xff55ff55; - colors[158]=0xff55ffaa; - colors[159]=0xff55ffff; - colors[160]=0xffaa0000; - colors[161]=0xffaa0055; - colors[162]=0xffaa00aa; - colors[163]=0xffaa00ff; - colors[164]=0xffaa5500; - colors[165]=0xffaa5555; - colors[166]=0xffaa55aa; - colors[167]=0xffaa55ff; - colors[168]=0xffaaaa00; - colors[169]=0xffaaaa55; - colors[170]=0xffaaaaaa; - colors[171]=0xffaaaaff; - colors[172]=0xffaaff00; - colors[173]=0xffaaff55; - colors[174]=0xffaaffaa; - colors[175]=0xffaaffff; - colors[176]=0xffff0000; - colors[177]=0xffff0055; - colors[178]=0xffff00aa; - colors[179]=0xffff00ff; - colors[180]=0xffff5500; - colors[181]=0xffff5555; - colors[182]=0xffff55aa; - colors[183]=0xffff55ff; - colors[184]=0xffffaa00; - colors[185]=0xffffaa55; - colors[186]=0xffffaaaa; - colors[187]=0xffffaaff; - colors[188]=0xffffff00; - colors[189]=0xffffff55; - colors[190]=0xffffffaa; - colors[191]=0xffffffff; - colors[192]=0xff000000; - colors[193]=0xff000055; - colors[194]=0xff0000aa; - colors[195]=0xff0000ff; - colors[196]=0xff005500; - colors[197]=0xff005555; - colors[198]=0xff0055aa; - colors[199]=0xff0055ff; - colors[200]=0xff00aa00; - colors[201]=0xff00aa55; - colors[202]=0xff00aaaa; - colors[203]=0xff00aaff; - colors[204]=0xff00ff00; - colors[205]=0xff00ff55; - colors[206]=0xff00ffaa; - colors[207]=0xff00ffff; - colors[208]=0xff550000; - colors[209]=0xff550055; - colors[210]=0xff5500aa; - colors[211]=0xff5500ff; - colors[212]=0xff555500; - colors[213]=0xff555555; - colors[214]=0xff5555aa; - colors[215]=0xff5555ff; - colors[216]=0xff55aa00; - colors[217]=0xff55aa55; - colors[218]=0xff55aaaa; - colors[219]=0xff55aaff; - colors[220]=0xff55ff00; - colors[221]=0xff55ff55; - colors[222]=0xff55ffaa; - colors[223]=0xff55ffff; - colors[224]=0xffaa0000; - colors[225]=0xffaa0055; - colors[226]=0xffaa00aa; - colors[227]=0xffaa00ff; - colors[228]=0xffaa5500; - colors[229]=0xffaa5555; - colors[230]=0xffaa55aa; - colors[231]=0xffaa55ff; - colors[232]=0xffaaaa00; - colors[233]=0xffaaaa55; - colors[234]=0xffaaaaaa; - colors[235]=0xffaaaaff; - colors[236]=0xffaaff00; - colors[237]=0xffaaff55; - colors[238]=0xffaaffaa; - colors[239]=0xffaaffff; - colors[240]=0xffff0000; - colors[241]=0xffff0055; - colors[242]=0xffff00aa; - colors[243]=0xffff00ff; - colors[244]=0xffff5500; - colors[245]=0xffff5555; - colors[246]=0xffff55aa; - colors[247]=0xffff55ff; - colors[248]=0xffffaa00; - colors[249]=0xffffaa55; - colors[250]=0xffffaaaa; - colors[251]=0xffffaaff; - colors[252]=0xffffff00; - colors[253]=0xffffff55; - colors[254]=0xffffffaa; - colors[255]=0xffffffff; - - } -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/ColorModel8.java b/limbo-android-lib/src/main/java/android/androidVNC/ColorModel8.java deleted file mode 100644 index 0c1b2de7b..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/ColorModel8.java +++ /dev/null @@ -1,266 +0,0 @@ -package android.androidVNC; - -public class ColorModel8 { - - public final static int [] colors; - - static { - colors = new int[256]; - colors[0]=0xff000000; - colors[1]=0xff0000ff; - colors[2]=0xff00ff00; - colors[3]=0xff00ffff; - colors[4]=0xffff0000; - colors[5]=0xffff00ff; - colors[6]=0xffffff00; - colors[7]=0xffffffff; - colors[8]=0xff000000; - colors[9]=0xff0000ff; - colors[10]=0xff00ff00; - colors[11]=0xff00ffff; - colors[12]=0xffff0000; - colors[13]=0xffff00ff; - colors[14]=0xffffff00; - colors[15]=0xffffffff; - colors[16]=0xff000000; - colors[17]=0xff0000ff; - colors[18]=0xff00ff00; - colors[19]=0xff00ffff; - colors[20]=0xffff0000; - colors[21]=0xffff00ff; - colors[22]=0xffffff00; - colors[23]=0xffffffff; - colors[24]=0xff000000; - colors[25]=0xff0000ff; - colors[26]=0xff00ff00; - colors[27]=0xff00ffff; - colors[28]=0xffff0000; - colors[29]=0xffff00ff; - colors[30]=0xffffff00; - colors[31]=0xffffffff; - colors[32]=0xff000000; - colors[33]=0xff0000ff; - colors[34]=0xff00ff00; - colors[35]=0xff00ffff; - colors[36]=0xffff0000; - colors[37]=0xffff00ff; - colors[38]=0xffffff00; - colors[39]=0xffffffff; - colors[40]=0xff000000; - colors[41]=0xff0000ff; - colors[42]=0xff00ff00; - colors[43]=0xff00ffff; - colors[44]=0xffff0000; - colors[45]=0xffff00ff; - colors[46]=0xffffff00; - colors[47]=0xffffffff; - colors[48]=0xff000000; - colors[49]=0xff0000ff; - colors[50]=0xff00ff00; - colors[51]=0xff00ffff; - colors[52]=0xffff0000; - colors[53]=0xffff00ff; - colors[54]=0xffffff00; - colors[55]=0xffffffff; - colors[56]=0xff000000; - colors[57]=0xff0000ff; - colors[58]=0xff00ff00; - colors[59]=0xff00ffff; - colors[60]=0xffff0000; - colors[61]=0xffff00ff; - colors[62]=0xffffff00; - colors[63]=0xffffffff; - colors[64]=0xff000000; - colors[65]=0xff0000ff; - colors[66]=0xff00ff00; - colors[67]=0xff00ffff; - colors[68]=0xffff0000; - colors[69]=0xffff00ff; - colors[70]=0xffffff00; - colors[71]=0xffffffff; - colors[72]=0xff000000; - colors[73]=0xff0000ff; - colors[74]=0xff00ff00; - colors[75]=0xff00ffff; - colors[76]=0xffff0000; - colors[77]=0xffff00ff; - colors[78]=0xffffff00; - colors[79]=0xffffffff; - colors[80]=0xff000000; - colors[81]=0xff0000ff; - colors[82]=0xff00ff00; - colors[83]=0xff00ffff; - colors[84]=0xffff0000; - colors[85]=0xffff00ff; - colors[86]=0xffffff00; - colors[87]=0xffffffff; - colors[88]=0xff000000; - colors[89]=0xff0000ff; - colors[90]=0xff00ff00; - colors[91]=0xff00ffff; - colors[92]=0xffff0000; - colors[93]=0xffff00ff; - colors[94]=0xffffff00; - colors[95]=0xffffffff; - colors[96]=0xff000000; - colors[97]=0xff0000ff; - colors[98]=0xff00ff00; - colors[99]=0xff00ffff; - colors[100]=0xffff0000; - colors[101]=0xffff00ff; - colors[102]=0xffffff00; - colors[103]=0xffffffff; - colors[104]=0xff000000; - colors[105]=0xff0000ff; - colors[106]=0xff00ff00; - colors[107]=0xff00ffff; - colors[108]=0xffff0000; - colors[109]=0xffff00ff; - colors[110]=0xffffff00; - colors[111]=0xffffffff; - colors[112]=0xff000000; - colors[113]=0xff0000ff; - colors[114]=0xff00ff00; - colors[115]=0xff00ffff; - colors[116]=0xffff0000; - colors[117]=0xffff00ff; - colors[118]=0xffffff00; - colors[119]=0xffffffff; - colors[120]=0xff000000; - colors[121]=0xff0000ff; - colors[122]=0xff00ff00; - colors[123]=0xff00ffff; - colors[124]=0xffff0000; - colors[125]=0xffff00ff; - colors[126]=0xffffff00; - colors[127]=0xffffffff; - colors[128]=0xff000000; - colors[129]=0xff0000ff; - colors[130]=0xff00ff00; - colors[131]=0xff00ffff; - colors[132]=0xffff0000; - colors[133]=0xffff00ff; - colors[134]=0xffffff00; - colors[135]=0xffffffff; - colors[136]=0xff000000; - colors[137]=0xff0000ff; - colors[138]=0xff00ff00; - colors[139]=0xff00ffff; - colors[140]=0xffff0000; - colors[141]=0xffff00ff; - colors[142]=0xffffff00; - colors[143]=0xffffffff; - colors[144]=0xff000000; - colors[145]=0xff0000ff; - colors[146]=0xff00ff00; - colors[147]=0xff00ffff; - colors[148]=0xffff0000; - colors[149]=0xffff00ff; - colors[150]=0xffffff00; - colors[151]=0xffffffff; - colors[152]=0xff000000; - colors[153]=0xff0000ff; - colors[154]=0xff00ff00; - colors[155]=0xff00ffff; - colors[156]=0xffff0000; - colors[157]=0xffff00ff; - colors[158]=0xffffff00; - colors[159]=0xffffffff; - colors[160]=0xff000000; - colors[161]=0xff0000ff; - colors[162]=0xff00ff00; - colors[163]=0xff00ffff; - colors[164]=0xffff0000; - colors[165]=0xffff00ff; - colors[166]=0xffffff00; - colors[167]=0xffffffff; - colors[168]=0xff000000; - colors[169]=0xff0000ff; - colors[170]=0xff00ff00; - colors[171]=0xff00ffff; - colors[172]=0xffff0000; - colors[173]=0xffff00ff; - colors[174]=0xffffff00; - colors[175]=0xffffffff; - colors[176]=0xff000000; - colors[177]=0xff0000ff; - colors[178]=0xff00ff00; - colors[179]=0xff00ffff; - colors[180]=0xffff0000; - colors[181]=0xffff00ff; - colors[182]=0xffffff00; - colors[183]=0xffffffff; - colors[184]=0xff000000; - colors[185]=0xff0000ff; - colors[186]=0xff00ff00; - colors[187]=0xff00ffff; - colors[188]=0xffff0000; - colors[189]=0xffff00ff; - colors[190]=0xffffff00; - colors[191]=0xffffffff; - colors[192]=0xff000000; - colors[193]=0xff0000ff; - colors[194]=0xff00ff00; - colors[195]=0xff00ffff; - colors[196]=0xffff0000; - colors[197]=0xffff00ff; - colors[198]=0xffffff00; - colors[199]=0xffffffff; - colors[200]=0xff000000; - colors[201]=0xff0000ff; - colors[202]=0xff00ff00; - colors[203]=0xff00ffff; - colors[204]=0xffff0000; - colors[205]=0xffff00ff; - colors[206]=0xffffff00; - colors[207]=0xffffffff; - colors[208]=0xff000000; - colors[209]=0xff0000ff; - colors[210]=0xff00ff00; - colors[211]=0xff00ffff; - colors[212]=0xffff0000; - colors[213]=0xffff00ff; - colors[214]=0xffffff00; - colors[215]=0xffffffff; - colors[216]=0xff000000; - colors[217]=0xff0000ff; - colors[218]=0xff00ff00; - colors[219]=0xff00ffff; - colors[220]=0xffff0000; - colors[221]=0xffff00ff; - colors[222]=0xffffff00; - colors[223]=0xffffffff; - colors[224]=0xff000000; - colors[225]=0xff0000ff; - colors[226]=0xff00ff00; - colors[227]=0xff00ffff; - colors[228]=0xffff0000; - colors[229]=0xffff00ff; - colors[230]=0xffffff00; - colors[231]=0xffffffff; - colors[232]=0xff000000; - colors[233]=0xff0000ff; - colors[234]=0xff00ff00; - colors[235]=0xff00ffff; - colors[236]=0xffff0000; - colors[237]=0xffff00ff; - colors[238]=0xffffff00; - colors[239]=0xffffffff; - colors[240]=0xff000000; - colors[241]=0xff0000ff; - colors[242]=0xff00ff00; - colors[243]=0xff00ffff; - colors[244]=0xffff0000; - colors[245]=0xffff00ff; - colors[246]=0xffffff00; - colors[247]=0xffffffff; - colors[248]=0xff000000; - colors[249]=0xff0000ff; - colors[250]=0xff00ff00; - colors[251]=0xff00ffff; - colors[252]=0xffff0000; - colors[253]=0xffff00ff; - colors[254]=0xffffff00; - colors[255]=0xffffffff; - } -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/CompactBitmapData.java b/limbo-android-lib/src/main/java/android/androidVNC/CompactBitmapData.java deleted file mode 100644 index 25e53c48e..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/CompactBitmapData.java +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package android.androidVNC; - -import java.io.IOException; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; - -import com.max2idea.android.limbo.main.Config; - -/** - * @author Michael A. MacDonald - * - */ -class CompactBitmapData extends AbstractBitmapData { - - class CompactBitmapDrawable extends AbstractBitmapDrawable - { - CompactBitmapDrawable() - { - super(CompactBitmapData.this); - } - /* (non-Javadoc) - * @see android.graphics.drawable.DrawableContainer#draw(android.graphics.Canvas) - */ - @Override - public void draw(Canvas canvas) { - draw(canvas, 0, 0); - } - } - - CompactBitmapData(RfbProto rfb, VncCanvas c) - { - super(rfb,c); - bitmapwidth=framebufferwidth; - bitmapheight=framebufferheight; - - mbitmap = Bitmap.createBitmap(rfb.framebufferWidth, rfb.framebufferHeight, Config.bitmapConfig); - memGraphics = new Canvas(mbitmap); - bitmapPixels = new int[rfb.framebufferWidth * rfb.framebufferHeight]; - } - - @Override - void writeFullUpdateRequest(boolean incremental) throws IOException { - rfb.writeFramebufferUpdateRequest(0, 0, framebufferwidth, framebufferheight, incremental); - } - - @Override - boolean validDraw(int x, int y, int w, int h) { - return true; - } - - @Override - int offset(int x, int y) { - return y * bitmapwidth + x; - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#createDrawable() - */ - @Override - AbstractBitmapDrawable createDrawable() { - return new CompactBitmapDrawable(); - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#updateBitmap(int, int, int, int) - */ - @Override - void updateBitmap(int x, int y, int w, int h) { - mbitmap.setPixels(bitmapPixels, offset(x,y), bitmapwidth, x, y, w, h); - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#copyRect(android.graphics.Rect, android.graphics.Rect, android.graphics.Paint) - */ - @Override - void copyRect(Rect src, Rect dest, Paint paint) { - memGraphics.drawBitmap(mbitmap, src, dest, paint); - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#drawRect(int, int, int, int, android.graphics.Paint) - */ - @Override - void drawRect(int x, int y, int w, int h, Paint paint) { - memGraphics.drawRect(x, y, x + w, y + h, paint); - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#scrollChanged(int, int) - */ - @Override - void scrollChanged(int newx, int newy) { - // Don't need to do anything here - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#syncScroll() - */ - @Override - void syncScroll() { - // Don't need anything here either - - } -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/ConnectionBean.java b/limbo-android-lib/src/main/java/android/androidVNC/ConnectionBean.java deleted file mode 100644 index c62b54485..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/ConnectionBean.java +++ /dev/null @@ -1,181 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package android.androidVNC; - -import com.max2idea.android.limbo.main.Config; - -import android.widget.ImageView.ScaleType; - -/** - * @author Michael A. MacDonald - * - */ -public class ConnectionBean { - - private String address = "localhost"; - private String password = ""; - private int port = 5901; - private String colorModel = COLORMODEL.C64.nameString(); - private String InputMode = VncCanvasActivity.TOUCH_ZOOM_MODE; - private String scaleMode = ""; - private String nickname = "limbo"; - private long forceFull = 0; - private boolean useLocalCursor = false; - private boolean followMouse = true; - private String userName; - private long id = 0; - - public ConnectionBean() { - setAddress(Config.defaultVNCHost); - setUserName(Config.defaultVNCUsername); - setPassword(Config.defaultVNCPasswd); - setPort(Config.defaultVNCPort + 5900); - setColorModel(Config.defaultVNCColorMode); - if (Config.enable_qemu_fullScreen) - setScaleMode(Config.defaultFullscreenScaleMode); - else - setScaleMode(Config.defaultScaleModeCenter); - setInputMode(Config.defaultInputMode); - } - - private void setUserName(String string) { - - this.userName = string; - - } - - public void setInputMode(String touchZoomMode) { - - this.InputMode = touchZoomMode; - - } - - void setPort(int i) { - - this.port = i; - } - - void setColorModel(String nameString) { - - this.colorModel = nameString; - - } - - void setAddress(String string) { - - this.address = string; - } - - void setPassword(String string) { - - this.password = string; - } - - public long get_Id() { - - return 0; - } - - ScaleType getScaleMode() { - return ScaleType.valueOf(getScaleModeAsString()); - } - - private String getScaleModeAsString() { - - return scaleMode; - } - - void setScaleMode(ScaleType value) { - setScaleModeAsString(value.toString()); - } - - private void setScaleModeAsString(String string) { - - this.scaleMode = string; - - } - - public String getAddress() { - - return this.address; - } - - public void setNickname(String address2) { - - this.nickname = address2; - } - - public int getPort() { - - return port; - } - - public String getInputMode() { - - return this.InputMode; - } - - public String getPassword() { - - return this.password; - } - - public long getForceFull() { - - return this.forceFull; - } - - public boolean getUseLocalCursor() { - - return this.useLocalCursor; - } - - public String getNickname() { - - return nickname; - } - - public String getColorModel() { - - return this.colorModel; - } - - public void setForceFull(long l) { - - this.forceFull = l; - } - - public void setUseLocalCursor(boolean checked) { - - this.setUseLocalCursor(checked); - } - - public void setFollowMouse(boolean b) { - - this.followMouse = b; - - } - - public boolean getFollowMouse() { - - return this.followMouse; - } - - public String getUserName() { - - return userName; - } - - public void setConnectionId(long get_Id) { - - this.id = get_Id; - - } - - public boolean getFollowPan() { - - return false; - } - -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/ConnectionSettable.java b/limbo-android-lib/src/main/java/android/androidVNC/ConnectionSettable.java deleted file mode 100644 index dfc64cc02..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/ConnectionSettable.java +++ /dev/null @@ -1,12 +0,0 @@ -/** - * - */ -package android.androidVNC; - -/** - * @author mike - * - */ -interface ConnectionSettable { - void setConnection(ConnectionBean connection); -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/DH.java b/limbo-android-lib/src/main/java/android/androidVNC/DH.java deleted file mode 100644 index aaf6b1099..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/DH.java +++ /dev/null @@ -1,183 +0,0 @@ -package android.androidVNC; -// CRYPTO LIBRARY FOR EXCHANGING KEYS -// USING THE DIFFIE-HELLMAN KEY EXCHANGE PROTOCOL - -// The diffie-hellman can be used to securely exchange keys -// between parties, where a third party eavesdropper given -// the values being transmitted cannot determine the key. - -// Implemented by Lee Griffiths, Jan 2004. -// This software is freeware, you may use it to your discretion, -// however by doing so you take full responsibility for any damage -// it may cause. - -// Hope you find it useful, even if you just use some of the functions -// out of it like the prime number generator and the XtoYmodN function. - -// It would be great if you could send me emails to: lee.griffiths@first4internet.co.uk -// with any suggestions, comments, or questions! - -// Enjoy. - -// Adopted to ms-logon for ultravnc and ported to Java by marscha, 2006. - -//import java.lang.Math; - -public class DH { - - public DH() { - maxNum = (((long) 1) << DH_MAX_BITS) - 1; - } - - public DH(long generator, long modulus) throws Exception { - maxNum = (((long) 1) << DH_MAX_BITS) - 1; - if (generator >= maxNum || modulus >= maxNum) - throw new Exception("Modulus or generator too large."); - gen = generator; - mod = modulus; - } - - private long rng(long limit) { - return (long) (java.lang.Math.random() * limit); - } - - //Performs the miller-rabin primality test on a guessed prime n. - //trials is the number of attempts to verify this, because the function - //is not 100% accurate it may be a composite. However setting the trial - //value to around 5 should guarantee success even with very large primes - private boolean millerRabin (long n, int trials) { - long a = 0; - - for (int i = 0; i < trials; i++) { - a = rng(n - 3) + 2;// gets random value in [2..n-1] - if (XpowYmodN(a, n - 1, n) != 1) return false; //n composite, return false - } - return true; // n probably prime - } - - //Generates a large prime number by - //choosing a randomly large integer, and ensuring the value is odd - //then uses the miller-rabin primality test on it to see if it is prime - //if not the value gets increased until it is prime - private long generatePrime() { - long prime = 0; - - do { - long start = rng(maxNum); - prime = tryToGeneratePrime(start); - } while (prime == 0); - return prime; - } - - private long tryToGeneratePrime(long prime) { - //ensure it is an odd number - if ((prime & 1) == 0) - prime += 1; - - long cnt = 0; - while (!millerRabin(prime, 25) && (cnt++ < DH_RANGE) && prime < maxNum) { - prime += 2; - if ((prime % 3) == 0) prime += 2; - } - return (cnt >= DH_RANGE || prime >= maxNum) ? 0 : prime; - } - - //Raises X to the power Y in modulus N - //the values of X, Y, and N can be massive, and this can be - //achieved by first calculating X to the power of 2 then - //using power chaining over modulus N - private long XpowYmodN(long x, long y, long N) { - long result = 1; - final long oneShift63 = ((long) 1) << 63; - - for (int i = 0; i < 64; y <<= 1, i++){ - result = result * result % N; - if ((y & oneShift63) != 0) - result = result * x % N; - } - return result; - } - - public void createKeys() { - gen = generatePrime(); - mod = generatePrime(); - - if (gen > mod) { - long swap = gen; - gen = mod; - mod = swap; - } - } - - public long createInterKey() { - priv = rng(maxNum); - return pub = XpowYmodN(gen,priv,mod); - } - - public long createEncryptionKey(long interKey) throws Exception { - if (interKey >= maxNum){ - throw new Exception("interKey too large"); - } - return key = XpowYmodN(interKey,priv,mod); - } - - - public long getValue(int flags) { - switch (flags) { - case DH_MOD: - return mod; - case DH_GEN: - return gen; - case DH_PRIV: - return priv; - case DH_PUB: - return pub; - case DH_KEY: - return key; - default: - return (long) 0; - } - } - - public int bits(long number){ - for (int i = 0; i < 64; i++){ - number /= 2; - if (number < 2) return i; - } - return 0; - } - - public static byte[] longToBytes(long number) { - byte[] bytes = new byte[8]; - for (int i = 0; i < 8; i++) { - bytes[i] = (byte) (0xff & (number >> (8 * (7 - i)))); - } - return bytes; - } - - public static long bytesToLong(byte[] bytes) { - long result = 0; - for (int i = 0; i < 8; i++) { - result <<= 8; - result += (byte) bytes[i]; - } - return result; - } - - private long gen; - private long mod; - private long priv; - private long pub; - private long key; - private long maxNum; - - private static final int DH_MAX_BITS = 31; - private static final int DH_RANGE = 100; - - private static final int DH_MOD = 1; - private static final int DH_GEN = 2; - private static final int DH_PRIV = 3; - private static final int DH_PUB = 4; - private static final int DH_KEY = 5; - -} \ No newline at end of file diff --git a/limbo-android-lib/src/main/java/android/androidVNC/DPadMouseKeyHandler.java b/limbo-android-lib/src/main/java/android/androidVNC/DPadMouseKeyHandler.java deleted file mode 100644 index 5f75df509..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/DPadMouseKeyHandler.java +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Copyright (c) 2010 Michael A. MacDonald - */ -package android.androidVNC; - -import android.graphics.PointF; -import android.os.Handler; -import android.view.KeyEvent; -import android.view.MotionEvent; - -/** - * Input handlers delegate to this class to handle keystrokes; this detects keystrokes - * from the DPad and uses them to perform mouse actions; other keystrokes are passed to - * VncCanvasActivity.defaultKeyXXXHandler - * - * @author Michael A. MacDonald - * - */ -class DPadMouseKeyHandler { - private MouseMover mouseMover; - private boolean mouseDown; - private VncCanvasActivity activity; - private VncCanvas canvas; - private boolean isMoving; - - DPadMouseKeyHandler(VncCanvasActivity activity, Handler handler) - { - this.activity = activity; - canvas = activity.vncCanvas; - mouseMover = new MouseMover(activity, handler); - } - - public boolean onKeyDown(int keyCode, KeyEvent evt) { - int xv = 0; - int yv = 0; - boolean result = true; - switch (keyCode) { -// case KeyEvent.KEYCODE_DPAD_LEFT: -// xv = -1; -// break; -// case KeyEvent.KEYCODE_DPAD_RIGHT: -// xv = 1; -// break; -// case KeyEvent.KEYCODE_DPAD_UP: -// yv = -1; -// break; -// case KeyEvent.KEYCODE_DPAD_DOWN: -// yv = 1; -// break; -// case KeyEvent.KEYCODE_DPAD_CENTER: -// if (!mouseDown) { -// mouseDown = true; -// result = canvas.processPointerEvent(canvas.mouseX, canvas.mouseY, MotionEvent.ACTION_DOWN, evt.getMetaState(), mouseDown, canvas.cameraButtonDown); -// } -// break; - default: - result = activity.defaultKeyDownHandler(keyCode, evt); - break; - } - if ((xv != 0 || yv != 0) && !isMoving) { - final int x = xv; - final int y = yv; - isMoving = true; - mouseMover.start(x, y, new Panner.VelocityUpdater() { - - /* - * (non-Javadoc) - * - * @see android.androidVNC.Panner.VelocityUpdater#updateVelocity(android.graphics.Point, - * long) - */ - @Override - public boolean updateVelocity(PointF p, long interval) { - double scale = (1.2 * (double) interval / 50.0); - if (Math.abs(p.x) < 500) - p.x += (int) (scale * x); - if (Math.abs(p.y) < 500) - p.y += (int) (scale * y); - return true; - } - - }); - canvas.processPointerEvent(canvas.mouseX + x, canvas.mouseY + y, MotionEvent.ACTION_MOVE, evt.getMetaState(), - mouseDown, canvas.cameraButtonDown, false, false); - - } - return result; - } - - public boolean onKeyUp(int keyCode, KeyEvent evt) { - boolean result = false; - - switch (keyCode) { -// case KeyEvent.KEYCODE_DPAD_LEFT: -// case KeyEvent.KEYCODE_DPAD_RIGHT: -// case KeyEvent.KEYCODE_DPAD_UP: -// case KeyEvent.KEYCODE_DPAD_DOWN: -// mouseMover.stop(); -// isMoving = false; -// result = true; -// break; - case KeyEvent.KEYCODE_DPAD_CENTER: - if (mouseDown) { - mouseDown = false; - result = canvas.processPointerEvent(canvas.mouseX, canvas.mouseY, MotionEvent.ACTION_UP, evt.getMetaState(), - mouseDown, canvas.cameraButtonDown, false, false); - } else { - result = true; - } - break; - default: - result = activity.defaultKeyUpHandler(keyCode, evt); - break; - } - return result; - } -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/DesCipher.java b/limbo-android-lib/src/main/java/android/androidVNC/DesCipher.java deleted file mode 100644 index 2d7bed357..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/DesCipher.java +++ /dev/null @@ -1,539 +0,0 @@ -// -// This DES class has been extracted from package Acme.Crypto for use in VNC. -// The bytebit[] array has been reversed so that the most significant bit -// in each byte of the key is ignored, not the least significant. Also the -// unnecessary odd parity code has been removed. -// -// These changes are: -// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. -// -// This software is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// - -// DesCipher - the DES encryption method -// -// The meat of this code is by Dave Zimmerman , and is: -// -// Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved. -// -// Permission to use, copy, modify, and distribute this software -// and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and -// without fee is hereby granted, provided that this copyright notice is kept -// intact. -// -// WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY -// OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -// TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -// PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE LIABLE -// FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR -// DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. -// -// THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE -// CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE -// PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT -// NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE -// SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE -// SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE -// PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). WIDGET WORKSHOP -// SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR -// HIGH RISK ACTIVITIES. -// -// -// The rest is: -// -// Copyright (C) 1996 by Jef Poskanzer . All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -// SUCH DAMAGE. -// -// Visit the ACME Labs Java page for up-to-date versions of this and other -// fine Java utilities: http://www.acme.com/java/ - -/// The DES encryption method. -//

-// This is surprisingly fast, for pure Java. On a SPARC 20, wrapped -// in Acme.Crypto.EncryptedOutputStream or Acme.Crypto.EncryptedInputStream, -// it does around 7000 bytes/second. -//

-// Most of this code is by Dave Zimmerman , and is -// Copyright (c) 1996 Widget Workshop, Inc. See the source file for details. -//

-// Fetch the software.
-// Fetch the entire Acme package. -//

-// @see Des3Cipher -// @see EncryptedOutputStream -// @see EncryptedInputStream - -package android.androidVNC; - -//- import java.io.*; - - -public class DesCipher - { - - // Constructor, byte-array key. - public DesCipher( byte[] key ) - { - setKey( key ); - } - - // Key routines. - - private int[] encryptKeys = new int[32]; - private int[] decryptKeys = new int[32]; - - /// Set the key. - public void setKey( byte[] key ) - { - deskey( key, true, encryptKeys ); - deskey( key, false, decryptKeys ); - } - - // Turn an 8-byte key into internal keys. - private void deskey( byte[] keyBlock, boolean encrypting, int[] KnL ) - { - int i, j, l, m, n; - int[] pc1m = new int[56]; - int[] pcr = new int[56]; - int[] kn = new int[32]; - - for ( j = 0; j < 56; ++j ) - { - l = pc1[j]; - m = l & 07; - pc1m[j] = ( (keyBlock[l >>> 3] & bytebit[m]) != 0 )? 1: 0; - } - - for ( i = 0; i < 16; ++i ) - { - if ( encrypting ) - m = i << 1; - else - m = (15-i) << 1; - n = m+1; - kn[m] = kn[n] = 0; - for ( j = 0; j < 28; ++j ) - { - l = j+totrot[i]; - if ( l < 28 ) - pcr[j] = pc1m[l]; - else - pcr[j] = pc1m[l-28]; - } - for ( j=28; j < 56; ++j ) - { - l = j+totrot[i]; - if ( l < 56 ) - pcr[j] = pc1m[l]; - else - pcr[j] = pc1m[l-28]; - } - for ( j = 0; j < 24; ++j ) - { - if ( pcr[pc2[j]] != 0 ) - kn[m] |= bigbyte[j]; - if ( pcr[pc2[j+24]] != 0 ) - kn[n] |= bigbyte[j]; - } - } - cookey( kn, KnL ); - } - - private void cookey( int[] raw, int KnL[] ) - { - int raw0, raw1; - int rawi, KnLi; - int i; - - for ( i = 0, rawi = 0, KnLi = 0; i < 16; ++i ) - { - raw0 = raw[rawi++]; - raw1 = raw[rawi++]; - KnL[KnLi] = (raw0 & 0x00fc0000) << 6; - KnL[KnLi] |= (raw0 & 0x00000fc0) << 10; - KnL[KnLi] |= (raw1 & 0x00fc0000) >>> 10; - KnL[KnLi] |= (raw1 & 0x00000fc0) >>> 6; - ++KnLi; - KnL[KnLi] = (raw0 & 0x0003f000) << 12; - KnL[KnLi] |= (raw0 & 0x0000003f) << 16; - KnL[KnLi] |= (raw1 & 0x0003f000) >>> 4; - KnL[KnLi] |= (raw1 & 0x0000003f); - ++KnLi; - } - } - - - // Block encryption routines. - - private int[] tempInts = new int[2]; - - /// Encrypt a block of eight bytes. - public void encrypt( byte[] clearText, int clearOff, byte[] cipherText, int cipherOff ) - { - squashBytesToInts( clearText, clearOff, tempInts, 0, 2 ); - des( tempInts, tempInts, encryptKeys ); - spreadIntsToBytes( tempInts, 0, cipherText, cipherOff, 2 ); - } - - /// Decrypt a block of eight bytes. - public void decrypt( byte[] cipherText, int cipherOff, byte[] clearText, int clearOff ) - { - squashBytesToInts( cipherText, cipherOff, tempInts, 0, 2 ); - des( tempInts, tempInts, decryptKeys ); - spreadIntsToBytes( tempInts, 0, clearText, clearOff, 2 ); - } - - // Encrypt a text which is a multiple of 8 bytes. - public void encryptText(byte[] clearText, byte[] cipherText, byte[] key) - { - int i, j; - - for(i=0; i<8; i++) - { - clearText[i] ^= key[i]; - } - encrypt(clearText, 0, cipherText, 0); - for(i=8; i0; i-=8) - { - decrypt(cipherText, i, clearText, i); - for(j=0; j<8; j++) - { - clearText[i+j] ^= cipherText[i+j-8]; - } - } - /* i = 0 */ - decrypt(cipherText, 0, clearText, 0); - for(i=0; i<8; i++) - { - clearText[i] ^= key[i]; - } - } - - // The DES function. - private void des( int[] inInts, int[] outInts, int[] keys ) - { - int fval, work, right, leftt; - int round; - int keysi = 0; - - leftt = inInts[0]; - right = inInts[1]; - - work = ((leftt >>> 4) ^ right) & 0x0f0f0f0f; - right ^= work; - leftt ^= (work << 4); - - work = ((leftt >>> 16) ^ right) & 0x0000ffff; - right ^= work; - leftt ^= (work << 16); - - work = ((right >>> 2) ^ leftt) & 0x33333333; - leftt ^= work; - right ^= (work << 2); - - work = ((right >>> 8) ^ leftt) & 0x00ff00ff; - leftt ^= work; - right ^= (work << 8); - right = (right << 1) | ((right >>> 31) & 1); - - work = (leftt ^ right) & 0xaaaaaaaa; - leftt ^= work; - right ^= work; - leftt = (leftt << 1) | ((leftt >>> 31) & 1); - - for ( round = 0; round < 8; ++round ) - { - work = (right << 28) | (right >>> 4); - work ^= keys[keysi++]; - fval = SP7[ work & 0x0000003f ]; - fval |= SP5[(work >>> 8) & 0x0000003f ]; - fval |= SP3[(work >>> 16) & 0x0000003f ]; - fval |= SP1[(work >>> 24) & 0x0000003f ]; - work = right ^ keys[keysi++]; - fval |= SP8[ work & 0x0000003f ]; - fval |= SP6[(work >>> 8) & 0x0000003f ]; - fval |= SP4[(work >>> 16) & 0x0000003f ]; - fval |= SP2[(work >>> 24) & 0x0000003f ]; - leftt ^= fval; - work = (leftt << 28) | (leftt >>> 4); - work ^= keys[keysi++]; - fval = SP7[ work & 0x0000003f ]; - fval |= SP5[(work >>> 8) & 0x0000003f ]; - fval |= SP3[(work >>> 16) & 0x0000003f ]; - fval |= SP1[(work >>> 24) & 0x0000003f ]; - work = leftt ^ keys[keysi++]; - fval |= SP8[ work & 0x0000003f ]; - fval |= SP6[(work >>> 8) & 0x0000003f ]; - fval |= SP4[(work >>> 16) & 0x0000003f ]; - fval |= SP2[(work >>> 24) & 0x0000003f ]; - right ^= fval; - } - - right = (right << 31) | (right >>> 1); - work = (leftt ^ right) & 0xaaaaaaaa; - leftt ^= work; - right ^= work; - leftt = (leftt << 31) | (leftt >>> 1); - work = ((leftt >>> 8) ^ right) & 0x00ff00ff; - right ^= work; - leftt ^= (work << 8); - work = ((leftt >>> 2) ^ right) & 0x33333333; - right ^= work; - leftt ^= (work << 2); - work = ((right >>> 16) ^ leftt) & 0x0000ffff; - leftt ^= work; - right ^= (work << 16); - work = ((right >>> 4) ^ leftt) & 0x0f0f0f0f; - leftt ^= work; - right ^= (work << 4); - outInts[0] = right; - outInts[1] = leftt; - } - - - // Tables, permutations, S-boxes, etc. - - private static byte[] bytebit = { - (byte)0x01, (byte)0x02, (byte)0x04, (byte)0x08, - (byte)0x10, (byte)0x20, (byte)0x40, (byte)0x80 - }; - private static int[] bigbyte = { - 0x800000, 0x400000, 0x200000, 0x100000, - 0x080000, 0x040000, 0x020000, 0x010000, - 0x008000, 0x004000, 0x002000, 0x001000, - 0x000800, 0x000400, 0x000200, 0x000100, - 0x000080, 0x000040, 0x000020, 0x000010, - 0x000008, 0x000004, 0x000002, 0x000001 - }; - private static byte[] pc1 = { - (byte)56, (byte)48, (byte)40, (byte)32, (byte)24, (byte)16, (byte) 8, - (byte) 0, (byte)57, (byte)49, (byte)41, (byte)33, (byte)25, (byte)17, - (byte) 9, (byte) 1, (byte)58, (byte)50, (byte)42, (byte)34, (byte)26, - (byte)18, (byte)10, (byte) 2, (byte)59, (byte)51, (byte)43, (byte)35, - (byte)62, (byte)54, (byte)46, (byte)38, (byte)30, (byte)22, (byte)14, - (byte) 6, (byte)61, (byte)53, (byte)45, (byte)37, (byte)29, (byte)21, - (byte)13, (byte) 5, (byte)60, (byte)52, (byte)44, (byte)36, (byte)28, - (byte)20, (byte)12, (byte) 4, (byte)27, (byte)19, (byte)11, (byte)3 - }; - private static int[] totrot = { - 1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28 - }; - - private static byte[] pc2 = { - (byte)13, (byte)16, (byte)10, (byte)23, (byte) 0, (byte) 4, - (byte) 2, (byte)27, (byte)14, (byte) 5, (byte)20, (byte) 9, - (byte)22, (byte)18, (byte)11, (byte)3 , (byte)25, (byte) 7, - (byte)15, (byte) 6, (byte)26, (byte)19, (byte)12, (byte) 1, - (byte)40, (byte)51, (byte)30, (byte)36, (byte)46, (byte)54, - (byte)29, (byte)39, (byte)50, (byte)44, (byte)32, (byte)47, - (byte)43, (byte)48, (byte)38, (byte)55, (byte)33, (byte)52, - (byte)45, (byte)41, (byte)49, (byte)35, (byte)28, (byte)31, - }; - - private static int[] SP1 = { - 0x01010400, 0x00000000, 0x00010000, 0x01010404, - 0x01010004, 0x00010404, 0x00000004, 0x00010000, - 0x00000400, 0x01010400, 0x01010404, 0x00000400, - 0x01000404, 0x01010004, 0x01000000, 0x00000004, - 0x00000404, 0x01000400, 0x01000400, 0x00010400, - 0x00010400, 0x01010000, 0x01010000, 0x01000404, - 0x00010004, 0x01000004, 0x01000004, 0x00010004, - 0x00000000, 0x00000404, 0x00010404, 0x01000000, - 0x00010000, 0x01010404, 0x00000004, 0x01010000, - 0x01010400, 0x01000000, 0x01000000, 0x00000400, - 0x01010004, 0x00010000, 0x00010400, 0x01000004, - 0x00000400, 0x00000004, 0x01000404, 0x00010404, - 0x01010404, 0x00010004, 0x01010000, 0x01000404, - 0x01000004, 0x00000404, 0x00010404, 0x01010400, - 0x00000404, 0x01000400, 0x01000400, 0x00000000, - 0x00010004, 0x00010400, 0x00000000, 0x01010004 - }; - private static int[] SP2 = { - 0x80108020, 0x80008000, 0x00008000, 0x00108020, - 0x00100000, 0x00000020, 0x80100020, 0x80008020, - 0x80000020, 0x80108020, 0x80108000, 0x80000000, - 0x80008000, 0x00100000, 0x00000020, 0x80100020, - 0x00108000, 0x00100020, 0x80008020, 0x00000000, - 0x80000000, 0x00008000, 0x00108020, 0x80100000, - 0x00100020, 0x80000020, 0x00000000, 0x00108000, - 0x00008020, 0x80108000, 0x80100000, 0x00008020, - 0x00000000, 0x00108020, 0x80100020, 0x00100000, - 0x80008020, 0x80100000, 0x80108000, 0x00008000, - 0x80100000, 0x80008000, 0x00000020, 0x80108020, - 0x00108020, 0x00000020, 0x00008000, 0x80000000, - 0x00008020, 0x80108000, 0x00100000, 0x80000020, - 0x00100020, 0x80008020, 0x80000020, 0x00100020, - 0x00108000, 0x00000000, 0x80008000, 0x00008020, - 0x80000000, 0x80100020, 0x80108020, 0x00108000 - }; - private static int[] SP3 = { - 0x00000208, 0x08020200, 0x00000000, 0x08020008, - 0x08000200, 0x00000000, 0x00020208, 0x08000200, - 0x00020008, 0x08000008, 0x08000008, 0x00020000, - 0x08020208, 0x00020008, 0x08020000, 0x00000208, - 0x08000000, 0x00000008, 0x08020200, 0x00000200, - 0x00020200, 0x08020000, 0x08020008, 0x00020208, - 0x08000208, 0x00020200, 0x00020000, 0x08000208, - 0x00000008, 0x08020208, 0x00000200, 0x08000000, - 0x08020200, 0x08000000, 0x00020008, 0x00000208, - 0x00020000, 0x08020200, 0x08000200, 0x00000000, - 0x00000200, 0x00020008, 0x08020208, 0x08000200, - 0x08000008, 0x00000200, 0x00000000, 0x08020008, - 0x08000208, 0x00020000, 0x08000000, 0x08020208, - 0x00000008, 0x00020208, 0x00020200, 0x08000008, - 0x08020000, 0x08000208, 0x00000208, 0x08020000, - 0x00020208, 0x00000008, 0x08020008, 0x00020200 - }; - private static int[] SP4 = { - 0x00802001, 0x00002081, 0x00002081, 0x00000080, - 0x00802080, 0x00800081, 0x00800001, 0x00002001, - 0x00000000, 0x00802000, 0x00802000, 0x00802081, - 0x00000081, 0x00000000, 0x00800080, 0x00800001, - 0x00000001, 0x00002000, 0x00800000, 0x00802001, - 0x00000080, 0x00800000, 0x00002001, 0x00002080, - 0x00800081, 0x00000001, 0x00002080, 0x00800080, - 0x00002000, 0x00802080, 0x00802081, 0x00000081, - 0x00800080, 0x00800001, 0x00802000, 0x00802081, - 0x00000081, 0x00000000, 0x00000000, 0x00802000, - 0x00002080, 0x00800080, 0x00800081, 0x00000001, - 0x00802001, 0x00002081, 0x00002081, 0x00000080, - 0x00802081, 0x00000081, 0x00000001, 0x00002000, - 0x00800001, 0x00002001, 0x00802080, 0x00800081, - 0x00002001, 0x00002080, 0x00800000, 0x00802001, - 0x00000080, 0x00800000, 0x00002000, 0x00802080 - }; - private static int[] SP5 = { - 0x00000100, 0x02080100, 0x02080000, 0x42000100, - 0x00080000, 0x00000100, 0x40000000, 0x02080000, - 0x40080100, 0x00080000, 0x02000100, 0x40080100, - 0x42000100, 0x42080000, 0x00080100, 0x40000000, - 0x02000000, 0x40080000, 0x40080000, 0x00000000, - 0x40000100, 0x42080100, 0x42080100, 0x02000100, - 0x42080000, 0x40000100, 0x00000000, 0x42000000, - 0x02080100, 0x02000000, 0x42000000, 0x00080100, - 0x00080000, 0x42000100, 0x00000100, 0x02000000, - 0x40000000, 0x02080000, 0x42000100, 0x40080100, - 0x02000100, 0x40000000, 0x42080000, 0x02080100, - 0x40080100, 0x00000100, 0x02000000, 0x42080000, - 0x42080100, 0x00080100, 0x42000000, 0x42080100, - 0x02080000, 0x00000000, 0x40080000, 0x42000000, - 0x00080100, 0x02000100, 0x40000100, 0x00080000, - 0x00000000, 0x40080000, 0x02080100, 0x40000100 - }; - private static int[] SP6 = { - 0x20000010, 0x20400000, 0x00004000, 0x20404010, - 0x20400000, 0x00000010, 0x20404010, 0x00400000, - 0x20004000, 0x00404010, 0x00400000, 0x20000010, - 0x00400010, 0x20004000, 0x20000000, 0x00004010, - 0x00000000, 0x00400010, 0x20004010, 0x00004000, - 0x00404000, 0x20004010, 0x00000010, 0x20400010, - 0x20400010, 0x00000000, 0x00404010, 0x20404000, - 0x00004010, 0x00404000, 0x20404000, 0x20000000, - 0x20004000, 0x00000010, 0x20400010, 0x00404000, - 0x20404010, 0x00400000, 0x00004010, 0x20000010, - 0x00400000, 0x20004000, 0x20000000, 0x00004010, - 0x20000010, 0x20404010, 0x00404000, 0x20400000, - 0x00404010, 0x20404000, 0x00000000, 0x20400010, - 0x00000010, 0x00004000, 0x20400000, 0x00404010, - 0x00004000, 0x00400010, 0x20004010, 0x00000000, - 0x20404000, 0x20000000, 0x00400010, 0x20004010 - }; - private static int[] SP7 = { - 0x00200000, 0x04200002, 0x04000802, 0x00000000, - 0x00000800, 0x04000802, 0x00200802, 0x04200800, - 0x04200802, 0x00200000, 0x00000000, 0x04000002, - 0x00000002, 0x04000000, 0x04200002, 0x00000802, - 0x04000800, 0x00200802, 0x00200002, 0x04000800, - 0x04000002, 0x04200000, 0x04200800, 0x00200002, - 0x04200000, 0x00000800, 0x00000802, 0x04200802, - 0x00200800, 0x00000002, 0x04000000, 0x00200800, - 0x04000000, 0x00200800, 0x00200000, 0x04000802, - 0x04000802, 0x04200002, 0x04200002, 0x00000002, - 0x00200002, 0x04000000, 0x04000800, 0x00200000, - 0x04200800, 0x00000802, 0x00200802, 0x04200800, - 0x00000802, 0x04000002, 0x04200802, 0x04200000, - 0x00200800, 0x00000000, 0x00000002, 0x04200802, - 0x00000000, 0x00200802, 0x04200000, 0x00000800, - 0x04000002, 0x04000800, 0x00000800, 0x00200002 - }; - private static int[] SP8 = { - 0x10001040, 0x00001000, 0x00040000, 0x10041040, - 0x10000000, 0x10001040, 0x00000040, 0x10000000, - 0x00040040, 0x10040000, 0x10041040, 0x00041000, - 0x10041000, 0x00041040, 0x00001000, 0x00000040, - 0x10040000, 0x10000040, 0x10001000, 0x00001040, - 0x00041000, 0x00040040, 0x10040040, 0x10041000, - 0x00001040, 0x00000000, 0x00000000, 0x10040040, - 0x10000040, 0x10001000, 0x00041040, 0x00040000, - 0x00041040, 0x00040000, 0x10041000, 0x00001000, - 0x00000040, 0x10040040, 0x00001000, 0x00041040, - 0x10001000, 0x00000040, 0x10000040, 0x10040000, - 0x10040040, 0x10000000, 0x00040000, 0x10001040, - 0x00000000, 0x10041040, 0x00040040, 0x10000040, - 0x10040000, 0x10001000, 0x10001040, 0x00000000, - 0x10041040, 0x00041000, 0x00041000, 0x00001040, - 0x00001040, 0x00040040, 0x10000000, 0x10041000 - }; - - // Routines taken from other parts of the Acme utilities. - - /// Squash bytes down to ints. - public static void squashBytesToInts( byte[] inBytes, int inOff, int[] outInts, int outOff, int intLen ) - { - for ( int i = 0; i < intLen; ++i ) - outInts[outOff + i] = - ( ( inBytes[inOff + i * 4 ] & 0xff ) << 24 ) | - ( ( inBytes[inOff + i * 4 + 1] & 0xff ) << 16 ) | - ( ( inBytes[inOff + i * 4 + 2] & 0xff ) << 8 ) | - ( inBytes[inOff + i * 4 + 3] & 0xff ); - } - - /// Spread ints into bytes. - public static void spreadIntsToBytes( int[] inInts, int inOff, byte[] outBytes, int outOff, int intLen ) - { - for ( int i = 0; i < intLen; ++i ) - { - outBytes[outOff + i * 4 ] = (byte) ( inInts[inOff + i] >>> 24 ); - outBytes[outOff + i * 4 + 1] = (byte) ( inInts[inOff + i] >>> 16 ); - outBytes[outOff + i * 4 + 2] = (byte) ( inInts[inOff + i] >>> 8 ); - outBytes[outOff + i * 4 + 3] = (byte) inInts[inOff + i]; - } - } - } diff --git a/limbo-android-lib/src/main/java/android/androidVNC/FitToScreenScaling.java b/limbo-android-lib/src/main/java/android/androidVNC/FitToScreenScaling.java deleted file mode 100644 index 840f51cbd..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/FitToScreenScaling.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package android.androidVNC; - -import com.limbo.emu.lib.R; - -import android.widget.ImageView.ScaleType; - -/** - * @author Michael A. MacDonald - */ -class FitToScreenScaling extends AbstractScaling { - - /** - * @param id - * @param scaleType - */ - FitToScreenScaling() { - super(R.id.itemFitToScreen, ScaleType.FIT_CENTER); - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractScaling#isAbleToPan() - */ - @Override - boolean isAbleToPan() { - return false; - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractScaling#isValidInputMode(int) - */ - @Override - boolean isValidInputMode(int mode) { - return mode == R.id.itemInputFitToScreen; - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractScaling#getDefaultHandlerId() - */ - @Override - int getDefaultHandlerId() { - return R.id.itemInputFitToScreen; - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractScaling#setCanvasScaleType(android.androidVNC.VncCanvas) - */ - @Override - public void setScaleTypeForActivity(VncCanvasActivity activity) { - super.setScaleTypeForActivity(activity); - activity.vncCanvas.absoluteXPosition = activity.vncCanvas.absoluteYPosition = 0; - activity.vncCanvas.scrollTo(0, 0); - } - -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/FullBufferBitmapData.java b/limbo-android-lib/src/main/java/android/androidVNC/FullBufferBitmapData.java deleted file mode 100644 index 8fce6534f..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/FullBufferBitmapData.java +++ /dev/null @@ -1,221 +0,0 @@ -/** - * Copyright (c) 2010 Michael A. MacDonald - */ -package android.androidVNC; - -import java.io.IOException; -import java.util.Arrays; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.widget.ImageView; - -import com.max2idea.android.limbo.main.Config; - -/** - * @author Michael A. MacDonald - * - */ -class FullBufferBitmapData extends AbstractBitmapData { - - int xoffset; - int yoffset; - - /** - * @author Michael A. MacDonald - * - */ - class Drawable extends AbstractBitmapDrawable { - - /** - * @param data - */ - public Drawable(AbstractBitmapData data) { - super(data); - // TODO Auto-generated constructor stub - } - - /* (non-Javadoc) - * @see android.graphics.drawable.DrawableContainer#draw(android.graphics.Canvas) - */ - @Override - public void draw(Canvas canvas) { - if (vncCanvas.getScaleType() == ImageView.ScaleType.FIT_CENTER) - { - //canvas.drawBitmap(data.bitmapPixels, 0, data.framebufferwidth, xoffset, yoffset, framebufferwidth, framebufferheight, false, null); - - //XXX; Limbo: for Hardware accelerated surfaces we have to stop using the above deprecated method and use a bitmap-backed method - // this fixes the issue with Nougat Devices displaying black screen for 24bit color mode C24bit - Bitmap bitmapTmp = Bitmap.createBitmap(framebufferwidth, framebufferheight, Config.bitmapConfig); - bitmapTmp.setPixels(bitmapPixels, 0, data.framebufferwidth, 0, 0, framebufferwidth, framebufferheight); - canvas.drawBitmap(bitmapTmp, xoffset, yoffset, null); - - } - else - { - float scale = vncCanvas.getScale(); - int xo = xoffset < 0 ? 0 : xoffset; - int yo = yoffset < 0 ? 0 : yoffset; - /* - if (scale == 1 || scale <= 0) - { - */ - int drawWidth = vncCanvas.getVisibleWidth(); - if (drawWidth + xo > data.framebufferwidth) - drawWidth = data.framebufferwidth - xo; - int drawHeight = vncCanvas.getVisibleHeight(); - if (drawHeight + yo > data.framebufferheight) - drawHeight = data.framebufferheight - yo; - - - //canvas.drawBitmap(data.bitmapPixels, offset(xo, yo), data.framebufferwidth, xo, yo, drawWidth, drawHeight, false, null); - - //XXX; for Hardware accelerated surfaces we have to stop using the above deprecated method and use a bitmap-backed method - // this fixes the issue with Nougat Devices displaying black screen for 24bit color mode C24bit - Bitmap bitmapTmp = Bitmap.createBitmap(framebufferwidth, framebufferheight, Config.bitmapConfig); - bitmapTmp.setPixels(bitmapPixels, offset(xo, yo), data.framebufferwidth, 0, 0, drawWidth, drawHeight); - canvas.drawBitmap(bitmapTmp, xo, yo, null); - - /* - } - else - { - int scalewidth = (int)(vncCanvas.getVisibleWidth() / scale + 1); - if (scalewidth + xo > data.framebufferwidth) - scalewidth = data.framebufferwidth - xo; - int scaleheight = (int)(vncCanvas.getVisibleHeight() / scale + 1); - if (scaleheight + yo > data.framebufferheight) - scaleheight = data.framebufferheight - yo; - canvas.drawBitmap(data.bitmapPixels, offset(xo, yo), data.framebufferwidth, xo, yo, scalewidth, scaleheight, false, null); - } - */ - } - if(data.vncCanvas.connection.getUseLocalCursor()) - { - setCursorRect(data.vncCanvas.mouseX, data.vncCanvas.mouseY); - clipRect.set(cursorRect); - if (canvas.clipRect(cursorRect)) - { - drawCursor(canvas); - } - } - } - } - - /** - * Multiply this times total number of pixels to get estimate of process size with all buffers plus - * safety factor - */ - static final int CAPACITY_MULTIPLIER = 7; - - /** - * @param p - * @param c - */ - public FullBufferBitmapData(RfbProto p, VncCanvas c, int capacity) { - super(p, c); - framebufferwidth=rfb.framebufferWidth; - framebufferheight=rfb.framebufferHeight; - bitmapwidth=framebufferwidth; - bitmapheight=framebufferheight; - android.util.Log.i("FBBM", "bitmapsize = ("+bitmapwidth+","+bitmapheight+")"); - bitmapPixels = new int[framebufferwidth * framebufferheight]; - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#copyRect(android.graphics.Rect, android.graphics.Rect, android.graphics.Paint) - */ - @Override - void copyRect(Rect src, Rect dest, Paint paint) { - // TODO copy rect working? - throw new RuntimeException( "copyrect Not implemented"); - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#createDrawable() - */ - @Override - AbstractBitmapDrawable createDrawable() { - return new Drawable(this); - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#drawRect(int, int, int, int, android.graphics.Paint) - */ - @Override - void drawRect(int x, int y, int w, int h, Paint paint) { - int color = paint.getColor(); - int offset = offset(x,y); - if (w > 10) - { - for (int j = 0; j < h; j++, offset += framebufferwidth) - { - Arrays.fill(bitmapPixels, offset, offset + w, color); - } - } - else - { - for (int j = 0; j < h; j++, offset += framebufferwidth - w) - { - for (int k = 0; k < w; k++, offset++) - { - bitmapPixels[offset] = color; - } - } - } - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#offset(int, int) - */ - @Override - int offset(int x, int y) { - return x + y * framebufferwidth; - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#scrollChanged(int, int) - */ - @Override - void scrollChanged(int newx, int newy) { - xoffset = newx; - yoffset = newy; - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#syncScroll() - */ - @Override - void syncScroll() { - // Don't need to do anything here - - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#updateBitmap(int, int, int, int) - */ - @Override - void updateBitmap(int x, int y, int w, int h) { - // Don't need to do anything here - - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#validDraw(int, int, int, int) - */ - @Override - boolean validDraw(int x, int y, int w, int h) { - return true; - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#writeFullUpdateRequest(boolean) - */ - @Override - void writeFullUpdateRequest(boolean incremental) throws IOException { - rfb.writeFramebufferUpdateRequest(0, 0, framebufferwidth, framebufferheight, incremental); - } - -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/InStream.java b/limbo-android-lib/src/main/java/android/androidVNC/InStream.java deleted file mode 100644 index cbbda0667..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/InStream.java +++ /dev/null @@ -1,155 +0,0 @@ -/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -// -// rdr::InStream marshalls data from a buffer stored in RDR (RFB Data -// Representation). -// -package android.androidVNC; - - -abstract public class InStream { - - // check() ensures there is buffer data for at least one item of size - // itemSize bytes. Returns the number of items in the buffer (up to a - // maximum of nItems). - - public final int check(int itemSize, int nItems) throws Exception { - if (ptr + itemSize * nItems > end) { - if (ptr + itemSize > end) - return overrun(itemSize, nItems); - - nItems = (end - ptr) / itemSize; - } - return nItems; - } - - public final void check(int itemSize) throws Exception { - if (ptr + itemSize > end) - overrun(itemSize, 1); - } - - // readU/SN() methods read unsigned and signed N-bit integers. - - public final int readS8() throws Exception { - check(1); return b[ptr++]; - } - - public final int readS16() throws Exception { - check(2); int b0 = b[ptr++]; - int b1 = b[ptr++] & 0xff; return b0 << 8 | b1; - } - - public final int readS32() throws Exception { - check(4); int b0 = b[ptr++]; - int b1 = b[ptr++] & 0xff; - int b2 = b[ptr++] & 0xff; - int b3 = b[ptr++] & 0xff; - return b0 << 24 | b1 << 16 | b2 << 8 | b3; - } - - public final int readU8() throws Exception { - return readS8() & 0xff; - } - - public final int readU16() throws Exception { - return readS16() & 0xffff; - } - - public final int readU32() throws Exception { - return readS32() & 0xffffffff; - } - - public final void skip(int bytes) throws Exception { - while (bytes > 0) { - int n = check(1, bytes); - ptr += n; - bytes -= n; - } - } - - // readBytes() reads an exact number of bytes into an array at an offset. - - public void readBytes(byte[] data, int offset, int length) throws Exception { - int offsetEnd = offset + length; - while (offset < offsetEnd) { - int n = check(1, offsetEnd - offset); - System.arraycopy(b, ptr, data, offset, n); - ptr += n; - offset += n; - } - } - - // readOpaqueN() reads a quantity "without byte-swapping". Because java has - // no byte-ordering, we just use big-endian. - - public final int readOpaque8() throws Exception { - return readU8(); - } - - public final int readOpaque16() throws Exception { - return readU16(); - } - - public final int readOpaque32() throws Exception { - return readU32(); - } - - public final int readOpaque24A() throws Exception { - check(3); int b0 = b[ptr++]; - int b1 = b[ptr++]; int b2 = b[ptr++]; - return b0 << 24 | b1 << 16 | b2 << 8; - } - - public final int readOpaque24B() throws Exception { - check(3); int b0 = b[ptr++]; - int b1 = b[ptr++]; int b2 = b[ptr++]; - return b0 << 16 | b1 << 8 | b2; - } - - // pos() returns the position in the stream. - - abstract public int pos(); - - // bytesAvailable() returns true if at least one byte can be read from the - // stream without blocking. i.e. if false is returned then readU8() would - // block. - - public boolean bytesAvailable() { return end != ptr; } - - // getbuf(), getptr(), getend() and setptr() are "dirty" methods which allow - // you to manipulate the buffer directly. This is useful for a stream which - // is a wrapper around an underlying stream. - - public final byte[] getbuf() { return b; } - public final int getptr() { return ptr; } - public final int getend() { return end; } - public final void setptr(int p) { ptr = p; } - - // overrun() is implemented by a derived class to cope with buffer overrun. - // It ensures there are at least itemSize bytes of buffer data. Returns - // the number of items in the buffer (up to a maximum of nItems). itemSize - // is supposed to be "small" (a few bytes). - - abstract protected int overrun(int itemSize, int nItems) throws Exception; - - protected InStream() {} - protected byte[] b; - protected int ptr; - protected int end; -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/LargeBitmapData.java b/limbo-android-lib/src/main/java/android/androidVNC/LargeBitmapData.java deleted file mode 100644 index 4a07c2d3d..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/LargeBitmapData.java +++ /dev/null @@ -1,319 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package android.androidVNC; - -import java.io.IOException; - -import com.antlersoft.android.drawing.OverlappingCopy; -import com.antlersoft.android.drawing.RectList; -import com.antlersoft.util.ObjectPool; -import com.max2idea.android.limbo.main.Config; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Rect; - -/** - * @author Michael A. MacDonald - * - */ -class LargeBitmapData extends AbstractBitmapData { - - /** - * Multiply this times total number of pixels to get estimate of process size with all buffers plus - * safety factor - */ - static final int CAPACITY_MULTIPLIER = 21; - - int xoffset; - int yoffset; - int scrolledToX; - int scrolledToY; - private Rect bitmapRect; - private Paint defaultPaint; - private RectList invalidList; - private RectList pendingList; - - /** - * Pool of temporary rectangle objects. Need to synchronize externally access from - * multiple threads. - */ - private static ObjectPool rectPool = new ObjectPool() { - - /* (non-Javadoc) - * @see com.antlersoft.util.ObjectPool#itemForPool() - */ - @Override - protected Rect itemForPool() { - return new Rect(); - } - }; - - class LargeBitmapDrawable extends AbstractBitmapDrawable - { - LargeBitmapDrawable() - { - super(LargeBitmapData.this); - } - /* (non-Javadoc) - * @see android.graphics.drawable.DrawableContainer#draw(android.graphics.Canvas) - */ - @Override - public void draw(Canvas canvas) { - //android.util.Log.i("LBM", "Drawing "+xoffset+" "+yoffset); - int xoff, yoff; - synchronized ( LargeBitmapData.this ) - { - xoff=xoffset; - yoff=yoffset; - } - draw(canvas, xoff, yoff); - } - } - - /** - * - * @param p Protocol implementation - * @param c View that will display screen - * @param displayWidth - * @param displayHeight - * @param capacity Max process heap size in bytes - */ - LargeBitmapData(RfbProto p, VncCanvas c, int displayWidth, int displayHeight, int capacity) - { - super(p,c); - double scaleMultiplier = Math.sqrt((double)(capacity * 1024 * 1024) / (double)(CAPACITY_MULTIPLIER * framebufferwidth * framebufferheight)); - if (scaleMultiplier > 1) - scaleMultiplier = 1; - bitmapwidth=(int)((double)framebufferwidth * scaleMultiplier); - if (bitmapwidth < displayWidth) - bitmapwidth = displayWidth; - bitmapheight=(int)((double)framebufferheight * scaleMultiplier); - if (bitmapheight < displayHeight) - bitmapheight = displayHeight; - android.util.Log.i("LBM", "bitmapsize = ("+bitmapwidth+","+bitmapheight+")"); - mbitmap = Bitmap.createBitmap(bitmapwidth, bitmapheight, Config.bitmapConfig); - memGraphics = new Canvas(mbitmap); - bitmapPixels = new int[bitmapwidth * bitmapheight]; - invalidList = new RectList(rectPool); - pendingList = new RectList(rectPool); - bitmapRect=new Rect(0,0,bitmapwidth,bitmapheight); - defaultPaint = new Paint(); - } - - @Override - AbstractBitmapDrawable createDrawable() - { - return new LargeBitmapDrawable(); - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#copyRect(android.graphics.Rect, android.graphics.Rect, android.graphics.Paint) - */ - @Override - void copyRect(Rect src, Rect dest, Paint paint) { - // TODO copy rect working? - throw new RuntimeException( "copyrect Not implemented"); - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#drawRect(int, int, int, int, android.graphics.Paint) - */ - @Override - void drawRect(int x, int y, int w, int h, Paint paint) { - x-=xoffset; - y-=yoffset; - memGraphics.drawRect(x, y, x+w, y+h, paint); - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#offset(int, int) - */ - @Override - int offset(int x, int y) { - return (y - yoffset) * bitmapwidth + x - xoffset; - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#scrollChanged(int, int) - */ - @Override - synchronized void scrollChanged(int newx, int newy) { - //android.util.Log.i("LBM","scroll "+newx+" "+newy); - int newScrolledToX = scrolledToX; - int newScrolledToY = scrolledToY; - int visibleWidth = vncCanvas.getVisibleWidth(); - int visibleHeight = vncCanvas.getVisibleHeight(); - if (newx - xoffset < 0 ) - { - newScrolledToX = newx + visibleWidth / 2 - bitmapwidth / 2; - if (newScrolledToX < 0) - newScrolledToX = 0; - } - else if (newx - xoffset + visibleWidth > bitmapwidth) - { - newScrolledToX = newx + visibleWidth / 2 - bitmapwidth / 2; - if (newScrolledToX + bitmapwidth > framebufferwidth) - newScrolledToX = framebufferwidth - bitmapwidth; - } - if (newy - yoffset < 0 ) - { - newScrolledToY = newy + visibleHeight / 2 - bitmapheight / 2; - if (newScrolledToY < 0) - newScrolledToY = 0; - } - else if (newy - yoffset + visibleHeight > bitmapheight) - { - newScrolledToY = newy + visibleHeight / 2 - bitmapheight / 2; - if (newScrolledToY + bitmapheight > framebufferheight) - newScrolledToY = framebufferheight - bitmapheight; - } - if (newScrolledToX != scrolledToX || newScrolledToY != scrolledToY) - { - scrolledToX = newScrolledToX; - scrolledToY = newScrolledToY; - if ( waitingForInput) - syncScroll(); - } - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#updateBitmap(int, int, int, int) - */ - @Override - void updateBitmap(int x, int y, int w, int h) { - mbitmap.setPixels(bitmapPixels, offset(x,y), bitmapwidth, x-xoffset, y-yoffset, w, h); - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#validDraw(int, int, int, int) - */ - @Override - synchronized boolean validDraw(int x, int y, int w, int h) { - //android.util.Log.i("LBM", "Validate Drawing "+x+" "+y+" "+w+" "+h+" "+xoffset+" "+yoffset+" "+(x-xoffset>=0 && x-xoffset+w<=bitmapwidth && y-yoffset>=0 && y-yoffset+h<=bitmapheight)); - boolean result = x-xoffset>=0 && x-xoffset+w<=bitmapwidth && y-yoffset>=0 && y-yoffset+h<=bitmapheight; - ObjectPool.Entry entry = rectPool.reserve(); - Rect r = entry.get(); - r.set(x, y, x+w, y+h); - pendingList.subtract(r); - if ( ! result) - { - invalidList.add(r); - } - else - invalidList.subtract(r); - rectPool.release(entry); - return result; - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#writeFullUpdateRequest(boolean) - */ - @Override - synchronized void writeFullUpdateRequest(boolean incremental) throws IOException { - if (! incremental) { - ObjectPool.Entry entry = rectPool.reserve(); - Rect r = entry.get(); - r.left=xoffset; - r.top=yoffset; - r.right=xoffset + bitmapwidth; - r.bottom=yoffset + bitmapheight; - pendingList.add(r); - invalidList.add(r); - rectPool.release(entry); - } - rfb.writeFramebufferUpdateRequest(xoffset, yoffset, bitmapwidth, bitmapheight, incremental); - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractBitmapData#syncScroll() - */ - @Override - synchronized void syncScroll() { - - int deltaX = xoffset - scrolledToX; - int deltaY = yoffset - scrolledToY; - xoffset=scrolledToX; - yoffset=scrolledToY; - bitmapRect.top=scrolledToY; - bitmapRect.bottom=scrolledToY+bitmapheight; - bitmapRect.left=scrolledToX; - bitmapRect.right=scrolledToX+bitmapwidth; - invalidList.intersect(bitmapRect); - if ( deltaX != 0 || deltaY != 0) - { - boolean didOverlapping = false; - if (Math.abs(deltaX) < bitmapwidth && Math.abs(deltaY) < bitmapheight) { - ObjectPool.Entry sourceEntry = rectPool.reserve(); - ObjectPool.Entry addedEntry = rectPool.reserve(); - try - { - Rect added = addedEntry.get(); - Rect sourceRect = sourceEntry.get(); - sourceRect.set(deltaX<0 ? -deltaX : 0, - deltaY<0 ? -deltaY : 0, - deltaX<0 ? bitmapwidth : bitmapwidth - deltaX, - deltaY < 0 ? bitmapheight : bitmapheight - deltaY); - if (! invalidList.testIntersect(sourceRect)) { - didOverlapping = true; - OverlappingCopy.Copy(mbitmap, memGraphics, defaultPaint, sourceRect, deltaX + sourceRect.left, deltaY + sourceRect.top, rectPool); - // Write request for side pixels - if (deltaX != 0) { - added.left = deltaX < 0 ? bitmapRect.right + deltaX : bitmapRect.left; - added.right = added.left + Math.abs(deltaX); - added.top = bitmapRect.top; - added.bottom = bitmapRect.bottom; - invalidList.add(added); - } - if (deltaY != 0) { - added.left = deltaX < 0 ? bitmapRect.left : bitmapRect.left + deltaX; - added.top = deltaY < 0 ? bitmapRect.bottom + deltaY : bitmapRect.top; - added.right = added.left + bitmapwidth - Math.abs(deltaX); - added.bottom = added.top + Math.abs(deltaY); - invalidList.add(added); - } - } - } - finally { - rectPool.release(addedEntry); - rectPool.release(sourceEntry); - } - } - if (! didOverlapping) - { - try - { - //android.util.Log.i("LBM","update req "+xoffset+" "+yoffset); - mbitmap.eraseColor(Color.GREEN); - writeFullUpdateRequest(false); - } - catch ( IOException ioe) - { - // TODO log this - } - } - } - int size = pendingList.getSize(); - for (int i=0; i { - public int keySym; - int mouseButtons; - int keyEvent; - String name; - boolean isMouse; - boolean isKeyEvent; - - MetaKeyBase(int mouseButtons, String name) - { - this.mouseButtons = mouseButtons; - this.name = name; - this.isMouse = true; - this.isKeyEvent = false; - } - - MetaKeyBase(String name, int keySym, int keyEvent) - { - this.name = name; - this.keySym = keySym; - this.keyEvent = keyEvent; - this.isMouse = false; - this.isKeyEvent = true; - } - - MetaKeyBase(String name, int keySym) - { - this.name = name; - this.keySym = keySym; - this.isMouse = false; - this.isKeyEvent = false; - } - - /* (non-Javadoc) - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ - @Override - public int compareTo(MetaKeyBase another) { - return name.compareTo(another.name); - } -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/MetaKeyBean.java b/limbo-android-lib/src/main/java/android/androidVNC/MetaKeyBean.java deleted file mode 100644 index c5d82b56e..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/MetaKeyBean.java +++ /dev/null @@ -1,267 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package android.androidVNC; - -import java.util.ArrayList; -import java.util.HashMap; - -import android.view.KeyEvent; - -/** - * @author Michael A. MacDonald - * - */ -public class MetaKeyBean { - int keySym; - int metaFlags; - - static final ArrayList allKeys; - static final String[] allKeysNames; - public static final HashMap keysByKeyCode; - static final HashMap keysByMouseButton; - static final HashMap keysByKeySym; - static final MetaKeyBean keyCtrlAltDel; - static final MetaKeyBean keyCtrlC; - static final MetaKeyBean keyArrowLeft; - static final MetaKeyBean keyArrowRight; - static final MetaKeyBean keyArrowUp; - static final MetaKeyBean keyArrowDown; - - static { - allKeys = new ArrayList(); - - allKeys.add(new MetaKeyBase("Hangul", 0xff31)); - allKeys.add(new MetaKeyBase("Hangul_Start", 0xff32)); - allKeys.add(new MetaKeyBase("Hangul_End", 0xff33)); - allKeys.add(new MetaKeyBase("Hangul_Hanja", 0xff34)); - allKeys.add(new MetaKeyBase("Kana_Shift", 0xff2e)); - allKeys.add(new MetaKeyBase("Right_Alt", 0xffea)); - - allKeys.add(new MetaKeyBase(VncCanvas.MOUSE_BUTTON_LEFT, "Mouse Left")); - allKeys.add(new MetaKeyBase(VncCanvas.MOUSE_BUTTON_MIDDLE, - "Mouse Middle")); - allKeys.add(new MetaKeyBase(VncCanvas.MOUSE_BUTTON_RIGHT, "Mouse Right")); - allKeys.add(new MetaKeyBase(VncCanvas.MOUSE_BUTTON_SCROLL_DOWN, - "Mouse Scroll Down")); - allKeys.add(new MetaKeyBase(VncCanvas.MOUSE_BUTTON_SCROLL_UP, - "Mouse Scroll Up")); - - allKeys.add(new MetaKeyBase("Home", 0xFF50)); - allKeys.add(new MetaKeyBase("Arrow Left", 0xFF51)); - allKeys.add(new MetaKeyBase("Arrow Up", 0xFF52)); - allKeys.add(new MetaKeyBase("Arrow Right", 0xFF53)); - allKeys.add(new MetaKeyBase("Arrow Down", 0xFF54)); - allKeys.add(new MetaKeyBase("Page Up", 0xFF55)); - allKeys.add(new MetaKeyBase("Page Down", 0xFF56)); - allKeys.add(new MetaKeyBase("End", 0xFF57)); - allKeys.add(new MetaKeyBase("Insert", 0xFF63)); - allKeys.add(new MetaKeyBase("Delete", 0xFFFF, KeyEvent.KEYCODE_DEL)); - allKeys.add(new MetaKeyBase("Num Lock", 0xFF7F)); - allKeys.add(new MetaKeyBase("Break", 0xFF6b)); - allKeys.add(new MetaKeyBase("Scroll Lock", 0xFF14)); - allKeys.add(new MetaKeyBase("Print Scrn", 0xFF15)); - allKeys.add(new MetaKeyBase("Escape", 0xFF1B)); - allKeys.add(new MetaKeyBase("Enter", 0xFF0D, KeyEvent.KEYCODE_ENTER)); - allKeys.add(new MetaKeyBase("Tab", 0xFF09, KeyEvent.KEYCODE_TAB)); - allKeys.add(new MetaKeyBase("BackSpace", 0xFF08)); - allKeys.add(new MetaKeyBase("Space", 0x020, KeyEvent.KEYCODE_SPACE)); - - StringBuilder sb = new StringBuilder(" "); - for (int i = 0; i < 26; i++) { - sb.setCharAt(0, (char) ('A' + i)); - allKeys.add(new MetaKeyBase(sb.toString(), 'a' + i, - KeyEvent.KEYCODE_A + i)); - } - - for (int i = 0; i < 10; i++) { - sb.setCharAt(0, (char) ('0' + i)); - allKeys.add(new MetaKeyBase(sb.toString(), '0' + i, - KeyEvent.KEYCODE_0 + i)); - } - - for (int i = 0; i < 12; i++) { - sb.setLength(0); - sb.append('F'); - if (i < 9) - sb.append(' '); - sb.append(Integer.toString(i + 1)); - allKeys.add(new MetaKeyBase(sb.toString(), 0xFFBE + i)); - } - - java.util.Collections.sort(allKeys); - allKeysNames = new String[allKeys.size()]; - keysByKeyCode = new HashMap(); - keysByMouseButton = new HashMap(); - keysByKeySym = new HashMap(); - for (int i = 0; i < allKeysNames.length; ++i) { - MetaKeyBase b = allKeys.get(i); - allKeysNames[i] = b.name; - if (b.isKeyEvent) - keysByKeyCode.put(b.keyEvent, b); - if (b.isMouse) - keysByMouseButton.put(b.mouseButtons, b); - else - keysByKeySym.put(b.keySym, b); - } - keyCtrlAltDel = new MetaKeyBean(0, VncCanvas.CTRL_MASK - | VncCanvas.ALT_MASK, keysByKeyCode.get(KeyEvent.KEYCODE_DEL)); - keyCtrlC = new MetaKeyBean(0, VncCanvas.CTRL_MASK, - keysByKeyCode.get(KeyEvent.KEYCODE_C)); - keyArrowLeft = new MetaKeyBean(0, 0, keysByKeySym.get(0xFF51)); - keyArrowUp = new MetaKeyBean(0, 0, keysByKeySym.get(0xFF52)); - keyArrowRight = new MetaKeyBean(0, 0, keysByKeySym.get(0xFF53)); - keyArrowDown = new MetaKeyBean(0, 0, keysByKeySym.get(0xFF54)); - } - - private boolean _regenDesc; - private boolean mouseClick; - - MetaKeyBean() { - } - - MetaKeyBean(MetaKeyBean toCopy) { - _regenDesc = true; - if (toCopy.isMouseClick()) - setMouseButtons(toCopy.getMouseButtons()); - else - setKeySym(toCopy.getKeySym()); - setMetaListId(toCopy.getMetaListId()); - setMetaFlags(toCopy.getMetaFlags()); - } - - private void setMetaListId(Object metaListId) { - - - } - - private Object getMetaListId() { - - return null; - } - - MetaKeyBean(long listId, int metaFlags, MetaKeyBase base) { - - setKeyBase(base); - setMetaFlags(metaFlags); - _regenDesc = true; - } - - public String getKeyDesc() { - if (_regenDesc) { - synchronized (this) { - if (_regenDesc) { - StringBuilder sb = new StringBuilder(); - int meta = getMetaFlags(); - if (0 != (meta & VncCanvas.SHIFT_MASK)) { - sb.append("Shift"); - } - if (0 != (meta & VncCanvas.CTRL_MASK)) { - if (sb.length() > 0) - sb.append('-'); - sb.append("Ctrl"); - } - if (0 != (meta & VncCanvas.ALT_MASK)) { - if (sb.length() > 0) - sb.append('-'); - sb.append("Alt"); - } - if (sb.length() > 0) - sb.append(' '); - MetaKeyBase base; - if (isMouseClick()) - base = keysByMouseButton.get(getMouseButtons()); - else - base = keysByKeySym.get(getKeySym()); - sb.append(base.name); - setKeyDesc(sb.toString()); - } - } - } - return getKeyDesc(); - } - - public void setKeyDesc(String arg_keyDesc) { - _regenDesc = false; - } - - public void setKeySym(int arg_keySym) { - if (arg_keySym != getKeySym() || isMouseClick()) { - setMouseClick(false); - _regenDesc = true; - - } - this.keySym = arg_keySym ; - } - - int getKeySym() { - - return keySym; - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractMetaKeyBean#setMetaFlags(int) - */ - public void setMetaFlags(int arg_metaFlags) { - if (arg_metaFlags != getMetaFlags()) { - _regenDesc = true; - } - this.metaFlags = arg_metaFlags; - } - - int getMetaFlags() { - - return this.metaFlags; - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractMetaKeyBean#setMouseButtons(int) - */ - public void setMouseButtons(int arg_mouseButtons) { - if (arg_mouseButtons != getMouseButtons() || !isMouseClick()) { - setMouseClick(true); - _regenDesc = true; - } - } - - private void setMouseClick(boolean b) { - - this.mouseClick = b; - } - - boolean isMouseClick() { - - return false; - } - - int getMouseButtons() { - - return 0; - } - - void setKeyBase(MetaKeyBase base) { - if (base.isMouse) { - setMouseButtons(base.mouseButtons); - } else { - setKeySym(base.keySym); - } - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object o) { - if (o instanceof MetaKeyBean) { - return getKeyDesc().equals(((MetaKeyBean) o).getKeyDesc()); - } - return false; - } - -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/MetaList.java b/limbo-android-lib/src/main/java/android/androidVNC/MetaList.java deleted file mode 100644 index f4b3d8974..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/MetaList.java +++ /dev/null @@ -1,77 +0,0 @@ -// This class was generated from android.androidVNC.IMetaList by a tool -// Do not edit this file directly! PLX THX -package android.androidVNC; - -public class MetaList { - - public static final String GEN_TABLE_NAME = "META_LIST"; - public static final int GEN_COUNT = 2; - - // Field constants - public static final String GEN_FIELD__ID = "_id"; - public static final int GEN_ID__ID = 0; - public static final String GEN_FIELD_NAME = "NAME"; - public static final int GEN_ID_NAME = 1; - - // SQL Command for creating the table - public static String GEN_CREATE = "CREATE TABLE META_LIST (" + - "_id INTEGER PRIMARY KEY AUTOINCREMENT," + - "NAME TEXT" + - ")"; - - // Members corresponding to defined fields - private long gen__Id; - private java.lang.String gen_name; - - - public String Gen_tableName() { return GEN_TABLE_NAME; } - - // Field accessors - public long get_Id() { return gen__Id; } - public void set_Id(long arg__Id) { gen__Id = arg__Id; } - public java.lang.String getName() { return gen_name; } - public void setName(java.lang.String arg_name) { gen_name = arg_name; } - - public android.content.ContentValues Gen_getValues() { - android.content.ContentValues values=new android.content.ContentValues(); - values.put(GEN_FIELD__ID,Long.toString(this.gen__Id)); - values.put(GEN_FIELD_NAME,this.gen_name); - return values; - } - - /** - * Return an array that gives the column index in the cursor for each field defined - * @param cursor Database cursor over some columns, possibly including this table - * @return array of column indices; -1 if the column with that id is not in cursor - */ - public int[] Gen_columnIndices(android.database.Cursor cursor) { - int[] result=new int[GEN_COUNT]; - result[0] = cursor.getColumnIndex(GEN_FIELD__ID); - // Make compatible with database generated by older version of plugin with uppercase column name - if (result[0] == -1) { - result[0] = cursor.getColumnIndex("_ID"); - } - result[1] = cursor.getColumnIndex(GEN_FIELD_NAME); - return result; - } - - /** - * Populate one instance from a cursor - */ - public void Gen_populate(android.database.Cursor cursor,int[] columnIndices) { - if ( columnIndices[GEN_ID__ID] >= 0 && ! cursor.isNull(columnIndices[GEN_ID__ID])) { - gen__Id = cursor.getLong(columnIndices[GEN_ID__ID]); - } - if ( columnIndices[GEN_ID_NAME] >= 0 && ! cursor.isNull(columnIndices[GEN_ID_NAME])) { - gen_name = cursor.getString(columnIndices[GEN_ID_NAME]); - } - } - - /** - * Populate one instance from a ContentValues - */ - public void Gen_populate(android.content.ContentValues values) { - gen__Id = values.getAsLong(GEN_FIELD__ID); - gen_name = values.getAsString(GEN_FIELD_NAME); - } -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/MouseMover.java b/limbo-android-lib/src/main/java/android/androidVNC/MouseMover.java deleted file mode 100644 index 618ab0957..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/MouseMover.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright 2010 Michael A. MacDonald - */ -package android.androidVNC; - -import android.os.Handler; -import android.os.SystemClock; -import android.view.MotionEvent; - -/** - * Specialization of panner that moves the mouse instead of panning the screen - * - * @author Michael A. MacDonald - * - */ -class MouseMover extends Panner { - - public MouseMover(VncCanvasActivity act, Handler hand) { - super(act, hand); - } - - /* (non-Javadoc) - * @see java.lang.Runnable#run() - */ - @Override - public void run() { - long interval = SystemClock.uptimeMillis() - lastSent; - lastSent += interval; - double scale = (double)interval / 50.0; - VncCanvas canvas = activity.vncCanvas; - //Log.v(TAG, String.format("panning %f %d %d", scale, (int)((double)velocity.x * scale), (int)((double)velocity.y * scale))); - if ( canvas.processPointerEvent((int)(canvas.mouseX + ((double)velocity.x * scale)), - (int)(canvas.mouseY + ((double)velocity.y * scale)), - MotionEvent.ACTION_MOVE, 0, - false, false, false, false)) - { - if (updater.updateVelocity(velocity, interval)) - { - handler.postDelayed(this, 50); - } - else - { - //Log.v(TAG, "Updater requests stop"); - stop(); - } - } - else - { - //Log.v(TAG, "Panning failed"); - stop(); - } - } - -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/OneToOneScaling.java b/limbo-android-lib/src/main/java/android/androidVNC/OneToOneScaling.java deleted file mode 100644 index 62461c87a..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/OneToOneScaling.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package android.androidVNC; - -import com.limbo.emu.lib.R; - -import android.widget.ImageView.ScaleType; - -/** - * @author Michael A. MacDonald - */ -class OneToOneScaling extends AbstractScaling { - - /** - * @param id - * @param scaleType - */ - public OneToOneScaling() { - super(R.id.itemOneToOne,ScaleType.CENTER); - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractScaling#getDefaultHandlerId() - */ - @Override - int getDefaultHandlerId() { - return R.id.itemInputTouchPanTrackballMouse; - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractScaling#isAbleToPan() - */ - @Override - boolean isAbleToPan() { - return true; - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractScaling#isValidInputMode(int) - */ - @Override - boolean isValidInputMode(int mode) { - return mode != R.id.itemInputFitToScreen; - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractScaling#setScaleTypeForActivity(android.androidVNC.VncCanvasActivity) - */ - @Override - public void setScaleTypeForActivity(VncCanvasActivity activity) { - super.setScaleTypeForActivity(activity); - activity.vncCanvas.scrollToAbsolute(); - activity.vncCanvas.pan(0,0); - } - -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/Panner.java b/limbo-android-lib/src/main/java/android/androidVNC/Panner.java deleted file mode 100644 index 4298f1ad8..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/Panner.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package android.androidVNC; - -import android.graphics.PointF; -import android.os.Handler; -import android.os.SystemClock; - -/** - * Handles panning the screen continuously over a period of time - * @author Michael A. MacDonald - */ -class Panner implements Runnable { - - VncCanvasActivity activity; - Handler handler; - PointF velocity; - long lastSent; - VelocityUpdater updater; - - private static final String TAG = "PANNER"; - - /** - * Specify how the panning velocity changes over time - * @author Michael A. MacDonald - */ - interface VelocityUpdater { - /** - * Called approximately every 50 ms to update the velocity of panning - * @param p X and Y components to update - * @param interval Milliseconds since last update - * @return False if the panning should stop immediately; true otherwise - */ - boolean updateVelocity(PointF p, long interval); - } - - static class DefaultUpdater implements VelocityUpdater { - - static DefaultUpdater instance = new DefaultUpdater(); - - /** - * Don't change velocity - */ - @Override - public boolean updateVelocity(PointF p, long interval) { - return true; - } - - } - - Panner(VncCanvasActivity act, Handler hand) { - activity = act; - velocity = new PointF(); - handler = hand; - } - - void stop() - { - handler.removeCallbacks(this); - } - - void start(float xv, float yv, VelocityUpdater update) - { - if (update == null) - update = DefaultUpdater.instance; - updater = update; - velocity.x = xv; - velocity.y = yv; - //Log.v(TAG, String.format("pan start %f %f", velocity.x, velocity.y)); - lastSent = SystemClock.uptimeMillis(); - - handler.postDelayed(this, 50); - } - - /* (non-Javadoc) - * @see java.lang.Runnable#run() - */ - @Override - public void run() { - long interval = SystemClock.uptimeMillis() - lastSent; - lastSent += interval; - double scale = (double)interval / 50.0; - //Log.v(TAG, String.format("panning %f %d %d", scale, (int)((double)velocity.x * scale), (int)((double)velocity.y * scale))); - if ( activity.vncCanvas.pan((int)((double)velocity.x * scale), (int)((double)velocity.y * scale))) - { - if (updater.updateVelocity(velocity, interval)) - { - handler.postDelayed(this, 50); - } - else - { - //Log.v(TAG, "Updater requests stop"); - stop(); - } - } - else - { - //Log.v(TAG, "Panning failed"); - stop(); - } - } - -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/RepeaterDialog.java b/limbo-android-lib/src/main/java/android/androidVNC/RepeaterDialog.java deleted file mode 100644 index cd7213a6e..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/RepeaterDialog.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * - */ -package android.androidVNC; - -import com.limbo.emu.lib.R; - -import android.app.Activity; -import android.app.Dialog; -import android.os.Bundle; -import android.text.Html; -import android.view.View; -import android.widget.Button; -import android.widget.EditText; -import android.widget.TextView; - -/** - * @author Michael A. MacDonald - * - */ -class RepeaterDialog extends Dialog { - private EditText _repeaterId; - androidVNC _configurationDialog; - - RepeaterDialog(androidVNC context) { - super(context); - setOwnerActivity((Activity)context); - _configurationDialog = context; - } - - /* (non-Javadoc) - * @see android.app.Dialog#onCreate(android.os.Bundle) - */ - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setTitle(R.string.repeater_dialog_title); - - setContentView(R.layout.repeater_dialog); - _repeaterId=(EditText)findViewById(R.id.textRepeaterInfo); - ((TextView)findViewById(R.id.textRepeaterCaption)).setText(Html.fromHtml(getContext().getString(R.string.repeater_caption))); - ((Button)findViewById(R.id.buttonSaveRepeater)).setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - _configurationDialog.updateRepeaterInfo(true, _repeaterId.getText().toString()); - dismiss(); - } - }); - ((Button)findViewById(R.id.buttonClearRepeater)).setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - _configurationDialog.updateRepeaterInfo(false, ""); - dismiss(); - } - }); - } -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/RfbProto.java b/limbo-android-lib/src/main/java/android/androidVNC/RfbProto.java deleted file mode 100644 index 9fec1bbd2..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/RfbProto.java +++ /dev/null @@ -1,1416 +0,0 @@ -// -// Copyright (C) 2001-2004 HorizonLive.com, Inc. All Rights Reserved. -// Copyright (C) 2001-2006 Constantin Kaplinsky. All Rights Reserved. -// Copyright (C) 2000 Tridia Corporation. All Rights Reserved. -// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. -// -// This is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This software is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this software; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, -// USA. -// - -// -// RfbProto.java -// - -package android.androidVNC; - -import android.net.LocalSocket; -import android.net.LocalSocketAddress; -import android.os.Handler; -import android.os.HandlerThread; -import android.util.Log; - -import com.max2idea.android.limbo.main.Config; - -import java.io.BufferedInputStream; -import java.io.DataInputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.net.Socket; - -//- import java.awt.*; -//- import java.awt.event.*; -//- import java.util.zip.*; - -/** - * Access the RFB protocol through a socket. - *

- * This class has no knowledge of the android-specific UI; it sees framebuffer updates - * and input events as defined in the RFB protocol. - * - */ -public class RfbProto { - - final static String TAG = "RfbProto"; - - final static String - versionMsg_3_3 = "RFB 003.003\n", - versionMsg_3_7 = "RFB 003.007\n", - versionMsg_3_8 = "RFB 003.008\n"; - - // Vendor signatures: standard VNC/RealVNC, TridiaVNC, and TightVNC - final static String - StandardVendor = "STDV", - TridiaVncVendor = "TRDV", - TightVncVendor = "TGHT"; - - // Security types - final static int - SecTypeInvalid = 0, - SecTypeNone = 1, - SecTypeVncAuth = 2, - SecTypeTight = 16, - SecTypeUltra34 = 0xfffffffa; - - // Supported tunneling types - final static int - NoTunneling = 0; - final static String - SigNoTunneling = "NOTUNNEL"; - - // Supported authentication types - final static int - AuthNone = 1, - AuthVNC = 2, - AuthUnixLogin = 129, - AuthUltra = 17; - final static String - SigAuthNone = "NOAUTH__", - SigAuthVNC = "VNCAUTH_", - SigAuthUnixLogin = "ULGNAUTH"; - - // VNC authentication results - final static int - VncAuthOK = 0, - VncAuthFailed = 1, - VncAuthTooMany = 2; - - // Server-to-client messages - final static int - FramebufferUpdate = 0, - SetColourMapEntries = 1, - Bell = 2, - ServerCutText = 3, - TextChat = 11; - - // Client-to-server messages - final static int - SetPixelFormat = 0, - FixColourMapEntries = 1, - SetEncodings = 2, - FramebufferUpdateRequest = 3, - KeyboardEvent = 4, - PointerEvent = 5, - ClientCutText = 6; - - // Supported encodings and pseudo-encodings - final static int - EncodingRaw = 0, - EncodingCopyRect = 1, - EncodingRRE = 2, - EncodingCoRRE = 4, - EncodingHextile = 5, - EncodingZlib = 6, - EncodingTight = 7, - EncodingZRLE = 16, - EncodingCompressLevel0 = 0xFFFFFF00, - EncodingQualityLevel0 = 0xFFFFFFE0, - EncodingXCursor = 0xFFFFFF10, - EncodingRichCursor = 0xFFFFFF11, - EncodingPointerPos = 0xFFFFFF18, - EncodingLastRect = 0xFFFFFF20, - EncodingNewFBSize = 0xFFFFFF21; - final static String - SigEncodingRaw = "RAW_____", - SigEncodingCopyRect = "COPYRECT", - SigEncodingRRE = "RRE_____", - SigEncodingCoRRE = "CORRE___", - SigEncodingHextile = "HEXTILE_", - SigEncodingZlib = "ZLIB____", - SigEncodingTight = "TIGHT___", - SigEncodingZRLE = "ZRLE____", - SigEncodingCompressLevel0 = "COMPRLVL", - SigEncodingQualityLevel0 = "JPEGQLVL", - SigEncodingXCursor = "X11CURSR", - SigEncodingRichCursor = "RCHCURSR", - SigEncodingPointerPos = "POINTPOS", - SigEncodingLastRect = "LASTRECT", - SigEncodingNewFBSize = "NEWFBSIZ"; - - final static int MaxNormalEncoding = 255; - - // Contstants used in the Hextile decoder - final static int - HextileRaw = 1, - HextileBackgroundSpecified = 2, - HextileForegroundSpecified = 4, - HextileAnySubrects = 8, - HextileSubrectsColoured = 16; - - // Contstants used in the Tight decoder - final static int TightMinToCompress = 12; - final static int - TightExplicitFilter = 0x04, - TightFill = 0x08, - TightJpeg = 0x09, - TightMaxSubencoding = 0x09, - TightFilterCopy = 0x00, - TightFilterPalette = 0x01, - TightFilterGradient = 0x02; - - // Constants used for UltraVNC chat extension - final static int - CHAT_OPEN = -1, - CHAT_CLOSE = -2, - CHAT_FINISHED = -3; - - String host; - int port; - Socket sock; - DataInputStream is; - LimboOutputStream os; - private OutputStream sos; - - //Limbo: need to send the network operations of the Main UI - // but also use a mechanism like IntentService to keep - // the network message queue intact - class LimboOutputStream { - - OutputStream os; - HandlerThread rfbQueueThread = null; - Handler handler = null; - - public LimboOutputStream(OutputStream sos) { - os = sos; - - rfbQueueThread = new HandlerThread("RfbQueue"); - rfbQueueThread.start(); - handler = new Handler(rfbQueueThread.getLooper()); - } - - public void write(final int data) throws IOException { - final int data0 = data; - - handler.post(new Runnable() { - @Override - public void run() { - try { - sos.write(data0); - } catch (IOException e) { - e.printStackTrace(); - } - } - }); - } - - public void write(final byte[] bytes) throws IOException { - final byte [] buffer0 = java.util.Arrays.copyOf(bytes, bytes.length); - - handler.post(new Runnable() { - @Override - public void run() { - try { - sos.write(buffer0); - } catch (IOException e) { - Log.w(TAG, "Error while sending VNC data"); - if(Config.debug) - e.printStackTrace(); - } - } - }); - } - - public void write(final byte[] buffer, final int offset, final int count) throws IOException { - final byte [] buffer0 = java.util.Arrays.copyOf(buffer, count); - final int offset0 = offset; - final int count0 = count; - - handler.post(new Runnable() { - @Override - public void run() { - try { - sos.write(buffer0, offset0, count0); - } catch (IOException e) { - e.printStackTrace(); - } - } - }); - } - - public void close() { - rfbQueueThread.interrupt(); - handler.removeCallbacks(null); - handler.getLooper().quit(); - } - - } - - DH dh; - long dh_resp; - - //- SessionRecorder rec; - boolean inNormalProtocol = false; - //- VncViewer viewer; - - // Java on UNIX does not call keyPressed() on some keys, for example - // swedish keys To prevent our workaround to produce duplicate - // keypresses on JVMs that actually works, keep track of if - // keyPressed() for a "broken" key was called or not. - boolean brokenKeyPressed = false; - - // This will be set to true on the first framebuffer update - // containing Zlib-, ZRLE- or Tight-encoded data. - boolean wereZlibUpdates = false; - - // This will be set to false if the startSession() was called after - // we have received at least one Zlib-, ZRLE- or Tight-encoded - // framebuffer update. - boolean recordFromBeginning = true; - - // This fields are needed to show warnings about inefficiently saved - // sessions only once per each saved session file. - boolean zlibWarningShown; - boolean tightWarningShown; - - // Before starting to record each saved session, we set this field - // to 0, and increment on each framebuffer update. We don't flush - // the SessionRecorder data into the file before the second update. - // This allows us to write initial framebuffer update with zero - // timestamp, to let the player show initial desktop before - // playback. - int numUpdatesInSession; - - // Measuring network throughput. - boolean timing; - long timeWaitedIn100us; - long timedKbits; - - // Protocol version and TightVNC-specific protocol options. - int serverMajor, serverMinor; - int clientMajor, clientMinor; - boolean protocolTightVNC; - CapsContainer tunnelCaps, authCaps; - CapsContainer serverMsgCaps, clientMsgCaps; - CapsContainer encodingCaps; - - // If true, informs that the RFB socket was closed. - private boolean closed; - - - // - // Constructor. Make TCP connection to RFB server. - // - - LocalSocket localSocket = null; - public static boolean allow_external = false; - - //-RfbProto(String h, int p, VncViewer v) throws IOException { - RfbProto(String h, int p) throws IOException{ - //- viewer = v; - host = h; - port = p; - - /* - if (viewer.socketFactory == null) { - sock = new Socket(host, port); - } else { - try { - Class factoryClass = Class.forName(viewer.socketFactory); - SocketFactory factory = (SocketFactory)factoryClass.newInstance(); - //- if (viewer.inAnApplet) - //- sock = factory.createSocket(host, port, viewer); - //- else - sock = factory.createSocket(host, port, viewer.mainArgs); - } - catch(Exception e) { - e.printStackTrace(); - throw new IOException(e.getMessage()); - } - } */ - //+ - - if(!allow_external) { - localSocket = new LocalSocket(); - String localVNCSocketPath = Config.getLocalVNCSocketPath(); - LocalSocketAddress localSocketAddr = new LocalSocketAddress(localVNCSocketPath, LocalSocketAddress.Namespace.FILESYSTEM); - localSocket.connect(localSocketAddr); - is = new DataInputStream(new BufferedInputStream(localSocket.getInputStream(), - 32768)); - sos = localSocket.getOutputStream(); - - } else { - sock = new Socket(host, port); - is = new DataInputStream(new BufferedInputStream(sock.getInputStream(), - 16384)); - sos = sock.getOutputStream(); - } - - os = new LimboOutputStream(sos); - - timing = false; - timeWaitedIn100us = 5; - timedKbits = 0; - } - - - synchronized void close() { - - try { - os.close(); - } catch (Exception ex) { - ex.printStackTrace(); - } - - try { - sock.close(); - closed = true; - //- System.out.println("RFB socket closed"); - Log.v(TAG, "RFB socket closed"); - /*- - if (rec != null) { - rec.close(); - rec = null; - - } */ - } catch (Exception e) { - e.printStackTrace(); - } - - - } - - synchronized boolean closed() { - return closed; - } - - // - // Read server's protocol version message - // - - void readVersionMsg() throws Exception { - - byte[] b = new byte[12]; - - readFully(b); - - if ((b[0] != 'R') || (b[1] != 'F') || (b[2] != 'B') || (b[3] != ' ') - || (b[4] < '0') || (b[4] > '9') || (b[5] < '0') || (b[5] > '9') - || (b[6] < '0') || (b[6] > '9') || (b[7] != '.') - || (b[8] < '0') || (b[8] > '9') || (b[9] < '0') || (b[9] > '9') - || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n')) - { - Log.i(TAG,new String(b)); - throw new Exception("Host " + host + " port " + port + - " is not an RFB server"); - } - - serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0'); - serverMinor = (b[8] - '0') * 100 + (b[9] - '0') * 10 + (b[10] - '0'); - - if (serverMajor < 3) { - throw new Exception("RFB server does not support protocol version 3"); - } - } - - - // - // Write our protocol version message - // - - synchronized void writeVersionMsg() throws IOException { - clientMajor = 3; - if (serverMajor > 3 || serverMinor >= 8) { - clientMinor = 8; - os.write(versionMsg_3_8.getBytes()); - } else if (serverMinor >= 7) { - clientMinor = 7; - os.write(versionMsg_3_7.getBytes()); - } else { - clientMinor = 3; - os.write(versionMsg_3_3.getBytes()); - } - protocolTightVNC = false; - } - - - // - // Negotiate the authentication scheme. - // - - int negotiateSecurity(int bitPref) throws Exception { - return (clientMinor >= 7) ? - selectSecurityType(bitPref) : readSecurityType(bitPref); - } - - // - // Read security type from the server (protocol version 3.3). - // - - int readSecurityType(int bitPref) throws Exception { - int secType = is.readInt(); - - switch (secType) { - case SecTypeInvalid: - readConnFailedReason(); - return SecTypeInvalid; // should never be executed - case SecTypeNone: - case SecTypeVncAuth: - return secType; - case SecTypeUltra34: - if((bitPref & 1) == 1) - return secType; - throw new Exception("Username required."); - default: - throw new Exception("Unknown security type from RFB server: " + secType); - } - } - - // - // Select security type from the server's list (protocol versions 3.7/3.8). - // - - int selectSecurityType(int bitPref) throws Exception { - int secType = SecTypeInvalid; - - // Read the list of security types. - int nSecTypes = is.readUnsignedByte(); - if (nSecTypes == 0) { - readConnFailedReason(); - return SecTypeInvalid; // should never be executed - } - byte[] secTypes = new byte[nSecTypes]; - readFully(secTypes); - - // Find out if the server supports TightVNC protocol extensions - for (int i = 0; i < nSecTypes; i++) { - if (secTypes[i] == SecTypeTight) { - protocolTightVNC = true; - os.write(SecTypeTight); - return SecTypeTight; - } - } - - // Find first supported security type. - for (int i = 0; i < nSecTypes; i++) { - if (secTypes[i] == SecTypeNone || secTypes[i] == SecTypeVncAuth) { - secType = secTypes[i]; - break; - } - } - - if (secType == SecTypeInvalid) { - throw new Exception("Server did not offer supported security type"); - } else { - os.write(secType); - } - - return secType; - } - - // - // Perform "no authentication". - // - - void authenticateNone() throws Exception { - if (clientMinor >= 8) - readSecurityResult("No authentication"); - } - - // - // Perform standard VNC Authentication. - // - - void authenticateVNC(String pw) throws Exception { - byte[] challenge = new byte[16]; - readFully(challenge); - - if (pw.length() > 8) - pw = pw.substring(0, 8); // Truncate to 8 chars - - // Truncate password on the first zero byte. - int firstZero = pw.indexOf(0); - if (firstZero != -1) - pw = pw.substring(0, firstZero); - - byte[] key = {0, 0, 0, 0, 0, 0, 0, 0}; - System.arraycopy(pw.getBytes(), 0, key, 0, pw.length()); - - DesCipher des = new DesCipher(key); - - des.encrypt(challenge, 0, challenge, 0); - des.encrypt(challenge, 8, challenge, 8); - - os.write(challenge); - - readSecurityResult("VNC authentication"); - } - - // - // Read security result. - // Throws an exception on authentication failure. - // - - void readSecurityResult(String authType) throws Exception { - int securityResult = is.readInt(); - - switch (securityResult) { - case VncAuthOK: - System.out.println(authType + ": success"); - break; - case VncAuthFailed: - if (clientMinor >= 8) - readConnFailedReason(); - throw new Exception(authType + ": failed"); - case VncAuthTooMany: - throw new Exception(authType + ": failed, too many tries"); - default: - throw new Exception(authType + ": unknown result " + securityResult); - } - } - - // - // Read the string describing the reason for a connection failure, - // and throw an exception. - // - - void readConnFailedReason() throws Exception { - int reasonLen = is.readInt(); - byte[] reason = new byte[reasonLen]; - readFully(reason); - String reasonString = new String(reason); - Log.v(TAG, reasonString); - throw new Exception(reasonString); - } - - void prepareDH() throws Exception - { - long gen = is.readLong(); - long mod = is.readLong(); - dh_resp = is.readLong(); - - dh = new DH(gen,mod); - long pub = dh.createInterKey(); - - os.write(DH.longToBytes(pub)); - } - - void authenticateDH(String us,String pw) throws Exception - { - long key = dh.createEncryptionKey(dh_resp); - - DesCipher des = new DesCipher(DH.longToBytes(key)); - - byte user[] = new byte[256]; - byte passwd[] = new byte[64]; - int i; - System.arraycopy(us.getBytes(),0,user,0,us.length()); - if(us.length() < 256) - { - for(i=us.length(); i<256; i++) - { - user[i]=0; - } - } - System.arraycopy(pw.getBytes(),0,passwd,0,pw.length()); - if(pw.length() < 64) - { - for(i=pw.length(); i<64; i++) - { - passwd[i]=0; - } - } - - des.encryptText(user,user,DH.longToBytes(key)); - des.encryptText(passwd,passwd,DH.longToBytes(key)); - - os.write(user); - os.write(passwd); - - readSecurityResult("VNC authentication"); - } - // - // Initialize capability lists (TightVNC protocol extensions). - // - - void initCapabilities() { - tunnelCaps = new CapsContainer(); - authCaps = new CapsContainer(); - serverMsgCaps = new CapsContainer(); - clientMsgCaps = new CapsContainer(); - encodingCaps = new CapsContainer(); - - // Supported authentication methods - authCaps.add(AuthNone, StandardVendor, SigAuthNone, - "No authentication"); - authCaps.add(AuthVNC, StandardVendor, SigAuthVNC, - "Standard VNC password authentication"); - - // Supported encoding types - encodingCaps.add(EncodingCopyRect, StandardVendor, - SigEncodingCopyRect, "Standard CopyRect encoding"); - encodingCaps.add(EncodingRRE, StandardVendor, - SigEncodingRRE, "Standard RRE encoding"); - encodingCaps.add(EncodingCoRRE, StandardVendor, - SigEncodingCoRRE, "Standard CoRRE encoding"); - encodingCaps.add(EncodingHextile, StandardVendor, - SigEncodingHextile, "Standard Hextile encoding"); - encodingCaps.add(EncodingZRLE, StandardVendor, - SigEncodingZRLE, "Standard ZRLE encoding"); - encodingCaps.add(EncodingZlib, TridiaVncVendor, - SigEncodingZlib, "Zlib encoding"); - encodingCaps.add(EncodingTight, TightVncVendor, - SigEncodingTight, "Tight encoding"); - - // Supported pseudo-encoding types - encodingCaps.add(EncodingCompressLevel0, TightVncVendor, - SigEncodingCompressLevel0, "Compression level"); - encodingCaps.add(EncodingQualityLevel0, TightVncVendor, - SigEncodingQualityLevel0, "JPEG quality level"); - encodingCaps.add(EncodingXCursor, TightVncVendor, - SigEncodingXCursor, "X-style cursor shape update"); - encodingCaps.add(EncodingRichCursor, TightVncVendor, - SigEncodingRichCursor, "Rich-color cursor shape update"); - encodingCaps.add(EncodingPointerPos, TightVncVendor, - SigEncodingPointerPos, "Pointer position update"); - encodingCaps.add(EncodingLastRect, TightVncVendor, - SigEncodingLastRect, "LastRect protocol extension"); - encodingCaps.add(EncodingNewFBSize, TightVncVendor, - SigEncodingNewFBSize, "Framebuffer size change"); - } - - // - // Setup tunneling (TightVNC protocol extensions) - // - - void setupTunneling() throws IOException { - int nTunnelTypes = is.readInt(); - if (nTunnelTypes != 0) { - readCapabilityList(tunnelCaps, nTunnelTypes); - - // We don't support tunneling yet. - writeInt(NoTunneling); - } - } - - // - // Negotiate authentication scheme (TightVNC protocol extensions) - // - - int negotiateAuthenticationTight() throws Exception { - int nAuthTypes = is.readInt(); - if (nAuthTypes == 0) - return AuthNone; - - readCapabilityList(authCaps, nAuthTypes); - for (int i = 0; i < authCaps.numEnabled(); i++) { - int authType = authCaps.getByOrder(i); - if (authType == AuthNone || authType == AuthVNC) { - writeInt(authType); - return authType; - } - } - throw new Exception("No suitable authentication scheme found"); - } - - // - // Read a capability list (TightVNC protocol extensions) - // - - void readCapabilityList(CapsContainer caps, int count) throws IOException { - int code; - byte[] vendor = new byte[4]; - byte[] name = new byte[8]; - for (int i = 0; i < count; i++) { - code = is.readInt(); - readFully(vendor); - readFully(name); - caps.enable(new CapabilityInfo(code, vendor, name)); - } - } - - // - // Write a 32-bit integer into the output stream. - // - - byte[] writeIntBuffer = new byte[4]; - void writeInt(int value) throws IOException { - writeIntBuffer[0] = (byte) ((value >> 24) & 0xff); - writeIntBuffer[1] = (byte) ((value >> 16) & 0xff); - writeIntBuffer[2] = (byte) ((value >> 8) & 0xff); - writeIntBuffer[3] = (byte) (value & 0xff); - os.write(writeIntBuffer); - } - - // - // Write the client initialisation message - // - - void writeClientInit() throws IOException { - /*- if (viewer.options.shareDesktop) { - os.write(1); - } else { - os.write(0); - } - viewer.options.disableShareDesktop(); - */ - os.write(0); - } - - - // - // Read the server initialisation message - // - - String desktopName; - public int framebufferWidth, framebufferHeight; - int bitsPerPixel, depth; - boolean bigEndian, trueColour; - int redMax, greenMax, blueMax, redShift, greenShift, blueShift; - - void readServerInit() throws IOException { - framebufferWidth = is.readUnsignedShort(); - framebufferHeight = is.readUnsignedShort(); - bitsPerPixel = is.readUnsignedByte(); - depth = is.readUnsignedByte(); - bigEndian = (is.readUnsignedByte() != 0); - trueColour = (is.readUnsignedByte() != 0); - redMax = is.readUnsignedShort(); - greenMax = is.readUnsignedShort(); - blueMax = is.readUnsignedShort(); - redShift = is.readUnsignedByte(); - greenShift = is.readUnsignedByte(); - blueShift = is.readUnsignedByte(); - byte[] pad = new byte[3]; - readFully(pad); - int nameLength = is.readInt(); - byte[] name = new byte[nameLength]; - readFully(name); - desktopName = new String(name); - - // Read interaction capabilities (TightVNC protocol extensions) - if (protocolTightVNC) { - int nServerMessageTypes = is.readUnsignedShort(); - int nClientMessageTypes = is.readUnsignedShort(); - int nEncodingTypes = is.readUnsignedShort(); - is.readUnsignedShort(); - readCapabilityList(serverMsgCaps, nServerMessageTypes); - readCapabilityList(clientMsgCaps, nClientMessageTypes); - readCapabilityList(encodingCaps, nEncodingTypes); - } - - inNormalProtocol = true; - } - - - // - // Create session file and write initial protocol messages into it. - // - /*- - void startSession(String fname) throws IOException { - rec = new SessionRecorder(fname); - rec.writeHeader(); - rec.write(versionMsg_3_3.getBytes()); - rec.writeIntBE(SecTypeNone); - rec.writeShortBE(framebufferWidth); - rec.writeShortBE(framebufferHeight); - byte[] fbsServerInitMsg = { - 32, 24, 0, 1, 0, - (byte)0xFF, 0, (byte)0xFF, 0, (byte)0xFF, - 16, 8, 0, 0, 0, 0 - }; - rec.write(fbsServerInitMsg); - rec.writeIntBE(desktopName.length()); - rec.write(desktopName.getBytes()); - numUpdatesInSession = 0; - - // FIXME: If there were e.g. ZRLE updates only, that should not - // affect recording of Zlib and Tight updates. So, actually - // we should maintain separate flags for Zlib, ZRLE and - // Tight, instead of one ``wereZlibUpdates'' variable. - // - if (wereZlibUpdates) - recordFromBeginning = false; - - zlibWarningShown = false; - tightWarningShown = false; - } - - // - // Close session file. - // - - void closeSession() throws IOException { - if (rec != null) { - rec.close(); - rec = null; - } - } - */ - - // - // Set new framebuffer size - // - - void setFramebufferSize(int width, int height) { - framebufferWidth = width; - framebufferHeight = height; - } - - - // - // Read the server message type - // - - int readServerMessageType() throws IOException { - int msgType = is.readUnsignedByte(); - - // If the session is being recorded: - /*- - if (rec != null) { - if (msgType == Bell) { // Save Bell messages in session files. - rec.writeByte(msgType); - if (numUpdatesInSession > 0) - rec.flush(); - } - } - */ - - return msgType; - } - - - // - // Read a FramebufferUpdate message - // - - int updateNRects; - - void readFramebufferUpdate() throws IOException { - is.readByte(); - updateNRects = is.readUnsignedShort(); - - // If the session is being recorded: - /*- - if (rec != null) { - rec.writeByte(FramebufferUpdate); - rec.writeByte(0); - rec.writeShortBE(updateNRects); - } - */ - - numUpdatesInSession++; - } - - // Read a FramebufferUpdate rectangle header - - int updateRectX, updateRectY, updateRectW, updateRectH, updateRectEncoding; - - void readFramebufferUpdateRectHdr() throws Exception { - updateRectX = is.readUnsignedShort(); - updateRectY = is.readUnsignedShort(); - updateRectW = is.readUnsignedShort(); - updateRectH = is.readUnsignedShort(); - updateRectEncoding = is.readInt(); - - if (updateRectEncoding == EncodingZlib || - updateRectEncoding == EncodingZRLE || - updateRectEncoding == EncodingTight) - wereZlibUpdates = true; - - // If the session is being recorded: - /*- - if (rec != null) { - if (numUpdatesInSession > 1) - rec.flush(); // Flush the output on each rectangle. - rec.writeShortBE(updateRectX); - rec.writeShortBE(updateRectY); - rec.writeShortBE(updateRectW); - rec.writeShortBE(updateRectH); - if (updateRectEncoding == EncodingZlib && !recordFromBeginning) { - // Here we cannot write Zlib-encoded rectangles because the - // decoder won't be able to reproduce zlib stream state. - if (!zlibWarningShown) { - System.out.println("Warning: Raw encoding will be used " + - "instead of Zlib in recorded session."); - zlibWarningShown = true; - } - rec.writeIntBE(EncodingRaw); - } else { - rec.writeIntBE(updateRectEncoding); - if (updateRectEncoding == EncodingTight && !recordFromBeginning && - !tightWarningShown) { - System.out.println("Warning: Re-compressing Tight-encoded " + - "updates for session recording."); - tightWarningShown = true; - } - } - } - */ - - if (updateRectEncoding != RfbProto.EncodingPointerPos && ( updateRectEncoding < 0 || updateRectEncoding > MaxNormalEncoding )) - return; - - if (updateRectX + updateRectW > framebufferWidth || - updateRectY + updateRectH > framebufferHeight) { - throw new Exception("Framebuffer update rectangle too large: " + - updateRectW + "x" + updateRectH + " at (" + - updateRectX + "," + updateRectY + ")"); - } - } - - // Read CopyRect source X and Y. - - int copyRectSrcX, copyRectSrcY; - - void readCopyRect() throws IOException { - copyRectSrcX = is.readUnsignedShort(); - copyRectSrcY = is.readUnsignedShort(); - - // If the session is being recorded: - /*- - if (rec != null) { - rec.writeShortBE(copyRectSrcX); - rec.writeShortBE(copyRectSrcY); - } - */ - } - - - // - // Read a ServerCutText message - // - - String readServerCutText() throws IOException { - byte[] pad = new byte[3]; - readFully(pad); - int len = is.readInt(); - byte[] text = new byte[len]; - readFully(text); - return new String(text); - } - - - // - // Read an integer in compact representation (1..3 bytes). - // Such format is used as a part of the Tight encoding. - // Also, this method records data if session recording is active and - // the viewer's recordFromBeginning variable is set to true. - // - - int readCompactLen() throws IOException { - int[] portion = new int[3]; - portion[0] = is.readUnsignedByte(); - int byteCount = 1; - int len = portion[0] & 0x7F; - if ((portion[0] & 0x80) != 0) { - portion[1] = is.readUnsignedByte(); - byteCount++; - len |= (portion[1] & 0x7F) << 7; - if ((portion[1] & 0x80) != 0) { - portion[2] = is.readUnsignedByte(); - byteCount++; - len |= (portion[2] & 0xFF) << 14; - } - } - /*- - if (rec != null && recordFromBeginning) - for (int i = 0; i < byteCount; i++) - rec.writeByte(portion[i]); - */ - return len; - } - - - // - // Write a FramebufferUpdateRequest message - // - - byte[] framebufferUpdateRequest = new byte[10]; - synchronized void writeFramebufferUpdateRequest(int x, int y, int w, int h, - boolean incremental) - throws IOException - { - framebufferUpdateRequest[0] = (byte) FramebufferUpdateRequest; - framebufferUpdateRequest[1] = (byte) (incremental ? 1 : 0); - framebufferUpdateRequest[2] = (byte) ((x >> 8) & 0xff); - framebufferUpdateRequest[3] = (byte) (x & 0xff); - framebufferUpdateRequest[4] = (byte) ((y >> 8) & 0xff); - framebufferUpdateRequest[5] = (byte) (y & 0xff); - framebufferUpdateRequest[6] = (byte) ((w >> 8) & 0xff); - framebufferUpdateRequest[7] = (byte) (w & 0xff); - framebufferUpdateRequest[8] = (byte) ((h >> 8) & 0xff); - framebufferUpdateRequest[9] = (byte) (h & 0xff); - - os.write(framebufferUpdateRequest); - } - - - // - // Write a SetPixelFormat message - // - - synchronized void writeSetPixelFormat(int bitsPerPixel, int depth, boolean bigEndian, - boolean trueColour, - int redMax, int greenMax, int blueMax, - int redShift, int greenShift, int blueShift, boolean fGreyScale) // sf@2005) - throws IOException - { - byte[] b = new byte[20]; - - b[0] = (byte) SetPixelFormat; - b[4] = (byte) bitsPerPixel; - b[5] = (byte) depth; - b[6] = (byte) (bigEndian ? 1 : 0); - b[7] = (byte) (trueColour ? 1 : 0); - b[8] = (byte) ((redMax >> 8) & 0xff); - b[9] = (byte) (redMax & 0xff); - b[10] = (byte) ((greenMax >> 8) & 0xff); - b[11] = (byte) (greenMax & 0xff); - b[12] = (byte) ((blueMax >> 8) & 0xff); - b[13] = (byte) (blueMax & 0xff); - b[14] = (byte) redShift; - b[15] = (byte) greenShift; - b[16] = (byte) blueShift; - b[17] = (byte) (fGreyScale ? 1 : 0); // sf@2005 - - os.write(b); - } - - - // - // Write a FixColourMapEntries message. The values in the red, green and - // blue arrays are from 0 to 65535. - // - - synchronized void writeFixColourMapEntries(int firstColour, int nColours, - int[] red, int[] green, int[] blue) - throws IOException - { - byte[] b = new byte[6 + nColours * 6]; - - b[0] = (byte) FixColourMapEntries; - b[2] = (byte) ((firstColour >> 8) & 0xff); - b[3] = (byte) (firstColour & 0xff); - b[4] = (byte) ((nColours >> 8) & 0xff); - b[5] = (byte) (nColours & 0xff); - - for (int i = 0; i < nColours; i++) { - b[6 + i * 6] = (byte) ((red[i] >> 8) & 0xff); - b[6 + i * 6 + 1] = (byte) (red[i] & 0xff); - b[6 + i * 6 + 2] = (byte) ((green[i] >> 8) & 0xff); - b[6 + i * 6 + 3] = (byte) (green[i] & 0xff); - b[6 + i * 6 + 4] = (byte) ((blue[i] >> 8) & 0xff); - b[6 + i * 6 + 5] = (byte) (blue[i] & 0xff); - } - - os.write(b); - } - - - // - // Write a SetEncodings message - // - - synchronized void writeSetEncodings(int[] encs, int len) throws IOException { - byte[] b = new byte[4 + 4 * len]; - - b[0] = (byte) SetEncodings; - b[2] = (byte) ((len >> 8) & 0xff); - b[3] = (byte) (len & 0xff); - - for (int i = 0; i < len; i++) { - b[4 + 4 * i] = (byte) ((encs[i] >> 24) & 0xff); - b[5 + 4 * i] = (byte) ((encs[i] >> 16) & 0xff); - b[6 + 4 * i] = (byte) ((encs[i] >> 8) & 0xff); - b[7 + 4 * i] = (byte) (encs[i] & 0xff); - } - - os.write(b); - } - - - // - // Write a ClientCutText message - // - - synchronized void writeClientCutText(String text) throws IOException { - byte[] b = new byte[8 + text.length()]; - - b[0] = (byte) ClientCutText; - b[4] = (byte) ((text.length() >> 24) & 0xff); - b[5] = (byte) ((text.length() >> 16) & 0xff); - b[6] = (byte) ((text.length() >> 8) & 0xff); - b[7] = (byte) (text.length() & 0xff); - - System.arraycopy(text.getBytes(), 0, b, 8, text.length()); - - os.write(b); - } - - - // - // A buffer for putting pointer and keyboard events before being sent. This - // is to ensure that multiple RFB events generated from a single Java Event - // will all be sent in a single network packet. The maximum possible - // length is 4 modifier down events, a single key event followed by 4 - // modifier up events i.e. 9 key events or 72 bytes. - // - - byte[] eventBuf = new byte[72]; - int eventBufLen; - - - /** - * Write a pointer event message. We may need to send modifier key events - * around it to set the correct modifier state. - * @param x - * @param y - * @param modifiers - * @param pointerMask - * @throws IOException - */ - synchronized void writePointerEvent( int x, int y, int modifiers, int pointerMask) throws IOException - { - eventBufLen = 0; - writeModifierKeyEvents(modifiers); - - eventBuf[eventBufLen++] = (byte) PointerEvent; - eventBuf[eventBufLen++] = (byte) pointerMask; - eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff); - eventBuf[eventBufLen++] = (byte) (x & 0xff); - eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff); - eventBuf[eventBufLen++] = (byte) (y & 0xff); - - // - // Always release all modifiers after an "up" event - // - - if (pointerMask == 0) { - writeModifierKeyEvents(0); - } - - os.write(eventBuf, 0, eventBufLen); - } - - void writeCtrlAltDel() throws IOException { - final int DELETE = 0xffff; - final int CTRLALT = VncCanvas.CTRL_MASK | VncCanvas.ALT_MASK; - try { - // Press - eventBufLen = 0; - writeModifierKeyEvents(CTRLALT); - writeKeyEvent(DELETE, true); - os.write(eventBuf, 0, eventBufLen); - - // Release - eventBufLen = 0; - writeModifierKeyEvents(CTRLALT); - writeKeyEvent(DELETE, false); - - // Reset VNC server modifiers state - writeModifierKeyEvents(0); - os.write(eventBuf, 0, eventBufLen); - } catch (IOException e) { - e.printStackTrace(); - } - } - - // - // Write a key event message. We may need to send modifier key events - // around it to set the correct modifier state. Also we need to translate - // from the Java key values to the X keysym values used by the RFB protocol. - // - public synchronized void writeKeyEvent(int keySym, int metaState, boolean down) throws IOException { -// Log.i("writeKeyEvent","key = " + keySym + " metastate = " + metaState + " down = " + down); - eventBufLen = 0; - if (down) - writeModifierKeyEvents(metaState); - if (keySym != 0) - writeKeyEvent(keySym, down); - - // Always release all modifiers after an "up" event - if (!down) - writeModifierKeyEvents(0); - - os.write(eventBuf, 0, eventBufLen); - } - - - - - // - // Add a raw key event with the given X keysym to eventBuf. - // - - private void writeKeyEvent(int keysym, boolean down) { - eventBuf[eventBufLen++] = (byte) KeyboardEvent; - eventBuf[eventBufLen++] = (byte) (down ? 1 : 0); - eventBuf[eventBufLen++] = (byte) 0; - eventBuf[eventBufLen++] = (byte) 0; - eventBuf[eventBufLen++] = (byte) ((keysym >> 24) & 0xff); - eventBuf[eventBufLen++] = (byte) ((keysym >> 16) & 0xff); - eventBuf[eventBufLen++] = (byte) ((keysym >> 8) & 0xff); - eventBuf[eventBufLen++] = (byte) (keysym & 0xff); - } - - - // - // Write key events to set the correct modifier state. - // - - int oldModifiers = 0; - - void writeModifierKeyEvents(int newModifiers) { - if ((newModifiers & VncCanvas.CTRL_MASK) != (oldModifiers & VncCanvas.CTRL_MASK)) - writeKeyEvent(0xffe3, (newModifiers & VncCanvas.CTRL_MASK) != 0); - - if ((newModifiers & VncCanvas.SHIFT_MASK) != (oldModifiers & VncCanvas.SHIFT_MASK)) - writeKeyEvent(0xffe1, (newModifiers & VncCanvas.SHIFT_MASK) != 0); - - if ((newModifiers & VncCanvas.META_MASK) != (oldModifiers & VncCanvas.META_MASK)) - writeKeyEvent(0xffe7, (newModifiers & VncCanvas.META_MASK) != 0); - - if ((newModifiers & VncCanvas.ALT_MASK) != (oldModifiers & VncCanvas.ALT_MASK)) - writeKeyEvent(0xffe9, (newModifiers & VncCanvas.ALT_MASK) != 0); - - oldModifiers = newModifiers; - } - // - // Compress and write the data into the recorded session file. This - // method assumes the recording is on (rec != null). - // - - - public void startTiming() { - timing = true; - - // Carry over up to 1s worth of previous rate for smoothing. - - if (timeWaitedIn100us > 10000) { - timedKbits = timedKbits * 10000 / timeWaitedIn100us; - timeWaitedIn100us = 10000; - } - } - - public void stopTiming() { - timing = false; - if (timeWaitedIn100us < timedKbits/2) - timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s - } - - public long kbitsPerSecond() { - return timedKbits * 10000 / timeWaitedIn100us; - } - - public long timeWaited() { - return timeWaitedIn100us; - } - - public void readFully(byte b[]) throws IOException { - readFully(b, 0, b.length); - } - - public void readFully(byte b[], int off, int len) throws IOException { - long before = 0; - timing = false; // for test - - if (timing) - before = System.currentTimeMillis(); - - is.readFully(b, off, len); - - if (timing) { - long after = System.currentTimeMillis(); - long newTimeWaited = (after - before) * 10; - int newKbits = len * 8 / 1000; - - // limit rate to between 10kbit/s and 40Mbit/s - - if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000; - if (newTimeWaited < newKbits/4) newTimeWaited = newKbits/4; - - timeWaitedIn100us += newTimeWaited; - timedKbits += newKbits; - } - } - - synchronized void writeOpenChat() throws Exception { - os.write(TextChat); // byte type - os.write(0); // byte pad 1 - os.write(0); // byte pad 2 - os.write(0); // byte pad 2 - writeInt(CHAT_OPEN); // int message length - } - - synchronized void writeCloseChat() throws Exception { - os.write(TextChat); // byte type - os.write(0); // byte pad 1 - os.write(0); // byte pad 2 - os.write(0); // byte pad 2 - writeInt(CHAT_CLOSE); // int message length - } - - synchronized void writeFinishedChat() throws Exception { - os.write(TextChat); // byte type - os.write(0); // byte pad 1 - os.write(0); // byte pad 2 - os.write(0); // byte pad 2 - writeInt(CHAT_FINISHED); // int message length - } - - String readTextChatMsg() throws Exception { - byte[] pad = new byte[3]; - readFully(pad); - int len = is.readInt(); - if (len == CHAT_OPEN) { - // Remote user requests chat - ///viewer.openChat(); - // Respond to chat request - writeOpenChat(); - return null; - } else if (len == CHAT_CLOSE) { - // Remote user ends chat - ///viewer.closeChat(); - return null; - } else if (len == CHAT_FINISHED) { - // Remote user says chat finished. - // Not sure why I should care about this state. - return null; - } else { - // Remote user sends message!! - if (len > 0) { - byte[] msg = new byte[len]; - readFully(msg); - return new String(msg); - } - } - return null; - } - - public synchronized void writeChatMessage(String msg) throws Exception { - os.write(TextChat); // byte type - os.write(0); // byte pad 1 - os.write(0); // byte pad 2 - os.write(0); // byte pad 2 - byte [] bytes = msg.getBytes("8859_1"); - byte [] outgoing = bytes; - if (bytes.length > 4096) { - outgoing = new byte[4096]; - System.arraycopy(bytes, 0, outgoing, 0, 4096); - } - writeInt(outgoing.length); // int message length - os.write(outgoing); // message - } -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/Utils.java b/limbo-android-lib/src/main/java/android/androidVNC/Utils.java deleted file mode 100644 index d18f5eb2c..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/Utils.java +++ /dev/null @@ -1,75 +0,0 @@ -package android.androidVNC; - -import android.app.Activity; -import android.app.ActivityManager; -import android.app.AlertDialog; -import android.app.ActivityManager.MemoryInfo; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.content.Intent; -import android.net.Uri; -import android.text.Html; - -public class Utils { - - public static void showYesNoPrompt(Context _context, String title, String message, OnClickListener onYesListener, OnClickListener onNoListener) { - AlertDialog.Builder builder = new AlertDialog.Builder(_context); - builder.setTitle(title); - builder.setIcon(android.R.drawable.ic_dialog_info); // lame icon - builder.setMessage(message); - builder.setCancelable(false); - builder.setPositiveButton("Yes", onYesListener); - builder.setNegativeButton("No", onNoListener); - builder.show(); - } - - private static final Intent docIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://code.google.com/p/android-vnc-viewer/wiki/Documentation")); - - public static ActivityManager getActivityManager(Context context) - { - ActivityManager result = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE); - if (result == null) - throw new UnsupportedOperationException("Could not retrieve ActivityManager"); - return result; - } - - public static MemoryInfo getMemoryInfo(Context _context) { - MemoryInfo info = new MemoryInfo(); - getActivityManager(_context).getMemoryInfo(info); - return info; - } - - public static void showDocumentation(Context c) { - c.startActivity(docIntent); - } - - private static int nextNoticeID = 0; - public static int nextNoticeID() { - nextNoticeID++; - return nextNoticeID; - } - - public static void showErrorMessage(Context _context, String message) { - showMessage(_context, "Error!", message, android.R.drawable.ic_dialog_alert, null); - } - - public static void showFatalErrorMessage(final Context _context, String message) { - showMessage(_context, "Error!", message, android.R.drawable.ic_dialog_alert, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - ((Activity) _context).finish(); - } - }); - } - - public static void showMessage(Context _context, String title, String message, int icon, DialogInterface.OnClickListener ackHandler) { - AlertDialog.Builder builder = new AlertDialog.Builder(_context); - builder.setTitle(title); - builder.setMessage(Html.fromHtml(message)); - builder.setCancelable(false); - builder.setPositiveButton("Acknowledged", ackHandler); - builder.setIcon(icon); - builder.show(); - } -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/VncCanvas.java b/limbo-android-lib/src/main/java/android/androidVNC/VncCanvas.java deleted file mode 100644 index e517d8e0d..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/VncCanvas.java +++ /dev/null @@ -1,2083 +0,0 @@ -// -// Copyright (C) 2010 Michael A. MacDonald -// Copyright (C) 2004 Horizon Wimba. All Rights Reserved. -// Copyright (C) 2001-2003 HorizonLive.com, Inc. All Rights Reserved. -// Copyright (C) 2001,2002 Constantin Kaplinsky. All Rights Reserved. -// Copyright (C) 2000 Tridia Corporation. All Rights Reserved. -// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. -// -// This is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This software is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this software; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, -// USA. -// -// -// VncCanvas is a subclass of android.view.SurfaceView which draws a VNC -// desktop on it. -// -package android.androidVNC; - -import android.app.Activity; -import android.app.ProgressDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Style; -import android.graphics.Point; -import android.graphics.Rect; -import android.os.Handler; -import android.util.AttributeSet; -import android.util.Log; -import android.view.Display; -import android.view.InputDevice; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; -import android.widget.ImageView; -import android.widget.Toast; - -import com.antlersoft.android.bc.BCFactory; -import com.max2idea.android.limbo.main.Config; -import com.max2idea.android.limbo.main.LimboVNCActivity; -import com.max2idea.android.limbo.utils.UIUtils; - -import org.libsdl.app.SDLActivity; - -import java.io.IOException; -import java.util.zip.Inflater; - -import androidx.appcompat.widget.AppCompatImageView; - -public class VncCanvas extends AppCompatImageView { - - private final static String TAG = "VncCanvas"; - private final static boolean LOCAL_LOGV = true; - public AbstractScaling scaling; - // Available to activity - public int mouseX, mouseY; - // Connection parameters - ConnectionBean connection; - // Runtime control flags - private boolean maintainConnection = true; - private boolean showDesktopInfo = true; - private boolean repaintsEnabled = true; - /** - * Use camera button as meta key for right mouse button - */ - boolean cameraButtonDown = false; - // Keep track when a seeming key press was the result of a menu shortcut - int lastKeyDown; - boolean afterMenu; - // Color Model settings - private COLORMODEL pendingColorModel = COLORMODEL.C24bit; - public COLORMODEL colorModel = null; - private int bytesPerPixel = 0; - private int[] colorPalette = null; - // VNC protocol connection - public RfbProto rfb; - // Internal bitmap data - AbstractBitmapData bitmapData; - public Handler handler = new Handler(); - // VNC Encoding parameters - private boolean useCopyRect = false; // TODO CopyRect is not working - private int preferredEncoding = -1; - // Unimplemented VNC encoding parameters - private boolean requestCursorUpdates = false; - private boolean ignoreCursorUpdates = true; - // Unimplemented TIGHT encoding parameters - private int compressLevel = -1; - private int jpegQuality = -1; - // Used to determine if encoding update is necessary - private int[] encodingsSaved = new int[20]; - private int nEncodingsSaved = 0; - // ZRLE encoder's data. - private byte[] zrleBuf; - private int[] zrleTilePixels; - private ZlibInStream zrleInStream; - // Zlib encoder's data. - private byte[] zlibBuf; - private Inflater zlibInflater; - private MouseScrollRunnable scrollRunnable; - private Paint handleRREPaint; - /** - * Position of the top left portion of the visible part of the - * screen, in full-frame coordinates - */ - int absoluteXPosition = 0, absoluteYPosition = 0; - public boolean mouseDown; - - /** - * Constructor used by the inflation apparatus - * - * @param context - */ - public VncCanvas(final Context context, AttributeSet attrs) { - super(context, attrs); - scrollRunnable = new MouseScrollRunnable(); - handleRREPaint = new Paint(); - handleRREPaint.setStyle(Style.FILL); - } - - public VncCanvas(Context context) { - super(context); - } - - public static int retries; - public static int MAX_RETRIES = 5; - - /** - * Create a view showing a VNC connection - * - * @param context - * Containing context (activity) - * @param bean - * Connection settings - * @param setModes - * Callback to run on UI thread after connection is set up - */ - void initializeVncCanvas(ConnectionBean bean, final Runnable setModes) { - connection = bean; - this.pendingColorModel = COLORMODEL.valueOf(bean.getColorModel()); - - setOnGenericMotionListener(new VNCGenericMotionListener_API12()); - setOnTouchListener(new VNCOnTouchListener()); - - // Startup the RFB thread with a nifty progess dialog - final ProgressDialog pd = ProgressDialog.show(getContext(), "Connecting to VM Console", "Please wait...", true, - true, new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - closeConnection(); - handler.post(new Runnable() { - public void run() { - Utils.showErrorMessage(getContext(), "VNC connection aborted!"); - } - }); - } - }); - final Display display = pd.getWindow().getWindowManager().getDefaultDisplay(); - Thread t = new Thread() { - - public void run() { - try { - int width = 0; - int height = 0; - Point size = new Point(); - display.getSize(size); - width = size.x; - height = size.y; - - connectAndAuthenticate(connection.getUserName(), connection.getPassword()); - doProtocolInitialisation(width, height); - handler.post(new Runnable() { - public void run() { - // pd.setMessage("Downloading first frame.\nPlease - // wait..."); - } - }); - processNormalProtocol(getContext(), pd, setModes); - } catch (Throwable e) { - if (maintainConnection) { - Log.e(TAG, e.toString()); - if(Config.debug) - e.printStackTrace(); - // Ensure we dismiss the progress dialog - // before we fatal error finish - if (pd.isShowing()) { - pd.dismiss(); - } - if (e instanceof OutOfMemoryError) { - // TODO Not sure if this will happen but... - // figure out how to gracefully notify the user - // Instantiating an alert dialog here doesn't work - // because we are out of memory. :( - } else if (e instanceof ArrayIndexOutOfBoundsException || e instanceof java.net.ConnectException - || e instanceof java.io.IOException) { - // Retry - if (retries < MAX_RETRIES) { - retries++; - reload(); - } - } else { - String error = "VNC connection failed!"; - if (e.getMessage() != null && (e.getMessage().indexOf("authentication") > -1)) { - error = "VNC authentication failed!"; - } - final String error_ = error + "
" + e.getLocalizedMessage(); - handler.post(new Runnable() { - public void run() { - Utils.showFatalErrorMessage(getContext(), error_); - } - }); - } - } - } - } - }; - t.start(); - } - - public void reload() { - Log.d(TAG, "Reconnecting..."); - Activity activity = ((Activity) getContext()); - Intent data = new Intent(); - activity.setResult(Config.VNC_RESET_RESULT_CODE, data); - activity.finish(); - - } - - void connectAndAuthenticate(String us, String pw) throws Exception { - Log.i(TAG, "Connecting to " + connection.getAddress() + ", port " + connection.getPort() + "..."); - - rfb = new RfbProto(connection.getAddress(), connection.getPort()); - if (LOCAL_LOGV) { - Log.v(TAG, "Connected to server"); - - } - - rfb.readVersionMsg(); - Log.i(TAG, "RFB server supports protocol version " + rfb.serverMajor + "." + rfb.serverMinor); - - rfb.writeVersionMsg(); - Log.i(TAG, "Using RFB protocol version " + rfb.clientMajor + "." + rfb.clientMinor); - - int bitPref = 0; - if (connection.getUserName().length() > 0) { - bitPref |= 1; - } - Log.d("debug", "bitPref=" + bitPref); - int secType = rfb.negotiateSecurity(bitPref); - int authType; - if (secType == RfbProto.SecTypeTight) { - rfb.initCapabilities(); - rfb.setupTunneling(); - authType = rfb.negotiateAuthenticationTight(); - } else if (secType == RfbProto.SecTypeUltra34) { - rfb.prepareDH(); - authType = RfbProto.AuthUltra; - } else { - authType = secType; - } - - switch (authType) { - case RfbProto.AuthNone: - Log.i(TAG, "No authentication needed"); - rfb.authenticateNone(); - break; - case RfbProto.AuthVNC: - Log.i(TAG, "VNC authentication needed"); - rfb.authenticateVNC(pw); - break; - case RfbProto.AuthUltra: - rfb.authenticateDH(us, pw); - break; - default: - throw new Exception("Unknown authentication scheme " + authType); - } - } - - void doProtocolInitialisation(int dx, int dy) throws IOException { - rfb.writeClientInit(); - rfb.readServerInit(); - - Log.i(TAG, "Desktop name is " + rfb.desktopName); - Log.i(TAG, "Desktop size is " + rfb.framebufferWidth + " x " + rfb.framebufferHeight); - - boolean useFull = false; - int capacity = BCFactory.getInstance().getBCActivityManager() - .getMemoryClass(Utils.getActivityManager(getContext())); - if (connection.getForceFull() == BitmapImplHint.AUTO) { - if (rfb.framebufferWidth * rfb.framebufferHeight * FullBufferBitmapData.CAPACITY_MULTIPLIER <= capacity - * 1024 * 1024) { - useFull = true; - } - } else { - useFull = (connection.getForceFull() == BitmapImplHint.FULL); - } - if (!useFull) { - bitmapData = new LargeBitmapData(rfb, this, dx, dy, capacity); - } else { - bitmapData = new FullBufferBitmapData(rfb, this, capacity); - } - mouseX = rfb.framebufferWidth / 2; - mouseY = rfb.framebufferHeight / 2; - - setPixelFormat(); - } - - private void setPixelFormat() throws IOException { - pendingColorModel.setPixelFormat(rfb); - bytesPerPixel = pendingColorModel.bpp(); - colorPalette = pendingColorModel.palette(); - colorModel = pendingColorModel; - pendingColorModel = null; - } - - public void setColorModel(COLORMODEL cm) { - // Only update if color model changes - if (colorModel == null || !colorModel.equals(cm)) { - pendingColorModel = cm; - } - } - - public boolean isColorModel(COLORMODEL cm) { - return (colorModel != null) && colorModel.equals(cm); - } - - private void mouseFollowPan() { - if (scaling.isAbleToPan()) { - int scrollx = absoluteXPosition; - int scrolly = absoluteYPosition; - int width = getVisibleWidth(); - int height = getVisibleHeight(); - // Log.i(TAG,"scrollx " + scrollx + " scrolly " + scrolly + - // " mouseX " + mouseX +" Y " + mouseY + " w " + width + " h " + - // height); - if (mouseX < scrollx || mouseX >= scrollx + width || mouseY < scrolly || mouseY >= scrolly + height) { - // Log.i(TAG,"warp to " + scrollx+width/2 + "," + scrolly + - // height/2); - warpMouse(scrollx + width / 2, scrolly + height / 2); - } - } - } - - public void processNormalProtocol(final Context context, ProgressDialog pd, final Runnable setModes) - throws Exception { - try { - bitmapData.writeFullUpdateRequest(false); - - handler.post(setModes); - connected(); - // - // main dispatch loop - // - while (maintainConnection) { - bitmapData.syncScroll(); - // Read message type from the server. - int msgType = rfb.readServerMessageType(); - bitmapData.doneWaiting(); - // Process the message depending on its type. - switch (msgType) { - case RfbProto.FramebufferUpdate: - rfb.readFramebufferUpdate(); - - for (int i = 0; i < rfb.updateNRects; i++) { - rfb.readFramebufferUpdateRectHdr(); - int rx = rfb.updateRectX, ry = rfb.updateRectY; - int rw = rfb.updateRectW, rh = rfb.updateRectH; - - if (rfb.updateRectEncoding == RfbProto.EncodingLastRect) { - Log.v(TAG, "rfb.EncodingLastRect"); - break; - } - - if (rfb.updateRectEncoding == RfbProto.EncodingNewFBSize) { - rfb.setFramebufferSize(rw, rh); - // - updateFramebufferSize(); - Log.v(TAG, "rfb.EncodingNewFBSize"); - reload(); - break; - } - - if (rfb.updateRectEncoding == RfbProto.EncodingXCursor - || rfb.updateRectEncoding == RfbProto.EncodingRichCursor) { - // - handleCursorShapeUpdate(rfb.updateRectEncoding, - // rx, - // ry, rw, rh); - Log.v(TAG, "rfb.EncodingCursor"); - continue; - - } - - if (rfb.updateRectEncoding == RfbProto.EncodingPointerPos) { - // This never actually happens - mouseX = rx; - mouseY = ry; - Log.v(TAG, "rfb.EncodingPointerPos"); - continue; - } - - rfb.startTiming(); - - switch (rfb.updateRectEncoding) { - case RfbProto.EncodingRaw: - handleRawRect(rx, ry, rw, rh); - break; - case RfbProto.EncodingCopyRect: - handleCopyRect(rx, ry, rw, rh); - Log.v(TAG, "CopyRect is Buggy!"); - break; - case RfbProto.EncodingRRE: - handleRRERect(rx, ry, rw, rh); - break; - case RfbProto.EncodingCoRRE: - handleCoRRERect(rx, ry, rw, rh); - break; - case RfbProto.EncodingHextile: - handleHextileRect(rx, ry, rw, rh); - break; - case RfbProto.EncodingZRLE: - handleZRLERect(rx, ry, rw, rh); - break; - case RfbProto.EncodingZlib: - handleZlibRect(rx, ry, rw, rh); - break; - default: - Log.e(TAG, "Unknown RFB rectangle encoding " + rfb.updateRectEncoding + " (0x" - + Integer.toHexString(rfb.updateRectEncoding) + ")"); - } - - rfb.stopTiming(); - - // Hide progress dialog - if (pd.isShowing()) { - pd.dismiss(); - } - } - - boolean fullUpdateNeeded = false; - - if (pendingColorModel != null) { - setPixelFormat(); - fullUpdateNeeded = true; - } - - setEncodings(true); - bitmapData.writeFullUpdateRequest(!fullUpdateNeeded); - - break; - - case RfbProto.SetColourMapEntries: - throw new Exception("Can't handle SetColourMapEntries message"); - - case RfbProto.Bell: - handler.post(new Runnable() { - public void run() { - UIUtils.toastShort(context, "VNC Beep"); - } - }); - break; - - case RfbProto.ServerCutText: - String s = rfb.readServerCutText(); - if (s != null && s.length() > 0) { - // TODO implement cut & paste - } - break; - - case RfbProto.TextChat: - // UltraVNC extension - String msg = rfb.readTextChatMsg(); - if (msg != null && msg.length() > 0) { - // TODO implement chat interface - } - break; - - default: - throw new Exception("Unknown RFB message type " + msgType); - } - } - } catch (Exception e) { - throw e; - } finally { - Log.v(TAG, "Closing VNC Connection"); - rfb.close(); - } - } - - /** - * Apply scroll offset and scaling to convert touch-space coordinates to the - * corresponding point on the full frame. - * - * @param e - * MotionEvent with the original, touch space coordinates. This - * event is altered in place. - * @return e -- The same event passed in, with the coordinates mapped - */ - MotionEvent changeTouchCoordinatesToFullFrame(MotionEvent e) { - // Log.v(TAG, String.format("tap at %f,%f", e.getX(), e.getY())); - float scale = getScale(); - - // Adjust coordinates for Android notification bar. - e.offsetLocation(0, -1f * getTop()); - - e.setLocation(absoluteXPosition + e.getX() / scale, absoluteYPosition + e.getY() / scale); - - return e; - } - - public void onDestroy() { - Log.v(TAG, "Cleaning up resources"); - if (bitmapData != null) { - bitmapData.dispose(); - } - bitmapData = null; - } - - /** - * Warp the mouse to x, y in the RFB coordinates - * - * @param x - * @param y - */ - void warpMouse(int x, int y) { - bitmapData.invalidateMousePosition(); - mouseX = x; - mouseY = y; - bitmapData.invalidateMousePosition(); - try { - rfb.writePointerEvent(x, y, 0, MOUSE_BUTTON_NONE); - } catch (IOException ioe) { - Log.w(TAG, ioe); - } - } - - /* - * f(x,s) is a function that returns the coordinate in screen/scroll space - * corresponding to the coordinate x in full-frame space with scaling s. - * - * This function returns the difference between f(x,s1) and f(x,s2) - * - * f(x,s) = (x - i/2) * s + ((i - w)/2)) * s = s (x - i/2 + i/2 + w/2) = s - * (x + w/2) - * - * - * f(x,s) = (x - ((i - w)/2)) * s - * - * @param oldscaling - * - * @param scaling - * - * @param imageDim - * - * @param windowDim - * - * @param offset - * - * @return - */ - /** - * Change to Canvas's scroll position to match the absoluteXPosition - */ - void scrollToAbsolute() { - float scale = getScale(); - try { - scrollTo((int) ((absoluteXPosition + ((float) getWidth() - getImageWidth()) / 2) * scale), - (int) ((absoluteYPosition + ((float) getHeight() - getImageHeight()) / 2) * scale)); - } catch (Exception e) { - Log.v("VNC", "Error: " + e.getMessage()); - } - } - - /** - * Make sure mouse is visible on displayable part of screen - */ - void panToMouse() { - if (!connection.getFollowMouse()) { - return; - } - - if (scaling != null && !scaling.isAbleToPan()) { - return; - } - - int x = mouseX; - int y = mouseY; - boolean panned = false; - int w = getVisibleWidth(); - int h = getVisibleHeight(); - int iw = getImageWidth(); - int ih = getImageHeight(); - - int newX = absoluteXPosition; - int newY = absoluteYPosition; - - if (x - newX >= w - 5) { - newX = x - w + 5; - if (newX + w > iw) { - newX = iw - w; - } - } else if (x < newX + 5) { - newX = x - 5; - if (newX < 0) { - newX = 0; - } - } - if (newX != absoluteXPosition) { - absoluteXPosition = newX; - panned = true; - } - if (y - newY >= h - 5) { - newY = y - h + 5; - if (newY + h > ih) { - newY = ih - h; - } - } else if (y < newY + 5) { - newY = y - 5; - if (newY < 0) { - newY = 0; - } - } - if (newY != absoluteYPosition) { - absoluteYPosition = newY; - panned = true; - } - if (panned) { - scrollToAbsolute(); - } - } - - /** - * Pan by a number of pixels (relative pan) - * - * @param dX - * @param dY - * @return True if the pan changed the view (did not move view out of - * bounds); false otherwise - */ - boolean pan(int dX, int dY) { - - double scale = getScale(); - - double sX = (double) dX / scale; - double sY = (double) dY / scale; - - if (absoluteXPosition + sX < 0) // dX = diff to 0 - { - sX = -absoluteXPosition; - } - if (absoluteYPosition + sY < 0) { - sY = -absoluteYPosition; - } - - // Prevent panning right or below desktop image - - Point outSize = new Point(); - int height = 0; - int width = 0; - VncCanvasActivity.display.getSize(outSize); - height = outSize.y; - width = outSize.x; - - if (absoluteXPosition + getVisibleWidth() + sX > getImageWidth()) { - sX = getImageWidth() - getVisibleWidth() - absoluteXPosition; - } - if (absoluteYPosition + getVisibleHeight() + sY > getImageHeight() + height * .6) { - sY = getImageHeight() + height * .6 - getVisibleHeight() - absoluteYPosition; - } - - absoluteXPosition += sX; - absoluteYPosition += sY; - if (sX != 0.0 || sY != 0.0) { - scrollToAbsolute(); - return true; - } - return false; - } - - /* - * (non-Javadoc) - * - * @see android.view.View#onScrollChanged(int, int, int, int) - */ - @Override - protected void onScrollChanged(int l, int t, int oldl, int oldt) { - super.onScrollChanged(l, t, oldl, oldt); - bitmapData.scrollChanged(absoluteXPosition, absoluteYPosition); - mouseFollowPan(); - } - - void handleRawRect(int x, int y, int w, int h) throws IOException { - handleRawRect(x, y, w, h, true); - } - - byte[] handleRawRectBuffer = new byte[128]; - - void handleRawRect(int x, int y, int w, int h, boolean paint) throws IOException { - boolean valid = bitmapData.validDraw(x, y, w, h); - int[] pixels = bitmapData.bitmapPixels; - if (bytesPerPixel == 1) { - // 1 byte per pixel. Use palette lookup table. - if (w > handleRawRectBuffer.length) { - handleRawRectBuffer = new byte[w]; - } - int i, offset; - for (int dy = y; dy < y + h; dy++) { - rfb.readFully(handleRawRectBuffer, 0, w); - if (!valid) { - continue; - } - offset = bitmapData.offset(x, dy); - for (i = 0; i < w; i++) { - pixels[offset + i] = colorPalette[0xFF & handleRawRectBuffer[i]]; - } - } - } else { - // 4 bytes per pixel (argb) 24-bit color - - final int l = w * 4; - if (l > handleRawRectBuffer.length) { - handleRawRectBuffer = new byte[l]; - } - int i, offset; - for (int dy = y; dy < y + h; dy++) { - rfb.readFully(handleRawRectBuffer, 0, l); - if (!valid) { - continue; - } - offset = bitmapData.offset(x, dy); - for (i = 0; i < w; i++) { - final int idx = i * 4; - pixels[offset + i] = // 0xFF << 24 | - (handleRawRectBuffer[idx + 2] & 0xff) << 16 | (handleRawRectBuffer[idx + 1] & 0xff) << 8 - | (handleRawRectBuffer[idx] & 0xff); - } - } - } - - if (!valid) { - return; - } - - bitmapData.updateBitmap(x, y, w, h); - - if (paint) { - reDraw(); - } - } - - private Runnable reDraw = new Runnable() { - public void run() { - if (showDesktopInfo) { - // Show a Toast with the desktop info on first frame draw. - showDesktopInfo = false; - showConnectionInfo(); - } - if (bitmapData != null) { - bitmapData.updateView(VncCanvas.this); - } - } - }; - - private void reDraw() { - if (repaintsEnabled) { - handler.post(reDraw); - } - } - - public void disableRepaints() { - repaintsEnabled = false; - } - - public void enableRepaints() { - repaintsEnabled = true; - } - - public void showConnectionInfo() { - String msg = rfb.desktopName; - int idx = rfb.desktopName.indexOf("("); - if (idx > -1) { - // Breakup actual desktop name from IP addresses for improved - // readability - String dn = rfb.desktopName.substring(0, idx).trim(); - String ip = rfb.desktopName.substring(idx).trim(); - msg = dn + "\n" + ip; - } - msg += "\n" + rfb.framebufferWidth + "x" + rfb.framebufferHeight; - String enc = getEncoding(); - // Encoding might not be set when we display this message - if (enc != null && !enc.equals("")) { - msg += ", " + getEncoding() + " encoding, " + colorModel.toString(); - } else { - msg += ", " + colorModel.toString(); - } - // Toast.makeText(getContext(), msg, Toast.LENGTH_LONG).show(); - } - - private String getEncoding() { - switch (preferredEncoding) { - case RfbProto.EncodingRaw: - return "RAW"; - case RfbProto.EncodingTight: - return "TIGHT"; - case RfbProto.EncodingCoRRE: - return "CoRRE"; - case RfbProto.EncodingHextile: - return "HEXTILE"; - case RfbProto.EncodingRRE: - return "RRE"; - case RfbProto.EncodingZlib: - return "ZLIB"; - case RfbProto.EncodingZRLE: - return "ZRLE"; - } - return ""; - } - - // Useful shortcuts for modifier masks. - final static int CTRL_MASK = KeyEvent.META_SYM_ON; - final static int SHIFT_MASK = KeyEvent.META_SHIFT_ON; - final static int META_MASK = 0; - final static int ALT_MASK = KeyEvent.META_ALT_ON; - private static final int MOUSE_BUTTON_NONE = 0; - static final int MOUSE_BUTTON_LEFT = 1; - static final int MOUSE_BUTTON_MIDDLE = 2; - static final int MOUSE_BUTTON_RIGHT = 4; - static final int MOUSE_BUTTON_SCROLL_UP = 8; - static final int MOUSE_BUTTON_SCROLL_DOWN = 16; - /** - * Current state of "mouse" buttons Alt meta means use second mouse button 0 - * = none 1 = default button 2 = second button - */ - private int pointerMask = MOUSE_BUTTON_NONE; - private boolean ALT_PRESSED = false; - private boolean CTRL_PRESSED = false; - - /** - * Convert a motion event to a format suitable for sending over the wire - * - * @param evt - * motion event; x and y must already have been converted from - * screen coordinates to remote frame buffer coordinates. - * cameraButton flag is interpreted as second mouse button - * @param downEvent - * True if "mouse button" (touch or trackball button) is down - * when this happens - * @return true if event was actually sent - */ - public boolean processPointerEvent(MotionEvent evt, boolean downEvent) { - return processPointerEvent(evt, downEvent, cameraButtonDown); - } - - /** - * Convert a motion event to a format suitable for sending over the wire - * - * @param evt - * motion event; x and y must already have been converted from - * screen coordinates to remote frame buffer coordinates. - * @param downEvent - * True if "mouse button" (touch or trackball button) is down - * when this happens - * @param useRightButton - * If true, event is interpreted as happening with right mouse - * button - * @return true if event was actually sent - */ - public boolean processPointerEvent(MotionEvent evt, boolean downEvent, boolean useRightButton) { - boolean useMiddleButton = false; - - if(evt.getButtonState() == MotionEvent.BUTTON_SECONDARY){ - useRightButton = true; - } else if(evt.getButtonState() == MotionEvent.BUTTON_TERTIARY){ - useMiddleButton = true; - } - - //XXX: not reliable with laptop trackpads -// if(Config.mouseMode == Config.MouseMode.External -// && MotionEvent.TOOL_TYPE_FINGER == evt.getToolType(0)) -// return true; -// -// if(Config.mouseMode == Config.MouseMode.Trackpad -// && MotionEvent.TOOL_TYPE_MOUSE == evt.getToolType(0)) -// return true; - - return processPointerEvent((int) evt.getX(), (int) evt.getY(), evt.getAction(), evt.getMetaState(), downEvent, - useRightButton, useMiddleButton, false); - } - - boolean processPointerEvent(int x, int y, int action, - int modifiers, boolean mouseIsDown, boolean useRightButton, - boolean useMiddleButton, boolean scrollUp) { - //Log.v("Limbo", "processPointerEvent: " + x + ", " + y + ", " - // + action + ", " + modifiers + ", " + mouseIsDown + ", " - // + useRightButton + ", " + useMiddleButton + ", " + scrollUp - //); - - if (rfb != null && rfb.inNormalProtocol) { - if (action == MotionEvent.ACTION_DOWN || (mouseIsDown && action == MotionEvent.ACTION_MOVE)) { - if (useRightButton) { - // Log.v("Limbo", "Right Button Down"); - pointerMask |= MOUSE_BUTTON_RIGHT; - } else if (useMiddleButton) { - pointerMask |= MOUSE_BUTTON_MIDDLE; - }else { - //Log.v("Limbo", "Left Button Down: x=" + x + ", y=" + y); - pointerMask |= MOUSE_BUTTON_LEFT; - } - } else if (action == MotionEvent.ACTION_SCROLL) { - // Log.v("Limbo", "Button Up"); - if(scrollUp) - pointerMask |= MOUSE_BUTTON_SCROLL_UP; - else - pointerMask |= MOUSE_BUTTON_SCROLL_DOWN; - } else if (action == MotionEvent.ACTION_UP) { - // Log.v("Limbo", "Button Up"); - //pointerMask = 0; - if (useRightButton) { - // Log.v("Limbo", "Right Button Down"); - pointerMask &= ~MOUSE_BUTTON_RIGHT; - } else if (useMiddleButton) { - pointerMask &= ~MOUSE_BUTTON_MIDDLE; - }else { - //Log.v("Limbo", "Left Button Down: x=" + x + ", y=" + y); - //XXX: Mouse middle click cannot always be detected so we - // reset all buttons (left, middle, click) to be safe - pointerMask = 0; - } - } - bitmapData.invalidateMousePosition(); - mouseX = x; - mouseY = y; - if (mouseX < 0) { - mouseX = 0; - } else if (mouseX >= rfb.framebufferWidth) { - mouseX = rfb.framebufferWidth - 1; - } - if (mouseY < 0) { - mouseY = 0; - } else if (mouseY >= rfb.framebufferHeight) { - mouseY = rfb.framebufferHeight - 1; - } - bitmapData.invalidateMousePosition(); - try { - rfb.writePointerEvent(mouseX, mouseY, modifiers, pointerMask); - if (action == MotionEvent.ACTION_SCROLL) { - rfb.writePointerEvent(mouseX, mouseY, 0, 0); - pointerMask = 0; - } - } catch (Exception e) { - e.printStackTrace(); - } - panToMouse(); - return true; - } - - return false; - } - - private int isSpecialKey(int key) { - switch (key) { - case '!': - return '1'; - case '@': - return '2'; - case '#': - return '3'; - case '$': - return '4'; - case '%': - return '5'; - case '^': - return '6'; - case '&': - return '7'; - case '*': - return '8'; - case '(': - return '9'; - case ')': - return '0'; - case '_': - return '-'; - case '+': - return '='; - case '~': - return '`'; - case '{': - return '['; - case '}': - return ']'; - case '|': - return '\\'; - case '\"': - return '\''; - case ':': - return ';'; - case '<': - return ','; - case '>': - return '.'; - case '?': - return '/'; - default: - return -1; - - } } - - /** - * Moves the scroll while the volume key is held down - * - * @author Michael A. MacDonald - */ - class MouseScrollRunnable implements Runnable { - - int delay = 100; - int scrollButton = 0; - - /* - * (non-Javadoc) - * - * @see java.lang.Runnable#run() - */ - @Override - public void run() { - try { - rfb.writePointerEvent(mouseX, mouseY, 0, scrollButton); - rfb.writePointerEvent(mouseX, mouseY, 0, 0); - - handler.postDelayed(this, delay); - } catch (IOException ioe) { - } - } - } - - public synchronized boolean processLocalKeyEvent(int keyCode, KeyEvent evt) { - if (keyCode == KeyEvent.KEYCODE_MENU) // Ignore menu key - { - return true; - } - if (keyCode == KeyEvent.KEYCODE_CAMERA) { - cameraButtonDown = (evt.getAction() != KeyEvent.ACTION_UP); - } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) { - int mouseChange = keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ? MOUSE_BUTTON_SCROLL_DOWN - : MOUSE_BUTTON_SCROLL_UP; - if (evt.getAction() == KeyEvent.ACTION_DOWN) { - // If not auto-repeat - if (scrollRunnable.scrollButton != mouseChange) { - pointerMask |= mouseChange; - scrollRunnable.scrollButton = mouseChange; - handler.postDelayed(scrollRunnable, 200); - } - } else { - handler.removeCallbacks(scrollRunnable); - scrollRunnable.scrollButton = 0; - pointerMask &= ~mouseChange; - } - try { - rfb.writePointerEvent(mouseX, mouseY, evt.getMetaState(), pointerMask); - } catch (IOException ioe) { - // TODO: do something with exception - } - return true; - } - if (rfb != null && rfb.inNormalProtocol) { - boolean down = (evt.getAction() == KeyEvent.ACTION_DOWN); - int key; - int metaState = evt.getMetaState(); - metaState = 0; - // Log.v("Key Pressed", keyCode + ", metaState = " + metaState); - - switch (keyCode) { - case KeyEvent.KEYCODE_BACK: - // key = 0xff1b; - return false; - // break; - case KeyEvent.KEYCODE_DPAD_LEFT: - key = 0xff51; - break; - case KeyEvent.KEYCODE_DPAD_UP: - key = 0xff52; - break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - key = 0xff53; - break; - case KeyEvent.KEYCODE_DPAD_DOWN: - key = 0xff54; - break; - case KeyEvent.KEYCODE_DEL: - key = 0xff08; - break; - case KeyEvent.KEYCODE_FORWARD_DEL: - key = MetaKeyBean.keysByKeyCode.get(KeyEvent.KEYCODE_DEL).keySym; - break; - case KeyEvent.KEYCODE_ALT_LEFT: - case KeyEvent.KEYCODE_ALT_RIGHT: - this.ALT_PRESSED = true; - return true; - case KeyEvent.KEYCODE_MOVE_HOME: - key = 0xFF50; - break; - case KeyEvent.KEYCODE_INSERT: - key = 0xFF63; - break; - case KeyEvent.KEYCODE_MOVE_END: - key = 0xFF57; - break; - case KeyEvent.KEYCODE_PAGE_DOWN: - key = 0xFF56; - break; - case KeyEvent.KEYCODE_PAGE_UP: - key = 0xFF55; - break; - case KeyEvent.KEYCODE_ENTER: - key = 0xff0d; - break; - case KeyEvent.KEYCODE_DPAD_CENTER: - key = 0xff0d; - break; - case KeyEvent.KEYCODE_TAB: - key = 0xFF09; - break; - case 111: // ESCAPE - key = 0xff1b; - break; - default: - key = evt.getUnicodeChar(); - //Log.v("unicode", "Unicode Char for " + evt.getKeyCode() + " is " + key); - - //ΧΧΧ: Workaround for some chars not recognized by QEMU - int specialKey = isSpecialKey(key); - if (specialKey != -1) { - key = specialKey; - metaState = metaState | VncCanvas.SHIFT_MASK; - } else if (keyCode >= 131 && keyCode <= 142) { - // Function Key pressed - key = 0xFFBE + keyCode - 131; - } else if (key == 0){ - //Key is a meta combination or unknown - key = evt.getUnicodeChar(0); - } - - break; - } - - if ((evt.getMetaState() & KeyEvent.META_CTRL_ON) == KeyEvent.META_CTRL_ON) { - // Log.v("meta", "setting ctrl mask"); - metaState = metaState | VncCanvas.CTRL_MASK; - } - - if ((evt.getMetaState() & KeyEvent.META_ALT_ON) == KeyEvent.META_ALT_ON || this.ALT_PRESSED) { - // Log.v("meta", "setting alt mask"); - metaState = metaState | VncCanvas.ALT_MASK; - } - - if ((evt.getMetaState() & KeyEvent.META_SHIFT_ON) == KeyEvent.META_SHIFT_ON) { - // Log.v("meta", "setting shift mask"); - metaState = metaState | VncCanvas.SHIFT_MASK; - } - try { - if (afterMenu) { - afterMenu = false; - if (!down && key != lastKeyDown) { - return true; - } - } - if (down) { - lastKeyDown = key; - } - - rfb.writeKeyEvent(key, metaState, down); - this.ALT_PRESSED = false; - } catch (Exception e) { - e.printStackTrace(); - } - return true; - } - return false; - } - - public void closeConnection() { - maintainConnection = false; - } - - public void sendMetaKey1(int key, int flags) { - try { - rfb.writeKeyEvent(key, flags, true); - rfb.writeKeyEvent(key, flags, false); - } catch (IOException ioe) { - ioe.printStackTrace(); - } - - } - - public void sendText(String s) { - int l = s.length(); - for (int i = 0; i < l; i++) { - char c = s.charAt(i); - int meta = 0; - int keysym = c; - if (Character.isISOControl(c)) { - if (c == '\n') { - keysym = MetaKeyBean.keysByKeyCode.get(KeyEvent.KEYCODE_ENTER).keySym; - } else { - continue; - } - } - try { - rfb.writeKeyEvent(keysym, meta, true); - rfb.writeKeyEvent(keysym, meta, false); - } catch (IOException ioe) { - // TODO: log this - } - } - } - - void sendMetaKey(MetaKeyBean meta) { - if (meta.isMouseClick()) { - try { - rfb.writePointerEvent(mouseX, mouseY, meta.getMetaFlags(), meta.getMouseButtons()); - rfb.writePointerEvent(mouseX, mouseY, meta.getMetaFlags(), 0); - } catch (IOException ioe) { - ioe.printStackTrace(); - } - } else { - try { - rfb.writeKeyEvent(meta.getKeySym(), meta.getMetaFlags(), true); - rfb.writeKeyEvent(meta.getKeySym(), meta.getMetaFlags(), false); - } catch (IOException ioe) { - ioe.printStackTrace(); - } - } - } - - float getScale() { - if (scaling == null) { - return 1; - } - return scaling.getScale(); - } - - public int getVisibleWidth() { - return (int) ((double) getWidth() / getScale() + 0.5); - } - - public int getVisibleHeight() { - return (int) ((double) getHeight() / getScale() + 0.5); - } - - public int getImageWidth() { - return bitmapData.framebufferwidth; - } - - public int getImageHeight() { - return bitmapData.framebufferheight; - } - - public int getCenteredXOffset() { - int xoffset = (bitmapData.framebufferwidth - getWidth()) / 2; - return xoffset; - } - - public int getCenteredYOffset() { - int yoffset = (bitmapData.framebufferheight - getHeight()) / 2; - return yoffset; - } - - /** - * Additional Encodings - * - */ - private void setEncodings(boolean autoSelectOnly) { - if (rfb == null || !rfb.inNormalProtocol) { - return; - } - - if (preferredEncoding == -1) { - // Preferred format is ZRLE - preferredEncoding = RfbProto.EncodingZRLE; - } else { - // Auto encoder selection is not enabled. - if (autoSelectOnly) { - return; - } - } - - int[] encodings = new int[20]; - int nEncodings = 0; - - encodings[nEncodings++] = preferredEncoding; - if (useCopyRect) { - encodings[nEncodings++] = RfbProto.EncodingCopyRect; - } - // if (preferredEncoding != RfbProto.EncodingTight) - // encodings[nEncodings++] = RfbProto.EncodingTight; - if (preferredEncoding != RfbProto.EncodingZRLE) { - encodings[nEncodings++] = RfbProto.EncodingZRLE; - } - if (preferredEncoding != RfbProto.EncodingHextile) { - encodings[nEncodings++] = RfbProto.EncodingHextile; - } - if (preferredEncoding != RfbProto.EncodingZlib) { - encodings[nEncodings++] = RfbProto.EncodingZlib; - } - if (preferredEncoding != RfbProto.EncodingCoRRE) { - encodings[nEncodings++] = RfbProto.EncodingCoRRE; - } - if (preferredEncoding != RfbProto.EncodingRRE) { - encodings[nEncodings++] = RfbProto.EncodingRRE; - } - - if (compressLevel >= 0 && compressLevel <= 9) { - encodings[nEncodings++] = RfbProto.EncodingCompressLevel0 + compressLevel; - } - if (jpegQuality >= 0 && jpegQuality <= 9) { - encodings[nEncodings++] = RfbProto.EncodingQualityLevel0 + jpegQuality; - } - - if (requestCursorUpdates) { - encodings[nEncodings++] = RfbProto.EncodingXCursor; - encodings[nEncodings++] = RfbProto.EncodingRichCursor; - if (!ignoreCursorUpdates) { - encodings[nEncodings++] = RfbProto.EncodingPointerPos; - } - } - - encodings[nEncodings++] = RfbProto.EncodingLastRect; - encodings[nEncodings++] = RfbProto.EncodingNewFBSize; - - boolean encodingsWereChanged = false; - if (nEncodings != nEncodingsSaved) { - encodingsWereChanged = true; - } else { - for (int i = 0; i < nEncodings; i++) { - if (encodings[i] != encodingsSaved[i]) { - encodingsWereChanged = true; - break; - } - } - } - - if (encodingsWereChanged) { - try { - rfb.writeSetEncodings(encodings, nEncodings); - } catch (Exception e) { - e.printStackTrace(); - } - encodingsSaved = encodings; - nEncodingsSaved = nEncodings; - } - } - - // - // Handle a CopyRect rectangle. - // - final Paint handleCopyRectPaint = new Paint(); - - private void handleCopyRect(int x, int y, int w, int h) throws IOException { - - /** - * This does not work properly yet. - */ - rfb.readCopyRect(); - if (!bitmapData.validDraw(x, y, w, h)) { - return; - } - // Source Coordinates - int leftSrc = rfb.copyRectSrcX; - int topSrc = rfb.copyRectSrcY; - int rightSrc = topSrc + w; - int bottomSrc = topSrc + h; - - // Change - int dx = x - rfb.copyRectSrcX; - int dy = y - rfb.copyRectSrcY; - - // Destination Coordinates - int leftDest = leftSrc + dx; - int topDest = topSrc + dy; - int rightDest = rightSrc + dx; - int bottomDest = bottomSrc + dy; - - bitmapData.copyRect(new Rect(leftSrc, topSrc, rightSrc, bottomSrc), - new Rect(leftDest, topDest, rightDest, bottomDest), handleCopyRectPaint); - - reDraw(); - } - - byte[] bg_buf = new byte[4]; - byte[] rre_buf = new byte[128]; - - // - // Handle an RRE-encoded rectangle. - // - - private void handleRRERect(int x, int y, int w, int h) throws IOException { - boolean valid = bitmapData.validDraw(x, y, w, h); - int nSubrects = rfb.is.readInt(); - - rfb.readFully(bg_buf, 0, bytesPerPixel); - int pixel; - if (bytesPerPixel == 1) { - pixel = colorPalette[0xFF & bg_buf[0]]; - } else { - pixel = Color.rgb(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, bg_buf[0] & 0xFF); - } - handleRREPaint.setColor(pixel); - if (valid) { - bitmapData.drawRect(x, y, w, h, handleRREPaint); - } - - int len = nSubrects * (bytesPerPixel + 8); - if (len > rre_buf.length) { - rre_buf = new byte[len]; - } - - rfb.readFully(rre_buf, 0, len); - if (!valid) { - return; - } - - int sx, sy, sw, sh; - - int i = 0; - for (int j = 0; j < nSubrects; j++) { - if (bytesPerPixel == 1) { - pixel = colorPalette[0xFF & rre_buf[i++]]; - } else { - pixel = Color.rgb(rre_buf[i + 2] & 0xFF, rre_buf[i + 1] & 0xFF, rre_buf[i] & 0xFF); - i += 4; - } - sx = x + ((rre_buf[i] & 0xff) << 8) + (rre_buf[i + 1] & 0xff); - i += 2; - sy = y + ((rre_buf[i] & 0xff) << 8) + (rre_buf[i + 1] & 0xff); - i += 2; - sw = ((rre_buf[i] & 0xff) << 8) + (rre_buf[i + 1] & 0xff); - i += 2; - sh = ((rre_buf[i] & 0xff) << 8) + (rre_buf[i + 1] & 0xff); - i += 2; - - handleRREPaint.setColor(pixel); - bitmapData.drawRect(sx, sy, sw, sh, handleRREPaint); - } - - reDraw(); - } - - // - // Handle a CoRRE-encoded rectangle. - // - private void handleCoRRERect(int x, int y, int w, int h) throws IOException { - boolean valid = bitmapData.validDraw(x, y, w, h); - int nSubrects = rfb.is.readInt(); - - rfb.readFully(bg_buf, 0, bytesPerPixel); - int pixel; - if (bytesPerPixel == 1) { - pixel = colorPalette[0xFF & bg_buf[0]]; - } else { - pixel = Color.rgb(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, bg_buf[0] & 0xFF); - } - handleRREPaint.setColor(pixel); - if (valid) { - bitmapData.drawRect(x, y, w, h, handleRREPaint); - } - - int len = nSubrects * (bytesPerPixel + 8); - if (len > rre_buf.length) { - rre_buf = new byte[len]; - } - - rfb.readFully(rre_buf, 0, len); - if (!valid) { - return; - } - - int sx, sy, sw, sh; - int i = 0; - - for (int j = 0; j < nSubrects; j++) { - if (bytesPerPixel == 1) { - pixel = colorPalette[0xFF & rre_buf[i++]]; - } else { - pixel = Color.rgb(rre_buf[i + 2] & 0xFF, rre_buf[i + 1] & 0xFF, rre_buf[i] & 0xFF); - i += 4; - } - sx = x + (rre_buf[i++] & 0xFF); - sy = y + (rre_buf[i++] & 0xFF); - sw = rre_buf[i++] & 0xFF; - sh = rre_buf[i++] & 0xFF; - - handleRREPaint.setColor(pixel); - bitmapData.drawRect(sx, sy, sw, sh, handleRREPaint); - } - - reDraw(); - } - - // - // Handle a Hextile-encoded rectangle. - // - // These colors should be kept between handleHextileSubrect() calls. - private int hextile_bg, hextile_fg; - - private void handleHextileRect(int x, int y, int w, int h) throws IOException { - - hextile_bg = Color.BLACK; - hextile_fg = Color.BLACK; - - for (int ty = y; ty < y + h; ty += 16) { - int th = 16; - if (y + h - ty < 16) { - th = y + h - ty; - } - - for (int tx = x; tx < x + w; tx += 16) { - int tw = 16; - if (x + w - tx < 16) { - tw = x + w - tx; - } - - handleHextileSubrect(tx, ty, tw, th); - } - - // Finished with a row of tiles, now let's show it. - reDraw(); - } - } - - // - // Handle one tile in the Hextile-encoded data. - // - Paint handleHextileSubrectPaint = new Paint(); - byte[] backgroundColorBuffer = new byte[4]; - - private void handleHextileSubrect(int tx, int ty, int tw, int th) throws IOException { - - int subencoding = rfb.is.readUnsignedByte(); - - // Is it a raw-encoded sub-rectangle? - if ((subencoding & RfbProto.HextileRaw) != 0) { - handleRawRect(tx, ty, tw, th, false); - return; - } - - boolean valid = bitmapData.validDraw(tx, ty, tw, th); - // Read and draw the background if specified. - if (bytesPerPixel > backgroundColorBuffer.length) { - throw new RuntimeException("impossible colordepth"); - } - if ((subencoding & RfbProto.HextileBackgroundSpecified) != 0) { - rfb.readFully(backgroundColorBuffer, 0, bytesPerPixel); - if (bytesPerPixel == 1) { - hextile_bg = colorPalette[0xFF & backgroundColorBuffer[0]]; - } else { - hextile_bg = Color.rgb(backgroundColorBuffer[2] & 0xFF, backgroundColorBuffer[1] & 0xFF, - backgroundColorBuffer[0] & 0xFF); - } - } - handleHextileSubrectPaint.setColor(hextile_bg); - handleHextileSubrectPaint.setStyle(Paint.Style.FILL); - if (valid) { - bitmapData.drawRect(tx, ty, tw, th, handleHextileSubrectPaint); - } - - // Read the foreground color if specified. - if ((subencoding & RfbProto.HextileForegroundSpecified) != 0) { - rfb.readFully(backgroundColorBuffer, 0, bytesPerPixel); - if (bytesPerPixel == 1) { - hextile_fg = colorPalette[0xFF & backgroundColorBuffer[0]]; - } else { - hextile_fg = Color.rgb(backgroundColorBuffer[2] & 0xFF, backgroundColorBuffer[1] & 0xFF, - backgroundColorBuffer[0] & 0xFF); - } - } - - // Done with this tile if there is no sub-rectangles. - if ((subencoding & RfbProto.HextileAnySubrects) == 0) { - return; - } - - int nSubrects = rfb.is.readUnsignedByte(); - int bufsize = nSubrects * 2; - if ((subencoding & RfbProto.HextileSubrectsColoured) != 0) { - bufsize += nSubrects * bytesPerPixel; - } - if (rre_buf.length < bufsize) { - rre_buf = new byte[bufsize]; - } - rfb.readFully(rre_buf, 0, bufsize); - - int b1, b2, sx, sy, sw, sh; - int i = 0; - if ((subencoding & RfbProto.HextileSubrectsColoured) == 0) { - - // Sub-rectangles are all of the same color. - handleHextileSubrectPaint.setColor(hextile_fg); - for (int j = 0; j < nSubrects; j++) { - b1 = rre_buf[i++] & 0xFF; - b2 = rre_buf[i++] & 0xFF; - sx = tx + (b1 >> 4); - sy = ty + (b1 & 0xf); - sw = (b2 >> 4) + 1; - sh = (b2 & 0xf) + 1; - if (valid) { - bitmapData.drawRect(sx, sy, sw, sh, handleHextileSubrectPaint); - } - } - } else if (bytesPerPixel == 1) { - - // BGR233 (8-bit color) version for colored sub-rectangles. - for (int j = 0; j < nSubrects; j++) { - hextile_fg = colorPalette[0xFF & rre_buf[i++]]; - b1 = rre_buf[i++] & 0xFF; - b2 = rre_buf[i++] & 0xFF; - sx = tx + (b1 >> 4); - sy = ty + (b1 & 0xf); - sw = (b2 >> 4) + 1; - sh = (b2 & 0xf) + 1; - handleHextileSubrectPaint.setColor(hextile_fg); - if (valid) { - bitmapData.drawRect(sx, sy, sw, sh, handleHextileSubrectPaint); - } - } - - } else { - - // Full-color (24-bit) version for colored sub-rectangles. - for (int j = 0; j < nSubrects; j++) { - hextile_fg = Color.rgb(rre_buf[i + 2] & 0xFF, rre_buf[i + 1] & 0xFF, rre_buf[i] & 0xFF); - i += 4; - b1 = rre_buf[i++] & 0xFF; - b2 = rre_buf[i++] & 0xFF; - sx = tx + (b1 >> 4); - sy = ty + (b1 & 0xf); - sw = (b2 >> 4) + 1; - sh = (b2 & 0xf) + 1; - handleHextileSubrectPaint.setColor(hextile_fg); - if (valid) { - bitmapData.drawRect(sx, sy, sw, sh, handleHextileSubrectPaint); - } - } - - } - } - - // - // Handle a ZRLE-encoded rectangle. - // - Paint handleZRLERectPaint = new Paint(); - int[] handleZRLERectPalette = new int[128]; - - private void handleZRLERect(int x, int y, int w, int h) throws Exception { - - if (zrleInStream == null) { - zrleInStream = new ZlibInStream(); - } - - int nBytes = rfb.is.readInt(); - if (nBytes > 64 * 1024 * 1024) { - throw new Exception("ZRLE decoder: illegal compressed data size"); - } - - if (zrleBuf == null || zrleBuf.length < nBytes) { - zrleBuf = new byte[nBytes + 4096]; - } - - rfb.readFully(zrleBuf, 0, nBytes); - - zrleInStream.setUnderlying(new MemInStream(zrleBuf, 0, nBytes), nBytes); - - boolean valid = bitmapData.validDraw(x, y, w, h); - - for (int ty = y; ty < y + h; ty += 64) { - - int th = Math.min(y + h - ty, 64); - - for (int tx = x; tx < x + w; tx += 64) { - - int tw = Math.min(x + w - tx, 64); - - int mode = zrleInStream.readU8(); - boolean rle = (mode & 128) != 0; - int palSize = mode & 127; - - readZrlePalette(handleZRLERectPalette, palSize); - - if (palSize == 1) { - int pix = handleZRLERectPalette[0]; - int c = (bytesPerPixel == 1) ? colorPalette[0xFF & pix] : (0xFF000000 | pix); - handleZRLERectPaint.setColor(c); - handleZRLERectPaint.setStyle(Paint.Style.FILL); - if (valid) { - bitmapData.drawRect(tx, ty, tw, th, handleZRLERectPaint); - } - continue; - } - - if (!rle) { - if (palSize == 0) { - readZrleRawPixels(tw, th); - } else { - readZrlePackedPixels(tw, th, handleZRLERectPalette, palSize); - } - } else { - if (palSize == 0) { - readZrlePlainRLEPixels(tw, th); - } else { - readZrlePackedRLEPixels(tw, th, handleZRLERectPalette); - } - } - if (valid) { - handleUpdatedZrleTile(tx, ty, tw, th); - } - } - } - - zrleInStream.reset(); - - reDraw(); - } - - // - // Handle a Zlib-encoded rectangle. - // - byte[] handleZlibRectBuffer = new byte[128]; - - private void handleZlibRect(int x, int y, int w, int h) throws Exception { - boolean valid = bitmapData.validDraw(x, y, w, h); - int nBytes = rfb.is.readInt(); - - if (zlibBuf == null || zlibBuf.length < nBytes) { - zlibBuf = new byte[nBytes * 2]; - } - - rfb.readFully(zlibBuf, 0, nBytes); - - if (zlibInflater == null) { - zlibInflater = new Inflater(); - } - zlibInflater.setInput(zlibBuf, 0, nBytes); - - int[] pixels = bitmapData.bitmapPixels; - - if (bytesPerPixel == 1) { - // 1 byte per pixel. Use palette lookup table. - if (w > handleZlibRectBuffer.length) { - handleZlibRectBuffer = new byte[w]; - } - int i, offset; - for (int dy = y; dy < y + h; dy++) { - zlibInflater.inflate(handleZlibRectBuffer, 0, w); - if (!valid) { - continue; - } - offset = bitmapData.offset(x, dy); - for (i = 0; i < w; i++) { - pixels[offset + i] = colorPalette[0xFF & handleZlibRectBuffer[i]]; - } - } - } else { - // 24-bit color (ARGB) 4 bytes per pixel. - final int l = w * 4; - if (l > handleZlibRectBuffer.length) { - handleZlibRectBuffer = new byte[l]; - } - int i, offset; - for (int dy = y; dy < y + h; dy++) { - zlibInflater.inflate(handleZlibRectBuffer, 0, l); - if (!valid) { - continue; - } - offset = bitmapData.offset(x, dy); - for (i = 0; i < w; i++) { - final int idx = i * 4; - pixels[offset + i] = (handleZlibRectBuffer[idx + 2] & 0xFF) << 16 - | (handleZlibRectBuffer[idx + 1] & 0xFF) << 8 | (handleZlibRectBuffer[idx] & 0xFF); - } - } - } - if (!valid) { - return; - } - bitmapData.updateBitmap(x, y, w, h); - - reDraw(); - } - - private int readPixel(InStream is) throws Exception { - int pix; - if (bytesPerPixel == 1) { - pix = is.readU8(); - } else { - int p1 = is.readU8(); - int p2 = is.readU8(); - int p3 = is.readU8(); - pix = (p3 & 0xFF) << 16 | (p2 & 0xFF) << 8 | (p1 & 0xFF); - } - return pix; - } - - byte[] readPixelsBuffer = new byte[128]; - - private void readPixels(InStream is, int[] dst, int count) throws Exception { - if (bytesPerPixel == 1) { - if (count > readPixelsBuffer.length) { - readPixelsBuffer = new byte[count]; - } - is.readBytes(readPixelsBuffer, 0, count); - for (int i = 0; i < count; i++) { - dst[i] = (int) readPixelsBuffer[i] & 0xFF; - } - } else { - final int l = count * 3; - if (l > readPixelsBuffer.length) { - readPixelsBuffer = new byte[l]; - } - is.readBytes(readPixelsBuffer, 0, l); - for (int i = 0; i < count; i++) { - final int idx = i * 3; - dst[i] = ((readPixelsBuffer[idx + 2] & 0xFF) << 16 | (readPixelsBuffer[idx + 1] & 0xFF) << 8 - | (readPixelsBuffer[idx] & 0xFF)); - } - } - } - - private void readZrlePalette(int[] palette, int palSize) throws Exception { - readPixels(zrleInStream, palette, palSize); - } - - private void readZrleRawPixels(int tw, int th) throws Exception { - int len = tw * th; - if (zrleTilePixels == null || len > zrleTilePixels.length) { - zrleTilePixels = new int[len]; - } - readPixels(zrleInStream, zrleTilePixels, tw * th); // / - } - - private void readZrlePackedPixels(int tw, int th, int[] palette, int palSize) throws Exception { - - int bppp = ((palSize > 16) ? 8 : ((palSize > 4) ? 4 : ((palSize > 2) ? 2 : 1))); - int ptr = 0; - int len = tw * th; - if (zrleTilePixels == null || len > zrleTilePixels.length) { - zrleTilePixels = new int[len]; - } - - for (int i = 0; i < th; i++) { - int eol = ptr + tw; - int b = 0; - int nbits = 0; - - while (ptr < eol) { - if (nbits == 0) { - b = zrleInStream.readU8(); - nbits = 8; - } - nbits -= bppp; - int index = (b >> nbits) & ((1 << bppp) - 1) & 127; - if (bytesPerPixel == 1) { - if (index >= colorPalette.length) { - Log.e(TAG, "zrlePlainRLEPixels palette lookup out of bounds " + index + " (0x" - + Integer.toHexString(index) + ")"); - } - zrleTilePixels[ptr++] = colorPalette[0xFF & palette[index]]; - } else { - zrleTilePixels[ptr++] = palette[index]; - } - } - } - } - - private void readZrlePlainRLEPixels(int tw, int th) throws Exception { - int ptr = 0; - int end = ptr + tw * th; - if (zrleTilePixels == null || end > zrleTilePixels.length) { - zrleTilePixels = new int[end]; - } - while (ptr < end) { - int pix = readPixel(zrleInStream); - int len = 1; - int b; - do { - b = zrleInStream.readU8(); - len += b; - } while (b == 255); - - if (!(len <= end - ptr)) { - throw new Exception("ZRLE decoder: assertion failed" + " (len <= end-ptr)"); - } - - if (bytesPerPixel == 1) { - while (len-- > 0) { - zrleTilePixels[ptr++] = colorPalette[0xFF & pix]; - } - } else { - while (len-- > 0) { - zrleTilePixels[ptr++] = pix; - } - } - } - } - - private void readZrlePackedRLEPixels(int tw, int th, int[] palette) throws Exception { - - int ptr = 0; - int end = ptr + tw * th; - if (zrleTilePixels == null || end > zrleTilePixels.length) { - zrleTilePixels = new int[end]; - } - while (ptr < end) { - int index = zrleInStream.readU8(); - int len = 1; - if ((index & 128) != 0) { - int b; - do { - b = zrleInStream.readU8(); - len += b; - } while (b == 255); - - if (!(len <= end - ptr)) { - throw new Exception("ZRLE decoder: assertion failed" + " (len <= end - ptr)"); - } - } - - index &= 127; - int pix = palette[index]; - - if (bytesPerPixel == 1) { - while (len-- > 0) { - zrleTilePixels[ptr++] = colorPalette[0xFF & pix]; - } - } else { - while (len-- > 0) { - zrleTilePixels[ptr++] = pix; - } - } - } - } - - // - // Copy pixels from zrleTilePixels8 or zrleTilePixels24, then update. - // - private void handleUpdatedZrleTile(int x, int y, int w, int h) { - int offsetSrc = 0; - int[] destPixels = bitmapData.bitmapPixels; - for (int j = 0; j < h; j++) { - System.arraycopy(zrleTilePixels, offsetSrc, destPixels, bitmapData.offset(x, y + j), w); - offsetSrc += w; - } - - bitmapData.updateBitmap(x, y, w, h); - } - - public void connected() { - VncCanvasActivity activity = (VncCanvasActivity) VncCanvas.this.getContext(); - activity.onConnected(); - - } - - - class VNCOnTouchListener implements View.OnTouchListener { - - @Override - public boolean onTouch(View v, MotionEvent event) { - // TODO Auto-generated method stub - //Log.i("VNCOnTouchListener", "onTouch"); - if(Config.mouseMode == Config.MouseMode.Trackpad) { - return false; - } - return processPointerEvent(event, event.getAction() == MotionEvent.ACTION_DOWN); - } - - - } - - class VNCGenericMotionListener_API12 implements View.OnGenericMotionListener { - private VncCanvas mSurface; - - // Generic Motion (mouse hover, joystick...) events go here - @Override - public boolean onGenericMotion(View v, MotionEvent event) { - float x, y; - int action; - - switch (event.getSource()) { - case InputDevice.SOURCE_JOYSTICK: - case InputDevice.SOURCE_GAMEPAD: - case InputDevice.SOURCE_DPAD: - return true; - case InputDevice.SOURCE_MOUSE: - if(Config.mouseMode == Config.MouseMode.Trackpad) - break; - - action = event.getActionMasked(); - //Log.d("SDL", "onGenericMotion, action = " + action + "," + event.getX() + ", " + event.getY()); - switch (action) { - case MotionEvent.ACTION_SCROLL: - x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0); - y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0); - //Log.d("SDL", "Mouse Scroll: " +event.getX() + ":" + event.getY() + " => " + x + "," + y); - - //TODO: - //SDLActivity.onSDLNativeMouse(0, action, x, y); - //processPointerEvent(event,false); - - // Log.v("Limbo", "Button Up"); - boolean scrollUp=false; - if (y > 0) - scrollUp = true; - else if (y < 0) - scrollUp = false; - - return processPointerEvent((int) event.getX(), (int) event.getY(), event.getAction(), event.getMetaState(), false, - false, false, scrollUp); - //return true; - - case MotionEvent.ACTION_HOVER_MOVE: - if(Config.processMouseHistoricalEvents) { - final int historySize = event.getHistorySize(); - for (int h = 0; h < historySize; h++) { - float ex = event.getHistoricalX(h); - float ey = event.getHistoricalY(h); - float ep = event.getHistoricalPressure(h); - processHoverMouse(event, ex, ey, ep, action); - } - } - - float ex = event.getX(); - float ey = event.getY(); - float ep = event.getPressure(); - processHoverMouse(event, ex, ey, ep, action); - return true; - - case MotionEvent.ACTION_UP: - - default: - break; - } - break; - - default: - break; - } - - // Event was not managed - return false; - } - - private void processHoverMouse(MotionEvent event, float x,float y,float p, int action) { - - //Log.d("VncCanvas", "Mouse Hover: " + x + "," + y); - - if(Config.mouseMode == Config.MouseMode.External) { - -// float x_margin = (SDLActivity.width - LimboSDLActivity.vm_width * LimboSDLActivity.height / (float) LimboSDLActivity.vm_height) / 2; -// if (x < x_margin) { -// return; -// } else if (x > SDLActivity.width - x_margin) { -// return; -// } - - //TODO: - //SDLActivity.onSDLNativeMouse(0, action, x, y); - processPointerEvent(event, false, false); - } -// else if (Config.mouseMode == Config.MouseMode.External_Alt){ -// processHoverMouseAlt(x, y, p, action); -// } - } - - } -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/VncCanvasActivity.java b/limbo-android-lib/src/main/java/android/androidVNC/VncCanvasActivity.java deleted file mode 100644 index c96c1088e..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/VncCanvasActivity.java +++ /dev/null @@ -1,1854 +0,0 @@ -/* - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ -// -// CanvasView is the Activity for showing VNC Desktop. -// -package android.androidVNC; - -import android.app.Activity; -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnDismissListener; -import android.content.Intent; -import android.content.res.Configuration; -import android.graphics.PointF; -import android.net.Uri; -import android.os.Bundle; -import android.os.SystemClock; -import android.util.Log; -import android.view.Display; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.view.inputmethod.InputMethodManager; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ArrayAdapter; -import android.widget.ListView; -import android.widget.Toast; -import android.widget.ZoomControls; - -import com.antlersoft.android.bc.BCFactory; -import com.limbo.emu.lib.R; -import com.max2idea.android.limbo.main.Config; -import com.max2idea.android.limbo.main.LimboActivity; -import com.max2idea.android.limbo.utils.UIUtils; - -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import androidx.appcompat.app.AppCompatActivity; - -public class VncCanvasActivity extends AppCompatActivity { - - static Display display = null; - public static Activity activity; - - /** - * @author Michael A. MacDonald - */ - class ZoomInputHandler extends AbstractGestureInputHandler { - - /** - * In drag mode (entered with long press) you process mouse events - * without sending them through the gesture detector - */ - private boolean dragMode; - /** - * Key handler delegate that handles DPad-based mouse motion - */ - private DPadMouseKeyHandler keyHandler; - - /** - * @param c - */ - ZoomInputHandler() { - super(VncCanvasActivity.this); - keyHandler = new DPadMouseKeyHandler(VncCanvasActivity.this, vncCanvas.handler); - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#getHandlerDescription() - */ - @Override - public CharSequence getHandlerDescription() { - return getResources().getString(R.string.input_mode_touch_pan_zoom_mouse); - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#getName() - */ - @Override - public String getName() { - return TOUCH_ZOOM_MODE; - } - - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.VncCanvasActivity.ZoomInputHandler#onKeyDown(int, - * android.view.KeyEvent) - */ - @Override - public boolean onKeyDown(int keyCode, KeyEvent evt) { - return keyHandler.onKeyDown(keyCode, evt); - } - - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.VncCanvasActivity.ZoomInputHandler#onKeyUp(int, - * android.view.KeyEvent) - */ - @Override - public boolean onKeyUp(int keyCode, KeyEvent evt) { - return keyHandler.onKeyUp(keyCode, evt); - } - - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.AbstractInputHandler#onTrackballEvent(android. - * view.MotionEvent) - */ - @Override - public boolean onTrackballEvent(MotionEvent evt) { - return trackballMouse(evt); - } - - /* - * (non-Javadoc) - * - * @see - * android.view.GestureDetector.SimpleOnGestureListener#onDown(android - * .view.MotionEvent) - */ - @Override - public boolean onDown(MotionEvent e) { - panner.stop(); - return true; - } - - /** - * Divide stated fling velocity by this amount to get initial velocity - * per pan interval - */ - static final float FLING_FACTOR = 8; - - /* - * (non-Javadoc) - * - * @see - * android.view.GestureDetector.SimpleOnGestureListener#onFling(android - * .view.MotionEvent, android.view.MotionEvent, float, float) - */ - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - showZoomer(false); - panner.start(-(velocityX / FLING_FACTOR), -(velocityY / FLING_FACTOR), new Panner.VelocityUpdater() { - - /* - * (non-Javadoc) - * - * @see android.androidVNC.Panner.VelocityUpdater#updateVelocity - * (android.graphics.Point, long) - */ - @Override - public boolean updateVelocity(PointF p, long interval) { - double scale = Math.pow(0.8, interval / 50.0); - p.x *= scale; - p.y *= scale; - return (Math.abs(p.x) > 0.5 || Math.abs(p.y) > 0.5); - } - }); - return true; - } - - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.AbstractGestureInputHandler#onTouchEvent(android - * .view.MotionEvent) - */ - @Override - public boolean onTouchEvent(MotionEvent e) { - // MK - if (e.getAction() == MotionEvent.ACTION_CANCEL) - return true; - - if (dragMode) { - vncCanvas.changeTouchCoordinatesToFullFrame(e); - if (e.getAction() == MotionEvent.ACTION_UP) { - dragMode = false; - } - return vncCanvas.processPointerEvent(e, true); - } else { - return super.onTouchEvent(e); - } - } - - /* - * (non-Javadoc) - * - * @see - * android.view.GestureDetector.SimpleOnGestureListener#onLongPress( - * android.view.MotionEvent) - */ - @Override - public void onLongPress(MotionEvent e) { -// showZoomer(true); -// BCFactory.getInstance().getBCHaptic().performLongPressHaptic(vncCanvas); -// dragMode = true; -// vncCanvas.processPointerEvent(vncCanvas.changeTouchCoordinatesToFullFrame(e), true); - } - - /* - * (non-Javadoc) - * - * @see - * android.view.GestureDetector.SimpleOnGestureListener#onScroll(android - * .view.MotionEvent, android.view.MotionEvent, float, float) - */ - @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - if (inScaling) { - return false; - } - showZoomer(false); - return vncCanvas.pan((int) distanceX, (int) distanceY); - } - - /* - * (non-Javadoc) - * - * @see android.view.GestureDetector.SimpleOnGestureListener# - * onSingleTapConfirmed (android.view.MotionEvent) - */ - @Override - public boolean onSingleTapConfirmed(MotionEvent e) { - vncCanvas.changeTouchCoordinatesToFullFrame(e); - vncCanvas.processPointerEvent(e, true); - e.setAction(MotionEvent.ACTION_UP); - return vncCanvas.processPointerEvent(e, false); - } - - /* - * (non-Javadoc) - * - * @see - * android.view.GestureDetector.SimpleOnGestureListener#onDoubleTap( - * android.view.MotionEvent) - */ - @Override - public boolean onDoubleTap(MotionEvent e) { - vncCanvas.changeTouchCoordinatesToFullFrame(e); - vncCanvas.processPointerEvent(e, true, true); - e.setAction(MotionEvent.ACTION_UP); - return vncCanvas.processPointerEvent(e, false, true); - } - } - - public class TouchpadInputHandler extends AbstractGestureInputHandler { - - /** - * In drag mode (entered with long press) you process mouse events - * without sending them through the gesture detector - */ - private boolean dragMode; - float dragX, dragY; - /** - * Key handler delegate that handles DPad-based mouse motion - */ - private DPadMouseKeyHandler keyHandler; - - TouchpadInputHandler() { - super(VncCanvasActivity.this); - keyHandler = new DPadMouseKeyHandler(VncCanvasActivity.this, vncCanvas.handler); - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#getHandlerDescription() - */ - @Override - public CharSequence getHandlerDescription() { - return getResources().getString(R.string.input_mode_touchpad); - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#getName() - */ - @Override - public String getName() { - return TOUCHPAD_MODE; - } - - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.VncCanvasActivity.ZoomInputHandler#onKeyDown(int, - * android.view.KeyEvent) - */ - @Override - public boolean onKeyDown(int keyCode, KeyEvent evt) { - return keyHandler.onKeyDown(keyCode, evt); - } - - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.VncCanvasActivity.ZoomInputHandler#onKeyUp(int, - * android.view.KeyEvent) - */ - @Override - public boolean onKeyUp(int keyCode, KeyEvent evt) { - return keyHandler.onKeyUp(keyCode, evt); - } - - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.AbstractInputHandler#onTrackballEvent(android. - * view.MotionEvent) - */ - @Override - public boolean onTrackballEvent(MotionEvent evt) { - return trackballMouse(evt); - } - - /** - * scale down delta when it is small. This will allow finer control when - * user is making a small movement on touch screen. Scale up delta when - * delta is big. This allows fast mouse movement when user is flinging. - * - * @param deltaX - * @return - */ - private float fineCtrlScale(float delta) { - float sign = (delta > 0) ? 1 : -1; - delta = Math.abs(delta); - if (delta >= 1 && delta <= 3) { - delta = 1; - } else if (delta <= 10) { - delta *= 0.34; - } else if (delta <= 30) { - delta *= delta / 30; - } else if (delta <= 90) { - delta *= (delta / 30); - } else { - delta *= 3.0; - } - return sign * delta; - } - - /* - * (non-Javadoc) - * - * @see - * android.view.GestureDetector.SimpleOnGestureListener#onLongPress( - * android.view.MotionEvent) - */ - @Override - public void onLongPress(MotionEvent e) { - if(Config.enableDragOnLongPress) - dragPointer(e); - } - - private void dragPointer(MotionEvent e) { - - showZoomer(true); - BCFactory.getInstance().getBCHaptic().performLongPressHaptic(vncCanvas); - dragMode = true; - dragX = e.getX(); - dragY = e.getY(); - // send a mouse down event to the remote without moving the mouse. - remoteMouseStayPut(e); - vncCanvas.processPointerEvent(e, true); - - } - - /* - * (non-Javadoc) - * - * @see - * android.view.GestureDetector.SimpleOnGestureListener#onScroll(android - * .view.MotionEvent, android.view.MotionEvent, float, float) - */ - @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - //LIMBO: Disable this for now -// if (BCFactory.getInstance().getBCMotionEvent().getPointerCount(e2) > 1) { -// if (inScaling) { -// return false; -// } -// showZoomer(true); -// return vncCanvas.pan((int) distanceX, (int) distanceY); -// } else { - // compute the relative movement offset on the remote screen. - float deltaX = -distanceX * vncCanvas.getScale(); - float deltaY = -distanceY * vncCanvas.getScale(); - deltaX = fineCtrlScale(deltaX); - deltaY = fineCtrlScale(deltaY); - - // compute the absolution new mouse pos on the remote site. - float newRemoteX = vncCanvas.mouseX + deltaX; - float newRemoteY = vncCanvas.mouseY + deltaY; - - if (dragMode) { - if (e2.getAction() == MotionEvent.ACTION_UP) { - dragMode = false; - } - dragX = e2.getX(); - dragY = e2.getY(); - e2.setLocation(newRemoteX, newRemoteY); - return vncCanvas.processPointerEvent(e2, true); - } else { - e2.setLocation(newRemoteX, newRemoteY); - vncCanvas.processPointerEvent(e2, false); - } -// } - return true; - } - - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.AbstractGestureInputHandler#onTouchEvent(android - * .view.MotionEvent) - */ - @Override - public boolean onTouchEvent(MotionEvent e) { - - // MK - if (e.getAction() == MotionEvent.ACTION_CANCEL) - return true; - - if (Config.mouseMode == Config.MouseMode.External) { - return true; - } - // if (e.getPointerCount() > 1) { - // // Log.v("Limbo", "Detected 2 finger tap in onTouchEvent"); - // rightClick(e); - // return true; - // } - - // compute the relative movement offset on the remote screen. - float deltaX = (e.getX() - dragX) * vncCanvas.getScale(); - float deltaY = (e.getY() - dragY) * vncCanvas.getScale(); - dragX = e.getX(); - dragY = e.getY(); - deltaX = fineCtrlScale(deltaX); - deltaY = fineCtrlScale(deltaY); - - // compute the absolution new mouse pos on the remote site. - float newRemoteX = vncCanvas.mouseX + deltaX; - float newRemoteY = vncCanvas.mouseY + deltaY; - - if (dragMode) { - boolean down = false; - if (e.getAction() == MotionEvent.ACTION_UP) { - dragMode = false; - } else if (e.getAction() == MotionEvent.ACTION_DOWN) { - down = true; - } - - e.setLocation(newRemoteX, newRemoteY); - vncCanvas.processPointerEvent(e, down); - return super.onTouchEvent(e); - - } else if (!Config.enableDragOnLongPress && e.getAction() == MotionEvent.ACTION_MOVE) { - e.setLocation(newRemoteX, newRemoteY); - return vncCanvas.processPointerEvent(e, false); - } else { - return super.onTouchEvent(e); - } - } - - public boolean rightClick(final MotionEvent e) { - Thread t = new Thread(new Runnable() { - public void run() { - remoteMouseStayPut(e); - // One - // Log.v("Double Click", "One"); - vncCanvas.processPointerEvent(e, true, true); - try { - Thread.sleep(100); - } catch (InterruptedException ex) { - Logger.getLogger(VncCanvasActivity.class.getName()).log(Level.SEVERE, null, ex); - } - e.setAction(MotionEvent.ACTION_UP); - vncCanvas.processPointerEvent(e, false, true); - } - }); - // t.setPriority(Thread.MAX_PRIORITY); - t.start(); - return true; - - } - - /** - * Modify the event so that it does not move the mouse on the remote - * server. - * - * @param e - */ - private void remoteMouseStayPut(MotionEvent e) { - e.setLocation(vncCanvas.mouseX, vncCanvas.mouseY); - - } - - /* - * (non-Javadoc) confirmed single tap: do a single mouse click on remote - * without moving the mouse. - * - * @see android.view.GestureDetector.SimpleOnGestureListener# - * onSingleTapConfirmed (android.view.MotionEvent) - */ - - @Override - public boolean onSingleTapConfirmed(MotionEvent e) { - singleClick(e); - return true; - // boolean multiTouch = - // (BCFactory.getInstance().getBCMotionEvent().getPointerCount(e) > - // 1); - // remoteMouseStayPut(e); - // vncCanvas.processPointerEvent(e, true, multiTouch || - // vncCanvas.cameraButtonDown); - // e.setAction(MotionEvent.ACTION_UP); - // return vncCanvas.processPointerEvent(e, false, multiTouch || - // vncCanvas.cameraButtonDown); - } - - private boolean singleClick(final MotionEvent e) { - Thread t = new Thread(new Runnable() { - public void run() { - remoteMouseStayPut(e); - // One - // Log.v("Double Click", "One"); - vncCanvas.processPointerEvent(e, true, false); - try { - Thread.sleep(50); - } catch (InterruptedException ex) { - Logger.getLogger(VncCanvasActivity.class.getName()).log(Level.SEVERE, null, ex); - } - e.setAction(MotionEvent.ACTION_UP); - vncCanvas.processPointerEvent(e, false, false); - } - }); - // t.setPriority(Thread.MAX_PRIORITY); - t.start(); - return true; - - } - - private boolean middleClick(final MotionEvent e) { - Thread t = new Thread(new Runnable() { - public void run() { - remoteMouseStayPut(e); - // One - // Log.v("Double Click", "One"); - //vncCanvas.processPointerEvent(e, true, false); - vncCanvas.processPointerEvent((int) e.getX(), (int) e.getY(), e.getAction(), 0, true, false, true, false); - try { - Thread.sleep(50); - } catch (InterruptedException ex) { - Logger.getLogger(VncCanvasActivity.class.getName()).log(Level.SEVERE, null, ex); - } - e.setAction(MotionEvent.ACTION_UP); - //vncCanvas.processPointerEvent(e, false, false); - vncCanvas.processPointerEvent((int) e.getX(), (int) e.getY(), e.getAction(), 0, false, false, true, false); - } - }); - // t.setPriority(Thread.MAX_PRIORITY); - t.start(); - return true; - - } - - /* - * (non-Javadoc) double tap: do two left mouse right mouse clicks on - * remote without moving the mouse. - * - * @see - * android.view.GestureDetector.SimpleOnGestureListener#onDoubleTap( - * android.view.MotionEvent) - */ - @Override - public boolean onDoubleTap(MotionEvent e) { - if(!Config.enableDragOnLongPress) - processDoubleTap(e); - else - doubleClick(e); - return false; - - } - - private void processDoubleTap(final MotionEvent e) { - Thread t = new Thread(new Runnable() { - public void run() { - try { - Thread.sleep(400); - } catch (InterruptedException e1) { - e1.printStackTrace(); - } - - if (vncCanvas.mouseDown) { -// panner.stop(); - dragPointer(e); - } else - doubleClick(e); - - } - }); - t.start(); - } - - /* - * (non-Javadoc) - * - * @see - * android.view.GestureDetector.SimpleOnGestureListener#onDown(android - * .view.MotionEvent) - */ - @Override - public boolean onDown(MotionEvent e) { - panner.stop(); - return true; - } - - private Object doubleClickLock = new Object(); - - private boolean doubleClick(final MotionEvent e1) { - Thread t = new Thread(new Runnable() { - public void run() { - synchronized (doubleClickLock) { - - //XXX: We make a copy of the event because we have some - // race condition here updating mouseX, mouseY - MotionEvent event = MotionEvent.obtain(e1.getDownTime(), - e1.getEventTime(), e1.getAction(), - e1.getX(), e1.getY(), e1.getMetaState()); - - remoteMouseStayPut(event); - // One - // Log.v("Double Click", "One"); - vncCanvas.processPointerEvent(event, true, false); - try { - Thread.sleep(50); - } catch (InterruptedException ex) { - Logger.getLogger(VncCanvasActivity.class.getName()).log(Level.SEVERE, null, ex); - } - event.setAction(MotionEvent.ACTION_UP); - vncCanvas.processPointerEvent(event, false, false); - try { - Thread.sleep(50); - } catch (InterruptedException ex) { - Logger.getLogger(VncCanvasActivity.class.getName()).log(Level.SEVERE, null, ex); - } - // Two - // Log.v("Double Click", "Two"); - event.setAction(MotionEvent.ACTION_DOWN); - vncCanvas.processPointerEvent(event, true, false); - try { - Thread.sleep(50); - } catch (InterruptedException ex) { - Logger.getLogger(VncCanvasActivity.class.getName()).log(Level.SEVERE, null, ex); - } - event.setAction(MotionEvent.ACTION_UP); - vncCanvas.processPointerEvent(event, false, false); - } - } - }); - // t.setPriority(Thread.MAX_PRIORITY); - t.start(); - return true; - - } - } - - private final static String TAG = "VncCanvasActivity"; - public AbstractInputHandler inputHandler; - public VncCanvas vncCanvas; - - public MenuItem[] inputModeMenuItems; - public AbstractInputHandler inputModeHandlers[]; - public ConnectionBean connection; - public boolean trackballButtonDown; - public static final int inputModeIds[] = { R.id.itemInputFitToScreen, R.id.itemInputTouchpad, R.id.itemInputMouse, - R.id.itemInputPan, R.id.itemInputTouchPanTrackballMouse, R.id.itemInputDPadPanTouchMouse, - R.id.itemInputTouchPanZoomMouse }; - ZoomControls zoomer; - Panner panner; - - @Override - public void onCreate(Bundle icicle) { - - super.onCreate(icicle); - activity = this; - - Intent i = getIntent(); - connection = new ConnectionBean(); - Uri data = i.getData(); - if ((data != null) && (data.getScheme().equals("vnc"))) { - String host = data.getHost(); - // This should not happen according to Uri contract, but bug - // introduced in Froyo (2.2) - // has made this parsing of host necessary - int index = host.indexOf(':'); - int port; - if (index != -1) { - try { - port = Integer.parseInt(host.substring(index + 1)); - } catch (NumberFormatException nfe) { - port = 0; - } - host = host.substring(0, index); - } else { - port = data.getPort(); - } - if (host.equals(VncConstants.CONNECTION)) { - ConnectionBean bean = new ConnectionBean(); - if (bean != null) { - bean.setConnectionId(connection.get_Id()); - } - } else { - connection.setAddress(host); - connection.setNickname(connection.getAddress()); - connection.setPort(port); - List path = data.getPathSegments(); - if (path.size() >= 1) { - connection.setColorModel(path.get(1)); - } - if (path.size() >= 2) { - connection.setPassword(path.get(1)); - } - } - } else { - - Bundle extras = i.getExtras(); - - if (connection.getPort() == 0) { - connection.setPort(5901); - } - - // Parse a HOST:PORT entry - String host = connection.getAddress(); - // if (host.indexOf(':') > -1) { - // String p = host.substring(host.indexOf(':') + 1); - // try { - // connection.setPort(Integer.parseInt(p)); - // } catch (Exception e) { - // } - // connection.setAddress(host.substring(0, host.indexOf(':'))); - // } - } - connection.setPassword(LimboActivity.getVnc_passwd()); - setContentView(); - - vncCanvas = (VncCanvas) findViewById(R.id.vnc_canvas); - zoomer = (ZoomControls) findViewById(R.id.zoomer); - - vncCanvas.initializeVncCanvas(connection, new Runnable() { - public void run() { - setModes(); - } - }); - zoomer.hide(); - zoomer.setOnZoomInClickListener(new View.OnClickListener() { - - /* - * (non-Javadoc) - * - * @see android.view.View.OnClickListener#onClick(android.view.View) - */ - @Override - public void onClick(View v) { - showZoomer(true); - vncCanvas.scaling.zoomIn(VncCanvasActivity.this); - - } - }); - zoomer.setOnZoomOutClickListener(new View.OnClickListener() { - - /* - * (non-Javadoc) - * - * @see android.view.View.OnClickListener#onClick(android.view.View) - */ - @Override - public void onClick(View v) { - showZoomer(true); - vncCanvas.scaling.zoomOut(VncCanvasActivity.this); - - } - }); - zoomer.setOnZoomInClickListener(new View.OnClickListener() { - - /* - * (non-Javadoc) - * - * @see android.view.View.OnClickListener#onClick(android.view.View) - */ - @Override - public void onClick(View v) { - InputMethodManager inputMgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - inputMgr.toggleSoftInput(0, 0); - } - }); - zoomer.setOnZoomOutClickListener(new View.OnClickListener() { - - /* - * (non-Javadoc) - * - * @see android.view.View.OnClickListener#onClick(android.view.View) - */ - @Override - public void onClick(View v) { - InputMethodManager inputMgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - inputMgr.toggleSoftInput(0, 0); - } - }); - panner = new Panner(this, vncCanvas.handler); - - inputHandler = getInputHandlerById(R.id.itemInputFitToScreen); - - display = ((WindowManager) this.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); - } - - public void setContentView() { - setContentView(R.layout.limbo_vnc); - } - - /** - * Set modes on start to match what is specified in the ConnectionBean; - * color mode (already done) scaling, input mode - */ - void setModes() { - AbstractInputHandler handler = getInputHandlerByName(connection.getInputMode()); - AbstractScaling.getByScaleType(connection.getScaleMode()).setScaleTypeForActivity(this); - this.inputHandler = handler; - showPanningState(); - } - - ConnectionBean getConnection() { - return connection; - } - - /* - * (non-Javadoc) - * - * @see android.app.Activity#onPrepareDialog(int, android.app.Dialog) - */ - @Override - protected void onPrepareDialog(int id, Dialog dialog) { - super.onPrepareDialog(id, dialog); - if (dialog instanceof ConnectionSettable) { - ((ConnectionSettable) dialog).setConnection(connection); - } - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - // ignore orientation/keyboard change - super.onConfigurationChanged(newConfig); - } - - @Override - protected void onStop() { - vncCanvas.disableRepaints(); - super.onStop(); - } - - @Override - protected void onRestart() { - vncCanvas.enableRepaints(); - super.onRestart(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.vnccanvasactivitymenu, menu); - - if (vncCanvas.scaling != null) { - menu.findItem(vncCanvas.scaling.getId()).setChecked(true); - } - - Menu inputMenu = menu.findItem(R.id.itemInputMode).getSubMenu(); - - inputModeMenuItems = new MenuItem[inputModeIds.length]; - for (int i = 0; i < inputModeIds.length; i++) { - inputModeMenuItems[i] = inputMenu.findItem(inputModeIds[i]); - } - updateInputMenu(); - return true; - } - - /** - * Change the input mode sub-menu to reflect change in scaling - */ - public void updateInputMenu() { - if (inputModeMenuItems == null || vncCanvas.scaling == null) { - return; - } - for (MenuItem item : inputModeMenuItems) { - item.setEnabled(vncCanvas.scaling.isValidInputMode(item.getItemId())); - if (getInputHandlerById(item.getItemId()) == inputHandler) { - item.setChecked(true); - } - } - } - - - - /** - * If id represents an input handler, return that; otherwise return null - * - * @param id - * @return - */ - public AbstractInputHandler getInputHandlerById(int id) { - if (inputModeHandlers == null) { - inputModeHandlers = new AbstractInputHandler[inputModeIds.length]; - } - for (int i = 0; i < inputModeIds.length; ++i) { - if (inputModeIds[i] == id) { - if (inputModeHandlers[i] == null) { - if (id == R.id.itemInputFitToScreen) - inputModeHandlers[i] = new FitToScreenMode(); - else if (id == R.id.itemInputPan) - inputModeHandlers[i] = new PanMode(); - else if (id == R.id.itemInputMouse) - inputModeHandlers[i] = new MouseMode(); - - else if (id == R.id.itemInputTouchPanTrackballMouse) - inputModeHandlers[i] = new TouchPanTrackballMouse(); - else if (id == R.id.itemInputDPadPanTouchMouse) - inputModeHandlers[i] = new DPadPanTouchMouseMode(); - - else if (id == R.id.itemInputTouchPanZoomMouse) - inputModeHandlers[i] = new ZoomInputHandler(); - - else if (id == R.id.itemInputTouchpad) - inputModeHandlers[i] = new TouchpadInputHandler(); - } - - return inputModeHandlers[i]; - } - } - return null; - } - - AbstractInputHandler getInputHandlerByName(String name) { - AbstractInputHandler result = null; - for (int id : inputModeIds) { - AbstractInputHandler handler = getInputHandlerById(id); - if (handler.getName().equals(name)) { - result = handler; - break; - } - } - if (result == null) { - result = getInputHandlerById(R.id.itemInputTouchPanZoomMouse); - } - return result; - } - - int getModeIdFromHandler(AbstractInputHandler handler) { - for (int id : inputModeIds) { - if (handler == getInputHandlerById(id)) { - return id; - } - } - return R.id.itemInputTouchPanZoomMouse; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - vncCanvas.afterMenu = true; - if (item.getItemId() == R.id.itemSpecialKeys) { - showDialog(R.layout.metakey); - return true; - }else if (item.getItemId() == R.id.itemColorMode) { - selectColorModel(); - return true; - } - // Following sets one of the scaling options - else if (item.getItemId() == R.id.itemZoomable || - item.getItemId() == R.id.itemOneToOne || - item.getItemId() == R.id.itemFitToScreen) { - AbstractScaling.getById(item.getItemId()).setScaleTypeForActivity(this); - item.setChecked(true); - showPanningState(); - return true; - } - // case R.id.itemCenterMouse: - // vncCanvas.warpMouse(vncCanvas.absoluteXPosition - // + vncCanvas.getVisibleWidth() / 2, - // vncCanvas.absoluteYPosition + vncCanvas.getVisibleHeight() - // / 2); - // return true; - else if (item.getItemId() == R.id.itemReconnect){ - vncCanvas.closeConnection(); - vncCanvas.reload(); - return true; - } else if (item.getItemId() == R.id.itemDisconnect){ - vncCanvas.closeConnection(); - finish(); - return true; - } else if (item.getItemId() == R.id.itemEnterText){ - showDialog(R.layout.entertext); - return true; - }else if (item.getItemId() == R.id.itemCtrlC) { - vncCanvas.sendMetaKey(MetaKeyBean.keyCtrlC); - return true; - }else if (item.getItemId() == R.id.itemCtrlAltDel){ - vncCanvas.sendMetaKey(MetaKeyBean.keyCtrlAltDel); - return true; - } - // case R.id.itemFollowMouse: - // vncCanvas.panToMouse(); - // return true; - // case R.id.itemFollowPan: - // return true; - // case R.id.itemArrowLeft: - // vncCanvas.sendMetaKey(MetaKeyBean.keyArrowLeft); - // return true; - // case R.id.itemArrowUp: - // vncCanvas.sendMetaKey(MetaKeyBean.keyArrowUp); - // return true; - // case R.id.itemArrowRight: - // vncCanvas.sendMetaKey(MetaKeyBean.keyArrowRight); - // return true; - // case R.id.itemArrowDown: - // vncCanvas.sendMetaKey(MetaKeyBean.keyArrowDown); - // return true; - else if (item.getItemId() == R.id.itemSendKeyAgain) { - return true; - }else if (item.getItemId() == R.id.itemOpenDoc){ - Utils.showDocumentation(this); - return true; - }else { - AbstractInputHandler input = getInputHandlerById(item.getItemId()); - if (input != null) { - inputHandler = input; - connection.setInputMode(input.getName()); - item.setChecked(true); - showPanningState(); - return true; - } - } - return super.onOptionsItemSelected(item); - } - - private MetaKeyBean lastSentKey; - - @Override - protected void onDestroy() { - super.onDestroy(); - if (isFinishing()) { - vncCanvas.closeConnection(); - vncCanvas.onDestroy(); - - - } - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent evt) { - if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { - MotionEvent e = MotionEvent.obtain(1000, 1000, MotionEvent.ACTION_DOWN, vncCanvas.mouseX, vncCanvas.mouseY, - 0); - ((TouchpadInputHandler) this.inputHandler).rightClick(e); - return true; - } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { - MotionEvent e = MotionEvent.obtain(1000, 1000, MotionEvent.ACTION_DOWN, vncCanvas.mouseX, vncCanvas.mouseY, - 0); - ((TouchpadInputHandler) this.inputHandler).middleClick(e); - return true; - } else if (keyCode == KeyEvent.KEYCODE_MENU) { - return super.onKeyDown(keyCode, evt); - } - - return inputHandler.onKeyDown(keyCode, evt); - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent evt) { - if (keyCode == KeyEvent.KEYCODE_MENU) { - return super.onKeyUp(keyCode, evt); - } - - return inputHandler.onKeyUp(keyCode, evt); - } - - public void showPanningState() { - // Toast.makeText(this, inputHandler.getHandlerDescription(), - // Toast.LENGTH_SHORT).show(); - } - - /* - * (non-Javadoc) - * - * @see android.app.Activity#onTrackballEvent(android.view.MotionEvent) - */ - @Override - public boolean onTrackballEvent(MotionEvent event) { - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - trackballButtonDown = true; - break; - case MotionEvent.ACTION_UP: - trackballButtonDown = false; - break; - } - return inputHandler.onTrackballEvent(event); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - // MK - if (event.getAction() == MotionEvent.ACTION_CANCEL) - return true; - - if(event.getAction() == MotionEvent.ACTION_DOWN) - vncCanvas.mouseDown = true; - else if(event.getAction() == MotionEvent.ACTION_UP) - vncCanvas.mouseDown = false; - - return inputHandler.onTouchEvent(event); - } - - protected void selectColorModel() { - // Stop repainting the desktop - // because the display is composited! - vncCanvas.disableRepaints(); - - String[] choices = new String[COLORMODEL.values().length]; - int currentSelection = -1; - for (int i = 0; i < choices.length; i++) { - COLORMODEL cm = COLORMODEL.values()[i]; - choices[i] = cm.toString(); - if (vncCanvas.isColorModel(cm)) { - currentSelection = i; - } - } - - final Dialog dialog = new Dialog(this); - dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); - ListView list = new ListView(this); - list.setAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_checked, choices)); - list.setChoiceMode(ListView.CHOICE_MODE_SINGLE); - list.setItemChecked(currentSelection, true); - list.setOnItemClickListener(new OnItemClickListener() { - public void onItemClick(AdapterView arg0, View arg1, int arg2, long arg3) { - if (dialog.isShowing()) { - dialog.dismiss(); - } - COLORMODEL cm = COLORMODEL.values()[arg2]; - vncCanvas.setColorModel(cm); - connection.setColorModel(cm.nameString()); - UIUtils.toastShort(VncCanvasActivity.this, "Updating Color Model to " + cm.toString()); - } - }); - dialog.setOnDismissListener(new OnDismissListener() { - @Override - public void onDismiss(DialogInterface arg0) { - Log.i(TAG, "Color Model Selector dismissed"); - // Restore desktop repaints - vncCanvas.enableRepaints(); - } - }); - dialog.setContentView(list); - dialog.show(); - } - - float panTouchX, panTouchY; - - /** - * Pan based on touch motions - * - * @param event - */ - private boolean pan(MotionEvent event) { - float curX = event.getX(); - float curY = event.getY(); - int dX = (int) (panTouchX - curX); - int dY = (int) (panTouchY - curY); - - return vncCanvas.pan(dX, dY); - } - - boolean defaultKeyDownHandler(int keyCode, KeyEvent evt) { - if (vncCanvas.processLocalKeyEvent(keyCode, evt)) { - return true; - } - return super.onKeyDown(keyCode, evt); - } - - boolean defaultKeyUpHandler(int keyCode, KeyEvent evt) { - if (vncCanvas.processLocalKeyEvent(keyCode, evt)) { - return true; - } - return super.onKeyUp(keyCode, evt); - } - - boolean touchPan(MotionEvent event) { - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - panTouchX = event.getX(); - panTouchY = event.getY(); - break; - case MotionEvent.ACTION_MOVE: - pan(event); - panTouchX = event.getX(); - panTouchY = event.getY(); - break; - case MotionEvent.ACTION_UP: - pan(event); - break; - } - return true; - } - - private static int convertTrackballDelta(double delta) { - return (int) Math.pow(Math.abs(delta) * 6.01, 2.5) * (delta < 0.0 ? -1 : 1); - } - - boolean trackballMouse(MotionEvent evt) { - // MK - if (evt.getAction() == MotionEvent.ACTION_CANCEL) - return false; - - int dx = convertTrackballDelta(evt.getX()); - int dy = convertTrackballDelta(evt.getY()); - - evt.offsetLocation(vncCanvas.mouseX + dx - evt.getX(), vncCanvas.mouseY + dy - evt.getY()); - - if (vncCanvas.processPointerEvent(evt, trackballButtonDown)) { - return true; - } - return VncCanvasActivity.super.onTouchEvent(evt); - } - - long hideZoomAfterMs; - static final long ZOOM_HIDE_DELAY_MS = 2500; - HideZoomRunnable hideZoomInstance = new HideZoomRunnable(); - - private void showZoomer(boolean force) { - - if (force || zoomer.getVisibility() != View.VISIBLE) { - // zoomer.show(); - hideZoomAfterMs = SystemClock.uptimeMillis() + ZOOM_HIDE_DELAY_MS; - vncCanvas.handler.postAtTime(hideZoomInstance, hideZoomAfterMs + 10); - } - } - - private class HideZoomRunnable implements Runnable { - - public void run() { - if (SystemClock.uptimeMillis() >= hideZoomAfterMs) { - zoomer.hide(); - } - } - } - - /** - * Touches and dpad (trackball) pan the screen - * - * @author Michael A. MacDonald - * - */ - class PanMode implements AbstractInputHandler { - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#onKeyDown(int, - * android.view.KeyEvent) - */ - @Override - public boolean onKeyDown(int keyCode, KeyEvent evt) { - // DPAD KeyDown events are move MotionEvents in Panning Mode - final int dPos = 100; - boolean result = false; - // MK - if (evt.getAction() == MotionEvent.ACTION_CANCEL) - return true; - - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_CENTER: - result = true; - break; - case KeyEvent.KEYCODE_DPAD_LEFT: - onTouchEvent(MotionEvent.obtain(1, System.currentTimeMillis(), MotionEvent.ACTION_MOVE, - panTouchX + dPos, panTouchY, 0)); - result = true; - break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - onTouchEvent(MotionEvent.obtain(1, System.currentTimeMillis(), MotionEvent.ACTION_MOVE, - panTouchX - dPos, panTouchY, 0)); - result = true; - break; - case KeyEvent.KEYCODE_DPAD_UP: - onTouchEvent(MotionEvent.obtain(1, System.currentTimeMillis(), MotionEvent.ACTION_MOVE, panTouchX, - panTouchY + dPos, 0)); - result = true; - break; - case KeyEvent.KEYCODE_DPAD_DOWN: - onTouchEvent(MotionEvent.obtain(1, System.currentTimeMillis(), MotionEvent.ACTION_MOVE, panTouchX, - panTouchY - dPos, 0)); - result = true; - break; - default: - result = defaultKeyDownHandler(keyCode, evt); - break; - } - return result; - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#onKeyUp(int, - * android.view.KeyEvent) - */ - @Override - public boolean onKeyUp(int keyCode, KeyEvent evt) { - // Ignore KeyUp events for DPAD keys in Panning Mode; trackball - // button switches to mouse mode - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_CENTER: - inputHandler = getInputHandlerById(R.id.itemInputMouse); - connection.setInputMode(inputHandler.getName()); - updateInputMenu(); - showPanningState(); - return true; - case KeyEvent.KEYCODE_DPAD_LEFT: - return true; - case KeyEvent.KEYCODE_DPAD_RIGHT: - return true; - case KeyEvent.KEYCODE_DPAD_UP: - return true; - case KeyEvent.KEYCODE_DPAD_DOWN: - return true; - } - return defaultKeyUpHandler(keyCode, evt); - } - - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.AbstractInputHandler#onTouchEvent(android.view - * .MotionEvent) - */ - @Override - public boolean onTouchEvent(MotionEvent event) { - // MK - if (event.getAction() == MotionEvent.ACTION_CANCEL) - return true; - - return touchPan(event); - } - - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.AbstractInputHandler#onTrackballEvent(android. - * view.MotionEvent) - */ - @Override - public boolean onTrackballEvent(MotionEvent evt) { - return false; - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#handlerDescription() - */ - @Override - public CharSequence getHandlerDescription() { - return getResources().getText(R.string.input_mode_panning); - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#getName() - */ - @Override - public String getName() { - return "PAN_MODE"; - } - } - - /** - * The touchscreen pans the screen; the trackball moves and clicks the - * mouse. - * - * @author Michael A. MacDonald - * - */ - public class TouchPanTrackballMouse implements AbstractInputHandler { - - private DPadMouseKeyHandler keyHandler = new DPadMouseKeyHandler(VncCanvasActivity.this, vncCanvas.handler); - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#onKeyDown(int, - * android.view.KeyEvent) - */ - @Override - public boolean onKeyDown(int keyCode, KeyEvent evt) { - return keyHandler.onKeyDown(keyCode, evt); - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#onKeyUp(int, - * android.view.KeyEvent) - */ - @Override - public boolean onKeyUp(int keyCode, KeyEvent evt) { - return keyHandler.onKeyUp(keyCode, evt); - } - - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.AbstractInputHandler#onTouchEvent(android.view - * .MotionEvent) - */ - @Override - public boolean onTouchEvent(MotionEvent evt) { - // MK - if (evt.getAction() == MotionEvent.ACTION_CANCEL) - return true; - - return touchPan(evt); - } - - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.AbstractInputHandler#onTrackballEvent(android. - * view.MotionEvent) - */ - @Override - public boolean onTrackballEvent(MotionEvent evt) { - return trackballMouse(evt); - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#handlerDescription() - */ - @Override - public CharSequence getHandlerDescription() { - return getResources().getText(R.string.input_mode_touchpad_pan_trackball_mouse); - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#getName() - */ - @Override - public String getName() { - return "TOUCH_PAN_TRACKBALL_MOUSE"; - } - } - - public static final String FIT_SCREEN_NAME = "FIT_SCREEN"; - /** - * Internal name for default input mode with Zoom scaling - */ - public static final String TOUCH_ZOOM_MODE = "TOUCH_ZOOM_MODE"; - public static final String TOUCHPAD_MODE = "TOUCHPAD_MODE"; - - /** - * In fit-to-screen mode, no panning. Trackball and touchscreen work as - * mouse. - * - * @author Michael A. MacDonald - * - */ - public class FitToScreenMode implements AbstractInputHandler { - - private DPadMouseKeyHandler keyHandler = new DPadMouseKeyHandler(VncCanvasActivity.this, vncCanvas.handler); - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#onKeyDown(int, - * android.view.KeyEvent) - */ - @Override - public boolean onKeyDown(int keyCode, KeyEvent evt) { - return keyHandler.onKeyDown(keyCode, evt); - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#onKeyUp(int, - * android.view.KeyEvent) - */ - @Override - public boolean onKeyUp(int keyCode, KeyEvent evt) { - return keyHandler.onKeyUp(keyCode, evt); - } - - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.AbstractInputHandler#onTouchEvent(android.view - * .MotionEvent) - */ - @Override - public boolean onTouchEvent(MotionEvent evt) { - // MK - if (evt.getAction() == MotionEvent.ACTION_CANCEL) - return true; - - return false; - } - - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.AbstractInputHandler#onTrackballEvent(android. - * view.MotionEvent) - */ - @Override - public boolean onTrackballEvent(MotionEvent evt) { - return trackballMouse(evt); - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#handlerDescription() - */ - @Override - public CharSequence getHandlerDescription() { - return getResources().getText(R.string.input_mode_fit_to_screen); - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#getName() - */ - @Override - public String getName() { - return FIT_SCREEN_NAME; - } - } - - /** - * Touch screen controls, clicks the mouse. - * - * @author Michael A. MacDonald - * - */ - class MouseMode implements AbstractInputHandler { - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#onKeyDown(int, - * android.view.KeyEvent) - */ - @Override - public boolean onKeyDown(int keyCode, KeyEvent evt) { - if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { - return true; - } - return defaultKeyDownHandler(keyCode, evt); - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#onKeyUp(int, - * android.view.KeyEvent) - */ - @Override - public boolean onKeyUp(int keyCode, KeyEvent evt) { - if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { - inputHandler = getInputHandlerById(R.id.itemInputPan); - showPanningState(); - connection.setInputMode(inputHandler.getName()); - updateInputMenu(); - return true; - } - return defaultKeyUpHandler(keyCode, evt); - } - - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.AbstractInputHandler#onTouchEvent(android.view - * .MotionEvent) - */ - @Override - public boolean onTouchEvent(MotionEvent event) { - // Mouse Pointer Control Mode - // Pointer event is absolute coordinates. - - // MK - if (event.getAction() == MotionEvent.ACTION_CANCEL) - return true; - - vncCanvas.changeTouchCoordinatesToFullFrame(event); - if (vncCanvas.processPointerEvent(event, true)) { - return true; - } - return VncCanvasActivity.super.onTouchEvent(event); - } - - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.AbstractInputHandler#onTrackballEvent(android. - * view.MotionEvent) - */ - @Override - public boolean onTrackballEvent(MotionEvent evt) { - return false; - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#handlerDescription() - */ - @Override - public CharSequence getHandlerDescription() { - return getResources().getText(R.string.input_mode_mouse); - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#getName() - */ - @Override - public String getName() { - return "MOUSE"; - } - } - - /** - * Touch screen controls, clicks the mouse. DPad pans the screen - * - * @author Michael A. MacDonald - * - */ - class DPadPanTouchMouseMode implements AbstractInputHandler { - - private boolean isPanning; - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#onKeyDown(int, - * android.view.KeyEvent) - */ - @Override - public boolean onKeyDown(int keyCode, KeyEvent evt) { - int xv = 0; - int yv = 0; - boolean result = true; - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_LEFT: - // xv = -1; - vncCanvas.sendMetaKey(MetaKeyBean.keyArrowLeft); - return result; - // break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - // xv = 1; - vncCanvas.sendMetaKey(MetaKeyBean.keyArrowRight); - return result; - // break; - case KeyEvent.KEYCODE_DPAD_UP: - // yv = -1; - vncCanvas.sendMetaKey(MetaKeyBean.keyArrowUp); - return result; - // break; - case KeyEvent.KEYCODE_DPAD_DOWN: - // yv = 1; - vncCanvas.sendMetaKey(MetaKeyBean.keyArrowDown); - return result; - // break; - default: - result = defaultKeyDownHandler(keyCode, evt); - break; - } - if ((xv != 0 || yv != 0) && !isPanning) { - final int x = xv; - final int y = yv; - isPanning = true; - panner.start(x, y, new Panner.VelocityUpdater() { - - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.Panner.VelocityUpdater#updateVelocity - * (android.graphics.Point, long) - */ - @Override - public boolean updateVelocity(PointF p, long interval) { - double scale = (2.0 * (double) interval / 50.0); - if (Math.abs(p.x) < 500) { - p.x += (int) (scale * x); - } - if (Math.abs(p.y) < 500) { - p.y += (int) (scale * y); - } - return true; - } - }); - vncCanvas.pan(x, y); - } - return result; - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#onKeyUp(int, - * android.view.KeyEvent) - */ - @Override - public boolean onKeyUp(int keyCode, KeyEvent evt) { - boolean result = false; - - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_LEFT: - case KeyEvent.KEYCODE_DPAD_RIGHT: - case KeyEvent.KEYCODE_DPAD_UP: - case KeyEvent.KEYCODE_DPAD_DOWN: - panner.stop(); - isPanning = false; - result = true; - break; - default: - result = defaultKeyUpHandler(keyCode, evt); - break; - } - return result; - } - - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.AbstractInputHandler#onTouchEvent(android.view - * .MotionEvent) - */ - @Override - public boolean onTouchEvent(MotionEvent event) { - // Mouse Pointer Control Mode - // Pointer event is absolute coordinates. - - // MK - if (event.getAction() == MotionEvent.ACTION_CANCEL) - return true; - - vncCanvas.changeTouchCoordinatesToFullFrame(event); - if (vncCanvas.processPointerEvent(event, true)) { - return true; - } - return VncCanvasActivity.super.onTouchEvent(event); - } - - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.AbstractInputHandler#onTrackballEvent(android. - * view.MotionEvent) - */ - @Override - public boolean onTrackballEvent(MotionEvent evt) { - return false; - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#handlerDescription() - */ - @Override - public CharSequence getHandlerDescription() { - return getResources().getText(R.string.input_mode_dpad_pan_touchpad_mouse); - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#getName() - */ - @Override - public String getName() { - return "DPAD_PAN_TOUCH_MOUSE"; - } - } - - public void onConnected() { - - } -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/VncConstants.java b/limbo-android-lib/src/main/java/android/androidVNC/VncConstants.java deleted file mode 100644 index 7aaa22bfd..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/VncConstants.java +++ /dev/null @@ -1,8 +0,0 @@ -package android.androidVNC; - -/** - * Keys for intent values - */ -public class VncConstants { - public static final String CONNECTION = "android.androidVNC.CONNECTION"; -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/ZlibInStream.java b/limbo-android-lib/src/main/java/android/androidVNC/ZlibInStream.java deleted file mode 100644 index 91103a184..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/ZlibInStream.java +++ /dev/null @@ -1,112 +0,0 @@ -package android.androidVNC; -/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -// -// A ZlibInStream reads from a zlib.io.InputStream -// - -public class ZlibInStream extends InStream { - - static final int defaultBufSize = 16384; - - public ZlibInStream(int bufSize_) { - bufSize = bufSize_; - b = new byte[bufSize]; - ptr = end = ptrOffset = 0; - inflater = new java.util.zip.Inflater(); - } - - public ZlibInStream() { this(defaultBufSize); } - - public void setUnderlying(InStream is, int bytesIn_) { - underlying = is; - bytesIn = bytesIn_; - ptr = end = 0; - } - - public void reset() throws Exception { - ptr = end = 0; - if (underlying == null) return; - - while (bytesIn > 0) { - decompress(); - end = 0; // throw away any data - } - underlying = null; - } - - public int pos() { return ptrOffset + ptr; } - - protected int overrun(int itemSize, int nItems) throws Exception { - if (itemSize > bufSize) - throw new Exception("ZlibInStream overrun: max itemSize exceeded"); - if (underlying == null) - throw new Exception("ZlibInStream overrun: no underlying stream"); - - if (end - ptr != 0) - System.arraycopy(b, ptr, b, 0, end - ptr); - - ptrOffset += ptr; - end -= ptr; - ptr = 0; - - while (end < itemSize) { - decompress(); - } - - if (itemSize * nItems > end) - nItems = end / itemSize; - - return nItems; - } - - // decompress() calls the decompressor once. Note that this won't - // necessarily generate any output data - it may just consume some input - // data. Returns false if wait is false and we would block on the underlying - // stream. - - private void decompress() throws Exception { - try { - underlying.check(1); - int avail_in = underlying.getend() - underlying.getptr(); - if (avail_in > bytesIn) - avail_in = bytesIn; - - if (inflater.needsInput()) { - inflater.setInput(underlying.getbuf(), underlying.getptr(), avail_in); - } - - int n = inflater.inflate(b, end, bufSize - end); - - end += n; - if (inflater.needsInput()) { - bytesIn -= avail_in; - underlying.setptr(underlying.getptr() + avail_in); - } - } catch (java.util.zip.DataFormatException e) { - throw new Exception("ZlibInStream: inflate failed"); - } - } - - private InStream underlying; - private int bufSize; - private int ptrOffset; - private java.util.zip.Inflater inflater; - private int bytesIn; -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/ZoomScaling.java b/limbo-android-lib/src/main/java/android/androidVNC/ZoomScaling.java deleted file mode 100644 index 533ffe1b6..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/ZoomScaling.java +++ /dev/null @@ -1,187 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package android.androidVNC; - -import com.limbo.emu.lib.R; - -import android.graphics.Matrix; -import android.widget.ImageView.ScaleType; - -/** - * @author Michael A. MacDonald - */ -class ZoomScaling extends AbstractScaling { - - static final String TAG = "ZoomScaling"; - - private Matrix matrix; - int canvasXOffset; - int canvasYOffset; - float scaling; - float minimumScale; - - /** - * @param id - * @param scaleType - */ - public ZoomScaling() { - super(R.id.itemZoomable, ScaleType.MATRIX); - matrix = new Matrix(); - scaling = 1; - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractScaling#getDefaultHandlerId() - */ - @Override - int getDefaultHandlerId() { - return R.id.itemInputTouchPanZoomMouse; - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractScaling#isAbleToPan() - */ - @Override - boolean isAbleToPan() { - return true; - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractScaling#isValidInputMode(int) - */ - @Override - boolean isValidInputMode(int mode) { - return mode != R.id.itemInputFitToScreen; - } - - /** - * Call after scaling and matrix have been changed to resolve scrolling - * @param activity - */ - private void resolveZoom(VncCanvasActivity activity) - { - activity.vncCanvas.scrollToAbsolute(); - activity.vncCanvas.pan(0,0); - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractScaling#zoomIn(android.androidVNC.VncCanvasActivity) - */ - @Override - void zoomIn(VncCanvasActivity activity) { - resetMatrix(); - standardizeScaling(); - scaling += 0.25; - if (scaling > 4.0) - { - scaling = (float)4.0; - activity.zoomer.setIsZoomInEnabled(false); - } - activity.zoomer.setIsZoomOutEnabled(false); //disable Zoomer - matrix.postScale(scaling, scaling); - //Log.v(TAG,String.format("before set matrix scrollx = %d scrolly = %d", activity.vncCanvas.getScrollX(), activity.vncCanvas.getScrollY())); - activity.vncCanvas.setImageMatrix(matrix); - resolveZoom(activity); - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractScaling#getScale() - */ - @Override - float getScale() { - return scaling; - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractScaling#zoomOut(android.androidVNC.VncCanvasActivity) - */ - @Override - void zoomOut(VncCanvasActivity activity) { - resetMatrix(); - standardizeScaling(); - scaling -= 0.25; - if (scaling < minimumScale) - { - scaling = minimumScale; - activity.zoomer.setIsZoomOutEnabled(false); - } - activity.zoomer.setIsZoomInEnabled(true); - matrix.postScale(scaling, scaling); - //Log.v(TAG,String.format("before set matrix scrollx = %d scrolly = %d", activity.vncCanvas.getScrollX(), activity.vncCanvas.getScrollY())); - activity.vncCanvas.setImageMatrix(matrix); - //Log.v(TAG,String.format("after set matrix scrollx = %d scrolly = %d", activity.vncCanvas.getScrollX(), activity.vncCanvas.getScrollY())); - resolveZoom(activity); - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractScaling#adjust(android.androidVNC.VncCanvasActivity, float, float, float) - */ - @Override - void adjust(VncCanvasActivity activity, float scaleFactor, float fx, - float fy) { - float newScale = scaleFactor * scaling; - if (scaleFactor < 1) - { - if (newScale < minimumScale) - { - newScale = minimumScale; - activity.zoomer.setIsZoomOutEnabled(false); - } - activity.zoomer.setIsZoomInEnabled(true); - } - else - { - if (newScale > 4) - { - newScale = 4; - activity.zoomer.setIsZoomInEnabled(false); - } - activity.zoomer.setIsZoomOutEnabled(true); - } - // ax is the absolute x of the focus - int xPan = activity.vncCanvas.absoluteXPosition; - float ax = (fx / scaling) + xPan; - float newXPan = (scaling * xPan - scaling * ax + newScale * ax)/newScale; - int yPan = activity.vncCanvas.absoluteYPosition; - float ay = (fy / scaling) + yPan; - float newYPan = (scaling * yPan - scaling * ay + newScale * ay)/newScale; - resetMatrix(); - scaling = newScale; - matrix.postScale(scaling, scaling); - activity.vncCanvas.setImageMatrix(matrix); - resolveZoom(activity); - activity.vncCanvas.pan((int)(newXPan - xPan), (int)(newYPan - yPan)); - } - - private void resetMatrix() - { - matrix.reset(); - matrix.preTranslate(canvasXOffset, canvasYOffset); - } - - /** - * Set scaling to one of the clicks on the zoom scale - */ - private void standardizeScaling() - { - scaling = ((float)((int)(scaling * 4))) / 4; - } - - /* (non-Javadoc) - * @see android.androidVNC.AbstractScaling#setScaleTypeForActivity(android.androidVNC.VncCanvasActivity) - */ - @Override - public void setScaleTypeForActivity(VncCanvasActivity activity) { - super.setScaleTypeForActivity(activity); - scaling = (float)1.0; - minimumScale = activity.vncCanvas.bitmapData.getMinimumScale(); - canvasXOffset = -activity.vncCanvas.getCenteredXOffset(); - canvasYOffset = -activity.vncCanvas.getCenteredYOffset(); - resetMatrix(); - activity.vncCanvas.setImageMatrix(matrix); - // Reset the pan position to (0,0) - resolveZoom(activity); - } - -} diff --git a/limbo-android-lib/src/main/java/android/androidVNC/androidVNC.java b/limbo-android-lib/src/main/java/android/androidVNC/androidVNC.java deleted file mode 100644 index bd51d47f1..000000000 --- a/limbo-android-lib/src/main/java/android/androidVNC/androidVNC.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -// -// androidVNC is the Activity for setting VNC server IP and port. -// - -package android.androidVNC; - -import java.util.ArrayList; - -import com.limbo.emu.lib.R; - -import android.app.Activity; -import android.app.ActivityManager.MemoryInfo; -import android.app.Dialog; -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.RadioGroup; -import android.widget.Spinner; -import android.widget.TextView; - -public class androidVNC extends Activity { - private EditText ipText; - private EditText portText; - private EditText passwordText; - private Button goButton; - private TextView repeaterText; - private RadioGroup groupForceFullScreen; - private Spinner colorSpinner; - private Spinner spinnerConnection; - private ConnectionBean selected; - private EditText textNickname; - private EditText textUsername; - private CheckBox checkboxKeepPassword; - private CheckBox checkboxLocalCursor; - private boolean repeaterTextSet; - - @Override - public void onCreate(Bundle icicle) { - - super.onCreate(icicle); - setContentView(R.layout.limbo_main); - - ipText = (EditText) findViewById(R.id.textIP); - portText = (EditText) findViewById(R.id.textPORT); - passwordText = (EditText) findViewById(R.id.textPASSWORD); - textNickname = (EditText) findViewById(R.id.textNickname); - textUsername = (EditText) findViewById(R.id.textUsername); - goButton = (Button) findViewById(R.id.buttonGO); - ((Button) findViewById(R.id.buttonRepeater)) - .setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - showDialog(R.layout.repeater_dialog); - } - }); - ((Button) findViewById(R.id.buttonImportExport)) - .setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - showDialog(R.layout.importexport); - } - }); - colorSpinner = (Spinner) findViewById(R.id.colorformat); - COLORMODEL[] models = COLORMODEL.values(); - ArrayAdapter colorSpinnerAdapter = new ArrayAdapter( - this, android.R.layout.simple_spinner_item, models); - groupForceFullScreen = (RadioGroup) findViewById(R.id.groupForceFullScreen); - checkboxKeepPassword = (CheckBox) findViewById(R.id.checkboxKeepPassword); - checkboxLocalCursor = (CheckBox) findViewById(R.id.checkboxUseLocalCursor); - colorSpinner.setAdapter(colorSpinnerAdapter); - colorSpinner.setSelection(0); - spinnerConnection = (Spinner) findViewById(R.id.spinnerConnection); - spinnerConnection - .setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView ad, View view, - int itemIndex, long id) { - selected = (ConnectionBean) ad.getSelectedItem(); - updateViewFromSelected(); - } - - @Override - public void onNothingSelected(AdapterView ad) { - selected = null; - } - }); - spinnerConnection - .setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { - - /* - * (non-Javadoc) - * - * @see android.widget.AdapterView.OnItemLongClickListener# - * onItemLongClick(android.widget.AdapterView, - * android.view.View, int, long) - */ - @Override - public boolean onItemLongClick(AdapterView arg0, - View arg1, int arg2, long arg3) { - spinnerConnection.setSelection(arg2); - selected = (ConnectionBean) spinnerConnection - .getItemAtPosition(arg2); - canvasStart(); - return true; - } - - }); - repeaterText = (TextView) findViewById(R.id.textRepeaterId); - goButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - canvasStart(); - } - }); - - } - - protected void onDestroy() { - - super.onDestroy(); - } - - /* - * (non-Javadoc) - * - * @see android.app.Activity#onCreateDialog(int) - */ - @Override - protected Dialog onCreateDialog(int id) { - return new RepeaterDialog(this); - } - - /* - * (non-Javadoc) - * - * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu) - */ - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.androidvncmenu, menu); - return true; - } - - - /* - * (non-Javadoc) - * - * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem) - */ - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == R.id.itemSaveAsCopy){ - updateSelectedFromView(); - arriveOnPage(); - } else if (item.getItemId() == R.id.itemDeleteConnection || - item.getItemId() == R.id.itemOpenDoc) { - Utils.showDocumentation(this); - } - return true; - } - - private void updateViewFromSelected() { - if (selected == null) - return; - ipText.setText(selected.getAddress()); - portText.setText(Integer.toString(selected.getPort())); - if (selected.getPassword().length() > 0) { - passwordText.setText(selected.getPassword()); - } - groupForceFullScreen - .check(selected.getForceFull() == BitmapImplHint.AUTO ? R.id.radioForceFullScreenAuto - : (selected.getForceFull() == BitmapImplHint.FULL ? R.id.radioForceFullScreenOn - : R.id.radioForceFullScreenOff)); - checkboxLocalCursor.setChecked(selected.getUseLocalCursor()); - textNickname.setText(selected.getNickname()); - COLORMODEL cm = COLORMODEL.valueOf(selected.getColorModel()); - COLORMODEL[] colors = COLORMODEL.values(); - for (int i = 0; i < colors.length; ++i) { - if (colors[i] == cm) { - colorSpinner.setSelection(i); - break; - } - } - } - - /** - * Called when changing view to match selected connection or from Repeater - * dialog to update the repeater information shown. - * - * @param repeaterId - * If null or empty, show text for not using repeater - */ - void updateRepeaterInfo(boolean useRepeater, String repeaterId) { - if (useRepeater) { - repeaterText.setText(repeaterId); - repeaterTextSet = true; - } else { - repeaterText.setText(getText(R.string.repeater_empty_text)); - repeaterTextSet = false; - } - } - - private void updateSelectedFromView() { - if (selected == null) { - return; - } - selected.setAddress(ipText.getText().toString()); - try { - selected.setPort(Integer.parseInt(portText.getText().toString())); - } catch (NumberFormatException nfe) { - - } - selected.setNickname(textNickname.getText().toString()); - - selected.setForceFull(groupForceFullScreen.getCheckedRadioButtonId() == R.id.radioForceFullScreenAuto ? BitmapImplHint.AUTO - : (groupForceFullScreen.getCheckedRadioButtonId() == R.id.radioForceFullScreenOn ? BitmapImplHint.FULL - : BitmapImplHint.TILE)); - selected.setPassword(passwordText.getText().toString()); - - selected.setUseLocalCursor(checkboxLocalCursor.isChecked()); - selected.setColorModel(((COLORMODEL) colorSpinner.getSelectedItem()) - .nameString()); - } - - protected void onStart() { - super.onStart(); - arriveOnPage(); - } - - void arriveOnPage() { - ArrayList connections = new ArrayList(); - connections.add(0, new ConnectionBean()); - int connectionIndex = 0; - - spinnerConnection.setAdapter(new ArrayAdapter(this, - android.R.layout.simple_spinner_item, connections - .toArray(new ConnectionBean[connections.size()]))); - spinnerConnection.setSelection(connectionIndex, false); - selected = connections.get(connectionIndex); - updateViewFromSelected(); - } - - protected void onStop() { - super.onStop(); - if (selected == null) { - return; - } - updateSelectedFromView(); - - } - - - - private void canvasStart() { - if (selected == null) - return; - MemoryInfo info = Utils.getMemoryInfo(this); - if (info.lowMemory) { - // Low Memory situation. Prompt. - Utils.showYesNoPrompt( - this, - "Continue?", - "Android reports low system memory.\nContinue with VNC connection?", - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - vnc(); - } - }, null); - } else - vnc(); - } - - private void vnc() { - updateSelectedFromView(); - Intent intent = new Intent(this, VncCanvasActivity.class); - startActivity(intent); - } - -} diff --git a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCActivityManagerDefault.java b/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCActivityManagerDefault.java deleted file mode 100644 index a5d9b603e..000000000 --- a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCActivityManagerDefault.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package com.antlersoft.android.bc; - -import android.app.ActivityManager; - -/** - * @author Michael A. MacDonald - */ -class BCActivityManagerDefault implements IBCActivityManager { - - /* (non-Javadoc) - * @see com.antlersoft.android.bc.IBCActivityManager#getMemoryClass(android.app.ActivityManager) - */ - @Override - public int getMemoryClass(ActivityManager am) { - return 16; - } - -} diff --git a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCActivityManagerV5.java b/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCActivityManagerV5.java deleted file mode 100644 index a764c76a9..000000000 --- a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCActivityManagerV5.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package com.antlersoft.android.bc; - -import android.app.ActivityManager; - -/** - * @author Michael A. MacDonald - */ -public class BCActivityManagerV5 implements IBCActivityManager { - - /* (non-Javadoc) - * @see com.antlersoft.android.bc.IBCActivityManager#getMemoryClass(android.app.ActivityManager) - */ - @Override - public int getMemoryClass(ActivityManager am) { - return am.getMemoryClass(); - } - -} diff --git a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCFactory.java b/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCFactory.java deleted file mode 100644 index d9cf102eb..000000000 --- a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCFactory.java +++ /dev/null @@ -1,191 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package com.antlersoft.android.bc; - -/** - * Create interface implementations appropriate to the current version of the SDK; - * implementations can allow use of higher-level SDK calls in .apk's that will still run - * on lower-level SDK's - * @author Michael A. MacDonald - */ -public class BCFactory { - - private static BCFactory _theInstance = new BCFactory(); - - private IBCActivityManager bcActivityManager; - private IBCHaptic bcHaptic; - private IBCMotionEvent bcMotionEvent; - private IBCStorageContext bcStorageContext; - - /** - * This is here so checking the static doesn't get optimized away; - * note we can't use SDK_INT because that is too new - * @return sdk version - */ - int getSdkVersion() - { - try - { - return android.os.Build.VERSION.SDK_INT; - } - catch (NumberFormatException nfe) - { - return 1; - } - } - - /** - * Return the implementation of IBCActivityManager appropriate for this SDK level - * @return - */ - public IBCActivityManager getBCActivityManager() - { - if (bcActivityManager == null) - { - synchronized (this) - { - if (bcActivityManager == null) - { - if (getSdkVersion() >= 5) - { - try - { - bcActivityManager = (IBCActivityManager)getClass().getClassLoader().loadClass("com.antlersoft.android.bc.BCActivityManagerV5").newInstance(); - } - catch (Exception ie) - { - bcActivityManager = new BCActivityManagerDefault(); - throw new RuntimeException("Error instantiating", ie); - } - } - else - { - bcActivityManager = new BCActivityManagerDefault(); - } - } - } - } - return bcActivityManager; - } - - - - /** - * Return the implementation of IBCHaptic appropriate for this SDK level - * - * Since we dropped support of SDK levels prior to 3, there is only one version at the moment. - * @return - */ - public IBCHaptic getBCHaptic() - { - if (bcHaptic == null) - { - synchronized (this) - { - if (bcHaptic == null) - { - try - { - bcHaptic = (IBCHaptic)getClass().getClassLoader().loadClass("com.antlersoft.android.bc.BCHapticDefault").newInstance(); - } - catch (Exception ie) - { - throw new RuntimeException("Error instantiating", ie); - } - } - } - } - return bcHaptic; - } - - /** - * Return the implementation of IBCMotionEvent appropriate for this SDK level - * @return - */ - public IBCMotionEvent getBCMotionEvent() - { - if (bcMotionEvent == null) - { - synchronized (this) - { - if (bcMotionEvent == null) - { - if (getSdkVersion() >= 5) - { - try - { - bcMotionEvent = (IBCMotionEvent)getClass().getClassLoader().loadClass("com.antlersoft.android.bc.BCMotionEvent5").newInstance(); - } - catch (Exception ie) - { - throw new RuntimeException("Error instantiating", ie); - } - } - else - { - try - { - bcMotionEvent = (IBCMotionEvent)getClass().getClassLoader().loadClass("com.antlersoft.android.bc.BCMotionEvent4").newInstance(); - } - catch (Exception ie) - { - throw new RuntimeException("Error instantiating", ie); - } - } - } - } - } - return bcMotionEvent; - } - - /** - * - * @return An implementation of IBCStorageContext appropriate for the running Android release - */ - public IBCStorageContext getStorageContext() - { - if (bcStorageContext == null) - { - synchronized (this) - { - if (bcStorageContext == null) - { - if (getSdkVersion() >= 8) - { - try - { - bcStorageContext = (IBCStorageContext)getClass().getClassLoader().loadClass("com.antlersoft.android.bc.BCStorageContext8").newInstance(); - } - catch (Exception ie) - { - throw new RuntimeException("Error instantiating", ie); - } - } - else - { - try - { - bcStorageContext = (IBCStorageContext)getClass().getClassLoader().loadClass("com.antlersoft.android.bc.BCStorageContext7").newInstance(); - } - catch (Exception ie) - { - throw new RuntimeException("Error instantiating", ie); - } - } - } - } - } - return bcStorageContext; - } - - /** - * Returns the only instance of this class, which manages the SDK specific interface - * implementations - * @return Factory instance - */ - public static BCFactory getInstance() - { - return _theInstance; - } -} diff --git a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCHapticDefault.java b/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCHapticDefault.java deleted file mode 100644 index c632c05ed..000000000 --- a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCHapticDefault.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package com.antlersoft.android.bc; - -import android.view.View; -import android.view.HapticFeedbackConstants; - -/** - * Implementation for SDK version >= 3 - * @author Michael A. MacDonald - */ -class BCHapticDefault implements IBCHaptic { - - /* (non-Javadoc) - * @see com.antlersoft.android.bc.IBCHaptic#performLongPressHaptic(android.view.View) - */ - @Override - public boolean performLongPressHaptic(View v) { - return v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING|HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING - ); - } - - /* (non-Javadoc) - * @see com.antlersoft.android.bc.IBCHaptic#setIsHapticEnabled(android.view.View, boolean) - */ -/* - * @Override - public boolean setIsHapticEnabled(View v, boolean enabled) { - return v.setHapticFeedbackEnabled(hapticFeedbackEnabled) - } -*/ -} diff --git a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCMotionEvent4.java b/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCMotionEvent4.java deleted file mode 100644 index 9e7eab2c8..000000000 --- a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCMotionEvent4.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2010 Michael A. MacDonald - */ -package com.antlersoft.android.bc; - -import android.view.MotionEvent; - -/** - * Pre-sdk 5 version; add fake multi-touch sensing later? - * - * @author Michael A. MacDonald - * - */ -class BCMotionEvent4 implements IBCMotionEvent { - - /* (non-Javadoc) - * @see com.antlersoft.android.bc.IBCMotionEvent#getPointerCount(android.view.MotionEvent) - */ - @Override - public int getPointerCount(MotionEvent evt) { - return 1; - } - -} diff --git a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCMotionEvent5.java b/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCMotionEvent5.java deleted file mode 100644 index af15236d0..000000000 --- a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCMotionEvent5.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) 2010 Michael A. MacDonald - */ -package com.antlersoft.android.bc; - -import android.view.MotionEvent; - -/** - * @author Michael A. MacDonald - * - */ -class BCMotionEvent5 implements IBCMotionEvent { - - /* (non-Javadoc) - * @see com.antlersoft.android.bc.IBCMotionEvent#getPointerCount(android.view.MotionEvent) - */ - @Override - public int getPointerCount(MotionEvent evt) { - return evt.getPointerCount(); - } - -} diff --git a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCStorageContext7.java b/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCStorageContext7.java deleted file mode 100644 index 27587830b..000000000 --- a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCStorageContext7.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) 2011 Michael A. MacDonald - */ -package com.antlersoft.android.bc; - -import java.io.File; - -import android.content.Context; - -import android.os.Environment; - -/** - * @author Michael A. MacDonald - * - */ -public class BCStorageContext7 implements IBCStorageContext { - - /* (non-Javadoc) - * @see com.antlersoft.android.bc.IBCStorageContext#getExternalStorageDir(android.content.Context, java.lang.String) - */ - @Override - public File getExternalStorageDir(Context context, String type) { - File f = Environment.getExternalStorageDirectory(); - f = new File(f, "Android/data/android.androidVNC/files"); - if (type != null) - f=new File(f, type); - f.mkdirs(); - return f; - } - -} diff --git a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCStorageContext8.java b/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCStorageContext8.java deleted file mode 100644 index 3a7988fef..000000000 --- a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/BCStorageContext8.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2010 Michael A. MacDonald - */ -package com.antlersoft.android.bc; - -import java.io.File; - -import android.content.Context; - -/** - * @author Michael A. MacDonald - * - */ -class BCStorageContext8 implements IBCStorageContext { - - /* (non-Javadoc) - * @see com.antlersoft.android.bc.IBCStorageContext#getExternalStorageDir(android.content.Context, java.lang.String) - */ - @Override - public File getExternalStorageDir(Context context, String type) { - return context.getExternalFilesDir(type); - } - -} diff --git a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/IBCActivityManager.java b/limbo-android-lib/src/main/java/com/antlersoft/android/bc/IBCActivityManager.java deleted file mode 100644 index 103c19ef1..000000000 --- a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/IBCActivityManager.java +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package com.antlersoft.android.bc; - -import android.app.ActivityManager; - -/** - * @author Michael A. MacDonald - */ -public interface IBCActivityManager { - public int getMemoryClass(ActivityManager am); -} diff --git a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/IBCHaptic.java b/limbo-android-lib/src/main/java/com/antlersoft/android/bc/IBCHaptic.java deleted file mode 100644 index 4a53f7c6a..000000000 --- a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/IBCHaptic.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package com.antlersoft.android.bc; - -import android.view.View; - -/** - * Access the Haptic interfaces added in version 3 without breaking compatibility - * @author Michael A. MacDonald - */ -public interface IBCHaptic { - public boolean performLongPressHaptic(View v); - /** - * Set whether haptic feedback is enabled on the view - * @param enabled - * @return Old value of setting - */ - //public boolean setIsHapticEnabled(View v, boolean enabled); -} diff --git a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/IBCMotionEvent.java b/limbo-android-lib/src/main/java/com/antlersoft/android/bc/IBCMotionEvent.java deleted file mode 100644 index 166f23a60..000000000 --- a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/IBCMotionEvent.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2010 Michael A. MacDonald - */ -package com.antlersoft.android.bc; - -import android.view.MotionEvent; - -/** - * Access to SDK-dependent features of MotionEvent - * - * @see android.view.MotionEvent - * - * @author Michael A. MacDonald - * - */ -public interface IBCMotionEvent { - /** - * Obtain the number of pointers active in the event - * @see android.view.MotionEvent#getPointerCount() - * @param evt - * @return number of pointers - */ - int getPointerCount(MotionEvent evt); -} diff --git a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/IBCStorageContext.java b/limbo-android-lib/src/main/java/com/antlersoft/android/bc/IBCStorageContext.java deleted file mode 100644 index 69d6ab615..000000000 --- a/limbo-android-lib/src/main/java/com/antlersoft/android/bc/IBCStorageContext.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) 2010 Michael A. MacDonald - */ -package com.antlersoft.android.bc; - -import android.content.Context; - -import java.io.File; - -/** - * Provides a way to access the directory on external storage as returned by - * Context.getExternal... added in API 8 that will work with earlier API releases. - * @author Michael A. MacDonald - * - */ -public interface IBCStorageContext { - /** - * - * @param context Context within the application with which the storage will be associated - * @param type May be null; if specified, references a sub-directory within the base directory - * for the app in the external storage - * @return File representing abstract path of storage directory; refer to android.os.Environment to - * see if the path is actually accessible - */ - public File getExternalStorageDir(Context context, String type); -} diff --git a/limbo-android-lib/src/main/java/com/antlersoft/android/drawing/OverlappingCopy.java b/limbo-android-lib/src/main/java/com/antlersoft/android/drawing/OverlappingCopy.java deleted file mode 100644 index 96e15fa2b..000000000 --- a/limbo-android-lib/src/main/java/com/antlersoft/android/drawing/OverlappingCopy.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.antlersoft.android.drawing; - -import com.antlersoft.util.ObjectPool; -import com.antlersoft.util.SafeObjectPool; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; - -public class OverlappingCopy -{ - private static SafeObjectPool ocRectPool = new SafeObjectPool() { - @Override - protected Rect itemForPool() - { - return new Rect(); - } - }; - private static void transformRect(Rect source, Rect transformedSource, int deltaX, int deltaY) - { - transformedSource.set(deltaX < 0 ? source.right * -1 : source.left, - deltaY < 0 ? source.bottom * -1 : source.top, - deltaX < 0 ? source.left * -1 : source.right, - deltaY < 0 ? source.top * -1 : source.bottom); - } - private static void copyTransformedRect(Rect stepSourceRect, Rect stepDestRect, int deltaX, int deltaY, Bitmap data, Canvas bitmapBackedCanvas, Paint paint) - { - transformRect(stepSourceRect,stepSourceRect,deltaX,deltaY); - stepDestRect.set(stepSourceRect); - stepDestRect.offset(deltaX,deltaY); - bitmapBackedCanvas.drawBitmap(data, stepSourceRect, stepDestRect, paint); - } - public static void Copy(Bitmap data, Canvas bitmapBackedCanvas, Paint paint, Rect source, int destX, int destY) - { - Copy(data,bitmapBackedCanvas,paint,source,destX,destY,ocRectPool); - } - public static void Copy(Bitmap data, Canvas bitmapBackedCanvas, Paint paint, Rect source, int destX, int destY, ObjectPool rectPool) - { - //android.util.Log.i("LBM","Copy "+source.toString()+" to "+destX+","+destY); - int deltaX = destX - source.left; - int deltaY = destY - source.top; - int absDeltaX = deltaX < 0 ? -deltaX : deltaX; - int absDeltaY = deltaY < 0 ? -deltaY : deltaY; - - // Look for degenerate case - if (absDeltaX == 0 && absDeltaY == 0) - return; - // Look for non-overlap case - if (absDeltaX >= source.right - source.left || absDeltaY >= source.bottom - source.top) - { - // Non-overlapping copy - ObjectPool.Entry entry = rectPool.reserve(); - Rect dest = entry.get(); - dest.set(source.left + deltaX, source.top + deltaY, source.right + deltaX, source.bottom + deltaY); - bitmapBackedCanvas.drawBitmap(data, source, dest, paint); - rectPool.release(entry); - return; - } - // Determine coordinate transform so that dest rectangle is always down and to the right. - ObjectPool.Entry transformedSourceEntry = rectPool.reserve(); - Rect transformedSource = transformedSourceEntry.get(); - transformRect(source,transformedSource,deltaX,deltaY); - ObjectPool.Entry transformedDestEntry = rectPool.reserve(); - Rect transformedDest = transformedDestEntry.get(); - transformedDest.set(transformedSource); - transformedDest.offset(absDeltaX, absDeltaY); - ObjectPool.Entry intersectEntry = rectPool.reserve(); - Rect intersect = intersectEntry.get(); - intersect.setIntersect(transformedSource, transformedDest); - - boolean xStepDone = false; - int xStepWidth; - int yStepHeight; - if (absDeltaX > absDeltaY) - { - xStepWidth = absDeltaX; - yStepHeight = source.bottom - source.top - absDeltaY; - } - else - { - xStepWidth = source.right - source.left - absDeltaX; - yStepHeight = absDeltaY; - } - - ObjectPool.Entry stepSourceEntry = rectPool.reserve(); - Rect stepSourceRect = stepSourceEntry.get(); - ObjectPool.Entry stepDestEntry = rectPool.reserve(); - Rect stepDestRect = stepDestEntry.get(); - - for (int xStep = 0; ! xStepDone; xStep++) - { - int stepRight = intersect.right - xStep * xStepWidth; - int stepLeft = stepRight - xStepWidth; - if (stepLeft <= intersect.left) - { - stepLeft = intersect.left; - xStepDone = true; - } - boolean yStepDone = false; - for (int yStep = 0; ! yStepDone; yStep++) - { - int stepBottom = intersect.bottom - yStep * yStepHeight; - int stepTop = stepBottom - yStepHeight; - if (stepTop <= intersect.top) - { - stepTop = intersect.top; - yStepDone = true; - } - stepSourceRect.set(stepLeft,stepTop,stepRight,stepBottom); - //android.util.Log.i("LBM","Copy transformed "+stepSourceRect.toString()+" "+deltaX+" "+deltaY); - copyTransformedRect(stepSourceRect, stepDestRect, deltaX, deltaY, data, bitmapBackedCanvas, paint); - } - } - if (absDeltaX>0) - { - // Copy left edge - stepSourceRect.set(transformedSource.left,transformedSource.top,intersect.left,transformedSource.bottom); - copyTransformedRect(stepSourceRect, stepDestRect, deltaX, deltaY, data, bitmapBackedCanvas, paint); - } - if (absDeltaY>0) - { - // Copy top excluding left edge - stepSourceRect.set(intersect.left,transformedSource.top,transformedSource.right,intersect.top); - copyTransformedRect(stepSourceRect, stepDestRect, deltaX, deltaY, data, bitmapBackedCanvas, paint); - } - - rectPool.release(stepDestEntry); - rectPool.release(stepSourceEntry); - rectPool.release(intersectEntry); - rectPool.release(transformedDestEntry); - rectPool.release(transformedSourceEntry); - } -} - - diff --git a/limbo-android-lib/src/main/java/com/antlersoft/android/drawing/RectList.java b/limbo-android-lib/src/main/java/com/antlersoft/android/drawing/RectList.java deleted file mode 100644 index c1de32087..000000000 --- a/limbo-android-lib/src/main/java/com/antlersoft/android/drawing/RectList.java +++ /dev/null @@ -1,550 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package com.antlersoft.android.drawing; - -import android.graphics.Rect; - -import java.util.ArrayList; - -import com.antlersoft.util.ObjectPool; - -/** - * A list of rectangular regions that together represent an area of interest. Provides - * a set of operations that apply to the whole area, adding, changing and mutating the - * rectangles in the list as required. - *

- * Invariants: None of the rectangles in the list overlap; no pair of rectangles in the list - * together make a single rectangle (none share a complete side) - *

- *

- * Instances of this class are not thread safe - *

- * @author Michael A. MacDonald - * - */ -public class RectList { - - enum OverlapType { - NONE, - SAME, - CONTAINS, - CONTAINED_BY, - COALESCIBLE, - PARTIAL - } - - static final int LEFT = 1; - static final int TOP_LEFT = 2; - static final int TOP = 4; - static final int TOP_RIGHT = 8; - static final int RIGHT = 16; - static final int BOTTOM_RIGHT = 32; - static final int BOTTOM = 64; - static final int BOTTOM_LEFT = 128; - - /** - * The part left over when one rectangle is subtracted from another - * @author Michael A. MacDonald - * - */ - static class NonOverlappingPortion - { - Rect leftPortion; - Rect topLeftPortion; - Rect topPortion; - Rect topRightPortion; - Rect rightPortion; - Rect bottomRightPortion; - Rect bottomPortion; - Rect bottomLeftPortion; - - int r1Owns; - int r2Owns; - int common; - int adjacent; - boolean horizontalOverlap; - boolean verticalOverlap; - - Rect coalesced; - - NonOverlappingPortion() - { - leftPortion = new Rect(); - topLeftPortion = new Rect(); - topPortion = new Rect(); - topRightPortion = new Rect(); - rightPortion = new Rect(); - bottomRightPortion = new Rect(); - bottomPortion = new Rect(); - bottomLeftPortion = new Rect(); - coalesced = new Rect(); - } - - void setCornerOwnership(int side1, int side2, int corner) - { - int combined = (side1 | side2); - if ((r1Owns & combined) == combined) - r1Owns |= corner; - else if ((r2Owns & combined) == combined) - r2Owns |= corner; - } - - void setCornerOwnership() - { - setCornerOwnership(LEFT,TOP,TOP_LEFT); - setCornerOwnership(TOP,RIGHT,TOP_RIGHT); - setCornerOwnership(BOTTOM,RIGHT,BOTTOM_RIGHT); - setCornerOwnership(BOTTOM,LEFT,BOTTOM_LEFT); - } - - /** - * Populates with the borders remaining when r2 is subtracted from r1 - * @param r1 - * @param r2 - * @return - */ - OverlapType overlap(Rect r1, Rect r2) - { - r1Owns = 0; - r2Owns = 0; - common = 0; - adjacent = 0; - OverlapType result = OverlapType.NONE; - horizontalOverlap = false; - verticalOverlap = false; - - if (r1.left < r2.left) - { - leftPortion.left = topLeftPortion.left = bottomLeftPortion.left = r1.left; - if (r2.left < r1.right) { - leftPortion.right = topLeftPortion.right = bottomLeftPortion.right = topPortion.left = bottomPortion.left = r2.left; - horizontalOverlap = true; - } else { - leftPortion.right = topLeftPortion.right = bottomLeftPortion.right = topPortion.left = bottomPortion.left = r1.right; - if (r2.left == r1.right) - adjacent |= LEFT; - } - r1Owns |= LEFT; - } - else - { - leftPortion.left = topLeftPortion.left = bottomLeftPortion.left = r2.left; - if (r1.left < r2.right) { - leftPortion.right = topLeftPortion.right = bottomLeftPortion.right = topPortion.left = bottomPortion.left = r1.left; - horizontalOverlap = true; - } else { - leftPortion.right = topLeftPortion.right = bottomLeftPortion.right = topPortion.left = bottomPortion.left = r2.right; - if ( r1.left == r2.right) - adjacent |= RIGHT; - } - if (r2.left < r1.left) - r2Owns |= LEFT; - else - common |= LEFT; - } - if (r1.top < r2.top) - { - topPortion.top = topLeftPortion.top = topRightPortion.top = r1.top; - if (r2.top < r1.bottom) { - topPortion.bottom = topLeftPortion.bottom = topRightPortion.bottom = leftPortion.top = rightPortion.top = r2.top; - verticalOverlap = true; - } else { - topPortion.bottom = topLeftPortion.bottom = topRightPortion.bottom = leftPortion.top = rightPortion.top = r1.bottom; - if (r2.top == r1.bottom) - adjacent |= TOP; - } - r1Owns |= TOP; - } - else - { - topPortion.top = topLeftPortion.top = topRightPortion.top = r2.top; - if (r1.top < r2.bottom) { - topPortion.bottom = topLeftPortion.bottom = topRightPortion.bottom = leftPortion.top = rightPortion.top = r1.top; - verticalOverlap = true; - } else { - topPortion.bottom = topLeftPortion.bottom = topRightPortion.bottom = leftPortion.top = rightPortion.top = r2.bottom; - if (r1.top == r2.bottom) - adjacent |= BOTTOM; - } - if (r2.top < r1.top) - r2Owns |= TOP; - else - common |= TOP; - } - if (r1.right > r2.right) - { - rightPortion.right = topRightPortion.right = bottomRightPortion.right = r1.right; - if (r2.right > r1.left) { - rightPortion.left = topRightPortion.left = bottomRightPortion.left = topPortion.right = bottomPortion.right = r2.right; - horizontalOverlap = true; - } else { - rightPortion.left = topRightPortion.left = bottomRightPortion.left = topPortion.right = bottomPortion.right = r1.left; - if (r2.right == r1.left) - adjacent |= RIGHT; - } - r1Owns |= RIGHT; - } - else - { - rightPortion.right = topRightPortion.right = bottomRightPortion.right = r2.right; - if (r1.right > r2.left) { - rightPortion.left = topRightPortion.left = bottomRightPortion.left = topPortion.right = bottomPortion.right = r1.right; - horizontalOverlap = true; - } else { - rightPortion.left = topRightPortion.left = bottomRightPortion.left = topPortion.right = bottomPortion.right = r2.left; - if (r1.right==r2.left) - adjacent |= LEFT; - } - if (r2.right > r1.right) - r2Owns |= RIGHT; - else - common |= RIGHT; - } - if (r1.bottom > r2.bottom) - { - bottomPortion.bottom = bottomLeftPortion.bottom = bottomRightPortion.bottom = r1.bottom; - if (r2.bottom > r1.top) { - bottomPortion.top = bottomLeftPortion.top = bottomRightPortion.top = leftPortion.bottom = rightPortion.bottom = r2.bottom; - verticalOverlap = true; - } else { - bottomPortion.top = bottomLeftPortion.top = bottomRightPortion.top = leftPortion.bottom = rightPortion.bottom = r1.top; - if (r2.bottom==r1.top) - adjacent |= BOTTOM; - } - r1Owns |= BOTTOM; - } - else - { - bottomPortion.bottom = bottomLeftPortion.bottom = bottomRightPortion.bottom = r2.bottom; - if (r1.bottom > r2.top) { - bottomPortion.top = bottomLeftPortion.top = bottomRightPortion.top = leftPortion.bottom = rightPortion.bottom = r1.bottom; - verticalOverlap = true; - } else { - bottomPortion.top = bottomLeftPortion.top = bottomRightPortion.top = leftPortion.bottom = rightPortion.bottom = r2.top; - if (r1.bottom==r2.top) - adjacent |= TOP; - } - if (r2.bottom > r1.bottom) - r2Owns |= BOTTOM; - else - common |= BOTTOM; - } - if ( common == (LEFT|RIGHT|TOP|BOTTOM)) - { - result = OverlapType.SAME; - } - else if ((common & (LEFT|RIGHT)) == (LEFT | RIGHT) && (verticalOverlap || (adjacent & (TOP | BOTTOM)) != 0)) - { - result = OverlapType.COALESCIBLE; - coalesced.left = r1.left; - coalesced.right = r1.right; - coalesced.top = topPortion.top; - coalesced.bottom = bottomPortion.bottom; - } - else if ((common & (TOP | BOTTOM)) == (TOP | BOTTOM) && (horizontalOverlap || (adjacent & (LEFT | RIGHT)) != 0)) - { - result = OverlapType.COALESCIBLE; - coalesced.left = leftPortion.left; - coalesced.right = rightPortion.right; - coalesced.top = r1.top; - coalesced.bottom = r1.bottom; - } - else if (verticalOverlap && horizontalOverlap) { - if (r2Owns == 0) - { - result = OverlapType.CONTAINED_BY; - } - else if (r1Owns == 0) - { - result = OverlapType.CONTAINS; - } - else - { - // Partial overlap, non coalescible case - result = OverlapType.PARTIAL; - setCornerOwnership(); - } - } - return result; - } - } - - /** - * Up to 8 Rect objects - * @author Michael A. MacDonald - * - */ - static class NonOverlappingRects - { - ObjectPool.Entry[] rectEntries; - int count; - static final int MAX_RECTS = 8; - - @SuppressWarnings("unchecked") - NonOverlappingRects() - { - rectEntries = new ObjectPool.Entry[MAX_RECTS]; - } - - private void addOwnedRect(int owner, int direction, ObjectPool pool, Rect r) - { - if ((owner & direction)==direction) - { - ObjectPool.Entry entry = pool.reserve(); - rectEntries[count++] = entry; - entry.get().set(r); - } - } - - void Populate(NonOverlappingPortion p, ObjectPool pool, int owner) - { - count = 0; - for (int i=0; i> list; - private ObjectPool pool; - private ObjectPool nonOverlappingRectsPool = new ObjectPool() { - - /* (non-Javadoc) - * @see com.antlersoft.util.ObjectPool#itemForPool() - */ - @Override - protected NonOverlappingRects itemForPool() { - return new NonOverlappingRects(); - } - - }; - private ObjectPool>> listRectsPool = new ObjectPool>>() { - - /* (non-Javadoc) - * @see com.antlersoft.util.ObjectPool#itemForPool() - */ - @Override - protected ArrayList> itemForPool() { - return new ArrayList>(NonOverlappingRects.MAX_RECTS); - } - }; - private NonOverlappingPortion nonOverlappingPortion; - - public RectList(ObjectPool pool) - { - this.pool = pool; - list = new ArrayList>(); - nonOverlappingPortion = new NonOverlappingPortion(); - } - - public int getSize() - { - return list.size(); - } - - public Rect get(int i) - { - return list.get(i).get(); - } - - /** - * Remove all rectangles from the list and release them from the pool - */ - public void clear() - { - for (int i=list.size()-1; i>=0; i--) - { - ObjectPool.Entry r = list.get(i); - pool.release(r); - } - list.clear(); - } - - private void recursiveAdd(ObjectPool.Entry toAdd, int level) - { - if (level>=list.size()) - { - list.add(toAdd); - return; - } - Rect addRect = toAdd.get(); - ObjectPool.Entry thisEntry = list.get(level); - Rect thisRect = thisEntry.get(); - switch (nonOverlappingPortion.overlap(thisRect, addRect)) - { - case NONE : - recursiveAdd(toAdd,level + 1); - break; - case SAME : - case CONTAINS : - pool.release(toAdd); - break; - case CONTAINED_BY : - pool.release(thisEntry); - list.remove(level); - recursiveAdd(toAdd,level); - break; - case COALESCIBLE : - pool.release(thisEntry); - list.remove(level); - addRect.set(nonOverlappingPortion.coalesced); - recursiveAdd(toAdd,0); - break; - case PARTIAL : - pool.release(toAdd); - ObjectPool.Entry rectsEntry = nonOverlappingRectsPool.reserve(); - NonOverlappingRects rects = rectsEntry.get(); - rects.Populate(nonOverlappingPortion,pool,nonOverlappingPortion.r2Owns); - for (int i=0; i entry = pool.reserve(); - Rect r = entry.get(); - r.set(toAdd); - recursiveAdd(entry,0); - } - - /** - * Change the rectangle of interest to include only those portions - * that fall inside bounds. - * @param bounds - */ - public void intersect(Rect bounds) - { - int size = list.size(); - ObjectPool.Entry>> listEntry = listRectsPool.reserve(); - ArrayList> newList = listEntry.get(); - newList.clear(); - for (int i=0; i entry = list.get(i); - Rect rect = entry.get(); - if (rect.intersect(bounds)) - { - newList.add(entry); - } - else - pool.release(entry); - } - list.clear(); - size = newList.size(); - for (int i=0; i>> listEntry = listRectsPool.reserve(); - ArrayList> newList = listEntry.get(); - newList.clear(); - for (int i=0; i entry = list.get(i); - Rect rect = entry.get(); - switch (nonOverlappingPortion.overlap(rect, toSubtract)) - { - case SAME: - pool.release(entry); - newList.clear(); - list.remove(i); - return; - case CONTAINED_BY: - pool.release(entry); - list.remove(i); - i--; - size--; - break; - case NONE: - break; - case COALESCIBLE: - if (!nonOverlappingPortion.verticalOverlap || ! nonOverlappingPortion.horizontalOverlap) - break; - case CONTAINS : - nonOverlappingPortion.setCornerOwnership(); - case PARTIAL : - { - ObjectPool.Entry rectsEntry = nonOverlappingRectsPool.reserve(); - NonOverlappingRects rects = rectsEntry.get(); - rects.Populate(nonOverlappingPortion, pool, nonOverlappingPortion.r1Owns); - pool.release(entry); - list.remove(i); - i--; - size--; - for (int j=0; j m_max_size) - { - m_max_size = Math.max(2 * m_max_size, new_size); - byte[] new_buffer = new byte[m_max_size]; - System.arraycopy(m_buffer, 0, new_buffer, 0, result); - m_buffer = new_buffer; - } - - return result; - } - - public void release() - { - if (m_depth<1) - { - throw new IllegalStateException("release() without reserve()"); - } - m_depth--; - } -} diff --git a/limbo-android-lib/src/main/java/com/antlersoft/util/ObjectPool.java b/limbo-android-lib/src/main/java/com/antlersoft/util/ObjectPool.java deleted file mode 100644 index e5043096f..000000000 --- a/limbo-android-lib/src/main/java/com/antlersoft/util/ObjectPool.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package com.antlersoft.util; - -/** - * A pool of reusable object of a given type. You get the object from a Entry, which you get - * by calling reserve(). When you are done with the object, you call release() passing the Entry. - *

- * Failing to call release() does not leak memory--but you will not get the benefits - * of reusing the object. You will run into contention issues if you - * call release() while still holding a reference to the pool object. - * @author Michael A. MacDonald - * - */ -public abstract class ObjectPool { - public static class Entry { - S item; - Entry nextEntry; - - Entry(S i, Entry n) - { - item = i; - nextEntry = n; - } - - public S get() { - return item; - } - } - - private Entry next; - public ObjectPool() - { - next = null; - } - - public Entry reserve() - { - if (next == null) - { - next = new Entry(itemForPool(), null); - } - Entry result = next; - next = result.nextEntry; - result.nextEntry = null; - - return result; - } - - public void release(Entry entry) - { - entry.nextEntry = next; - next = entry; - } - - protected abstract R itemForPool(); -} diff --git a/limbo-android-lib/src/main/java/com/antlersoft/util/SafeObjectPool.java b/limbo-android-lib/src/main/java/com/antlersoft/util/SafeObjectPool.java deleted file mode 100644 index 3d197ab0c..000000000 --- a/limbo-android-lib/src/main/java/com/antlersoft/util/SafeObjectPool.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (C) 2009 Michael A. MacDonald - */ -package com.antlersoft.util; - -/** - * Synchronized object pool - * @author Michael A. MacDonald - * - */ -public abstract class SafeObjectPool extends ObjectPool { - - /* (non-Javadoc) - * @see com.antlersoft.util.ObjectPool#release(com.antlersoft.util.ObjectPool.Entry) - */ - @Override - public synchronized void release(com.antlersoft.util.ObjectPool.Entry entry) { - super.release(entry); - } - - /* (non-Javadoc) - * @see com.antlersoft.util.ObjectPool#reserve() - */ - @Override - public synchronized com.antlersoft.util.ObjectPool.Entry reserve() { - return super.reserve(); - } - -} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/dialog/DialogUtils.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/dialog/DialogUtils.java new file mode 100644 index 000000000..a05a04cb1 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/dialog/DialogUtils.java @@ -0,0 +1,78 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.dialog; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.graphics.Color; +import android.widget.ScrollView; +import android.widget.TextView; + +public class DialogUtils { + private static final String TAG = "DialogUtils"; + + public static void UIAlert(Activity activity, String title, String body) { + + final AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(activity).create(); + alertDialog.setTitle(title); + TextView textView = new TextView(activity); + textView.setPadding(20, 20, 20, 20); + textView.setText(body); + ScrollView view = new ScrollView(activity); + view.addView(textView); + alertDialog.setView(view); + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, + activity.getResources().getString(android.R.string.ok), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + alertDialog.dismiss(); + } + }); + alertDialog.show(); + } + + public static void UIAlert(Activity activity, String title, String body, int textSize, boolean cancelable, + String button1title, DialogInterface.OnClickListener button1Listener, + String button2title, DialogInterface.OnClickListener button2Listener, + String button3title, DialogInterface.OnClickListener button3Listener + ) { + + AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(activity).create(); + alertDialog.setTitle(title); + alertDialog.setCanceledOnTouchOutside(cancelable); + TextView textView = new TextView(activity); + textView.setPadding(20, 20, 20, 20); + textView.setText(body); + if (textSize > 0) + textView.setTextSize(textSize); + ScrollView view = new ScrollView(activity); + view.addView(textView); + alertDialog.setView(view); + if (button1title != null) + alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, button1title, button1Listener); + if (button2title != null) + alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, button2title, button2Listener); + if (button3title != null) + alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, button3title, button3Listener); + alertDialog.show(); + } +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/FileInstaller.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/files/FileInstaller.java similarity index 71% rename from limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/FileInstaller.java rename to limbo-android-lib/src/main/java/com/max2idea/android/limbo/files/FileInstaller.java index 08d0dfd02..041a05c30 100644 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/FileInstaller.java +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/files/FileInstaller.java @@ -1,5 +1,5 @@ /* - Copyright (C) Max Kastanas 2012 +Copyright (C) Max Kastanas 2012 * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,15 +16,19 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -package com.max2idea.android.limbo.utils; +package com.max2idea.android.limbo.files; import android.app.Activity; +import android.content.Context; import android.content.res.AssetManager; import android.net.Uri; import androidx.documentfile.provider.DocumentFile; import android.util.Log; +import com.limbo.emu.lib.R; import com.max2idea.android.limbo.main.Config; +import com.max2idea.android.limbo.main.LimboApplication; +import com.max2idea.android.limbo.toast.ToastUtils; import java.io.File; import java.io.FileOutputStream; @@ -39,33 +43,37 @@ * @author dev */ public class FileInstaller { + private static final String TAG = "FileInstaller"; public static void installFiles(Activity activity, boolean force) { - - Log.v("Installer", "Installing files..."); - File tmpDir = new File(Config.getBasefileDir()); + Log.d(TAG, "Installing files"); + File tmpDir = new File(LimboApplication.getBasefileDir()); if (!tmpDir.exists()) { - tmpDir.mkdirs(); + if(tmpDir.mkdirs()) { + ToastUtils.toastShort(activity, activity.getString(R.string.CouldNotCreateBaseDir) + ": " + tmpDir.getPath()); + return; + } } - File tmpDir1 = new File(Config.getMachineDir()); + File tmpDir1 = new File(LimboApplication.getMachineDir()); if (!tmpDir1.exists()) { - tmpDir1.mkdirs(); + if(tmpDir.mkdirs()) { + ToastUtils.toastShort(activity, activity.getString(R.string.CouldNotCreateMachineDir) + ": " + tmpDir.getPath()); + } } - //Install base dir - File dir = new File(Config.getBasefileDir()); + File dir = new File(LimboApplication.getBasefileDir()); if (dir.exists() && dir.isDirectory()) { //don't create again } else if (dir.exists() && !dir.isDirectory()) { - Log.v("Installer", "Could not create Dir, file found: " + Config.getBasefileDir()); + Log.e(TAG, "Could not create Dir, file found: " + LimboApplication.getBasefileDir()); return; } else if (!dir.exists()) { dir.mkdir(); } - String destDir = Config.getBasefileDir(); + String destDir = LimboApplication.getBasefileDir(); //Get each file in assets under ./roms/ and install in SDCARD AssetManager am = activity.getResources().getAssets(); @@ -74,12 +82,11 @@ public static void installFiles(Activity activity, boolean force) { files = am.list("roms"); } catch (IOException ex) { Logger.getLogger(FileInstaller.class.getName()).log(Level.SEVERE, null, ex); - Log.v("Installer", "Could not install files: " + ex.getMessage()); + Log.e(TAG, "Could not install files: " + ex.getMessage()); return; } for (int i = 0; i < files.length; i++) { - //Log.v("Installer", "File: " + files[i]); String[] subfiles = null; try { subfiles = am.list("roms/" + files[i]); @@ -88,11 +95,11 @@ public static void installFiles(Activity activity, boolean force) { } if (subfiles != null && subfiles.length > 0) { //Install base dir - File dir1 = new File(Config.getBasefileDir() + files[i]); + File dir1 = new File(LimboApplication.getBasefileDir() + files[i]); if (dir1.exists() && dir1.isDirectory()) { //don't create again } else if (dir1.exists() && !dir1.isDirectory()) { - Log.v("Installer", "Could not create Dir, file found: " + Config.getBasefileDir() + files[i]); + Log.e(TAG, "Could not create Dir, file found: " + LimboApplication.getBasefileDir() + files[i]); return; } else if (!dir1.exists()) { dir1.mkdir(); @@ -101,20 +108,18 @@ public static void installFiles(Activity activity, boolean force) { File file = new File(destDir, files[i] + "/" + subfiles[k]); if(!file.exists() || force) { - Log.v("Installer", "Installing file: " + file.getPath()); + Log.d(TAG, "Installing file: " + file.getPath()); installAssetFile(activity, files[i] + "/" + subfiles[k], destDir, "roms", null); } } } else { File file = new File(destDir, files[i]); if(!file.exists() || force) { - Log.v("Installer", "Installing file: " + file.getPath()); - installAssetFile(activity, files[i], Config.getBasefileDir(), "roms", null); + Log.d(TAG, "Installing file: " + file.getPath()); + installAssetFile(activity, files[i], LimboApplication.getBasefileDir(), "roms", null); } } } -// InputStream is = am.open(srcFile); - } public static boolean installAssetFile(Activity activity, String srcFile, @@ -126,7 +131,7 @@ public static boolean installAssetFile(Activity activity, String srcFile, if (!destDirF.exists()) { boolean res = destDirF.mkdirs(); if(!res){ - UIUtils.toastShort(activity, "Could not create directory for image"); + ToastUtils.toastShort(activity, activity.getString(R.string.CouldNotCreateDirForImage)); } } @@ -142,12 +147,12 @@ public static boolean installAssetFile(Activity activity, String srcFile, is.close(); return true; } catch (Exception ex) { - Log.e("Installer", "failed to install file: " + destFile + ", Error:" + ex.getMessage()); + Log.e(TAG, "failed to install file: " + destFile + ", Error:" + ex.getMessage()); return false; } } - public static Uri installImageTemplateToSDCard(Activity activity, String srcFile, + public static Uri installImageTemplateToSDCard(Context context, String srcFile, Uri destDir, String assetsDir, String destFile) { DocumentFile destFileF = null; @@ -157,8 +162,8 @@ public static Uri installImageTemplateToSDCard(Activity activity, String srcFile try { - DocumentFile dir = DocumentFile.fromTreeUri(activity, destDir); - AssetManager am = activity.getResources().getAssets(); // get the local asset manager + DocumentFile dir = DocumentFile.fromTreeUri(context, destDir); + AssetManager am = context.getResources().getAssets(); // get the local asset manager is = am.open(assetsDir + "/" + srcFile); // open the input stream for reading if(destFile==null) @@ -170,12 +175,12 @@ public static Uri installImageTemplateToSDCard(Activity activity, String srcFile destFileF = dir.createFile("application/octet-stream", destFile); } else { - UIUtils.toastShort(activity, "File exists, choose another filename"); + ToastUtils.toastShort(context, context.getString(R.string.FileExistsChooseAnotherFilename)); return null; } //Write to the dest - os = activity.getContentResolver().openOutputStream(destFileF.getUri()); + os = context.getContentResolver().openOutputStream(destFileF.getUri()); //OutputStream os = new FileOutputStream(destDir + "/" + destFile); byte[] buf = new byte[8092]; int n; @@ -187,7 +192,7 @@ public static Uri installImageTemplateToSDCard(Activity activity, String srcFile uri = destFileF.getUri(); } catch (Exception ex) { - Log.e("Installer", "failed to install file: " + destFile + ", Error:" + ex.getMessage()); + Log.e(TAG, "failed to install file: " + destFile + ", Error:" + ex.getMessage()); } finally { if(os!=null) { try { @@ -209,8 +214,8 @@ public static Uri installImageTemplateToSDCard(Activity activity, String srcFile } - public static String installImageTemplateToExternalStorage(Activity activity, String srcFile, - String destDir, String assetsDir, String destFile) { + public static String installImageTemplateToExternalStorage(Context context, String srcFile, + String destDir, String assetsDir, String destFile) { File file = new File(destDir, destFile); String filePath = null; @@ -218,7 +223,7 @@ public static String installImageTemplateToExternalStorage(Activity activity, St InputStream is = null; try { - AssetManager am = activity.getResources().getAssets(); // get the local asset manager + AssetManager am = context.getResources().getAssets(); // get the local asset manager is = am.open(assetsDir + "/" + srcFile); // open the input stream for reading if(destFile==null) @@ -229,7 +234,7 @@ public static String installImageTemplateToExternalStorage(Activity activity, St file.createNewFile(); } else { - UIUtils.toastShort(activity, "File exists, choose another filename"); + ToastUtils.toastShort(context, context.getString(R.string.FileExistsChooseAnotherFilename)); return null; } @@ -247,7 +252,7 @@ public static String installImageTemplateToExternalStorage(Activity activity, St filePath = file.getAbsolutePath(); } catch (Exception ex) { - Log.e("Installer", "failed to install file: " + destFile + ", Error:" + ex.getMessage()); + Log.e(TAG, "failed to install file: " + destFile + ", Error:" + ex.getMessage()); } finally { if(os!=null) { try { diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/files/FileUtils.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/files/FileUtils.java new file mode 100644 index 000000000..229f83bb8 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/files/FileUtils.java @@ -0,0 +1,658 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.files; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.util.Log; +import android.webkit.MimeTypeMap; + +import androidx.documentfile.provider.DocumentFile; + +import com.limbo.emu.lib.R; +import com.max2idea.android.limbo.dialog.DialogUtils; +import com.max2idea.android.limbo.machine.Machine.FileType; +import com.max2idea.android.limbo.main.Config; +import com.max2idea.android.limbo.main.LimboActivity; +import com.max2idea.android.limbo.main.LimboApplication; +import com.max2idea.android.limbo.main.LimboSettingsManager; +import com.max2idea.android.limbo.toast.ToastUtils; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.HashMap; + +/** + * @author dev + */ +public class FileUtils { + private final static String TAG = "FileUtils"; + private static final Object fdsLock = new Object(); + private static HashMap fds = new HashMap(); + + public static String getNativeLibDir(Context context) { + return context.getApplicationInfo().nativeLibraryDir; + } + + public static String getFullPathFromDocumentFilePath(String filePath) { + + filePath = filePath.replaceAll("%3A", "^3A"); + int index = filePath.lastIndexOf("^3A"); + if (index > 0) + filePath = filePath.substring(index + 3); + if (!filePath.startsWith("/")) + filePath = "/" + filePath; + + //remove any spaces encoded by the ASF + try { + filePath = URLDecoder.decode(filePath, "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + return filePath; + } + + public static String getFilenameFromPath(String filePath) { + filePath = filePath.replaceAll("%2F", "/"); + filePath = filePath.replaceAll("%3A", "/"); + filePath = filePath.replaceAll("\\^2F", "/"); + filePath = filePath.replaceAll("\\^3A", "/"); + + + int index = filePath.lastIndexOf("/"); + if (index > 0) + return filePath.substring(index + 1); + return filePath; + } + + public static String decodeDocumentFilePath(String filePath) { + if (filePath != null && filePath.startsWith("/content//")) { + filePath = filePath.replace("/content//", "content://"); + filePath = filePath.replaceAll("\\^\\^\\^", "%"); + } + return filePath; + } + + /** + * Encode the path % chars to ^ + * + * @param filePath + * @return + */ + public static String encodeDocumentFilePath(String filePath) { + if (filePath != null && filePath.startsWith("content://")) { + filePath = filePath.replace("content://", "/content//"); + filePath = filePath.replaceAll("%", "\\^\\^\\^"); + ; + + } + return filePath; + } + + public static void saveFileContents(String filePath, String contents) { + // TODO: we assume that the contents are of small size so we keep in an array + byteArrayToFile(contents.getBytes(), new File(filePath)); + } + + public static void byteArrayToFile(byte[] byteData, File filePath) { + + try { + FileOutputStream fos = new FileOutputStream(filePath); + fos.write(byteData); + fos.close(); + + } catch (FileNotFoundException ex) { + System.out.println("FileNotFoundException : " + ex); + } catch (IOException ioe) { + System.out.println("IOException : " + ioe); + } + + } + + public static InputStream getStreamFromFilePath(String filePath) throws FileNotFoundException { + if (filePath.startsWith("content://")) { + Uri uri = Uri.parse(filePath); + String mode = "rw"; + ParcelFileDescriptor pfd = LimboApplication.getInstance().getContentResolver().openFileDescriptor(uri, mode); + return new FileInputStream(pfd.getFileDescriptor()); + } else { + return new FileInputStream(filePath); + } + } + + public static void closeFileDescriptor(String filePath) throws IOException { + if (filePath.startsWith("content://")) { + Uri uri = Uri.parse(filePath); + String mode = "rw"; + ParcelFileDescriptor pfd = LimboApplication.getInstance().getContentResolver().openFileDescriptor(uri, mode); + pfd.close(); + } + } + + public static String getFileContents(String filePath) { + File file = new File(filePath); + if (!file.exists()) + return ""; + StringBuilder builder = new StringBuilder(""); + try { + FileInputStream stream = new FileInputStream(file); + byte[] buff = new byte[32768]; + int bytesRead = 0; + while ((bytesRead = stream.read(buff, 0, buff.length)) > 0) { + //FIXME: log file can be too large appending might fail with out of memory + builder.append(new String(buff, 0, bytesRead)); + } + } catch (Exception e) { + e.printStackTrace(); + } + + String contents = builder.toString(); + return contents; + } + + public static boolean fileValid(String path) { + if (path == null || path.equals("")) + return true; + if (path.startsWith("content://") || path.startsWith("/content/")) { + int fd = get_fd(path); + if (fd <= 0) + return false; + } else { + File file = new File(path); + file.setWritable(true); + return file.exists(); + } + return true; + } + + //TODO: we should pass the modes from the backend and translate them + // instead of blindly using "rw". ie ISOs should be read only. + public static int get_fd(String path) { + synchronized (fdsLock) { + int fd = 0; + if (path == null) + return 0; + if (path.startsWith("/content//") || path.startsWith("content://")) { + String npath = decodeDocumentFilePath(path); + try { + Uri uri = Uri.parse(npath); + String mode = "rw"; + if (path.toLowerCase().endsWith(".iso")) + mode = "r"; + ParcelFileDescriptor pfd = LimboApplication.getInstance().getContentResolver().openFileDescriptor(uri, mode); + fd = pfd.getFd(); + fds.put(fd, new FileInfo(path, npath, pfd)); + Log.d(TAG, "Opening Content Uri: " + npath + ", FD: " + fd); + } catch (Exception e) { + String msg = LimboApplication.getInstance().getString(R.string.CouldNotOpenDocFile) + " " + + FileUtils.getFullPathFromDocumentFilePath(npath) + + "\n" + LimboApplication.getInstance().getString(R.string.PleaseReassingYourDiskFiles); + ToastUtils.toastLong(LimboApplication.getInstance(), msg); + e.printStackTrace(); + } + } else { + try { + int mode = ParcelFileDescriptor.MODE_READ_WRITE; + if (path.toLowerCase().endsWith(".iso")) + mode = ParcelFileDescriptor.MODE_READ_ONLY; + File file = new File(path); + if (!file.exists()) + file.createNewFile(); + ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, mode); + fd = pfd.getFd(); + fds.put(fd, new FileInfo(path, path, pfd)); + Log.d(TAG, "Opening File: " + path + ", FD: " + fd); + } catch (Exception e) { + Log.e(TAG, "Could not open File: " + path + ", FD: " + fd); + if (Config.debug) + e.printStackTrace(); + } + } + return fd; + } + } + + public static void close_fds() { + synchronized (fds) { + Integer[] fds = FileUtils.fds.keySet().toArray(new Integer[FileUtils.fds.keySet().size()]); + for (int i = 0; i < fds.length; i++) { + FileUtils.close_fd(fds[i]); + } + } + } + + /** + * Closing File Descriptors + * + * @param fd File Descriptro to be closed + * @return Returns 0 if fd is closed successfully otherwise -1 + */ + public static int close_fd(int fd) { + if (!Config.closeFileDescriptors) { + return 0; + } + synchronized (fds) { + if (FileUtils.fds.containsKey(fd)) { + FileInfo info = FileUtils.fds.get(fd); + try { + ParcelFileDescriptor pfd = info.pfd; + if(Config.syncFilesOnClose) { + try { + pfd.getFileDescriptor().sync(); + } catch (IOException e) { + if (Config.debug) { + Log.w(TAG, "Syncing DocumentFile: " + info.path + ": " + fd + " : " + e); + e.printStackTrace(); + } + } + } + pfd.close(); + FileUtils.fds.remove(fd); + return 0; + } catch (IOException e) { + Log.e(TAG, "Error Closing DocumentFile: " + info.path + ": " + fd + " : " + e); + if (Config.debug) + e.printStackTrace(); + } + } else { + ParcelFileDescriptor pfd = null; + + try { + String path = ""; + FileInfo info = FileUtils.fds.get(fd); + if (info != null) { + pfd = info.pfd; + path = info.path; + } + if (pfd == null) + pfd = ParcelFileDescriptor.fromFd(fd); + if(Config.syncFilesOnClose) { + try { + pfd.getFileDescriptor().sync(); + } catch (IOException e) { + if (Config.debug) { + Log.w(TAG, "Error Syncing File: " + path + ": " + fd + " : " + e); + e.printStackTrace(); + } + } + } + pfd.close(); + return 0; + } catch (Exception e) { + Log.e(TAG, "Error Closing File FD: " + fd + " : " + e); + if (Config.debug) + e.printStackTrace(); + } + } + return -1; + } + } + + + public static void startLogging() { + if (Config.logFilePath == null) { + Log.w(TAG, "Log file is not ready"); + return; + } + Thread t = new Thread(new Runnable() { + public void run() { + FileOutputStream os = null; + File logFile = null; + try { + logFile = new File(Config.logFilePath); + if (logFile.exists()) { + if (!logFile.delete()) { + Log.w(TAG, "Could not delete previous log file!"); + } + } + logFile.createNewFile(); + Runtime.getRuntime().exec("logcat -c"); + Process process = Runtime.getRuntime().exec("logcat v main"); + os = new FileOutputStream(logFile); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream())); + + StringBuilder log = new StringBuilder(""); + String line = ""; + while ((line = bufferedReader.readLine()) != null) { + log.setLength(0); + log.append(line).append("\n"); + os.write(log.toString().getBytes("UTF-8")); + os.flush(); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (os != null) { + os.flush(); + os.close(); + } + } catch (IOException e) { + + e.printStackTrace(); + } + + } + + } + }); + t.setName("LimboLogger"); + t.start(); + } + + public static String LoadFile(Context context, String fileName, boolean loadFromRawFolder) throws IOException { + // Create a InputStream to read the file into + InputStream iS; + if (loadFromRawFolder) { + // get the resource id from the file name + int rID = context.getResources().getIdentifier(LimboApplication.getInstance().getClass().getPackage().getName() + ":raw/" + fileName, + null, null); + // get the file as a stream + iS = context.getResources().openRawResource(rID); + } else { + // get the file as a stream + iS = context.getResources().getAssets().open(fileName); + } + + ByteArrayOutputStream oS = new ByteArrayOutputStream(); + byte[] buffer = new byte[iS.available()]; + int bytesRead = 0; + while ((bytesRead = iS.read(buffer)) > 0) { + oS.write(buffer); + } + oS.close(); + iS.close(); + + // return the output stream as a String + return oS.toString(); + } + + public static String getExtensionFromFilename(String fileName) { + if (fileName == null) + return ""; + + int index = fileName.lastIndexOf("."); + if (index >= 0) { + return fileName.substring(index + 1); + } else + return ""; + } + + public static int getIconForFile(String file) { + file = file.toLowerCase(); + String ext = FileUtils.getExtensionFromFilename(file).toLowerCase(); + + if (ext.equals("img") || ext.equals("qcow") + || ext.equals("qcow2") || ext.equals("vmdk") || ext.equals("vdi") || ext.equals("cow") + || ext.equals("dmg") || ext.equals("bochs") || ext.equals("vpc") + || ext.equals("vhd") || ext.equals("fs")) + return R.drawable.harddisk; + else if (ext.equals("iso")) + return R.drawable.cd; + else if (ext.equals("ima")) + return R.drawable.floppy; + else if (ext.equals("csv")) + return R.drawable.importvms; + else if (file.contains("kernel") || file.contains("vmlinuz") || file.contains("initrd")) + return R.drawable.sysfile; + else + return R.drawable.close; + + } + + + public static Uri saveLogFileSDCard(Activity activity, Uri destDir) { + DocumentFile destFileF; + OutputStream os = null; + Uri uri = null; + + try { + String logFileContents = getFileContents(Config.logFilePath); + DocumentFile dir = DocumentFile.fromTreeUri(activity, destDir); + if (dir == null) + throw new Exception("Could not get log path directory"); + + //Create the file if doesn't exist + destFileF = dir.findFile(Config.destLogFilename); + if (destFileF == null) { + destFileF = dir.createFile(MimeTypeMap.getSingleton().getMimeTypeFromExtension("txt"), Config.destLogFilename); + } + + //Write to the dest + os = activity.getContentResolver().openOutputStream(destFileF.getUri()); + os.write(logFileContents.getBytes()); + + //success + uri = destFileF.getUri(); + + } catch (Exception ex) { + ToastUtils.toastShort(activity, activity.getString(R.string.FailedToSaveLogFile) + ex.getMessage()); + } finally { + if (os != null) { + try { + os.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + } + return uri; + } + + public static Uri exportFileContents(Activity activity, + Uri destDir, String destFile, byte[] contents) { + + DocumentFile destFileF = null; + OutputStream os = null; + Uri uri = null; + + try { + DocumentFile dir = DocumentFile.fromTreeUri(activity, destDir); + + //Create the file if doesn't exist + destFileF = dir.findFile(destFile); + if (destFileF == null) { + destFileF = dir.createFile(MimeTypeMap.getSingleton().getMimeTypeFromExtension("csv"), destFile); + } else { + ToastUtils.toastShort(activity, activity.getString(R.string.FileExistsChooseAnotherFilename)); + return null; + } + + //Write to the dest + os = activity.getContentResolver().openOutputStream(destFileF.getUri()); + os.write(contents); + + //success + uri = destFileF.getUri(); + + } catch (Exception ex) { + ToastUtils.toastShort(activity, activity.getString(R.string.FailedToExportFile) +": " + destFile + ", " + activity.getString(R.string.Error) + ":" + ex.getMessage()); + } finally { + if (os != null) { + try { + os.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + } + return uri; + } + + public static String saveLogFileLegacy(Activity activity, + String destLogFilePath) { + + String filePath = null; + File destFileF = new File(destLogFilePath, Config.destLogFilename); + + try { + String logFileContents = getFileContents(Config.logFilePath); + FileUtils.saveFileContents(destFileF.getAbsolutePath(), logFileContents); + + //success + filePath = destFileF.getAbsolutePath(); + + } catch (Exception ex) { + ToastUtils.toastShort(activity, activity.getString(R.string.FailedSaveLogFile) + ": " + destFileF.getAbsolutePath() + ", " + activity.getString(R.string.Error)+ ": " + ex.getMessage()); + } finally { + } + return filePath; + } + + + public static String getFileUriFromIntent(Activity activity, Intent data, boolean write) { + if (data == null) + return null; + + Uri uri = data.getData(); + DocumentFile pickedFile = DocumentFile.fromSingleUri(activity, uri); + String file = uri.toString(); + if (!file.contains("com.android.externalstorage.documents")) { + showFileNotSupported(activity); + return null; + } + activity.grantUriPermission(activity.getPackageName(), uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); + if (write) + activity.grantUriPermission(activity.getPackageName(), uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + activity.grantUriPermission(activity.getPackageName(), uri, Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); + + int takeFlags = data.getFlags() & Intent.FLAG_GRANT_READ_URI_PERMISSION; + if (write) + takeFlags = takeFlags | Intent.FLAG_GRANT_WRITE_URI_PERMISSION; + + activity.getContentResolver().takePersistableUriPermission(uri, takeFlags); + return file; + } + + public static String getDirPathFromIntent(Activity activity, Intent data) { + if (data == null) + return null; + Bundle b = data.getExtras(); + String file = b.getString("currDir"); + return file; + } + + + public static String getFilePathFromIntent(Activity activity, Intent data) { + if (data == null) + return null; + Bundle b = data.getExtras(); + String file = b.getString("file"); + return file; + } + + public static FileType getFileTypeFromIntent(Activity activity, Intent data) { + if (data == null) + return null; + Bundle b = data.getExtras(); + FileType fileType = (FileType) b.getSerializable("fileType"); + return fileType; + } + + public static void showFileNotSupported(Activity context) { + DialogUtils.UIAlert(context, context.getString(R.string.Error), context.getString(R.string.FilePathNotSupportedWarning)); + } + + public static void saveLogToFile(final Activity activity, final String logFileDestDir) { + new Thread(new Runnable() { + public void run() { + + String displayName = null; + if (logFileDestDir.startsWith("content://")) { + Uri exportDirUri = Uri.parse(logFileDestDir); + Uri fileCreatedUri = FileUtils.saveLogFileSDCard(activity, + exportDirUri); + displayName = FileUtils.getFullPathFromDocumentFilePath(fileCreatedUri.toString()); + } else { + String filePath = FileUtils.saveLogFileLegacy(activity, logFileDestDir); + displayName = filePath; + } + + if (displayName != null) { + + ToastUtils.toastShort(activity, activity.getString(R.string.LogfileSaved)); + } + } + }).start(); + } + + public static String convertFilePath(String text, int position) { + try { + text = FileUtils.getFullPathFromDocumentFilePath(text); + } catch (Exception ex) { + if (Config.debug) + ex.printStackTrace(); + } + + return text; + } + + public static class FileInfo { + public String path; + public String npath; + public ParcelFileDescriptor pfd; + + public FileInfo(String path, String npath, ParcelFileDescriptor pfd) { + this.npath = npath; + this.path = path; + this.pfd = pfd; + } + } + + public static String createImgFromTemplate(Context context, String templateImage, String destImage, FileType imgType) { + + String imagesDir = LimboSettingsManager.getImagesDir(context); + String displayName = null; + String filePath = null; + if (imagesDir.startsWith("content://")) { + Uri imagesDirUri = Uri.parse(imagesDir); + Uri fileCreatedUri = FileInstaller.installImageTemplateToSDCard(context, templateImage, + imagesDirUri, "hdtemplates", destImage); + if (fileCreatedUri != null) { + displayName = FileUtils.getFullPathFromDocumentFilePath(fileCreatedUri.toString()); + filePath = fileCreatedUri.toString(); + } + } else { + filePath = FileInstaller.installImageTemplateToExternalStorage(context, templateImage, imagesDir, "hdtemplates", destImage); + displayName = filePath; + } + if (displayName != null) { + ToastUtils.toastShort(context, context.getString(R.string.ImageCreated) + ": " + displayName); + return filePath; + } + return null; + } +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/help/Help.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/help/Help.java new file mode 100644 index 000000000..0c19bf23d --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/help/Help.java @@ -0,0 +1,85 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.help; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; + +import com.limbo.emu.lib.R; +import com.max2idea.android.limbo.main.Config; +import com.max2idea.android.limbo.main.LimboApplication; +import com.max2idea.android.limbo.main.LimboSettingsManager; +import com.max2idea.android.limbo.network.NetworkUtils; + +public class Help { + private static final String TAG = "Help"; + + public static void showHelp(final Activity activity) { + final AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(activity).create(); + alertDialog.setTitle(Config.APP_NAME + " " + LimboApplication.getLimboVersionString() + + " " + "QEMU" + " " + LimboApplication.getQemuVersionString() ); + + LinearLayout mLayout = new LinearLayout(activity); + mLayout.setOrientation(LinearLayout.VERTICAL); + TextView textView = new TextView(activity); + textView.setTextSize(15); + textView.setText(activity.getResources().getString(R.string.welcomeText)); + textView.setPadding(20, 20, 20, 20); + ScrollView scrollView = new ScrollView(activity); + scrollView.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, 300)); + scrollView.addView(textView); + mLayout.addView(scrollView); + + CheckBox checkUpdates = new CheckBox(activity); + checkUpdates.setText(R.string.checkForUpdates); + checkUpdates.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean b) { + LimboSettingsManager.setPromptUpdateVersion(activity, b); + } + }); + checkUpdates.setChecked(LimboSettingsManager.getPromptUpdateVersion(activity)); + mLayout.addView(checkUpdates); + alertDialog.setView(mLayout); + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.GoToWiki), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + NetworkUtils.openURL(activity, Config.guidesLink); + } + }); + alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, activity.getString(android.R.string.ok), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + + } + }); + alertDialog.show(); + } +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/install/Installer.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/install/Installer.java new file mode 100644 index 000000000..b790a5dcc --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/install/Installer.java @@ -0,0 +1,87 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.install; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Context; +import android.os.AsyncTask; + +import com.limbo.emu.lib.R; +import com.max2idea.android.limbo.files.FileInstaller; +import com.max2idea.android.limbo.toast.ToastUtils; + +import java.io.InputStream; +import java.util.ArrayList; + +public class Installer extends AsyncTask { + private static final String TAG = "Installer"; + + private boolean force; + private Activity activity; + private ProgressDialog progDialog; + + private Installer(Activity activity, boolean force) { + this.activity = activity; + this.force = force; + } + + public static String[] getAttrs(Context context, int res) { + StringBuilder stringBuilder = new StringBuilder(); + try { + InputStream stream = context.getResources().openRawResource(res); + byte[] buff = new byte[32768]; + int bytesRead = 0; + while ((bytesRead = stream.read(buff, 0, buff.length)) > 0) { + stringBuilder.append(new String(buff, 0, bytesRead)); + } + } catch(Exception ex) { + ToastUtils.toastShort(context, context.getString(R.string.CouldNotOpenRawFile) +": " + ex); + } + String fileContents = stringBuilder.toString(); + return fileContents.split("\\r\\n|\\n"); + } + + public static void installFiles(Activity activity, boolean force) { + Installer installer = new Installer(activity, force); + installer.force = force; + installer.execute(); + } + + @Override + protected Void doInBackground(Void... arg0) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + progDialog = ProgressDialog.show(activity, activity.getString(R.string.PleaseWait), + activity.getString(R.string.InstallingBIOS), + true); + } + }); + FileInstaller.installFiles(activity, force); + return null; + } + + @Override + protected void onPostExecute(Void test) { + ToastUtils.toastShort(activity, "BIOS and Keymap files installed"); + if (progDialog.isShowing()) + progDialog.dismiss(); + } +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/jni/MachineExecutorFactory.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/jni/MachineExecutorFactory.java new file mode 100644 index 000000000..22450b195 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/jni/MachineExecutorFactory.java @@ -0,0 +1,38 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.jni; + +import com.max2idea.android.limbo.machine.MachineController; +import com.max2idea.android.limbo.machine.MachineExecutor; + +public class MachineExecutorFactory { + private static final String TAG = "MachineExecutorFactory"; + + public static MachineExecutor createMachineExecutor(MachineController machineController, MachineExecutorType type) { + switch (type) { + case QEMU: + return new VMExecutor(machineController); + } + return null; + } + + public enum MachineExecutorType { + QEMU + } +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/jni/VMExecutor.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/jni/VMExecutor.java index 22889c267..5206e32ac 100644 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/jni/VMExecutor.java +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/jni/VMExecutor.java @@ -18,473 +18,203 @@ */ package com.max2idea.android.limbo.jni; -import android.app.Notification; import android.content.Context; import android.content.Intent; +import android.content.res.Configuration; import android.os.Bundle; import android.util.Log; -import android.view.MotionEvent; - +import android.view.Gravity; + +import com.limbo.emu.lib.R; +import com.max2idea.android.limbo.files.FileUtils; +import com.max2idea.android.limbo.machine.MachineAction; +import com.max2idea.android.limbo.machine.MachineController; +import com.max2idea.android.limbo.machine.MachineExecutor; +import com.max2idea.android.limbo.machine.MachineProperty; import com.max2idea.android.limbo.main.Config; +import com.max2idea.android.limbo.main.LimboApplication; import com.max2idea.android.limbo.main.LimboSDLActivity; -import com.max2idea.android.limbo.main.LimboService; import com.max2idea.android.limbo.main.LimboSettingsManager; -import com.max2idea.android.limbo.utils.FileUtils; -import com.max2idea.android.limbo.utils.Machine; -import com.max2idea.android.limbo.utils.QmpClient; -import com.max2idea.android.limbo.utils.UIUtils; +import com.max2idea.android.limbo.qmp.QmpClient; +import com.max2idea.android.limbo.toast.ToastUtils; import org.json.JSONException; import org.json.JSONObject; import java.io.File; -import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.Arrays; -public class VMExecutor { +/** + * Class is used to start and stop the qemu process and communicate file descriptions, mouse, + * and keyboard events. + */ +class VMExecutor extends MachineExecutor { private static final String TAG = "VMExecutor"; - private static Context context; - - String[] params = null; - - //native lib - private String libqemu = null; - - //qmp server - public int enableqmp; - private String qmp_server; - private int qmp_port; - - //state - public int paused; - public String snapshot_name = "limbo"; - public String save_state_name = null; - private String save_dir; - public int current_fd = 0; - - public String base_dir; - public String dns_addr; - public String append = ""; - public boolean busy = false; - public String name; - - //ui - public int enablespice = 0; - public String keyboard_layout = Config.defaultKeyboardLayout; - - public String mouse = null; - public int enablevnc; - public int vnc_allow_external = 0; - public int qmp_allow_external = 0; - public String vnc_passwd = null; - - // cpu/board settings - private String cpu; - private String arch = "x86"; - private String machine_type; - private int memory = 128; - private int cpuNum = 1; - public int enablekvm; - public int enable_mttcg; - - // disks - private String hda_img_path; - private String hdb_img_path; - private String hdc_img_path; - private String hdd_img_path; - public String shared_folder_path; - public int shared_folder_readonly = 1; - private String hd_cache = "default"; - - //removable devices - public String cd_iso_path; - public String fda_img_path; - public String fdb_img_path; - public String sd_img_path; - - //boot options - private String bootdevice = null; - private String kernel; - private String initrd; - - //graphics - private String vga_type = "std"; - - //audio - public String sound_card; - - // net - private String net_cfg = "None"; - private String nic_card = null; - private String hostfwd = null; - private String guestfwd = null; - - //advanced - private int disableacpi = 0; - private int disablehpet = 0; - private int disabletsc = 0; - private String extra_params; + + private static final String cdDeviceName = "ide1-cd0"; + private static final String fdaDeviceName = "floppy0"; + private static final String fdbDeviceName = "floppy1"; + private static final String sdDeviceName = "sd0"; + private static int vm_width; + private static int vm_height; + //TODO: make this a proper singleton but the views should not be able to access it + private static VMExecutor mInstance; + + VMExecutor(MachineController machineController) { + super(machineController); + mInstance = this; + } /** - * @throws Exception + * This function is called when the machine resolution changes. This is called from SDL compat + * extensions, see folder jni/compat/sdl-extensions + * + * @param width Width + * @param height Height */ - public VMExecutor(Machine machine, Context context) throws Exception { - - name = machine.machinename; - base_dir = Config.getBasefileDir(); - save_dir = Config.getMachineDir() + name; - save_state_name = save_dir + "/" + Config.state_filename; - this.context = context; - this.memory = machine.memory; - this.cpuNum = machine.cpuNum; - this.vga_type = machine.vga_type; - this.hd_cache = machine.hd_cache; - this.snapshot_name = machine.snapshot_name; - this.disableacpi = machine.disableacpi; - this.disablehpet = machine.disablehpet; - this.disabletsc = machine.disabletsc; - this.enableqmp = machine.enableqmp; - this.qmp_server = Config.QMPServer; - this.qmp_port = Config.QMPPort; - this.enablevnc = machine.enablevnc; - this.enablevnc = machine.enablespice; - if (Config.enable_SDL_sound) - if (!machine.soundcard.equals("none")) - this.sound_card = machine.soundcard; - this.kernel = machine.kernel; - this.initrd = machine.initrd; - - this.mouse = machine.mouse; - this.keyboard_layout= machine.keyboard; - - // kvm - enablekvm = machine.enableKVM; - - //mttcg, needs qemu 3.1.0 and above - enable_mttcg = machine.enableMTTCG; - - - this.cpu = machine.cpu; - - if (machine.arch.endsWith("ARM") || machine.arch.endsWith("ARM64")) { - 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 = machine.machine_type.split(" ")[0]; - this.disablehpet = 0; - this.disableacpi = 0; - this.disabletsc = 0; - } else if (machine.arch.endsWith("x64")) { - this.libqemu = FileUtils.getNativeLibDir(context) + "/libqemu-system-x86_64.so"; - this.arch = "x86_64"; - if (machine.machine_type == null) - this.machine_type = "pc"; - else - this.machine_type = machine.machine_type; - } else if (machine.arch.endsWith("x86")) { - this.cpu = machine.cpu; - this.libqemu = FileUtils.getNativeLibDir(context) + "/libqemu-system-i386.so"; - File libFile = new File(libqemu); - if (!libFile.exists()) { - this.libqemu = FileUtils.getNativeLibDir(context) + "/libqemu-system-x86_64.so"; - libFile = new File(libqemu); - } - this.arch = "x86"; - if (machine.machine_type == null) - this.machine_type = "pc"; - else - this.machine_type = machine.machine_type; - } else if (machine.arch.endsWith("MIPS")) { - this.cpu = machine.cpu; - this.libqemu = FileUtils.getNativeLibDir(context) + "/libqemu-system-mips.so"; - this.arch = "mips"; - if (machine.machine_type == null) - this.machine_type = "malta"; - else - this.machine_type = machine.machine_type; - } else if (machine.arch.endsWith("PPC")) { - this.cpu = machine.cpu; - this.libqemu = FileUtils.getNativeLibDir(context) + "/libqemu-system-ppc.so"; - File libFile = new File(libqemu); - if (!libFile.exists()) { - this.libqemu = FileUtils.getNativeLibDir(context) + "/libqemu-system-ppc64.so"; - libFile = new File(libqemu); - } - this.arch = "ppc"; - if (machine.machine_type == null || machine.machine_type.equals("Default")) - this.machine_type = null; - else - this.machine_type = machine.machine_type; - } else if (machine.arch.endsWith("PPC64")) { - this.cpu = machine.cpu; - this.libqemu = FileUtils.getNativeLibDir(context) + "/libqemu-system-ppc64.so"; - this.arch = "ppc64"; - if (machine.machine_type == null || machine.machine_type.equals("Default")) - this.machine_type = null; - else - this.machine_type = machine.machine_type; - } else if (machine.arch.endsWith("m68k")) { - this.cpu = machine.cpu; - this.libqemu = FileUtils.getNativeLibDir(context) + "/libqemu-system-m68k.so"; - this.arch = "m68k"; - if (machine.machine_type == null) - this.machine_type = "Default"; - else - this.machine_type = machine.machine_type; - } else if (machine.arch.endsWith("SPARC") || machine.arch.endsWith("SPARC64")) { - this.cpu = machine.cpu; - this.libqemu = FileUtils.getNativeLibDir(context) + "/libqemu-system-sparc.so"; - File libFile = new File(libqemu); - if (!libFile.exists()) { - this.libqemu = FileUtils.getNativeLibDir(context) + "/libqemu-system-sparc64.so"; - libFile = new File(libqemu); - } - this.arch = "SPARC"; - if (machine.machine_type == null || machine.machine_type.equals("Default")) - this.machine_type = null; - else - this.machine_type = machine.machine_type; - } - - if (machine.cd_iso_path == null || machine.cd_iso_path.equals("None")) { - this.cd_iso_path = null; - } else { - this.cd_iso_path = machine.cd_iso_path; - } - if (machine.hda_img_path == null || machine.hda_img_path.equals("None")) { - this.hda_img_path = null; - } else { - - this.hda_img_path = machine.hda_img_path; - } - - if (machine.hdb_img_path == null || machine.hdb_img_path.equals("None")) { - this.hdb_img_path = null; - } else { + public static void onVMResolutionChanged(int width, int height) { + vm_width = width; + vm_height = height; + mInstance.onResolutionChanged(vm_width, vm_height); + } - this.hdb_img_path = machine.hdb_img_path; - } + //JNI Methods + private native String start(String storage_dir, String base_dir, + String lib_filename, String lib_path, + int sdl_scale_hint, Object[] params); - // Check if CD is set - if (machine.hdc_img_path != null || machine.hdc_img_path == null || machine.hdc_img_path.equals("None")) { - this.hdc_img_path = null; - } else { + private native String stop(int restart); - this.hdc_img_path = machine.hdc_img_path; - } + public native void setSDLRefreshRateDefault(int value); - if (machine.hdd_img_path == null || machine.hdd_img_path.equals("None")) { - this.hdd_img_path = null; - } else { - this.hdd_img_path = machine.hdd_img_path; - } + public native void setSDLRefreshRateIdle(int value); - if (machine.fda_img_path == null || machine.fda_img_path.equals("None")) { - this.fda_img_path = null; - } else { + public native int getSDLRefreshRateDefault(); - this.fda_img_path = machine.fda_img_path; - } + public native int getSDLRefreshRateIdle(); - if (machine.fdb_img_path == null || machine.fdb_img_path.equals("None")) { - this.fdb_img_path = null; - } else { + public native void nativeIgnoreBreakpointInvalidate(int value); - this.fdb_img_path = machine.fdb_img_path; - } + public native void nativeMouseEvent(int button, int action, int relative, int x, int y); - if (Config.enableFlashMemoryImages) { - if (machine.sd_img_path == null || machine.sd_img_path.equals("None")) { - this.sd_img_path = null; - } else { - this.sd_img_path = machine.sd_img_path; - } - } + public native void nativeMouseBounds(int xmin, int xmax, int ymin, int ymax); - if (this.arch.equals("arm")) { - this.bootdevice = null; - } else if (machine.bootdevice.equals("Default")) { - this.bootdevice = null; - } else if (machine.bootdevice.equals("CD Rom")) { - this.bootdevice = "d"; - } else if (machine.bootdevice.equals("Floppy")) { - this.bootdevice = "a"; - } else if (machine.bootdevice.equals("Hard Disk")) { - this.bootdevice = "c"; - } + public native void nativeFullscreen(); - if (machine.net_cfg == null || machine.net_cfg.equals("None")) { - this.net_cfg = "none"; - this.nic_card = null; - } else if (machine.net_cfg.equals("User")) { - this.net_cfg = "user"; - this.nic_card = machine.nic_card; - this.guestfwd = machine.guestfwd; - if (machine.hostfwd != null && !machine.hostfwd.equals("")) - this.hostfwd = machine.hostfwd; - else - this.hostfwd = null; - } else if (machine.net_cfg.equals("TAP")) { - this.net_cfg = "tap"; - this.nic_card = machine.nic_card; - } + public native void nativeRefreshScreen(int value); - if (machine.soundcard != null && machine.soundcard.equals("None")) { - this.sound_card = null; - } + public native void nativeEnableAaudio(int value, String aaudioLibName, String aaudioLibPath); - // Shared folder - if (machine.shared_folder != null && !machine.shared_folder.trim().equals("")) { - shared_folder_path = machine.shared_folder; - if (machine.shared_folder_mode == 0) - shared_folder_readonly = 1; - else if (machine.shared_folder_mode == 1) - shared_folder_readonly = 0; - } else - shared_folder_path = null; - - // Extra Params - if (machine.extra_params != null) { - this.extra_params = machine.extra_params; - } - this.prepPaths(); - } - - public static void onVMResolutionChanged(int width, int height) { - - if (LimboSDLActivity.mIsSurfaceReady) - LimboSDLActivity.onVMResolutionChanged(width, height); - } - - public void print(String[] params) { + /** + * Prints parameters in qemu format + * + * @param params Parameters to be printed + */ + public void printParams(String[] params) { Log.d(TAG, "Params:"); for (int i = 0; i < params.length; i++) { Log.d(TAG, i + ": " + params[i]); } - } - public String startvm() { - - String res = null; - try { - prepareParams(); - } catch (Exception ex) { - UIUtils.toastLong(context, ex.getMessage()); - return res; - } - - //set the exit code - LimboSettingsManager.setExitCode(context, 2); + // Translate to QEMU format + private String getSoundCard() { + if (Config.enableSDLSound && getMachine().getSoundCard() != null + && !getMachine().getSoundCard().toLowerCase().equals("none")) + return getMachine().getSoundCard(); + return null; + } - try { - res = start(Config.storagedir, this.base_dir, this.libqemu, Config.SDLHintScale, params, this.paused, this.save_state_name); - } catch (Exception ex) { - ex.printStackTrace(); - Log.e(TAG, "Limbo Exception: " + ex.toString()); +private String getQemuLibrary() { + switch (LimboApplication.arch) { + case x86: + return "libqemu-system-i386.so"; + case x86_64: + return "libqemu-system-x86_64.so"; + case arm: + return "libqemu-system-arm.so"; + case arm64: + return "libqemu-system-aarch64.so"; + case ppc: + return "libqemu-system-ppc.so"; + case ppc64: + return "libqemu-system-ppc64.so"; + case sparc: + return "libqemu-system-sparc.so"; + case sparc64: + return "libqemu-system-sparc64.so"; + default: + throw new IllegalStateException("Unexpected value: " + LimboApplication.arch); } - return res; } - public void prepareParams() throws Exception { - - params = null; - ArrayList paramsList = new ArrayList(); - - paramsList.add(libqemu); - - addUIOptions(paramsList); + private String getSaveStateName() { + String machineSaveDirectory = MachineController.getInstance().getMachineSaveDir(); + return machineSaveDirectory + "/" + Config.stateFilename; + } + private String[] prepareParams(Context context) throws Exception { + ArrayList paramsList = new ArrayList<>(); + paramsList.add(getQemuLibrary()); + addUIOptions(context, paramsList); addCpuBoardOptions(paramsList); - addDrives(paramsList); - addRemovableDrives(paramsList); - addBootOptions(paramsList); - addGraphicsOptions(paramsList); - addAudioOptions(paramsList); - addNetworkOptions(paramsList); - - addAdvancedOptions(paramsList); - - addGenericOptions(paramsList); - + addGenericOptions(context, paramsList); addStateOptions(paramsList); - - params = (String[]) paramsList.toArray(new String[paramsList.size()]); - - print(params); - + addAdvancedOptions(paramsList); + addAccelerationOptions(paramsList); + return paramsList.toArray(new String[0]); } + /** + * Adds the vm state file description to the qemu parameters for resuming the vm + * + * @param paramsList Existing parameter list to be passed to qemu + */ private void addStateOptions(ArrayList paramsList) { - if (paused == 1 && this.save_state_name != null && !save_state_name.equals("")) { - int fd_tmp = FileUtils.get_fd(context, save_state_name); + if (MachineController.getInstance().isPaused() && !getSaveStateName().equals("")) { + int fd_tmp = FileUtils.get_fd(getSaveStateName()); if (fd_tmp < 0) { - Log.e(TAG, "Error while getting fd for: " + save_state_name); + Log.e(TAG, "Error while getting fd for: " + getSaveStateName()); } else { - //Log.i(TAG, "Got new fd "+fd_tmp + " for: " +save_state_name); + Log.d(TAG, "Retrieved fd: " + fd_tmp + " for: " + getSaveStateName()); paramsList.add("-incoming"); paramsList.add("fd:" + fd_tmp); } } } - private void addUIOptions(ArrayList paramsList) { - if (enablevnc != 0) { - Log.v(TAG, "Enable VNC server"); + private void addUIOptions(Context context, ArrayList paramsList) { + if (MachineController.getInstance().isVNCEnabled()) { paramsList.add("-vnc"); - - if (vnc_allow_external != 0) { + String vncParam = ""; + if (LimboSettingsManager.getVNCEnablePassword(context)) { //TODO: Allow connections from External // Use with x509 auth and TLS for encryption - paramsList.add(":1,password"); + vncParam += ":1"; } else { - // Allow connections only from localhost using localsocket without a password - //paramsList.add(Config.defaultVNCHost+":" + Config.defaultVNCPort); - String qmpParams = "unix:"; - qmpParams += Config.getLocalVNCSocketPath(); - paramsList.add(qmpParams); + // Allow connections only from localhost using localsocket without + // a password + vncParam += Config.defaultVNCHost + ":" + Config.defaultVNCPort; } - //Allow monitor console only for VNC, - // SDL for android doesn't support more - // than 1 window + if (LimboSettingsManager.getVNCEnablePassword(context)) + vncParam += ",password"; + + paramsList.add(vncParam); + + //Allow monitor console though it's only supported for VNC, SDL for android doesn't support + // more than 1 window paramsList.add("-monitor"); paramsList.add("vc"); - } else if (enablespice != 0) { - //Not working right now - Log.v(TAG, "Enable SPICE server"); - paramsList.add("-spice"); - String spiceParams = "port=5902"; - - if (vnc_allow_external != 0 && vnc_passwd != null) { - spiceParams += ",password="; - spiceParams += vnc_passwd; - } else - spiceParams += ",addr=127.0.0.1"; // Allow only connections from localhost without password - - spiceParams += ",disable-ticketing"; - //argv.add("-chardev"); - //argv.add("spicevm"); } else { - //SDL needs explicit keyboard layout - Log.v(TAG, "Disabling VNC server, using SDL instead"); - if (keyboard_layout == null) { - paramsList.add("-k"); - paramsList.add("en-us"); - } - //XXX: monitor, serial, and parallel display crashes cause SDL doesn't support more than 1 window paramsList.add("-monitor"); paramsList.add("none"); @@ -496,102 +226,80 @@ private void addUIOptions(ArrayList paramsList) { paramsList.add("none"); } - if (keyboard_layout != null) { + if (getMachine().getKeyboard() != null) { paramsList.add("-k"); - paramsList.add(keyboard_layout); + paramsList.add(getMachine().getKeyboard()); } - if (mouse!=null && !mouse.equals("ps2")) { + if (getMachine().getMouse() != null && !getMachine().getMouse().equals("ps2")) { paramsList.add("-usb"); paramsList.add("-device"); - paramsList.add(mouse); + paramsList.add(getMachine().getMouse()); } - } private void addAdvancedOptions(ArrayList paramsList) { - if (disableacpi != 0) { - paramsList.add("-no-acpi"); //disable ACPI - } - if (disablehpet != 0) { - paramsList.add("-no-hpet"); // disable HPET - } - - //TODO:Extra options - if (extra_params != null && !extra_params.trim().equals("")) { - String[] paramsTmp = extra_params.split(" "); + if (getMachine().getExtraParams() != null && !getMachine().getExtraParams().trim().equals("")) { + String[] paramsTmp = getMachine().getExtraParams().split(" "); paramsList.addAll(Arrays.asList(paramsTmp)); } - } private void addAudioOptions(ArrayList paramsList) { - - if (sound_card != null && !sound_card.equals("None")) { + if (getSoundCard() != null) { paramsList.add("-soundhw"); - paramsList.add(sound_card); + paramsList.add(getSoundCard()); } - } - private void addGenericOptions(ArrayList paramsList) { - + private void addGenericOptions(Context context, ArrayList paramsList) { paramsList.add("-L"); - paramsList.add(base_dir); - - //XXX: Snapshots not working currently, use migrate/incoming instead - if (snapshot_name != null && !snapshot_name.equals("")) { - paramsList.add("-loadvm"); - paramsList.add(snapshot_name); - } - - if (enableqmp != 0) { - + paramsList.add(LimboApplication.getBasefileDir()); + if (LimboSettingsManager.getEnableQmp(context)) { paramsList.add("-qmp"); - - if(qmp_allow_external != 0) { + if (getQMPAllowExternal()) { String qmpParams = "tcp:"; - qmpParams += (":" + this.qmp_port); + qmpParams += (":" + Config.QMPPort); qmpParams += ",server,nowait"; paramsList.add(qmpParams); } else { //Specify a unix local domain as localhost to limit to local connections only String qmpParams = "unix:"; - qmpParams += Config.getLocalQMPSocketPath(); + qmpParams += LimboApplication.getLocalQMPSocketPath(); qmpParams += ",server,nowait"; paramsList.add(qmpParams); - } - - } //Enable Tracing log - // argv.add("-D"); - // argv.add("/sdcard/limbo/log.txt"); - // argv.add("--trace"); - // argv.add("events=/sdcard/limbo/tmp/events"); - // argv.add("--trace"); - // argv.add("file=/sdcard/limbo/tmp/trace"); + if (Config.enableTracingLog) { + paramsList.add("-D"); + paramsList.add(Config.traceLogFile); + paramsList.add("--trace"); + paramsList.add("events=" + Config.traceEventsFile); + paramsList.add("--trace"); + paramsList.add("file=" + Config.traceDir); + } -// paramsList.add("-tb-size"); -// paramsList.add("32M"); //Don't increase it crashes + if (Config.overrideTbSize) { + paramsList.add("-tb-size"); + paramsList.add(Config.tbSize); //Don't increase it crashes + } - paramsList.add("-realtime"); - paramsList.add("mlock=off"); + if (LimboApplication.getQemuVersion() == 20901) { + paramsList.add("-realtime"); + paramsList.add("mlock=off"); + } else { + paramsList.add("-overcommit"); + paramsList.add("mem-lock=off"); + } paramsList.add("-rtc"); paramsList.add("base=localtime"); - paramsList.add("-nodefaults"); - - - //XXX: Usb redir not working under User mode - //Redirect ports (SSH) - // argv.add("-redir"); - // argv.add("5555::22"); - + if (!Config.enableDefaultDevices) + paramsList.add("-nodefaults"); } private void addCpuBoardOptions(ArrayList paramsList) { @@ -600,87 +308,111 @@ private void addCpuBoardOptions(ArrayList paramsList) { //so we enable multi core only under KVM // anyway regular emulation is not gaining any benefit unless mttcg is enabled but that // doesn't work for x86 guests yet - if (this.cpuNum > 1 && - (enablekvm == 1 || enable_mttcg == 1 || !Config.enableSMPOnlyOnKVM)) { + if (getMachine().getCpuNum() > 1) { paramsList.add("-smp"); - paramsList.add(this.cpuNum + ""); + paramsList.add(getMachine().getCpuNum() + ""); } - - if (machine_type != null && !machine_type.equals("Default")) { + if (getMachineType() != null && !getMachineType().equals("Default")) { paramsList.add("-M"); - paramsList.add(machine_type); + paramsList.add(getMachineType()); } //FIXME: something is wrong with quoting that doesn't let sparc qemu find the cpu def // for now we remove the cpu drop downlist items for sparc - if (this.cpu != null && this.cpu.contains(" ")) - cpu = "'" + cpu + "'"; // XXX: needed for sparc cpu names + String cpu = getMachine().getCpu(); + if (getMachine().getCpu() != null && getMachine().getCpu().contains(" ")) + cpu = "'" + getMachine().getCpu() + "'"; // XXX: needed for sparc cpu names //XXX: we disable tsc feature for x86 since some guests are kernel panicking // if the cpu has not specified by user we use the internal qemu32/64 - if (disabletsc == 1 && (arch.equals("x86") || arch.equals("x86_64"))) { + if (getMachine().getDisableTSC() == 1 && (LimboApplication.arch == Config.Arch.x86 || LimboApplication.arch == Config.Arch.x86_64)) { if (cpu == null || cpu.equals("Default")) { - if (arch.equals("x86")) + if (LimboApplication.arch == Config.Arch.x86) cpu = "qemu32"; - else if (arch.equals("x86_64")) + else if (LimboApplication.arch == Config.Arch.x86_64) cpu = "qemu64"; } cpu += ",-tsc"; } - if (this.cpu != null && !cpu.equals("Default")) { + if (getMachine().getDisableAcpi() != 0) { + paramsList.add("-no-acpi"); //disable ACPI + } + if (getMachine().getDisableHPET() != 0) { + paramsList.add("-no-hpet"); // disable HPET + } + + if (cpu != null && !cpu.equals("Default")) { paramsList.add("-cpu"); paramsList.add(cpu); - } paramsList.add("-m"); - paramsList.add(this.memory + ""); + paramsList.add(getMachine().getMemory() + ""); + } + + private void addAccelerationOptions(ArrayList paramsList) { - if (enablekvm != 0) { + // XXX: we add the acceleration options after the extra params + // this is due to QEMU applying the first instance of this option + // so the extra params cannot override it. + if (getMachine().getEnableKVM() != 0) { paramsList.add("-enable-kvm"); - } else if (this.enable_mttcg != 0 && Machine.isHost64Bit()) { - //XXX: we should only do this for 64bit hosts + } else { paramsList.add("-accel"); String tcgParams = "tcg"; - if (cpuNum > 1) + if (getMachine().getEnableMTTCG() != 0) { tcgParams += ",thread=multi"; + } else { + tcgParams += ",thread=single"; + } paramsList.add(tcgParams); - //#endif } + } + private String getMachineType() { + String machineType = getMachine().getMachineType(); + if ((LimboApplication.arch == Config.Arch.x86 || LimboApplication.arch == Config.Arch.x86_64) + && machineType == null) { + machineType = "pc"; + } else if ((LimboApplication.arch == Config.Arch.ppc || LimboApplication.arch == Config.Arch.ppc64) + && machineType.equals("Default")) { + machineType = null; + } else if ((LimboApplication.arch == Config.Arch.sparc || LimboApplication.arch == Config.Arch.sparc64) + && machineType.equals("Default")) { + machineType = null; + } + return machineType; } private void addNetworkOptions(ArrayList paramsList) throws Exception { - if (this.net_cfg != null) { + String network = getNetCfg(); + if (network != null) { paramsList.add("-net"); - if (net_cfg.equals("user")) { - String netParams = net_cfg; - if (hostfwd != null) { + if (network.equals("user")) { + String netParams = network; + String hostFwd = getHostFwd(); + if (hostFwd != null) { //hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport{,hostfwd=...} // example forward ssh from guest port 2222 to guest port 22: // hostfwd=tcp::2222-:22 - if(hostfwd.startsWith("hostfwd")){ + if (hostFwd.startsWith("hostfwd")) { throw new Exception("Invalid format for Host Forward, should be: tcp:hostport1:guestport1,udp:hostport2:questport2,..."); } - String [] hostfwdparams = hostfwd.split(","); - for(int i=0; i paramsList) throws Exception { } } - if (nic_card != null) { + String networkCard = getNicCard(); + if (networkCard != null) { paramsList.add("-net"); String nicParams = "nic"; - if (net_cfg.equals("tap")) + if (network.equals("tap")) nicParams += ",vlan=0"; - if(!nic_card.equals("Default")) - nicParams += (",model=" + nic_card ); + if (!networkCard.equals("Default")) + nicParams += (",model=" + networkCard); paramsList.add(nicParams); } } + private String getHostFwd() { + if (getMachine().getNetwork().equals("User")) { + if (getMachine().getHostFwd() != null && !getMachine().getHostFwd().equals("")) + return getMachine().getHostFwd(); + } + return null; + } + + private String getNicCard() { + if (getMachine().getNetwork() == null || getMachine().getNetwork().equals("None")) { + return null; + } else if (getMachine().getNetwork().equals("User")) { + return getMachine().getNetworkCard(); + } else if (getMachine().getNetwork().equals("TAP")) { + return getMachine().getNetworkCard(); + } + return null; + } + + private String getNetCfg() { + if (getMachine().getNetwork() == null || getMachine().getNetwork().equals("None")) { + return "none"; + } else if (getMachine().getNetwork().equals("User")) { + return "user"; + } else if (getMachine().getNetwork().equals("TAP")) { + return "tap"; + } + return null; + } + private void addGraphicsOptions(ArrayList paramsList) { - if (vga_type != null) { - if (vga_type.equals("Default")) { + if (getMachine().getVga() != null) { + if (getMachine().getVga().equals("Default")) { //do nothing - } else if (vga_type.equals("virtio-gpu-pci")) { + } else if (getMachine().getVga().equals("virtio-gpu-pci")) { paramsList.add("-device"); - paramsList.add(vga_type); - } else if (vga_type.equals("nographic")) { + paramsList.add(getMachine().getVga()); + } else if (getMachine().getVga().equals("nographic")) { paramsList.add("-nographic"); } else { paramsList.add("-vga"); - paramsList.add(vga_type); + paramsList.add(getMachine().getVga()); } } - - } private void addBootOptions(ArrayList paramsList) { - if (this.bootdevice != null) { + if (getBootDevice() != null) { paramsList.add("-boot"); - paramsList.add(bootdevice); + paramsList.add(getBootDevice()); } - if (this.kernel != null && !this.kernel.equals("")) { + String kernel = getKernel(); + if (kernel != null && !kernel.equals("")) { paramsList.add("-kernel"); - paramsList.add(this.kernel); + paramsList.add(kernel); } + String initrd = getInitRd(); if (initrd != null && !initrd.equals("")) { paramsList.add("-initrd"); paramsList.add(initrd); } - if (append != null && !append.equals("")) { + if (getMachine().getAppend() != null && !getMachine().getAppend().equals("")) { paramsList.add("-append"); - paramsList.add(append); + paramsList.add(getMachine().getAppend()); } } - public void addDrives(ArrayList paramsList) { - if (hda_img_path != null) { - paramsList.add("-drive"); //empty - String param = "index=0"; - if (Config.enable_hd_if) { - param += ",if="; - param += Config.hd_if_type; - } - param += ",media=disk"; - if (!hda_img_path.equals("")) { - param += ",file=" + hda_img_path; - } - paramsList.add(param); + private String getBootDevice() { + if (LimboApplication.arch == Config.Arch.arm || LimboApplication.arch == Config.Arch.arm64) { + return null; + } else if (getMachine().getBootDevice().equals("Default")) { + return null; + } else if (getMachine().getBootDevice().equals("CDROM")) { + return "d"; + } else if (getMachine().getBootDevice().equals("Floppy")) { + return "a"; + } else if (getMachine().getBootDevice().equals("Hard Disk")) { + return "c"; } + return null; + } - if (hdb_img_path != null) { - paramsList.add("-drive"); //empty - String param = "index=1"; - if (Config.enable_hd_if) { - param += ",if="; - param += Config.hd_if_type; - } - param += ",media=disk"; - if (!hdb_img_path.equals("")) { - param += ",file=" + hdb_img_path; - } - paramsList.add(param); - } + private String getInitRd() { + return FileUtils.encodeDocumentFilePath(getMachine().getInitRd()); + } - if (hdc_img_path != null) { - paramsList.add("-drive"); //empty - String param = "index=2"; - if (Config.enable_hd_if) { + private String getKernel() { + return FileUtils.encodeDocumentFilePath(getMachine().getKernel()); + } + + public String getDriveFilePath(String driveFilePath) { + String imgPath = driveFilePath; + if (imgPath == null || imgPath.equals("None")) + return null; + imgPath = FileUtils.encodeDocumentFilePath(imgPath); + return imgPath; + } + + public void addDrives(ArrayList paramsList) { + addHardDisk(paramsList, getDriveFilePath(getMachine().getHdaImagePath()), + 0, getMachine().getHdaInterface()); + addHardDisk(paramsList, getDriveFilePath(getMachine().getHdbImagePath()), + 1, getMachine().getHdbInterface()); + addHardDisk(paramsList, getDriveFilePath(getMachine().getHdcImagePath()), + 2, getMachine().getHdcInterface()); + addHardDisk(paramsList, getDriveFilePath(getMachine().getHddImagePath()), + 3, getMachine().getHddInterface()); + addSharedFolder(paramsList, getDriveFilePath(getMachine().getSharedFolderPath())); + } + + public void addHardDisk(ArrayList paramsList, String imagePath, int index, String hdInterface) { + if (imagePath != null && !imagePath.trim().equals("")) { + if (Config.legacyDrives) { + switch (index) { + case 0: + paramsList.add("-hda"); + break; + case 1: + paramsList.add("-hdb"); + break; + case 2: + paramsList.add("-hdc"); + break; + case 3: + paramsList.add("-hdd"); + break; + } + paramsList.add(imagePath); + } else { + paramsList.add("-drive"); + String param = "index=" + index; param += ",if="; - param += Config.hd_if_type; - } - param += ",media=disk"; - if (!hdc_img_path.equals("")) { - param += ",file=" + hdc_img_path; + param += hdInterface; + param += ",media=disk"; + if (!imagePath.equals("")) { + param += ",file=" + imagePath; + } + String cache = LimboSettingsManager.getDiskCache(LimboApplication.getInstance()); + if(cache != null && !cache.equals("default")) + param += ",cache=" + cache; + paramsList.add(param); } - paramsList.add(param); } + } - if (hdd_img_path != null) { - paramsList.add("-drive"); //empty - String param = "index=3"; - if (Config.enable_hd_if) { - param += ",if="; - param += Config.hd_if_type; - } - param += ",media=disk"; - if (!hdd_img_path.equals("")) { - param += ",file=" + hdd_img_path; - } - paramsList.add(param); - } else if (Config.enableSharedFolder && shared_folder_path != null) { + public void addSharedFolder(ArrayList paramsList, String sharedFolderPath) { + if (Config.enableSharedFolder && sharedFolderPath != null) { //XXX; We use hdd to mount any virtual fat drives paramsList.add("-drive"); //empty String driveParams = "index=3"; driveParams += ",media=disk"; - if (Config.enable_hd_if) { - driveParams += ",if="; - driveParams += Config.hd_if_type; - } + driveParams += ",if=ide"; driveParams += ",format=raw"; driveParams += ",file=fat:"; driveParams += "rw:"; //Always Read/Write - driveParams += shared_folder_path; + driveParams += sharedFolderPath; paramsList.add(driveParams); } } public void addRemovableDrives(ArrayList paramsList) { - - if (cd_iso_path != null) { - paramsList.add("-drive"); //empty - String param = "index=2"; - if (Config.enable_hd_if) { + String cdImagePath = getDriveFilePath(getMachine().getCdImagePath()); + if (cdImagePath != null) { + if (Config.legacyDrives) { + paramsList.add("-cdrom"); + paramsList.add(cdImagePath); + } else { + paramsList.add("-drive"); //empty + String param = "index=2"; param += ",if="; - param += Config.hd_if_type; - } - param += ",media=cdrom"; - if (!cd_iso_path.equals("")) { - param += ",file=" + cd_iso_path; + param += getMachine().getCDInterface(); + param += ",media=cdrom"; + if (!cdImagePath.equals("")) { + param += ",file=" + cdImagePath; + } + paramsList.add(param); } - paramsList.add(param); } - if (Config.enableEmulatedFloppy && fda_img_path != null) { - paramsList.add("-drive"); //empty - String param = "index=0,if=floppy"; - if (!fda_img_path.equals("")) { - param += ",file=" + fda_img_path; + String fdaImagePath = getDriveFilePath(getMachine().getFdaImagePath()); + if (Config.enableEmulatedFloppy && fdaImagePath != null) { + if (Config.legacyDrives) { + paramsList.add("-fda"); + paramsList.add(fdaImagePath); + } else { + paramsList.add("-drive"); //empty + String param = "index=0,if=floppy"; + if (!fdaImagePath.equals("")) { + param += ",file=" + fdaImagePath; + } + paramsList.add(param); } - paramsList.add(param); } - if (Config.enableEmulatedFloppy && fdb_img_path != null) { - paramsList.add("-drive"); //empty - String param = "index=1,if=floppy"; - if (!fdb_img_path.equals("")) { - param += ",file=" + fdb_img_path; + String fdbImagePath = getDriveFilePath(getMachine().getFdbImagePath()); + if (Config.enableEmulatedFloppy && fdbImagePath != null) { + if (Config.legacyDrives) { + paramsList.add("-fdb"); + paramsList.add(fdbImagePath); + } else { + paramsList.add("-drive"); //empty + String param = "index=1,if=floppy"; + if (!fdbImagePath.equals("")) { + param += ",file=" + fdbImagePath; + } + paramsList.add(param); } - paramsList.add(param); } - if (Config.enableEmulatedSDCard && sd_img_path != null) { - paramsList.add("-device"); - paramsList.add("sd-card,drive=sd0,bus=sd-bus"); - paramsList.add("-drive"); - String param = "if=none,id=sd0"; - if (!sd_img_path.equals("")) { - param += ",file=" + sd_img_path; + String sdImagePath = getDriveFilePath(getMachine().getSdImagePath()); + if (Config.enableEmulatedSDCard && sdImagePath != null) { + if (Config.legacyDrives) { + paramsList.add("-sd"); + paramsList.add(sdImagePath); + } else { + paramsList.add("-device"); + paramsList.add("sd-card,drive=sd0,bus=sd-bus"); + paramsList.add("-drive"); + String param = "if=none,id=sd0"; + if (!sdImagePath.equals("")) { + param += ",file=" + sdImagePath; + } + paramsList.add(param); } - paramsList.add(param); } } - //JNI Methods - public native String start(String storage_dir, String base_dir, String lib_path, int sdl_scale_hint, Object[] params, int paused, String save_state_name); - - public native String stop(int restart); - - public native void setsdlrefreshrate(int value); - - public native void setvncrefreshrate(int value); - public native int getsdlrefreshrate(); - - public native int getvncrefreshrate(); - - private native int onmouse(int button, int action, int relative, float x, float y); - - private native int setrelativemousemode(int relativemousemode); - - protected void vncchangepassword(String vnc_passwd) { - String res = QmpClient.sendCommand(QmpClient.changevncpasswd(vnc_passwd)); + /** + * change the vnc password before we connect + * The user is also prompted to create a certificate + * + * @param vncPassword The VNC password to be send to QEMU + */ + protected void vncchangepassword(String vncPassword) throws Exception { + String res = QmpClient.sendCommand(QmpClient.getChangeVncPasswdCommand(vncPassword)); String desc = null; if (res != null && !res.equals("")) { - try { - JSONObject resObj = new JSONObject(res); - if (resObj != null && !resObj.equals("") && res.contains("error")) { - String resInfo = resObj.getString("error"); - if (resInfo != null && !resInfo.equals("")) { - JSONObject resInfoObj = new JSONObject(resInfo); - desc = resInfoObj.getString("desc"); - UIUtils.toastLong(context, "Could not set VNC Password: " + desc); - Log.e(TAG, desc); - } + JSONObject resObj = new JSONObject(res); + if (resObj != null && !resObj.equals("") && res.contains("error")) { + String resInfo = resObj.getString("error"); + if (resInfo != null && !resInfo.equals("")) { + JSONObject resInfoObj = new JSONObject(resInfo); + desc = resInfoObj.getString("desc"); + Log.e(TAG, desc); } - - } catch (JSONException e) { - e.printStackTrace(); } } } - protected String changedev(String dev, String dev_value) { - QmpClient.sendCommand(QmpClient.changedev(dev, dev_value)); - String display_dev_value = FileUtils.getFullPathFromDocumentFilePath(dev_value); - return "Changed device: " + dev + " to " + display_dev_value; + protected String changedev(String dev, String value) { + String response = QmpClient.sendCommand(QmpClient.getChangeDeviceCommand(dev, value)); + String displayDevValue = FileUtils.getFullPathFromDocumentFilePath(value); + if (Config.debug) + ToastUtils.toastLong(LimboApplication.getInstance(), Gravity.BOTTOM, + LimboApplication.getInstance().getString(R.string.ChangedDevice) + ": " + + dev + ": " + displayDevValue); + return response; } protected String ejectdev(String dev) { - QmpClient.sendCommand(QmpClient.ejectdev(dev)); - return "Ejected device: " + dev; + String response = QmpClient.sendCommand(QmpClient.getEjectDeviceCommand(dev)); + if (Config.debug) + ToastUtils.toastLong(LimboApplication.getInstance(), Gravity.BOTTOM, + LimboApplication.getInstance().getString(R.string.EjectedDevice) + ": " + dev); + return response; } - public String startvm(Context context, int ui) { - LimboService.executor = this; - Intent i = new Intent(Config.ACTION_START, null, context, LimboService.class); + + /** + * Starts the service that will later start the qemu process + */ + public void startService() { + Intent i = new Intent(Config.ACTION_START, null, LimboApplication.getInstance(), + MachineController.getInstance().getServiceClass()); Bundle b = new Bundle(); - // b.putString("machine_type", this.machine_type); - b.putInt("ui", ui); i.putExtras(b); - context.startService(i); - Log.v(TAG, "start VM service"); - return "startVMService"; + Log.d(TAG, "Starting VM service"); + LimboApplication.getInstance().startService(i); + } + + /** + * Starts the native process. This should be called from a background thread from a + * foreground service in order to prevent the process from being killed + * + * @return String from the native code vm-executor-jni.cpp + */ + public String start() { + String res = null; + try { + String[] params = prepareParams(LimboApplication.getInstance()); + printParams(params); + // XXX: for VNC we need to resume manually after a reasonable amount of time + if (getMachine().getPaused() == 1 && MachineController.getInstance().isVNCEnabled()) { + continueVM(5000); + } + if (MachineController.getInstance().isVNCEnabled() && LimboSettingsManager.getVNCEnablePassword(LimboApplication.getInstance())) { + changeVncPass(LimboApplication.getInstance(), 2000); + } + + ignoreBreakpointInvalidation(LimboSettingsManager.getIgnoreBreakpointInvalidation(LimboApplication.getInstance())?1:0, 2000); + QmpClient.setExternal(LimboSettingsManager.getEnableExternalQMP(LimboApplication.getInstance())); + String libFilename = getQemuLibrary(); + res = start(Config.storagedir, LimboApplication.getBasefileDir(), + libFilename, FileUtils.getNativeLibDir(LimboApplication.getInstance()) + "/" + libFilename, + Config.SDLHintScale, params); + } catch (Exception ex) { + ToastUtils.toastLong(LimboApplication.getInstance(), ex.getMessage()); + return res; + } + return res; } - public void stopvm(final int restart) { + private void changeVncPass(final Context context, final long delay) { + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + e.printStackTrace(); + } + try { + vncchangepassword(LimboSettingsManager.getVNCPass(context)); + } catch (Exception e) { + ToastUtils.toastLong(LimboApplication.getInstance(), + context.getString(R.string.CouldNotSetVNCPass) + ": " + e.getMessage()); + e.printStackTrace(); + } + } + }).start(); + } + + private void continueVM(final int delay) { + // TODO: We shouldn't have to go through the view dispatcher + LimboApplication.getViewListener().onAction(MachineAction.CONTINUE_VM, delay); + } + public void stopvm(final int restart) { new Thread(new Runnable() { @Override public void run() { - doStopVM(restart); + if (restart != 0) { + QmpClient.sendCommand(QmpClient.getResetCommand()); + } else { + //XXX: Qmp command only halts the VM but doesn't exit so we use force close +// QmpClient.sendCommand(QmpClient.powerDown()); + stop(restart); + } } }).start(); } - public void doStopVM(final int restart) { + @Override + public int getSdlRefreshRate(boolean idle) { + if (idle) + return getSDLRefreshRateIdle(); + else + return getSDLRefreshRateDefault(); + } - if (restart == 0) { - LimboService.stopService(); + @Override + public void setSdlRefreshRate(int value, boolean idle) { + if (idle) + setSDLRefreshRateIdle(value); + else + setSDLRefreshRateDefault(value); + } - //XXX: Wait till service goes down - try { - Thread.sleep(2000); - } catch (InterruptedException e) { - e.printStackTrace(); - } + @Override + public String getDeviceName(MachineProperty driveProperty) { + switch (driveProperty) { + case CDROM: + return cdDeviceName; + case FDA: + return fdaDeviceName; + case FDB: + return fdbDeviceName; + case SD: + return sdDeviceName; } + return null; + } - if (restart != 0) { - QmpClient.sendCommand(QmpClient.reset()); - } else { - //XXX: Qmp command only halts the VM but doesn't exit - // so we use force close -// QmpClient.sendCommand(QmpClient.powerDown()); - stop(restart); + @Override + public synchronized void updateDisplay(int width, int height, int orientation) { + if (!LimboSettingsManager.getPreventMouseOutOfBounds(LimboApplication.getInstance())) { + return; + } + String mouse = getMachine().getMouse(); + // If we use absolute pointer devices in the guest os (usb-tablet) we need to prevent + // the mouse from going out of bounds. This case happens when we use trackpad and when the + // guest display doesn't fit inside the Android Surface which is pretty much all the time. + // we could use SurfaceHolder.setFixedSize() to bound the surfaceview but it creates + // problems with refreshing the surfaceview plus we would still need this fix for trackpad + if (mouse != null && mouse.equals("usb-tablet")) { + int xmin = 0; + int xmax = width; + int ymin = 0; + int ymax = height; + if (orientation == Configuration.ORIENTATION_PORTRAIT) { + ymin = (int) (height - width * vm_height / (float) vm_width) / 2; + ymax = (int) (height + width * vm_height / (float) vm_width) / 2; + } else { + xmin = (int) (width - height * vm_width / (float) vm_height) / 2; + xmax = (int) (width + height * vm_width / (float) vm_height) / 2; + } + nativeMouseBounds(xmin, xmax, ymin, ymax); } } - public String savevm(String statename) { - // Set to delete previous snapshots after vm resumed - Log.v(TAG, "Save Snapshot"); - this.snapshot_name = statename; + @Override + public void setFullscreen() { + nativeFullscreen(); + //TODO: sparc doesn't not have vga so we need to + // see if we can apply similar call to the cg3 + if(LimboApplication.arch == Config.Arch.x86 + || LimboApplication.arch == Config.Arch.x86_64 + || LimboApplication.arch == Config.Arch.arm + || LimboApplication.arch == Config.Arch.arm64 + || LimboApplication.arch == Config.Arch.ppc + || LimboApplication.arch == Config.Arch.ppc64 + ) { + nativeRefreshScreen(1); + } + } - String res = null; - //TODO: - //res = QmpClient.sendCommand(QmpClient.saveSnapshot()); - return res; + @Override + public void enableAaudio(int value) { + nativeEnableAaudio(value, Config.aaudioLibName, + FileUtils.getNativeLibDir(LimboApplication.getInstance()) + + "/" + Config.aaudioLibName); } - public String resumevm() { - // Set to delete previous snapshots after vm resumed - Log.v(TAG, "Resume the VM"); - String res = startvm(); - Log.d(TAG, res); - return res; + @Override + public void ignoreBreakpointInvalidation(int value){ + ignoreBreakpointInvalidation(value, 0); } - public void change_vnc_password() { - Thread thread = new Thread(new Runnable() { + private void ignoreBreakpointInvalidation(final int value, final long delay) { + new Thread(new Runnable() { + @Override public void run() { - vncchangepassword(vnc_passwd); + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + e.printStackTrace(); + } + nativeIgnoreBreakpointInvalidate(value); } - }); - thread.start(); + }).start(); } - public String get_state() { - String res = QmpClient.sendCommand(QmpClient.getState()); + //TODO: re-enable getting status from the vm + public String getVmState() { + String res = QmpClient.sendCommand(QmpClient.getStateCommand()); String state = ""; if (res != null && !res.equals("")) { try { @@ -999,99 +908,166 @@ public String get_state() { return state; } - public void change_dev(final String dev, final String image_path) { + /** + * Function sends a command via qmp to change or eject the removable device + * + * @param drive The device to be changed + * @param imagePath If its null it ejects the drive otherwise it uses the disk file at that path + */ + public boolean changeRemovableDevice(final MachineProperty drive, final String imagePath) { + if (!LimboSettingsManager.getEnableQmp(LimboApplication.getInstance())) { + ToastUtils.toastShort(LimboApplication.getInstance(), LimboApplication.getInstance().getString(R.string.EnableQMPForChangingDrives)); + return false; + } + String dev = getDeviceName(drive); + + //XXX: first we eject any previous media + String response = VMExecutor.this.ejectdev(dev); - Thread thread = new Thread(new Runnable() { - public void run() { - String image_path_conv = FileUtils.convertDocumentFilePath(image_path); - if (image_path_conv == null || image_path_conv.trim().equals("")) { - VMExecutor.this.busy = true; - String res = VMExecutor.this.ejectdev(dev); - Log.d(TAG, res); - VMExecutor.this.busy = false; - } else if (FileUtils.fileValid(context, image_path_conv)) { - VMExecutor.this.busy = true; - String res = VMExecutor.this.changedev(dev, image_path_conv); - Log.d(TAG, res); - VMExecutor.this.busy = false; - } else { - Log.d(TAG, "File does not exist"); - } - } - }); - thread.setPriority(Thread.MIN_PRIORITY); - thread.start(); + // if there is no media there is nothing else to do + if (imagePath == null || imagePath.trim().equals("")) { + return true; + } + //XXX: we encode some characters from the document file path so it's processed + // correctly by qemu + String imagePathConverted = FileUtils.encodeDocumentFilePath(imagePath); + if (!FileUtils.fileValid(imagePathConverted)) { + String msg = LimboApplication.getInstance().getString(R.string.CouldNotOpenDocFile) + " " + + FileUtils.getFullPathFromDocumentFilePath(imagePathConverted) + + "\n" + LimboApplication.getInstance().getString(R.string.PleaseReassingYourDiskFiles); + ToastUtils.toastLong(LimboApplication.getInstance(), msg); + return false; + } + response = VMExecutor.this.changedev(dev, imagePathConverted); + if (response == null) + return false; + + return true; } + /** + * Fuction is a pass thru from the c get_fd() function called from native code + * This is bridged to the java code because it's the only way to open a file descriptor + * from the native code + * + * @param path File path + * @return Return value of FileUtils.get_fd() + */ public int get_fd(String path) { - int fd = FileUtils.get_fd(context, path); - return fd; - + return FileUtils.get_fd(path); } + /** + * Fuction is a pass thru from the c close_fd() function called from native code + * This is similar to the above get_fd but perhaps not needed. + * + * @param fd File Descriptor to be closed + * @return Return value of FileUtils.close_fd() + */ public int close_fd(int fd) { - int res = FileUtils.close_fd(fd); - return res; - + return FileUtils.close_fd(fd); } - public void prepPaths() { - File destDir = new File(save_dir); - if (!destDir.exists()) { - destDir.mkdirs(); - } - - // Protect the paths from qemu thinking they contain a protocol in the string + @Override + public String saveVM() { - this.hda_img_path = FileUtils.convertDocumentFilePath(this.hda_img_path); - if (this.hda_img_path != null && hda_img_path.equals("")) { - hda_img_path = null; - } - this.hdb_img_path = FileUtils.convertDocumentFilePath(this.hdb_img_path); - if (this.hdb_img_path != null && hdb_img_path.equals("")) { - hdb_img_path = null; - } - this.hdc_img_path = FileUtils.convertDocumentFilePath(this.hdc_img_path); - if (this.hdc_img_path != null && hdc_img_path.equals("")) { - hdc_img_path = null; + // Delete any previous state file + File file = new File(getSaveStateName()); + if (file.exists()) { + if (!file.delete()) { + return LimboApplication.getInstance().getString(R.string.CannotDeletePreviousStateFile); + } } - this.hdd_img_path = FileUtils.convertDocumentFilePath(this.hdd_img_path); - if (this.hdd_img_path != null && hdd_img_path.equals("")) { - hdd_img_path = null; + + if (Config.showToast) + ToastUtils.toastShort(LimboApplication.getInstance(), LimboApplication.getInstance().getString(R.string.PleaseWaitSavingVMState)); + + int currentFd = get_fd(getSaveStateName()); + String uri = "fd:" + currentFd; + String command = QmpClient.getStopVMCommand(); + String msg = QmpClient.sendCommand(command); + command = QmpClient.getMigrateCommand(false, false, uri); + msg = QmpClient.sendCommand(command); + if (msg != null) { + return processMigrationResponse(msg); } + return null; + } - // Removable disks - this.cd_iso_path = FileUtils.convertDocumentFilePath(this.cd_iso_path); - this.fda_img_path = FileUtils.convertDocumentFilePath(this.fda_img_path); + @Override + public void continueVM() { + String command = QmpClient.getContinueVMCommand(); + QmpClient.sendCommand(command); + } - this.fdb_img_path = FileUtils.convertDocumentFilePath(this.fdb_img_path); - this.sd_img_path = FileUtils.convertDocumentFilePath(this.sd_img_path); + @Override + public MachineController.MachineStatus getSaveVMStatus() { + String pauseState = ""; + String command = QmpClient.getQueryMigrationCommand(); + String res = QmpClient.sendCommand(command); - this.kernel = FileUtils.convertDocumentFilePath(this.kernel); - this.initrd = FileUtils.convertDocumentFilePath(this.initrd); + if (res != null && !res.equals("")) { + try { + JSONObject resObj = new JSONObject(res); + String resInfo = resObj.getString("return"); + JSONObject resInfoObj = new JSONObject(resInfo); + pauseState = resInfoObj.getString("status"); + } catch (JSONException e) { + if (Config.debug) + Log.e(TAG, "Error while checking saving vm: " + e.getMessage()); + } + if (pauseState.toUpperCase().equals("FAILED")) { + Log.e(TAG, "Error: " + res); + } + } + if (pauseState.toUpperCase().equals("ACTIVE")) { + return MachineController.MachineStatus.Saving; + } else if (pauseState.toUpperCase().equals("COMPLETED")) { + return MachineController.MachineStatus.SaveCompleted; + } else if (pauseState.toUpperCase().equals("FAILED")) { + return MachineController.MachineStatus.SaveFailed; + } + //TODO: proper error handling with user messages + return MachineController.MachineStatus.Unknown; } - public int setRelativeMouseMode(int relative) { - return setrelativemousemode(relative); + private String processMigrationResponse(String response) { + String errorStr = null; + try { + JSONObject object = new JSONObject(response); + errorStr = object.getString("error"); + } catch (Exception ex) { + if (Config.debug) + ex.printStackTrace(); + } + if (errorStr != null) { + String descStr = null; + + try { + JSONObject descObj = new JSONObject(errorStr); + descStr = descObj.getString("desc"); + } catch (Exception ex) { + if (Config.debug) + ex.printStackTrace(); + } + return descStr; + } + return null; } - public int onLimboMouse(int button, int action, int relative, float x, float y) { + public void sendMouseEvent(int button, int action, int relative, float x, float y) { //XXX: Make sure that mouse motion is not triggering crashes in SDL while resizing - if (!LimboSDLActivity.mIsSurfaceReady || LimboSDLActivity.isResizing) { -// Log.w(TAG, "onLimboMouse: Ignoring mouse event surface not ready"); - return -1; + if (LimboSDLActivity.isResizing) { + return; } - //XXX: Check boundaries, perhaps not necessary since SDL is also doing the same thing - if (relative == 1 - || (x >= 0 && x <= LimboSDLActivity.vm_width && y >= 0 && y <= LimboSDLActivity.vm_height) - || (action == MotionEvent.ACTION_SCROLL)) { -// Log.d(TAG, "onLimboMouse: B: " + button + ", A: " + action + ", R: " + relative + ", X: " + x + ", Y: " + y); - return onmouse(button, action, relative, x, y); - } - return -1; + nativeMouseEvent(button, action, relative, (int) x, (int) y); + } + + public boolean getQMPAllowExternal() { + return LimboSettingsManager.getEnableExternalQMP(LimboApplication.getInstance()); } } diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/keyboard/KeyboardUtils.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/keyboard/KeyboardUtils.java new file mode 100644 index 000000000..711f4af5f --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/keyboard/KeyboardUtils.java @@ -0,0 +1,58 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.keyboard; + +import android.app.Activity; +import android.content.Context; +import android.view.View; +import android.view.inputmethod.InputMethodManager; + +import com.max2idea.android.limbo.machine.MachineController; +import com.max2idea.android.limbo.main.Config; +import com.max2idea.android.limbo.main.LimboActivity; + +public class KeyboardUtils { + private static final String TAG = "KeyboardUtils"; + + public static boolean showKeyboard(Activity activity, boolean toggle, View view) { + // Prevent crashes from activating mouse when machine is paused + if ( MachineController.getInstance().isPaused()) + return !toggle; + + InputMethodManager inputMgr = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); + if (toggle || !Config.enableToggleKeyboard) { + if (view != null) { + view.requestFocus(); + inputMgr.showSoftInput(view, InputMethodManager.SHOW_FORCED); + } + } else { + if (view != null) { + inputMgr.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + } + return !toggle; + } + + public static void hideKeyboard(Activity activity, View view) { + InputMethodManager inputMgr = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); + if (view != null) { + inputMgr.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + } +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/keymapper/KeyMapManager.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/keymapper/KeyMapManager.java new file mode 100644 index 000000000..01b11ccbf --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/keymapper/KeyMapManager.java @@ -0,0 +1,585 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.keymapper; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Handler; +import android.os.Looper; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.Base64; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.widget.AdapterView; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.RelativeLayout; +import android.widget.SimpleAdapter; + +import com.limbo.emu.lib.R; +import com.max2idea.android.limbo.keyboard.KeyboardUtils; +import com.max2idea.android.limbo.main.Config; +import com.max2idea.android.limbo.main.LimboSettingsManager; +import com.max2idea.android.limbo.screen.ScreenUtils; +import com.max2idea.android.limbo.toast.ToastUtils; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** Manages loading, editing, storing, and viewing the keymapper layouts + * + */ +public class KeyMapManager { + private static final String TAG = "KeyMapManager"; + + private final Activity activity; + private final View view; + private final List> keyMappers = new ArrayList<>(); + private final int defaultRows; + private final int defaultCols; + public RelativeLayout mapperEditLayout; + public RelativeLayout mapperButtons; + + public KeySurfaceView keySurfaceView; + public KeyMapper keyMapper; + OnSendKeyEventListener sendKeyEventListener = null; + OnSendMouseEventListener sendMouseEventListener = null; + OnUnhandledTouchEventListener unhundledTouchEventListener = null; + private ImageButton mAddKeyMapper; + private ImageButton mRemoveKeyMapper; + private ImageButton mAddSpecialKeysButtons; + private ImageButton mUseKeyMapper; + private ImageButton mClearKey; + private ImageButton mRepeatKey; + private ListView mKeyMapperList; + private SimpleAdapter keyMapperAdapter; + private EditText mKeyMapperName; + private HashMap selectedMap; + private int lastOrientation = -1; + + public KeyMapManager(Activity activity, View view, int rows, int cols) throws Exception { + this.activity = activity; + this.view = view; + this.defaultRows = rows; + if (cols % 2 != 0) + throw new Exception("Cols should be even number!"); + this.defaultCols = cols; + setupWidgets(); + setupKeyMapper(); + } + + private void setupWidgets() { + mapperEditLayout = activity.findViewById(R.id.mapperEditLayout); + mapperEditLayout.setVisibility(View.GONE); + mapperButtons = activity.findViewById(R.id.mapperButtons); + mapperButtons.setVisibility(View.GONE); + mKeyMapperList = activity.findViewById(R.id.keyMapperList); + + mAddKeyMapper = activity.findViewById(R.id.addKeyMapper); + mKeyMapperName = activity.findViewById(R.id.key_mapper_name); + + mRemoveKeyMapper = activity.findViewById(R.id.removeKeyMapper); + mAddSpecialKeysButtons = activity.findViewById(R.id.addSpecialKeysButtons); + mUseKeyMapper = activity.findViewById(R.id.useKeyMapper); + mClearKey = activity.findViewById(R.id.clearKey); + mRepeatKey = activity.findViewById(R.id.repeatKey); + + + } + + public boolean toggleKeyMapper() { + boolean shown = false; + if (isEditMode() || isActive()) { + mapperEditLayout.setVisibility(View.GONE); + mapperButtons.setVisibility(View.GONE); + clearKeyMapper(); + ScreenUtils.updateOrientation(activity, lastOrientation); + shown = false; + } else { + mapperEditLayout.setVisibility(View.VISIBLE); + mapperButtons.setVisibility(View.VISIBLE); + lastOrientation = activity.getResources().getConfiguration().orientation; + shown = true; + } + return shown; + } + + private boolean isActive() { + return mapperButtons != null && mapperButtons.getVisibility() == View.VISIBLE; + } + + public boolean processKeyMap(KeyEvent event) { + if (isEditMode() && keySurfaceView.pointers.size() > 0) { + if (event != null) { + keySurfaceView.setKeyCode(event); + keySurfaceView.paint(true); + } + return true; + } + return false; + } + + + public boolean processMouseMap(int button, int action) { + if (isEditMode() + && keySurfaceView.pointers.size() > 0 + && (button == Config.SDL_MOUSE_LEFT || button == Config.SDL_MOUSE_MIDDLE || button == Config.SDL_MOUSE_RIGHT) + ) { + keySurfaceView.setMouseButton(button); + keySurfaceView.paint(true); + return true; + } + return false; + } + + public void sendKeyEvent(int keyCode, boolean down) { + if (sendKeyEventListener != null) + sendKeyEventListener.onSendKeyEvent(keyCode, down); + } + + public void sendMouseEvent(int button, boolean down) { + if (sendMouseEventListener != null) + sendMouseEventListener.onSendMouseEvent(button, down); + } + + private void setupKeyMapper() throws Exception { + setupKeyMapperButtons(); + setupKeyMapperList(); + setupKeyMapperLayout(); + } + + private void setupKeyMapperLayout() throws Exception { + mapperButtons.removeAllViews(); + keySurfaceView = new KeySurfaceView(activity, this); + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); + mapperButtons.addView(keySurfaceView, params); + } + + private void setupKeyMapperList() { + keyMapperAdapter = new KeyMapperListAdapter(activity, keyMappers, android.R.layout.simple_list_item_1, + new String[]{"keymapper_name"}, new int[]{android.R.id.text1}); + mKeyMapperList.setAdapter(keyMapperAdapter); + mKeyMapperList.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView adapterView, View view, int i, long l) { + loadKeyMapper(i); + } + }); + loadKeyMappers(); + + } + + private void setupKeyMapperButtons() { + + mAddKeyMapper.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + promptKeyMapperName(); + } + }); + mRemoveKeyMapper.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + promptDeleteKeyMapper(); + } + }); + mAddSpecialKeysButtons.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (!isKeyMapperActive()) + return; + advancedKey(); + } + }); + mUseKeyMapper.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (!isKeyMapperActive()) + return; + useKeyMapper(); + } + }); + mClearKey.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (!isKeyMapperActive()) + return; + clearKey(); + } + }); + mRepeatKey.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (!isKeyMapperActive()) + return; + repeatKey(); + } + }); + mKeyMapperName.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { + + } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { + if (keyMapper != null) { + try { + keyMapper.name = charSequence.toString(); + if (selectedMap != null) + selectedMap.put("keymapper_name", keyMapper.name); + saveKeyMappers(); + keyMapperAdapter.notifyDataSetChanged(); + } catch (Exception ex) { + ex.printStackTrace(); + } + + } + } + + @Override + public void afterTextChanged(Editable editable) { + + } + }); + } + + private KeyMapper getKeyMapperObj(String keyMapper) throws IOException, ClassNotFoundException { + byte[] bytes = Base64.decode(keyMapper, Base64.DEFAULT); + ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(bytes)); + KeyMapper keyMapperObj = (KeyMapper) stream.readObject(); + stream.close(); + return keyMapperObj; + } + + private String getKeyMapperString(KeyMapper keyMapperObj) throws IOException { + ByteArrayOutputStream bstream = new ByteArrayOutputStream(); + ObjectOutputStream stream = new ObjectOutputStream(bstream); + stream.writeObject(keyMapperObj); + stream.flush(); + byte[] bytes = bstream.toByteArray(); + stream.close(); + return new String(Base64.encode(bytes, Base64.DEFAULT)); + } + + private void loadKeyMapper(int position) { + selectedMap = keyMappers.get(position); + KeyMapper keyMapper = (KeyMapper) selectedMap.get("key_mapper"); + if (keyMapper != null) + loadKeyMapper(keyMapper); + } + + private void loadKeyMapper(KeyMapper keyMapper) { + this.keyMapper = keyMapper; + keySurfaceView.mapping = keyMapper.mapping; + keySurfaceView.updateDimensions(); + keySurfaceView.paint(true); + mKeyMapperName.setText(keyMapper.name); + } + + public void promptDeleteKeyMapper() { + if(keyMapper == null) + return; + final AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(activity).create(); + alertDialog.setTitle(activity.getString(R.string.KeyMapper)); + alertDialog.setMessage(activity.getString(R.string.DeleteKeyMapper)); + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.Delete), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + removeKeyMapper(); + alertDialog.dismiss(); + } + }); + alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, activity.getString(R.string.Cancel), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + alertDialog.dismiss(); + } + }); + alertDialog.show(); + } + + private void removeKeyMapper() { + if (keyMapper != null) { + HashMap keyMapMap = null; + for (HashMap map : keyMappers) { + if (map.containsKey("key_mapper") && map.get("key_mapper") == keyMapper) { + keyMapMap = map; + break; + } + } + if (keyMapMap != null) { + keyMappers.remove(keyMapMap); + saveKeyMappers(); + loadKeyMappers(); + clearKeyMapper(); + } + } + + } + + private void clearKeyMapper() { + keyMapper = null; + mKeyMapperName.setText(""); + keySurfaceView.updateDimensions(); + keySurfaceView.paint(true); + } + + private void advancedKey() { + promptAdvancedKey(); + } + + private void promptAdvancedKey() { + + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity); + alertDialogBuilder.setTitle(R.string.SpecialKeysButtons); + final CharSequence[] items = new CharSequence[]{"Left Ctrl", "Right Ctrl", "Left Alt", "Right Alt", + "Left Shift", "Right Shift", "Fn", "Mouse Btn Left", "Mouse Btn Middle", "Mouse Btn Right"}; + final int[] itemsKeyCodes = new int[]{KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_CTRL_RIGHT, + KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_ALT_RIGHT, KeyEvent.KEYCODE_SHIFT_LEFT, + KeyEvent.KEYCODE_SHIFT_RIGHT, KeyEvent.KEYCODE_FUNCTION, Config.SDL_MOUSE_LEFT, + Config.SDL_MOUSE_MIDDLE, Config.SDL_MOUSE_RIGHT + }; + final boolean[] itemsEnabled = new boolean[items.length]; + alertDialogBuilder.setMultiChoiceItems(items, itemsEnabled, new DialogInterface.OnMultiChoiceClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i, boolean b) { + int selected = 0; + for (boolean value : itemsEnabled) { + if (value) + selected++; + } + if (selected == KeyMapper.KeyMapping.MAX_KEY_MOUSE_BTNS) + ToastUtils.toastShort(activity, activity.getString(R.string.TooManyKeysButtons)); + itemsEnabled[i] = b; + } + }); + final AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, activity.getString(android.R.string.ok), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + for (int k = 0; k < itemsEnabled.length; k++) { + if (itemsEnabled[k]) { + if (keySurfaceView != null && keySurfaceView.pointers.size() > 0) { + // XXX: we should only have only button pressed under edit mode + for (KeyMapper.KeyMapping keyMapping : keySurfaceView.pointers.values()) { + if (k < 7) + keyMapping.addKeyCode(itemsKeyCodes[k], null); + else + keyMapping.addMouseButton(itemsKeyCodes[k]); + break; + } + saveKeyMappers(); + keySurfaceView.paint(false); + } + } + } + alertDialog.dismiss(); + } + }); + alertDialog.show(); + } + + public void promptKeyMapperName() { + final AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(activity).create(); + alertDialog.setTitle(activity.getString(R.string.KeyMapperName)); + final EditText keyMapperName = new EditText(activity); + keyMapperName.setText(""); + keyMapperName.setEnabled(true); + keyMapperName.setVisibility(View.VISIBLE); + keyMapperName.setSingleLine(); + alertDialog.setView(keyMapperName); + + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.Create), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + String keyMapperNameStr = keyMapperName.getText().toString(); + for (HashMap keyMapperKey : keyMappers) { + if (keyMapperKey.containsKey(keyMapperNameStr)) { + ToastUtils.toastShort(activity, activity.getString(R.string.MapperExistsChooseAnotherName)); + return; + } + } + KeyMapper nKeyMapper = new KeyMapper(keyMapperNameStr, defaultRows, defaultCols); + HashMap m = new HashMap<>(); + m.put("keymapper_name", keyMapperNameStr); + m.put("key_mapper", nKeyMapper); + keyMappers.add(m); + keyMapperAdapter.notifyDataSetChanged(); + loadKeyMapper(nKeyMapper); + saveKeyMappers(); + } + }); + alertDialog.show(); + } + + private void clearKey() { + if (keySurfaceView != null) { + if (keySurfaceView.pointers.size() > 0) { + for (KeyMapper.KeyMapping keyMapping : keySurfaceView.pointers.values()) { + keyMapping.clear(); + ToastUtils.toastShort(activity, activity.getString(R.string.ClearedKey)); + break; + } + } + keySurfaceView.paint(false); + saveKeyMappers(); + } + } + + private void repeatKey() { + if (keySurfaceView != null) { + for (KeyMapper.KeyMapping keyMapping : keySurfaceView.pointers.values()) { + keyMapping.toggleRepeat(); + if (keyMapping.isRepeat()) + ToastUtils.toastShort(activity, activity.getString(R.string.SetKeyRepeat)); + else + ToastUtils.toastShort(activity, activity.getString(R.string.RemovedKeyRepeat)); + break; + } + + keySurfaceView.paint(false); + saveKeyMappers(); + } + } + + public void useKeyMapper() { + keySurfaceView.pointers.clear(); + KeyboardUtils.hideKeyboard(activity, view); + mapperEditLayout.setVisibility(View.GONE); + mapperButtons.setVisibility(View.VISIBLE); + ScreenUtils.updateOrientation(activity, lastOrientation); + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + keySurfaceView.paint(true); + keySurfaceView.updateDimensions(); + ToastUtils.toastShort(activity, activity.getString(R.string.UsingKeyMapper) + ": " + keyMapper.name); + } + }, 500); + } + + private void loadKeyMappers() { + keyMappers.clear(); + keyMapper = null; + if (keySurfaceView != null) + keySurfaceView.updateDimensions(); + Set keyMappersSet = LimboSettingsManager.getKeyMappers(activity); + if (keyMappersSet != null) { + String[] keyMappersArr = keyMappersSet.toArray(new String[0]); + + for (String keyMapper : keyMappersArr) { + try { + KeyMapper keyMapperObj = getKeyMapperObj(keyMapper); + HashMap map = new HashMap<>(); + map.put("keymapper_name", keyMapperObj.name); + map.put("key_mapper", keyMapperObj); + keyMappers.add(map); + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); + } + } + } + Collections.sort(keyMappers, new Comparator>() { + @Override + public int compare(HashMap h1, HashMap h2) { + return ((String) h1.get("keymapper_name")).compareTo((String) h2.get("keymapper_name")); + } + }); + keyMapperAdapter.notifyDataSetChanged(); + } + + public void saveKeyMappers() { + HashSet keyMappersSet = new HashSet<>(); + for (HashMap map : keyMappers) { + KeyMapper mapper = (KeyMapper) map.get("key_mapper"); + if (mapper == null) + continue; + try { + String keyMapperStr = getKeyMapperString(mapper); + keyMappersSet.add(keyMapperStr); + } catch (IOException e) { + e.printStackTrace(); + } + } + LimboSettingsManager.setKeyMappers(activity, keyMappersSet); + } + + public boolean isKeyMapperActive() { + if (keyMapper == null) { + return false; + } else { + return true; + } + } + + public boolean isEditMode() { + return mapperEditLayout != null && mapperEditLayout.getVisibility() == View.VISIBLE; + } + + public void setOnSendKeyEventListener(OnSendKeyEventListener listener) { + this.sendKeyEventListener = listener; + } + + public void setOnSendMouseEventListener(OnSendMouseEventListener listener) { + this.sendMouseEventListener = listener; + } + + public void setOnUnhandledTouchEventListener(OnUnhandledTouchEventListener listener) { + this.unhundledTouchEventListener = listener; + } + + public interface OnSendKeyEventListener { + void onSendKeyEvent(int keyCode, boolean down); + } + + public interface OnSendMouseEventListener { + void onSendMouseEvent(int keyCode, boolean down); + } + + public interface OnUnhandledTouchEventListener { + void OnUnhandledTouchEvent(MotionEvent event); + } + + static class KeyMapperListAdapter extends SimpleAdapter { + public KeyMapperListAdapter(Context context, List> data, int resource, String[] from, int[] to) { + super(context, data, resource, from, to); + } + } +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/keymapper/KeyMapper.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/keymapper/KeyMapper.java new file mode 100644 index 000000000..db2ef45b5 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/keymapper/KeyMapper.java @@ -0,0 +1,159 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.keymapper; + +import android.view.KeyEvent; + +import java.io.Serializable; +import java.util.ArrayList; + +/** Definition of the Key mapper. It supports a 2d set of keys where col = 2 * rows. The keys are + * then split to 2 sets aligned to the left and right of the screen. + * + */ +public class KeyMapper implements Serializable { + private static final String TAG = "KeyMapper"; + private static final long serialVersionUID = 9114128818551070254L; + + public String name; + public KeyMapping[][] mapping; + public int rows; + public int cols; + + public KeyMapper(String name, int rows, int cols) { + this.name = name; + this.rows = rows; + this.cols = cols; + + mapping = new KeyMapping[rows][]; + init(); + } + + private void init() { + for (int row = 0; row < rows; row++) { + mapping[row] = new KeyMapping[cols]; + for (int col = 0; col < cols; col++) { + mapping[row][col] = new KeyMapping(row, col); + } + } + } + + public static class KeyMapping implements Serializable { + + public final static int MAX_KEY_MOUSE_BTNS = 6; + private static final long serialVersionUID = 7937229682428314497L; + + private ArrayList keyCodes = new ArrayList<>(); + private ArrayList mouseButtons = new ArrayList<>(); + private ArrayList unicodeChars = new ArrayList<>(); + private int x, y; + private boolean repeat; + private boolean modifiableKeys; + + public KeyMapping(int x, int y) { + this.x = x; + this.y = y; + } + + + public static boolean isKeyMeta(int keyCode) { + return keyCode == KeyEvent.KEYCODE_CTRL_LEFT + || keyCode == KeyEvent.KEYCODE_CTRL_RIGHT + || keyCode == KeyEvent.KEYCODE_ALT_LEFT + || keyCode == KeyEvent.KEYCODE_ALT_RIGHT + || keyCode == KeyEvent.KEYCODE_SHIFT_LEFT + || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT; + } + + public void addKeyCode(int keyCode, KeyEvent event) { + int unicodeChar = -1; + if(event!=null) { + unicodeChar = event.getUnicodeChar(); + if (keyCodes.contains(KeyEvent.KEYCODE_SHIFT_LEFT) + || keyCodes.contains(KeyEvent.KEYCODE_SHIFT_RIGHT) + ) { + int newUnicodeChar = event.getUnicodeChar(KeyEvent.META_SHIFT_ON); + if (newUnicodeChar != event.getUnicodeChar()) { + modifiableKeys = true; + unicodeChar = newUnicodeChar; + } + } + } + if (!availKeysButtons()) + return; + if (keyCodes.contains(keyCode)) + return; + keyCodes.add(keyCode); + unicodeChars.add(unicodeChar); + } + + public void addMouseButton(int button) { + if (!availKeysButtons()) + return; + if (mouseButtons.contains(button)) + return; + mouseButtons.add(button); + } + + private boolean availKeysButtons() { + return keyCodes.size() + mouseButtons.size() < MAX_KEY_MOUSE_BTNS; + } + + public void clear() { + keyCodes.clear(); + unicodeChars.clear(); + mouseButtons.clear(); + modifiableKeys = false; + } + + public void toggleRepeat() { + repeat = !repeat; + } + + public boolean hasModifiableKeys() { + return modifiableKeys; + } + + public ArrayList getKeyCodes() { + return new ArrayList<>(keyCodes); + } + + public boolean isRepeat() { + return repeat; + } + + public ArrayList getMouseButtons() { + return new ArrayList<>(mouseButtons); + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public ArrayList getUnicodeChars() { + return new ArrayList<>(unicodeChars); + } + } +} + + diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/keymapper/KeySurfaceView.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/keymapper/KeySurfaceView.java new file mode 100644 index 000000000..7b53c4b31 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/keymapper/KeySurfaceView.java @@ -0,0 +1,771 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.keymapper; + +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.util.AttributeSet; +import android.util.Log; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +import com.max2idea.android.limbo.main.Config; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * The view that renders the user-defined keys. If the user pressed outside of the user keys + * the events are delegated to the activity main surface. + */ +public class KeySurfaceView extends SurfaceView implements SurfaceHolder.Callback { + public static final int SOURCE_KEYMAP_SINGLEPOINT = -1; + public static final int SOURCE_KEYMAP_MULTIPOINT = -2; + private static final String TAG = "KeySurfaceView"; + private static final int KEY_CELL_PADDING = 4; + private static final long KEY_REPEAT_MS = 100; + private final Object drawLock = new Object(); + + public KeyMapper.KeyMapping[][] mapping; + public HashMap pointers = new HashMap<>(); + Paint mPaintKey = new Paint(); + Paint mPaintKeyRepeat = new Paint(); + Paint mPaintKeySelected = new Paint(); + Paint mPaintKeySelectedRepeat = new Paint(); + Paint mPaintKeyEmpty = new Paint(); + + Paint mPaintText = new Paint(); + Paint mPaintTextSmall = new Paint(); + ExecutorService repeaterExecutor = Executors.newFixedThreadPool(1); + + private SurfaceHolder surfaceHolder; + private KeyMapManager keyMapManager; + private boolean surfaceCreated; + private int buttonLayoutHeight; + private int buttonSize; + private int minRowLeft = -1; + private int minRowRight = -1; + private int vertFontOffset; + private int horizFontOffset; + + public KeySurfaceView(Context context) { + super(context); + setupPaints(); + } + + public KeySurfaceView(Context context, AttributeSet attrs) { + super(context, attrs); + setupSurfaceHolder(); + setupPaints(); + } + + public KeySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setupSurfaceHolder(); + setupPaints(); + } + + public KeySurfaceView(Context context, KeyMapManager keyMapManager) { + super(context); + setupSurfaceHolder(); + setupPaints(); + this.keyMapManager = keyMapManager; + } + + @Override + public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { + paint(true); + } + + @Override + public void surfaceDestroyed(SurfaceHolder surfaceHolder) { + + } + + @Override + public void onConfigurationChanged(Configuration configuration) { + updateDimensions(); + } + + private void setupPaints() { + mPaintKey.setColor(Color.parseColor("#7768EFFF")); + mPaintKeySelected.setColor(Color.parseColor("#7768B0FF")); + mPaintKeyRepeat.setColor(Color.parseColor("#77FBC69A")); + mPaintKeySelectedRepeat.setColor(Color.parseColor("#77FF7A6F")); + mPaintKeyEmpty.setColor(Color.parseColor("#00FFFFFF")); + + mPaintText.setColor(Color.BLACK); + mPaintText.setTextSize(36); + mPaintText.setFakeBoldText(true); + mPaintText.setTypeface(Typeface.create(Typeface.MONOSPACE, Typeface.BOLD)); + + mPaintTextSmall.setColor(Color.BLACK); + mPaintTextSmall.setTextSize(24); + mPaintTextSmall.setFakeBoldText(true); + mPaintTextSmall.setTypeface(Typeface.create(Typeface.MONOSPACE, Typeface.BOLD)); + + //offsets + Rect result = new Rect(); + mPaintText.getTextBounds("O", 0, 1, result); + vertFontOffset = result.height(); + horizFontOffset = result.width(); + } + + private void setupSurfaceHolder() { + surfaceHolder = getHolder(); + surfaceHolder.addCallback(this); + surfaceHolder.setFormat(PixelFormat.TRANSPARENT); + setZOrderOnTop(true); + } + + public boolean onTouchEvent(MotionEvent event) { + if (!keyMapManager.isKeyMapperActive()) + return false; + int action = event.getActionMasked(); + int pointers = 1; + if (!keyMapManager.isEditMode()) + pointers = event.getPointerCount(); + + HashSet addKeys = new HashSet<>(); + HashSet removeKeys = new HashSet<>(); + + Integer filterIndex = null; + if (action == MotionEvent.ACTION_POINTER_DOWN || action == MotionEvent.ACTION_POINTER_UP) + filterIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; + + for (int i = 0; i < pointers; i++) { + if (filterIndex != null && filterIndex != i) + continue; + int col = getColumnFromEvent(event, i); + int row = getRowFromEvent(event, i); + int pointerId = event.getPointerId(i); + + KeyMapper.KeyMapping prevKeyMapping = null; + if (this.pointers.containsKey(pointerId)) + prevKeyMapping = this.pointers.get(pointerId); + + // XXX: if it originated from outside of the visible keys + // don't process but delegate to the external surface view + boolean keyPressed = isKeyPressed(row, col, action, prevKeyMapping); + + if (keyPressed) { + processKeyPressed(pointerId, row, col, action, prevKeyMapping); + } else { + // delegate to the external surfaceview + cancelKeyPressed(pointerId, prevKeyMapping); + if (!keyMapManager.isEditMode() && keyMapManager.unhundledTouchEventListener != null) { + delegateEvent(event, i); + } + } + } + paint(true); + return true; + } + + private boolean isKeyPressed(int row, int col, int action, KeyMapper.KeyMapping prevKeyMapping) { + boolean keyPressed = false; + if ((action == MotionEvent.ACTION_MOVE || action == MotionEvent.ACTION_UP) && prevKeyMapping == null) + keyPressed = false; + else if (((col < keyMapManager.keyMapper.cols / 2 && row > minRowLeft) + || (col >= keyMapManager.keyMapper.cols / 2 && row > minRowRight)) + && row < keyMapManager.keyMapper.rows && row >= 0 + && col < keyMapManager.keyMapper.cols && col >= 0 + ) + keyPressed = true; + return keyPressed; + } + + private int getRowFromEvent(MotionEvent event, int i) { + int row = -1; + if (event.getY(i) - (getHeight() - buttonLayoutHeight) > 0) + row = (int) (event.getY(i) - (getHeight() - buttonLayoutHeight)) / buttonSize; + return row; + } + + private int getColumnFromEvent(MotionEvent event, int i) { + int col = -1; + if (event.getX(i) > this.getWidth() - buttonLayoutHeight) { + col = (int) (event.getX(i) - (this.getWidth() - buttonLayoutHeight)) / buttonSize + keyMapManager.keyMapper.cols / 2; + } else if (event.getX(i) < buttonLayoutHeight) { + col = (int) event.getX(i) / buttonSize; + } + return col; + } + + /** + * Cancel a Key Pressed + * + * @param pointerId Android MotionEvent PointerId + * @param keyMapping KeyMapping to be Canceled + */ + private void cancelKeyPressed(int pointerId, KeyMapper.KeyMapping keyMapping) { + if (!keyMapManager.isEditMode() && keyMapping != null) { + sendKeyEvents(keyMapping, false); + sendMouseEvents(keyMapping, false); + this.pointers.remove(pointerId); + } + } + + /** + * Processes a key pressed that has been mapped by the user + * + * @param pointerId Android MotionEvent pointerId + * @param row Key row + * @param col Key column + * @param action Android MotionEvent action + * @param prevKeyMapping Android Previous Key pressed by the same finger, this helps us keep + * track when the pointer moves over to another key + */ + private void processKeyPressed(int pointerId, int row, int col, int action, KeyMapper.KeyMapping prevKeyMapping) { + KeyMapper.KeyMapping keyMapping = mapping[row][col]; + if (action == MotionEvent.ACTION_DOWN + || action == MotionEvent.ACTION_POINTER_DOWN + || action == MotionEvent.ACTION_MOVE) { + if (prevKeyMapping != null && this.pointers.get(pointerId) != keyMapping) { + sendKeyEvents(prevKeyMapping, false); + sendMouseEvents(prevKeyMapping, false); + this.pointers.remove(pointerId); + } + if (!this.pointers.containsKey(pointerId)) { + this.pointers.put(pointerId, keyMapping); + if (keyMapping.isRepeat()) { + startRepeater(); + } else { + sendKeyEvents(keyMapping, true); + sendMouseEvents(keyMapping, true); + } + } else if (keyMapManager.isEditMode() && action == MotionEvent.ACTION_DOWN) { + if (this.pointers.get(pointerId) == keyMapping) { + this.pointers.remove(pointerId); + } + } + } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP) { + if (!keyMapManager.isEditMode() && prevKeyMapping != null) { + // XXX: if the key is repeating it should send the ACTION_UP when it stops + // otherwise if it's not repeating we send the action up here + if (!keyMapping.isRepeat()) { + sendKeyEvents(prevKeyMapping, false); + sendMouseEvents(prevKeyMapping, false); + } + this.pointers.remove(pointerId); + } + } + } + + + /** + * Delegates a MotionEvent to the surfaceView for handling as a mouse movement + * + * @param event Original MotionEvent + * @param index Pointer index from the MotionEvent that needs to be delegated + */ + private void delegateEvent(MotionEvent event, int index) { + int nAction = event.getActionMasked(); + if (nAction == MotionEvent.ACTION_POINTER_DOWN) + nAction = MotionEvent.ACTION_DOWN; + else if (nAction == MotionEvent.ACTION_POINTER_UP) + nAction = MotionEvent.ACTION_UP; + + // XXX: we need to generate a new event with 1 pointer and pass it to the external surfaceview + MotionEvent.PointerProperties pointerProperties = new MotionEvent.PointerProperties(); + event.getPointerProperties(index, pointerProperties); + MotionEvent.PointerCoords pointerCoordinates = new MotionEvent.PointerCoords(); + event.getPointerCoords(index, pointerCoordinates); + MotionEvent nEvent = MotionEvent.obtain(event.getDownTime(), event.getEventTime(), nAction, 1, + new MotionEvent.PointerProperties[]{new MotionEvent.PointerProperties(pointerProperties)}, + new MotionEvent.PointerCoords[]{new MotionEvent.PointerCoords(pointerCoordinates)}, + event.getMetaState(), event.getButtonState(), event.getXPrecision(), event.getYPrecision(), + event.getDeviceId(), event.getEdgeFlags(), event.getSource(), event.getFlags()); + if (event.getPointerCount() > 1) + nEvent.setSource(SOURCE_KEYMAP_MULTIPOINT); + else + nEvent.setSource(SOURCE_KEYMAP_SINGLEPOINT); + if(keyMapManager.unhundledTouchEventListener!=null) + keyMapManager.unhundledTouchEventListener.OnUnhandledTouchEvent(nEvent); + } + + /** + * Start the repeater for keys that were selected by the user via the Key Mapper. The executor + * guarantees only 1 thread will run. The function will process all repeating keys and will + * exit when there are none left. + */ + private void startRepeater() { + repeaterExecutor.submit(new Runnable() { + @Override + public void run() { + try { + runRepeater(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + }); + } + + private void runRepeater() { + // if there is at least one key to be repeated we go on + boolean atLeastOneKey = true; + while (atLeastOneKey) { + atLeastOneKey = false; + for (KeyMapper.KeyMapping keyMapping : pointers.values()) { + if (!keyMapping.isRepeat()) + continue; + if (keyMapping.getKeyCodes().size() == 0 && keyMapping.getMouseButtons().size() == 0) + continue; + atLeastOneKey = true; + sendKeyEvents(keyMapping, true); + sendMouseEvents(keyMapping, true); + delay(KEY_REPEAT_MS); + sendKeyEvents(keyMapping, false); + sendMouseEvents(keyMapping, false); + delay(KEY_REPEAT_MS); + } + } + } + + private void delay(long keyRepeatMs) { + try { + Thread.sleep(KEY_REPEAT_MS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + /** + * Sends a Mouse Event to the main SDL Interface + * + * @param keyMapping KeyMapping containing Mouse Button Events + * @param down If true, send Mouse Button events with action down + */ + private void sendMouseEvents(KeyMapper.KeyMapping keyMapping, boolean down) { + if (keyMapManager.isEditMode()) + return; + for (int mouseButton : keyMapping.getMouseButtons()) { + keyMapManager.sendMouseEvent(mouseButton, down); + } + } + + /** + * Sends a Key Event to the main SDL Interface + * + * @param keyMapping KeyMapping containing Key Events + * @param down If true, send Key Events with action down + */ + private void sendKeyEvents(KeyMapper.KeyMapping keyMapping, boolean down) { + if (keyMapManager.isEditMode()) + return; + for (int keyCode : keyMapping.getKeyCodes()) { + keyMapManager.sendKeyEvent(keyCode, down); + } + } + + public void surfaceCreated(SurfaceHolder arg0) { + this.surfaceCreated = true; + updateDimensions(); + paint(false); + } + + /** + * Refreshes the Dimension of the KeyMapper Buttons, this should be called when the screen + * configuration changes or when the layout resize, etc... + */ + public void updateDimensions() { + int height = getHeight(); + int width = getWidth(); + buttonLayoutHeight = 0; + if (height > width) + buttonLayoutHeight = width / 2; + else + buttonLayoutHeight = height / 2; + if (keyMapManager.keyMapper == null) + buttonSize = 1; + else + buttonSize = buttonLayoutHeight / keyMapManager.keyMapper.rows; + + minRowLeft = -1; + minRowRight = -1; + if (!keyMapManager.isEditMode() && keyMapManager.keyMapper != null) { + minRowLeft = getMinRow(0, keyMapManager.keyMapper.cols / 2); + minRowRight = getMinRow(keyMapManager.keyMapper.cols / 2, keyMapManager.keyMapper.cols); + } + } + /** Gets the maximum row with all empty cells starting from the top for both left and right key + * maps. This limit is used when detecting touch events and allows the user more space on the + * top for trackpad movement + * */ + + /** + * Gets the maximum row with all empty cells starting from the top for both left and right key + * maps. This limit is used when detecting touch events and allows the user more space on the + * top for trackpad movement. + * + * @param startCol Starting column + * @param endCol End Column + * @return The maximum row with all empty cells + */ + private int getMinRow(int startCol, int endCol) { + int minRow = -1; + for (int row = 0; row < keyMapManager.keyMapper.rows; row++) { + int cellsMissing = 0; + for (int col = startCol; col < endCol; col++) { + if (keyMapManager.keyMapper.mapping[row][col].getKeyCodes().size() == 0 + && keyMapManager.keyMapper.mapping[row][col].getMouseButtons().size() == 0) { + cellsMissing++; + } else { + break; + } + } + if (cellsMissing == keyMapManager.keyMapper.cols / 2) + minRow = row; + else + break; + } + return minRow; + } + + /** + * Paints the KeyMapper buttons + * + * @param clear Force clear the contents of the SurfaceView + */ + public synchronized void paint(boolean clear) { + Canvas canvas = null; + if (!surfaceCreated || surfaceHolder == null) { + Log.w(TAG, "Cannot paint surface not ready"); + return; + } + + try { + canvas = surfaceHolder.lockCanvas(); + synchronized (drawLock) { + canvas.drawColor(Color.parseColor("#00FFFFFF"), PorterDuff.Mode.CLEAR); + if (keyMapManager.keyMapper != null) { + for (KeyMapper.KeyMapping keyMapping : pointers.values()) { + drawButton(canvas, keyMapping.getX(), keyMapping.getY(), true, keyMapping.isRepeat()); + } + drawButtons(canvas); + } + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (canvas != null) { + surfaceHolder.unlockCanvasAndPost(canvas); + } + } + } + + private void drawButton(Canvas canvas, int row, int col, boolean isSelected, boolean repeat) { + int hOffset = col < keyMapManager.keyMapper.cols / 2 ? 0 : (this.getWidth() - buttonLayoutHeight); + int vOffset = this.getHeight() - buttonLayoutHeight; + Paint mPaint; + if (isSelected) { + mPaint = repeat ? mPaintKeySelectedRepeat : mPaintKeySelected; + } else { + mPaint = repeat ? mPaintKeyRepeat : mPaintKey; + } + + if (mapping[row][col].getKeyCodes() != null && mapping[row][col].getMouseButtons() != null + && (mapping[row][col].getKeyCodes().size() > 0 || mapping[row][col].getMouseButtons().size() > 0 + || keyMapManager.isEditMode())) { + canvas.drawRect((float) (hOffset + col % (keyMapManager.keyMapper.cols / 2) * buttonSize + KEY_CELL_PADDING), + (float) (vOffset + row * buttonSize + KEY_CELL_PADDING), + (float) (hOffset + (col % (keyMapManager.keyMapper.cols / 2) + 1) * buttonSize - KEY_CELL_PADDING), + (float) (vOffset + (row + 1) * buttonSize - KEY_CELL_PADDING), + mPaint); + } + } + + private void drawButtons(Canvas canvas) { + for (int row = 0; row < keyMapManager.keyMapper.rows; row++) { + for (int col = 0; col < keyMapManager.keyMapper.cols; col++) { + if (!pointers.containsValue(mapping[row][col]) && (keyMapManager.isEditMode() + || mapping[row][col].getKeyCodes().size() > 0 + || mapping[row][col].getMouseButtons().size() > 0)) { + drawButton(canvas, row, col, false, mapping[row][col].isRepeat()); + } + drawText(canvas, row, col); + } + } + } + + private void drawText(Canvas canvas, int row, int col) { + int hOffset = col < keyMapManager.keyMapper.cols / 2 ? 0 : (this.getWidth() - buttonLayoutHeight); + int vOffset = this.getHeight() - buttonLayoutHeight; + if (mapping[row][col].getKeyCodes() != null && mapping[row][col].getMouseButtons() != null) { + if (mapping[row][col].getKeyCodes().size() > 0 || mapping[row][col].getMouseButtons().size() > 0) { + String[] texts = getText(mapping[row][col]); + int count = 0; + for(String text : texts) { + if(text!=null) { + int vFontOffset = getVertFontOffset(count); + int hFontOffset = getHorizFontOffset(count)*2; + drawText(canvas, text, row, col, hOffset + hFontOffset, vOffset + vFontOffset, + count == 0? mPaintText: mPaintTextSmall, getAlignPos(count)); + } + count++; + } + } + } + } + + private int getHorizFontOffset(int count) { + + if(count == 1 || count == 3) { + return -horizFontOffset; + } else if(count == 2 || count == 4) { + return horizFontOffset; + } + return 0; + } + + private int getVertFontOffset(int count) { + + if (count == 1 || count == 2) + return -vertFontOffset; + else if(count == 3 || count == 4) + return vertFontOffset; + return 0; + } + + private Paint.Align getAlignPos(int pos) { +// switch(pos) { +// case 0: +// return Paint.Align.CENTER; +// case 1: +// case 3: +// return Paint.Align.RIGHT; +// case 2: +// case 4: +// return Paint.Align.LEFT; +// } + return Paint.Align.CENTER; + } + + private void drawText(Canvas canvas, String text, int row, int col, int hOffset, int vOffset, Paint paint, Paint.Align align) { + paint.setTextAlign(align); + canvas.drawText(text, + (float) (hOffset + (col % (keyMapManager.keyMapper.cols / 2)) * buttonSize + buttonSize / 2), + (float) (vOffset + row * buttonSize + buttonSize / 2), + paint); + } + + public void setKeyCode(KeyEvent event) { + KeyEvent e = new KeyEvent(event.getAction(), event.getKeyCode()); + for (KeyMapper.KeyMapping keyMapping : pointers.values()) { + keyMapping.addKeyCode(event.getKeyCode(), event); + break; + } + keyMapManager.saveKeyMappers(); + } + + public void setMouseButton(int button) { + for (KeyMapper.KeyMapping keyMapping : pointers.values()) { + keyMapping.addMouseButton(button); + break; + } + keyMapManager.saveKeyMappers(); + } + + /** + * Get the string representation of the KeyMapping to be used as a Button Label + * on the SurfaceView + * + * @return The Text Strings providing a short description of the Key and Mouse Button events + * defined by the user + * @param keyMapping + */ + public String[] getText(KeyMapper.KeyMapping keyMapping) { + // cell center occupies 2 texts so we use maxKeysMouseButtons-1 + LinkedHashSet keys = new LinkedHashSet<>(); + LinkedHashSet btns = new LinkedHashSet<>(); + LinkedHashSet meta = new LinkedHashSet<>(); + if (keyMapping.getKeyCodes() != null) { + + Iterator iter = keyMapping.getKeyCodes().iterator(); + Iterator iterChar = keyMapping.getUnicodeChars().iterator(); + while (iter.hasNext()) { + int keyCode = iter.next(); + int unicodeChar = iterChar.next(); + if (KeyMapper.KeyMapping.isKeyMeta(keyCode)) { + if(keyMapping.hasModifiableKeys() && (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT + || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) + ) + continue; + meta.add(translateCode(keyCode,-1)); + continue; + } + keys.add(translateCode(keyCode, unicodeChar)); + } + } + if (keyMapping.getMouseButtons() != null) { + for (int button : keyMapping.getMouseButtons()) { + btns.add(traslateMouseBtn(button)); + } + } + + StringBuilder textCenter = new StringBuilder(); + int centerKeys = 0; + centerKeys = moveTextToCenter(textCenter, new ArrayList<>(keys), keys, centerKeys); + centerKeys = moveTextToCenter(textCenter, new ArrayList<>(btns), btns, centerKeys); + moveTextToCenter(textCenter, new ArrayList<>(meta), meta, centerKeys); + + // now start placing the texts around the square + String [] texts = new String[KeyMapper.KeyMapping.MAX_KEY_MOUSE_BTNS -1]; + texts[0] = textCenter.toString(); + int count = 1; + for(String metaText : meta) + texts[count++] = metaText; + for(String keyText : keys) + texts[count++] = "+" + keyText; + for(String btnText : btns) + texts[count++] = "+" + btnText; + return texts; + } + + private String traslateMouseBtn(int button) { + switch (button) { + case Config.SDL_MOUSE_LEFT: + return "Mbl"; + case Config.SDL_MOUSE_MIDDLE: + return "Mbm"; + case Config.SDL_MOUSE_RIGHT: + return "Mbr"; + } + return null; + } + + private int moveTextToCenter(StringBuilder textCenter, + ArrayList texts, HashSet src, int centerKeys) { + for(String text : texts) { + if(centerKeys >= 2) // only 2 texts in the center + break; + if(!textCenter.toString().equals("")) + textCenter.insert(0, "+"); + textCenter.insert(0, text); + src.remove(text); + centerKeys++; + } + return centerKeys; + } + + /** + * Retrieves the keyboard character from KeyEvents and translates Special Keys to + * short abbreviations + * + * @param keycode Android KeyEvent keyCode + * @param unicodeChar Unicode Character if exists + * @return The string represented by the keyCode + */ + private String translateCode(int keycode, int unicodeChar) { + String text; + if (keycode == KeyEvent.KEYCODE_DPAD_UP) + text = ((char) 0x21E7) + ""; + else if (keycode == KeyEvent.KEYCODE_DPAD_DOWN) + text = ((char) 0x21E9) + ""; + else if (keycode == KeyEvent.KEYCODE_DPAD_RIGHT) + text = ((char) 0x21E8) + ""; + else if (keycode == KeyEvent.KEYCODE_DPAD_LEFT) + text = ((char) 0x21E6) + ""; + else if (keycode == KeyEvent.KEYCODE_ESCAPE) + text = "Esc"; + else if (keycode == KeyEvent.KEYCODE_ENTER) + text = ((char) 0x23CE) + ""; + else if (keycode == KeyEvent.KEYCODE_TAB) + text = "Tab"; + else if (keycode == KeyEvent.KEYCODE_CTRL_LEFT) + text = "Ctrl"; + else if (keycode == KeyEvent.KEYCODE_CTRL_RIGHT) + text = "Ctrl"; + else if (keycode == KeyEvent.KEYCODE_ALT_LEFT) + text = "Alt"; + else if (keycode == KeyEvent.KEYCODE_ALT_RIGHT) + text = "Alt"; + else if (keycode == KeyEvent.KEYCODE_SHIFT_LEFT) + text = "Shft"; + else if (keycode == KeyEvent.KEYCODE_SHIFT_RIGHT) + text = "Shft"; + else if (keycode == KeyEvent.KEYCODE_DEL) + text = "Bksp"; + else if (keycode == KeyEvent.KEYCODE_FORWARD_DEL) + text = "Del"; + else if (keycode == KeyEvent.KEYCODE_INSERT) + text = "Ins"; + else if (keycode == KeyEvent.KEYCODE_MOVE_END) + text = "End"; + else if (keycode == KeyEvent.KEYCODE_SYSRQ) + text = "Syrq"; + else if (keycode == KeyEvent.KEYCODE_MOVE_HOME) + text = "Home"; + else if (keycode == KeyEvent.KEYCODE_SPACE) + text = "Spc"; + else if (keycode == KeyEvent.KEYCODE_PAGE_UP) + text = "PgUp"; + else if (keycode == KeyEvent.KEYCODE_PAGE_DOWN) + text = "PgDn"; + else if (keycode == KeyEvent.KEYCODE_BREAK) + text = "Brk"; + else if (keycode == KeyEvent.KEYCODE_SCROLL_LOCK) + text = "Scrl"; + else if (keycode == KeyEvent.KEYCODE_NUM_LOCK) + text = "NumL"; + else if (keycode == KeyEvent.KEYCODE_F1) + text = "F1"; + else if (keycode == KeyEvent.KEYCODE_F2) + text = "F2"; + else if (keycode == KeyEvent.KEYCODE_F3) + text = "F3"; + else if (keycode == KeyEvent.KEYCODE_F4) + text = "F4"; + else if (keycode == KeyEvent.KEYCODE_F5) + text = "F5"; + else if (keycode == KeyEvent.KEYCODE_F6) + text = "F6"; + else if (keycode == KeyEvent.KEYCODE_F7) + text = "F7"; + else if (keycode == KeyEvent.KEYCODE_F8) + text = "F8"; + else if (keycode == KeyEvent.KEYCODE_F9) + text = "F9"; + else if (keycode == KeyEvent.KEYCODE_F10) + text = "F10"; + else if (keycode == KeyEvent.KEYCODE_F11) + text = "F11"; + else if (keycode == KeyEvent.KEYCODE_F12) + text = "F12"; + else + return ((char) unicodeChar) + ""; + return text; + } + +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/LinksManager.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/links/LinksManager.java similarity index 73% rename from limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/LinksManager.java rename to limbo-android-lib/src/main/java/com/max2idea/android/limbo/links/LinksManager.java index 87fbf7273..9aed65668 100644 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/LinksManager.java +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/links/LinksManager.java @@ -16,8 +16,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -package com.max2idea.android.limbo.utils; +package com.max2idea.android.limbo.links; +import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.net.Uri; @@ -31,77 +32,83 @@ import android.widget.ListView; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; + import com.limbo.emu.lib.R; import com.max2idea.android.limbo.main.Config; import java.util.ArrayList; -import java.util.Iterator; import java.util.Map; import java.util.Set; -import androidx.appcompat.app.AppCompatActivity; - /** - * @author thedoc + * Links for usefull applications and OS images */ -public class LinksManager extends AppCompatActivity { - - private static String TAG = "LinksManager"; +public class LinksManager extends AlertDialog { + private final static String TAG = "LinksManager"; private ListView listIsoView; private ArrayList itemsISOs = null; + public LinksManager(@NonNull Context context) { + super(context); + } + + protected LinksManager(@NonNull Context context, int themeResId) { + super(context, themeResId); + } + + protected LinksManager(@NonNull Context context, boolean cancelable, @Nullable OnCancelListener cancelListener) { + super(context, cancelable, cancelListener); + } + public void goToURL(String url) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(url)); - startActivity(intent); + getContext().startActivity(intent); } @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); - setContentView(R.layout.links_list); + setContentView(R.layout.os_links); setupListeners(); fill(); } private void setupListeners() { - listIsoView = (ListView) findViewById(R.id.listISOs); + listIsoView = (ListView) findViewById(R.id.os_list); listIsoView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View v, int position, long id) { int selectionRowID = (int) id; String path = itemsISOs.get(selectionRowID).url; - goToURL(path); + if(path!=null) + goToURL(path); + dismiss(); } }); } private void fill() { - itemsISOs = new ArrayList(); - + itemsISOs = new ArrayList<>(); if (Config.osImages != null) { Set> linkSet = Config.osImages.entrySet(); - Iterator> iter = linkSet.iterator(); - while (iter.hasNext()) { - Map.Entry item = iter.next(); + for (Map.Entry item : linkSet) { LinkInfo linkInfo = item.getValue(); itemsISOs.add(linkInfo); - - } } - FileAdapter fileList = new FileAdapter(this, R.layout.link_row, itemsISOs); + FileAdapter fileList = new FileAdapter(getContext(), R.layout.link_row, itemsISOs); listIsoView.setAdapter(fileList); - - - } public enum LinkType { - ISO, TOOL + ISO, TOOL, OTHER } public static class LinkInfo { @@ -118,7 +125,7 @@ public LinkInfo(String title, String descr, String url, LinkType type) { } } - public class FileAdapter extends ArrayAdapter { + public static class FileAdapter extends ArrayAdapter { private final Context context; private final ArrayList files; @@ -133,6 +140,7 @@ public View getView(int position, View convertView, ViewGroup parent) { LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + @SuppressLint("ViewHolder") View rowView = inflater.inflate(R.layout.link_row, parent, false); TextView textView = (TextView) rowView.findViewById(R.id.LINK_NAME); TextView descrView = (TextView) rowView.findViewById(R.id.LINK_DESCR); @@ -141,9 +149,9 @@ public View getView(int position, View convertView, ViewGroup parent) { textView.setText(linkInfo.title); descrView.setText(linkInfo.descr); - if(linkInfo.type == LinkType.ISO) + if (linkInfo.type == LinkType.ISO || linkInfo.type == LinkType.OTHER) imageView.setImageResource(R.drawable.cd); - else if(linkInfo.type == LinkType.TOOL) + else if (linkInfo.type == LinkType.TOOL) imageView.setImageResource(R.drawable.advanced); return rowView; diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/log/Logger.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/log/Logger.java new file mode 100644 index 000000000..83df996a8 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/log/Logger.java @@ -0,0 +1,196 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.log; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Color; +import android.net.Uri; +import android.text.InputType; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; +import android.util.Log; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; + +import com.limbo.emu.lib.R; +import com.max2idea.android.limbo.dialog.DialogUtils; +import com.max2idea.android.limbo.main.LimboApplication; +import com.max2idea.android.limbo.main.LimboFileManager; +import com.max2idea.android.limbo.files.FileUtils; +import com.max2idea.android.limbo.machine.Machine.FileType; +import com.max2idea.android.limbo.main.Config; +import com.max2idea.android.limbo.toast.ToastUtils; + +import java.io.File; +import java.io.IOException; +import java.util.Scanner; + +/** Custom logger for android so users can view the log if something went wrong + * + */ +public class Logger { + public static final String TAG = "Logger"; + + public static Spannable formatAndroidLog(String contents) { + Scanner scanner = null; + Spannable formattedString = new SpannableString(contents); + if (contents.length() == 0) + return formattedString; + + try { + scanner = new Scanner(contents); + int counter = 0; + ForegroundColorSpan colorSpan; + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + //FIXME: some devices don't have standard format for the log + if (line.startsWith("E/") || line.contains(" E ")) { + colorSpan = new ForegroundColorSpan(Color.parseColor("#F78181")); + } else if (line.startsWith("W/") || line.contains(" W ")) { + colorSpan = new ForegroundColorSpan(Color.parseColor("#81A3F7")); + } else if (line.startsWith("D/") || line.contains(" D ")) { + colorSpan = new ForegroundColorSpan(Color.parseColor("#B1FFD7")); + } else { + colorSpan = null; + } + if (colorSpan != null) { + formattedString.setSpan(colorSpan, counter, counter + line.length(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + counter += line.length() + 1; + } + + } catch (Exception ex) { + Log.w(TAG, "Could not format limbo log: " + ex.getMessage()); + } finally { + if (scanner != null) { + try { + scanner.close(); + } catch (Exception ex) { + if (Config.debug) + ex.printStackTrace(); + } + } + + } + return formattedString; + } + + public static void promptShowLog(final Activity activity) { + + final AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(activity).create(); + alertDialog.setTitle(activity.getString(R.string.ShowLog)); + TextView stateView = new TextView(activity); + stateView.setText(R.string.LogWarning); + stateView.setPadding(20, 20, 20, 20); + alertDialog.setView(stateView); + + // alertDialog.setMessage(body); + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.Yes), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + + viewLimboLog(activity); + } + }); + alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, activity.getString(R.string.Cancel), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + } + }); + alertDialog.show(); + } + + public static void UIAlertLog(final Activity activity, String title, Spannable body) { + + final AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(activity).create(); + alertDialog.setTitle(title); + TextView textView = new TextView(activity); + textView.setPadding(20, 20, 20, 20); + textView.setText(body); + textView.setBackgroundColor(Color.BLACK); + textView.setTextSize(12); + textView.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + textView.setSingleLine(false); + ScrollView view = new ScrollView(activity); + view.addView(textView); + alertDialog.setView(view); + alertDialog.setCanceledOnTouchOutside(false); + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.Ok), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + alertDialog.dismiss(); + } + }); + alertDialog.setButton(DialogInterface.BUTTON_NEUTRAL, activity.getString(R.string.CopyTo), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + ToastUtils.toastShort(activity, activity.getString(R.string.ChooseDirToSaveLogFile)); + LimboFileManager.browse(activity, FileType.LOG_DIR, Config.OPEN_LOG_FILE_DIR_REQUEST_CODE); + alertDialog.dismiss(); + } + }); + alertDialog.show(); + } + + public static void viewLimboLog(final Activity activity) { + + String contents = FileUtils.getFileContents(Config.logFilePath); + + if (contents.length() > 50 * 1024) + contents = contents.substring(0, 25 * 1024) + + "\n.....\n" + + contents.substring(contents.length() - 25 * 1024); + + final Spannable contentsFormatted = formatAndroidLog(contents); + + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + + if (Config.viewLogInternally) { + UIAlertLog(activity, activity.getString(R.string.LimboLog), contentsFormatted); + } else { + try { + Intent intent = new Intent(Intent.ACTION_EDIT); + File file = new File(Config.logFilePath); + Uri uri = Uri.fromFile(file); + intent.setDataAndType(uri, "text/plain"); + activity.startActivity(intent); + } catch (Exception ex) { + ex.printStackTrace(); + // fallback + UIAlertLog(activity, activity.getString(R.string.LimboLog), contentsFormatted); + } + } + } + }); + } + + public static void setupLogFile(String filePath) { + Config.logFilePath = LimboApplication.getInstance().getCacheDir() + filePath; + } +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/ArchDefinitions.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/ArchDefinitions.java new file mode 100644 index 000000000..354726a50 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/ArchDefinitions.java @@ -0,0 +1,189 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.machine; + +import android.content.Context; + +import com.limbo.emu.lib.R; +import com.max2idea.android.limbo.install.Installer; +import com.max2idea.android.limbo.main.Config; +import com.max2idea.android.limbo.main.LimboApplication; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * A simple utility class to retrieved often long architecture attribute lists + */ +public class ArchDefinitions { + private static String TAG = "ArchDefinitions"; + + public static ArrayList getSoundcards(Context context) { + ArrayList commonSoundcards = new ArrayList<>(); + commonSoundcards.addAll(Arrays.asList(Installer.getAttrs(context, R.raw.common_soundcards))); + return commonSoundcards; + } + + public static ArrayList getNetworkDevices(Context context) { + ArrayList commonNetworkCards = new ArrayList<>(); + commonNetworkCards.addAll(Arrays.asList(Installer.getAttrs(context, R.raw.common_nic_cards))); + + ArrayList networkCards = new ArrayList<>(); + switch (LimboApplication.arch) { + case x86: + case x86_64: + networkCards.add("Default"); + networkCards.addAll(commonNetworkCards); + break; + case arm: + case arm64: + networkCards.add("Default"); + networkCards.addAll(commonNetworkCards); + networkCards.addAll(Arrays.asList(Installer.getAttrs(context, R.raw.arm_nic_cards))); + break; + case ppc: + case ppc64: + networkCards.add("Default"); + networkCards.addAll(commonNetworkCards); + break; + case sparc: + case sparc64: + networkCards.add("Default"); + networkCards.addAll(Arrays.asList(Installer.getAttrs(context, R.raw.sparc_nic_cards))); + break; + } + return networkCards; + } + + public static ArrayList getVGAValues(Context context) { + ArrayList vgaValues = new ArrayList<>(); + if (LimboApplication.arch == Config.Arch.x86 || LimboApplication.arch == Config.Arch.x86_64 + || LimboApplication.arch == Config.Arch.arm || LimboApplication.arch == Config.Arch.arm64 + || LimboApplication.arch == Config.Arch.ppc || LimboApplication.arch == Config.Arch.ppc64) { + vgaValues.add("std"); + } + + if (LimboApplication.arch == Config.Arch.x86 || LimboApplication.arch == Config.Arch.x86_64) { + vgaValues.add("cirrus"); + vgaValues.add("vmware"); + } + + //override vga for sun4m (32bit) machines to cg3 + if (LimboApplication.arch == Config.Arch.sparc || LimboApplication.arch == Config.Arch.sparc64) { + vgaValues.add("cg3"); + } + + if (LimboApplication.arch == Config.Arch.arm || LimboApplication.arch == Config.Arch.arm64) { + vgaValues.add("virtio-gpu-pci"); + } + + //XXX: some archs don't support vga on QEMU like SPARC64 + vgaValues.add("nographic"); + + //TODO: Add XEN??? + // "xenfb" + return vgaValues; + } + + public static ArrayList getKeyboardValues(Context context) { + ArrayList arrList = new ArrayList<>(); + arrList.add("en-us"); + return arrList; + } + + public static ArrayList getMouseValues(Context context) { + ArrayList arrList = new ArrayList<>(); + arrList.add("ps2"); + arrList.add("usb-mouse"); + arrList.add("usb-tablet" + " " + context.getString(R.string.fixesMouseParen)); + return arrList; + } + + public static ArrayList getUIValues() { + ArrayList arrList = new ArrayList<>(); + arrList.add("VNC"); + if (Config.enable_SDL) + arrList.add("SDL"); + return arrList; + } + + public static ArrayList getMachineValues(Context context) { + ArrayList machinesList = new ArrayList<>(); + machinesList.add("None"); + machinesList.add("New"); + return machinesList; + } + + public static ArrayList getCpuValues(Context context) { + ArrayList arrList = new ArrayList<>(); + switch (LimboApplication.arch) { + case x86: + case x86_64: + arrList.add("Default"); + arrList.addAll(Arrays.asList(Installer.getAttrs(context, R.raw.x86_cpu))); + break; + case arm: + case arm64: + arrList.add("Default"); + arrList.addAll(Arrays.asList(Installer.getAttrs(context, R.raw.arm_cpu))); + break; + case ppc: + case ppc64: + arrList.add("Default"); + arrList.addAll(Arrays.asList(Installer.getAttrs(context, R.raw.ppc_cpu))); + break; + case sparc: + case sparc64: + arrList.add("Default"); + arrList.addAll(Arrays.asList(Installer.getAttrs(context, R.raw.arm_cpu))); + break; + } + + if (LimboApplication.arch == Config.Arch.x86 || LimboApplication.arch == Config.Arch.x86_64 + || LimboApplication.arch == Config.Arch.arm || LimboApplication.arch == Config.Arch.arm64) + arrList.add("host"); + return arrList; + } + + public static ArrayList getMachineTypeValues(Context context) { + ArrayList arrList = new ArrayList<>(); + switch (LimboApplication.arch) { + case x86: + case x86_64: + arrList.add("Default"); + arrList.addAll(Arrays.asList(Installer.getAttrs(context, R.raw.x86_machine_types))); + break; + case arm: + case arm64: + arrList.addAll(Arrays.asList(Installer.getAttrs(context, R.raw.arm_machine_types))); + break; + case ppc: + case ppc64: + arrList.add("Default"); + arrList.addAll(Arrays.asList(Installer.getAttrs(context, R.raw.ppc_machine_types))); + break; + case sparc: + case sparc64: + arrList.add("Default"); + arrList.addAll(Arrays.asList(Installer.getAttrs(context, R.raw.sparc_machine_types))); + break; + } + return arrList; + } +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/BIOSImporter.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/BIOSImporter.java new file mode 100644 index 000000000..2063761c8 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/BIOSImporter.java @@ -0,0 +1,143 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.machine; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.util.Log; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.documentfile.provider.DocumentFile; + +import com.limbo.emu.lib.R; +import com.max2idea.android.limbo.files.FileUtils; +import com.max2idea.android.limbo.install.Installer; +import com.max2idea.android.limbo.machine.Machine.FileType; +import com.max2idea.android.limbo.main.Config; +import com.max2idea.android.limbo.main.LimboActivity; +import com.max2idea.android.limbo.main.LimboApplication; +import com.max2idea.android.limbo.main.LimboFileManager; +import com.max2idea.android.limbo.toast.ToastUtils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Hashtable; + +public class BIOSImporter { + private static final String TAG = "BIOSImporter"; + + public static void promptImportBIOSFile(final Activity activity) { + final AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(activity).create(); + alertDialog.setTitle(activity.getString(R.string.ImportBIOSFile)); + + LinearLayout mLayout = new LinearLayout(activity); + mLayout.setOrientation(LinearLayout.VERTICAL); + mLayout.setPadding(20, 20, 20, 20); + + TextView imageNameView = new TextView(activity); + imageNameView.setVisibility(View.VISIBLE); + imageNameView.setText(activity.getResources().getString(R.string.importBIOSInstructions)); + + LinearLayout.LayoutParams searchViewParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + mLayout.addView(imageNameView, searchViewParams); + alertDialog.setView(mLayout); + + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.Ok), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + promptForImportBIOSFile(activity); + } + }); + alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, activity.getString(R.string.Cancel), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + alertDialog.dismiss(); + } + }); + alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + alertDialog.dismiss(); + } + }); + alertDialog.show(); + } + + private static void promptForImportBIOSFile(final Activity activity) { + new Thread(new Runnable() { + @Override + public void run() { + LimboFileManager.browse(activity, FileType.IMPORT_BIOS_FILE, Config.OPEN_IMPORT_BIOS_FILE_REQUEST_CODE); + } + }).start(); + + } + + public static void importBIOSFile(Activity activity, String importFilePath) { + InputStream stream = null; + FileOutputStream targetStream = null; + try { + stream = FileUtils.getStreamFromFilePath(importFilePath); + File target = new File(LimboApplication.getBasefileDir(), FileUtils.getFilenameFromPath(importFilePath)); + targetStream = new FileOutputStream(target); + byte[] buffer = new byte[32768]; + int bytesRead = 0; + int totalBytes = 0; + while ((bytesRead = stream.read(buffer, 0, buffer.length)) > 0) { + targetStream.write(buffer, 0, bytesRead); + totalBytes += bytesRead; + if(totalBytes > 100 * 1024 * 1024){ + throw new Exception("File too large"); + } + } + targetStream.flush(); + } catch (Exception ex) { + ToastUtils.toastShort(activity, ex.getMessage()); + ex.printStackTrace(); + } finally { + if(stream!=null) { + try { + stream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if(targetStream!=null) { + try { + targetStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + } + +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/Dispatcher.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/Dispatcher.java new file mode 100644 index 000000000..146932e75 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/Dispatcher.java @@ -0,0 +1,444 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.machine; + +import com.max2idea.android.limbo.main.ViewListener; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Dispatcher is responsible for routing user actions and field changes to the backend machine + * controller preventing direct write access to the model. The Activities can be notified directly by + * the model and update their views directly. + */ +public class Dispatcher implements ViewListener { + private static final String TAG = "Dispatcher"; + private static Dispatcher sInstance; + private final ExecutorService dispatcher = Executors.newFixedThreadPool(1); + + public static synchronized Dispatcher getInstance() { + if (sInstance == null) { + sInstance = new Dispatcher(); + } + return sInstance; + } + + private void setDriveValue(MachineProperty diskFileType, String drivePath) { + switch (diskFileType) { + case HDA: + getMachine().setHdaImagePath(drivePath); + MachineFilePaths.insertRecentFilePath(Machine.FileType.HDA, drivePath); + break; + case HDB: + getMachine().setHdbImagePath(drivePath); + MachineFilePaths.insertRecentFilePath(Machine.FileType.HDB, drivePath); + break; + case HDC: + getMachine().setHdcImagePath(drivePath); + MachineFilePaths.insertRecentFilePath(Machine.FileType.HDC, drivePath); + break; + case HDD: + getMachine().setHddImagePath(drivePath); + MachineFilePaths.insertRecentFilePath(Machine.FileType.HDD, drivePath); + break; + case SHARED_FOLDER: + getMachine().setSharedFolderPath(drivePath); + MachineFilePaths.insertRecentFilePath(Machine.FileType.SHARED_DIR, drivePath); + break; + } + switch (diskFileType) { + case CDROM: + MachineController.getInstance().changeRemovableDevice(diskFileType, drivePath); + MachineFilePaths.insertRecentFilePath(Machine.FileType.CDROM, drivePath); + break; + case FDA: + MachineFilePaths.insertRecentFilePath(Machine.FileType.FDA, drivePath); + MachineController.getInstance().changeRemovableDevice(diskFileType, drivePath); + break; + case FDB: + MachineController.getInstance().changeRemovableDevice(diskFileType, drivePath); + MachineFilePaths.insertRecentFilePath(Machine.FileType.FDB, drivePath); + break; + case SD: + MachineController.getInstance().changeRemovableDevice(diskFileType, drivePath); + MachineFilePaths.insertRecentFilePath(Machine.FileType.SD, drivePath); + break; + } + + } + + private void changeMouse(String mouseCfg) { + String mouseDB = mouseCfg.split(" ")[0]; + getMachine().setMouse(mouseDB); + } + + private void setMachineEnableDevice(MachineProperty machineProperty, boolean isChecked) { + switch (machineProperty) { + case CDROM: + getMachine().setEnableCDROM(isChecked); + break; + case FDA: + getMachine().setEnableFDA(isChecked); + break; + case FDB: + getMachine().setEnableFDB(isChecked); + break; + case SD: + getMachine().setEnableSD(isChecked); + break; + } + + } + + + private void changeUi(String ui) { + if (ui.equals("VNC")) + getMachine().setEnableVNC(1); + else if (ui.equals("SDL")) + getMachine().setEnableVNC(0); + } + + private void setCpuNum(int cpuNum) { + getMachine().setCpuNum(cpuNum); + } + + public void onFieldChange(final MachineProperty property, final Object value) { + dispatcher.submit(new Runnable() { + @Override + public void run() { + requestFieldChange(property, value); + } + }); + } + + private void requestFieldChange(MachineProperty property, Object value) { + if (getMachine() == null) + return; + + switch (property) { + case DRIVE_ENABLED: + setDriveEnabled(value); + break; + case NON_REMOVABLE_DRIVE: + setDrive(value); + break; + case REMOVABLE_DRIVE: + setDrive(value); + break; + case MEDIA_INTERFACE: + setDriveMediaInterface(value); + case SOUNDCARD: + getMachine().setSoundCard(convertString(property, value)); + break; + case CPU: + getMachine().setCpu((String) value); + break; + case MEMORY: + getMachine().setMemory(convertInt(property, value)); + break; + case CPUNUM: + setCpuNum(convertInt(property, value)); + break; + case KERNEL: + getMachine().setKernel(convertString(property, value)); + break; + case INITRD: + getMachine().setInitRd(convertString(property, value)); + break; + case APPEND: + getMachine().setAppend(convertString(property, value)); + break; + case BOOT_CONFIG: + getMachine().setBootDevice(convertString(property, value)); + break; + case NETCONFIG: + getMachine().setNetwork(convertString(property, value)); + break; + case NICCONFIG: + getMachine().setNetworkCard(convertString(property, value)); + break; + case DISABLE_HPET: + getMachine().setDisableHPET(convertBoolean(property, value) ? 1 : 0); + break; + case DISABLE_TSC: + getMachine().setDisableTSC(convertBoolean(property, value) ? 1 : 0); + break; + case VGA: + getMachine().setVga(convertString(property, value)); + break; + case DISABLE_ACPI: + getMachine().setDisableACPI((convertBoolean(property, value) ? 1 : 0)); + break; + case DISABLE_FD_BOOT_CHK: + getMachine().setDisableFdBootChk((convertBoolean(property, value) ? 1 : 0)); + break; + case ENABLE_KVM: + getMachine().setEnableKVM((convertBoolean(property, value) ? 1 : 0)); + break; + case ENABLE_MTTCG: + getMachine().setEnableMTTCG((convertBoolean(property, value) ? 1 : 0)); + break; + case HOSTFWD: + getMachine().setHostFwd(convertString(property, value)); + break; + case MOUSE: + changeMouse(convertString(property, value)); + break; + case UI: + changeUi(convertString(property, value)); + break; + case KEYBOARD: + getMachine().setKeyboard(convertString(property, value)); + break; + case MACHINETYPE: + getMachine().setMachineType(convertString(property, value)); + break; + case PAUSED: + getMachine().setPaused(convertInt(property, value)); + break; + case EXTRA_PARAMS: + getMachine().setExtraParams(convertString(property,value)); + default: + throw new RuntimeException("Umapped UI field: " + property); + } + } + + private void setDriveMediaInterface(Object value) { + Object[] params = (Object[]) value; + MachineProperty driveName = (MachineProperty) params[0]; + String driveInterface = (String) params[1]; + switch(driveName) { + case HDA: + getMachine().setHdaInterface(driveInterface); + break; + case HDB: + getMachine().setHdbInterface(driveInterface); + break; + case HDC: + getMachine().setHdcInterface(driveInterface); + break; + case HDD: + getMachine().setHddInterface(driveInterface); + break; + case CDROM: + getMachine().setCdInterface(driveInterface); + break; + } + } + + private void setDriveEnabled(Object value) { + Object[] params = (Object[]) value; + MachineProperty machineDriveName = (MachineProperty) params[0]; + boolean checked = (boolean) params[1]; + setMachineEnableDevice(machineDriveName, checked); + if (checked) { + setDriveValue(machineDriveName, ""); + } else { + setDriveValue(machineDriveName, null); + } + } + + private void setDrive(Object value) { + Object[] params = (Object[]) value; + MachineProperty machineDriveName = (MachineProperty) params[0]; + String diskFileValue = (String) params[1]; + if (diskFileValue.equals("None") && isDriveEnabled(machineDriveName)) { + setDriveValue(machineDriveName, ""); + } else if (diskFileValue.equals("None") || !isDriveEnabled(machineDriveName)) { + setDriveValue(machineDriveName, null); + } else if (isDriveEnabled(machineDriveName)) { + setDriveValue(machineDriveName, diskFileValue); + } + } + + private boolean isDriveEnabled(MachineProperty property) { + switch (property) { + case CDROM: + return getMachine().isEnableCDROM(); + case FDA: + return getMachine().isEnableFDA(); + case FDB: + return getMachine().isEnableFDB(); + case SD: + return getMachine().isEnableSD(); + default: + return true; + } + } + + @Override + public void onAction(final MachineAction action, final Object value) { + dispatcher.submit(new Runnable() { + @Override + public void run() { + requestAction(action, value); + } + }); + + } + + private void requestAction(MachineAction action, Object value) { + switch (action) { + case DELETE_VM: + deleteVM((Machine) value); + break; + case CREATE_VM: + createVM(convertString(action, value)); + break; + case STOP_VM: + MachineController.getInstance().stopvm(); + break; + case START_VM: + MachineController.getInstance().startvm(); + break; + case PAUSE_VM: + MachineController.getInstance().pausevm(); + break; + case CONTINUE_VM: + MachineController.getInstance().continueVM(convertInt(action, value)); + break; + case RESET_VM: + MachineController.getInstance().restartvm(); + break; + case LOAD_VM: + MachineController.getInstance().setStoredMachine((String) value); + break; + case IMPORT_VMS: + MachineController.getInstance().importMachines(convertString(action, value)); + break; + case SET_SDL_REFRESH_RATE: + changeSDLRefreshRate(value); + break; + case SEND_MOUSE_EVENT: + sendMouseEvent(value); + break; + case INSERT_FAV: + addDriveToList(value); + break; + case UPDATE_NOTIFICATION: + MachineService.getService().updateServiceNotification( + MachineController.getInstance().getMachine().getName() + ": " + + (String) value); + case DISPLAY_CHANGED: + displayChanged(value); + break; + case ENABLE_AAUDIO: + MachineController.getInstance().enableAaudio(convertInt(action, value)); + break; + case FULLSCREEN: + MachineController.getInstance().setFullscreen(); + break; + case IGNORE_BREAKPOINT_INVALIDATION: + MachineController.getInstance().ignoreBreakpointInvalidation(convertBoolean(action, value)); + break; + } + } + + private void changeSDLRefreshRate(Object value) { + Object[] params = (Object[]) value; + int ms = (int) params[0]; + boolean idle = (boolean) params[1]; + MachineController.getInstance().setSdlRefreshRate(ms, idle); + } + + private void displayChanged(Object value) { + Object[] params = (Object[]) value; + MachineController.getInstance().updateDisplay((int) params[0], (int) params[1], (int) params[2]); + } + + private void addDriveToList(Object value) { + Object[] params = (Object[]) value; + Machine.FileType fileType = (Machine.FileType) params[0]; + String filePath = (String) params[1]; + MachineFilePaths.insertRecentFilePath(fileType, filePath); + } + + private boolean deleteVM(Machine machine) { + return MachineController.getInstance().deleteMachine(machine); + } + + private boolean createVM(String machineName) { + return MachineController.getInstance().createVM(machineName); + } + + + private void sendMouseEvent(Object value) { + Object[] params = (Object[]) value; + MachineController.getInstance().sendMouseEvent((int) params[0], (int) params[1], (int) params[2], (float) params[3], (float) params[4]); + } + + private Machine getMachine() { + return MachineController.getInstance().getMachine(); + } + + private String convertString(MachineProperty property, Object value) { + if (value instanceof String) { + return (String) value; + } else { + throw new RuntimeException("Unknown property value: " + value + " for: " + property); + } + } + + private String convertString(MachineAction action, Object value) { + if (value instanceof String) { + return (String) value; + } else if (value != null) { + throw new RuntimeException("Unknown action value: " + value + " for: " + action); + } + return null; + } + + private int convertInt(MachineProperty property, Object value) { + if (value instanceof String) { + return Integer.parseInt((String) value); + } else if (value instanceof Integer) { + return (int) value; + } else { + throw new RuntimeException("Unknown property value: " + value + " for: " + property); + } + } + + private int convertInt(MachineAction action, Object value) { + if (value instanceof String) { + return Integer.parseInt((String) value); + } else if (value instanceof Integer) { + return (int) value; + } else { + throw new RuntimeException("Unknown action value: " + value + " for: " + action); + } + } + + private boolean convertBoolean(MachineProperty property, Object value) { + if (value instanceof Boolean) { + return (boolean) value; + } else { + throw new RuntimeException("Unknown property value: " + value + " for: " + property); + } + } + + private boolean convertBoolean(MachineAction action, Object value) { + if (value instanceof Boolean) { + return (boolean) value; + } else { + throw new RuntimeException("Unknown action value: " + value + " for: " + action); + } + } + +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/FavOpenHelper.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/FavOpenHelper.java similarity index 58% rename from limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/FavOpenHelper.java rename to limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/FavOpenHelper.java index dc0c262bf..4ec58fa4a 100644 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/FavOpenHelper.java +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/FavOpenHelper.java @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -package com.max2idea.android.limbo.utils; +package com.max2idea.android.limbo.machine; import android.content.ContentValues; import android.content.Context; @@ -24,11 +24,11 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; + import java.util.ArrayList; /** - * - * @author Dev + * Storage Implementation for our recent file paths. */ public class FavOpenHelper extends SQLiteOpenHelper { @@ -41,39 +41,43 @@ public class FavOpenHelper extends SQLiteOpenHelper { private static final String TABLE_NAME_FAV_FILES = "favorites"; private static final String TABLE_NAME_FAV_FILES_CREATE = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME_FAV_FILES + " (" - + FAVSEQ + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + FAVFILETYPE + " TEXT, " - + FAVFILE + " TEXT);"; + + FAVSEQ + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + FAVFILETYPE + " TEXT, " + + FAVFILE + " TEXT);"; private static FavOpenHelper sInstance; - public SQLiteDatabase db; + private SQLiteDatabase db; - - public FavOpenHelper(Context context) { + private FavOpenHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); getDB(); } + static FavOpenHelper getInstance() { + return sInstance; + } + + public static synchronized void initialize(Context context) { + if (sInstance == null) { + sInstance = new FavOpenHelper(context.getApplicationContext()); + sInstance.setWriteAheadLoggingEnabled(true); + } + } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(TABLE_NAME_FAV_FILES_CREATE); } - private synchronized void getDB() { - - if (db == null) - db = this.getWritableDatabase(); - } - - - - public static synchronized FavOpenHelper getInstance(Context context) { + private synchronized void getDB() { + if (db == null) + db = getWritableDatabase(); + } - if (sInstance == null) { - sInstance = new FavOpenHelper(context.getApplicationContext()); - } - return sInstance; + @Override + public void close() { + if (db != null) + db.close(); } @Override @@ -84,17 +88,17 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } } - public int getFavSeq(String favPath, String favType) { + int getFavSeq(String favType, String favPath) { int ret = -1; - String SELECT_FAVS = "select " + this.FAVSEQ - + " from " + this.TABLE_NAME_FAV_FILES - + " Where " + this.FAVFILE + "= ? " - + " and " + this.FAVFILETYPE + "= ? " - +";"; + String SELECT_FAVS = "select " + FAVSEQ + + " from " + TABLE_NAME_FAV_FILES + + " Where " + FAVFILE + "= ? " + + " and " + FAVFILETYPE + "= ? " + + ";"; Cursor cur = db.rawQuery(SELECT_FAVS, new String[]{favPath, favType}); cur.moveToFirst(); - while (cur.isAfterLast() == false) { + while (!cur.isAfterLast()) { ret = cur.getInt(0); cur.moveToNext(); } @@ -102,62 +106,33 @@ public int getFavSeq(String favPath, String favType) { return ret; } - public int insertFav(String favpath, String favtype) { - int seqnum = -1; -// Log.v("DB", "insert fav: " + favpath); + boolean insertFav(String favtype, String favpath) { + long row = 0; ContentValues stateValues = new ContentValues(); stateValues.put(FAVFILE, favpath); - stateValues.put(this.FAVFILETYPE, favtype); + stateValues.put(FAVFILETYPE, favtype); try { - seqnum = (int) db.insertOrThrow(this.TABLE_NAME_FAV_FILES, null, stateValues); + row = db.insertOrThrow(TABLE_NAME_FAV_FILES, null, stateValues); } catch (Exception e) { - //catch code - Log.v(TAG, "Error while Insert Fav Path: " + e.getMessage()); + Log.w(TAG, "Error while Insert Fav Path: " + e.getMessage()); } - - return seqnum; + return row>0; } - public ArrayList getFav(String favType) { - String qry = "select " + this.FAVFILE + " " - + " from " + this.TABLE_NAME_FAV_FILES - + " where " + this.FAVFILETYPE + " = ? " + ArrayList getFav(String favType) { + String qry = "select " + FAVFILE + " " + + " from " + TABLE_NAME_FAV_FILES + + " where " + FAVFILETYPE + " = ? " + ";"; - ArrayList arrStr = new ArrayList(); + ArrayList arrStr = new ArrayList<>(); Cursor cur = db.rawQuery(qry, new String[]{favType}); cur.moveToFirst(); - while (cur.isAfterLast() == false) { + while (!cur.isAfterLast()) { arrStr.add(cur.getString(0)); cur.moveToNext(); } cur.close(); return arrStr; } - - public int deleteFav(String favPath) { - int rowsAffected = 0; - // Insert arrFiles in - try { - db.delete(this.TABLE_NAME_FAV_FILES, this.FAVFILE + "=?", new String[]{favPath}); - } catch (Exception e) { - //catch code - } - - - return rowsAffected; - } - - public int deleteFiles() { - int rowsAffected = 0; - // Insert arrFiles in - try { - db.delete(this.FAVFILETYPE, null, null); - } catch (Exception e) { - //catch code - } - - - return rowsAffected; - } } diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/IMachineDatabase.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/IMachineDatabase.java new file mode 100644 index 000000000..5e8646092 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/IMachineDatabase.java @@ -0,0 +1,32 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.machine; + +import java.util.ArrayList; + +/** A DAO interface for saving user defined machines. This could be anything but for Android we + * prefer an SQLite Helpers + */ +public interface IMachineDatabase { + Machine getMachine(String value); + void updateMachineFieldAsync(Machine machine, MachineProperty property, String value); + int insertMachine(Machine machine); + ArrayList getMachineNames(); + boolean deleteMachine(Machine machine); +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/Machine.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/Machine.java new file mode 100644 index 000000000..f753faf73 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/Machine.java @@ -0,0 +1,708 @@ +/* + Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.machine; + +import com.max2idea.android.limbo.main.Config; +import com.max2idea.android.limbo.main.LimboApplication; + +import java.util.Observable; + +/** + * Class holds the machine properties. This class can notify any Observer for changes. + */ +// TODO: This class is QEM-gnostic it should be abstracted +public class Machine extends Observable { + private static String TAG = "Machine"; + + private String name; + private String keyboard = Config.defaultKeyboardLayout; + private String mouse = "ps2"; + private int enableVNC; + private String arch; + private String machineType; + private String cpu = "Default"; + private int cpuNum = 1; + private int memory = 128; + private int enableMTTCG; + private int enableKVM; + private int disableACPI = 0; + private int disableHPET = 0; + private int disableFdBootChk = 0; + private int disableTSC = 1; //disabling TSC by default + // Storage + private String hdaImagePath; + private String hdbImagePath; + private String hdcImagePath; + private String hddImagePath; + // HDD interface + private String hdaInterface = "ide"; + private String hdbInterface = "ide"; + private String hdcInterface = "ide"; + private String hddInterface = "ide"; + + private String sharedFolderPath; + //Removable devices + private boolean enableCDROM; + private boolean enableFDA; + private boolean enableFDB; + private boolean enableSD; + private String cdImagePath; + private String fdaImagePath; + private String fdbImagePath; + private String sdImagePath; + private String cdInterface = "ide"; + // Default Settings + private String bootDevice = "Default"; + private String kernel; + private String initRd; + private String append; + // net + private String network = null; + private String networkCard = "ne2k_pci"; + private String guestFwd; + private String hostFwd; + //display + private String vga = "std"; + //sound + private String soundCard = null; + //extra qemu params + private String extraParams; + private int paused; + private int sharedFolderMode; + + public Machine(String name, boolean loadDefaults) { + this.name = name; + if (loadDefaults) + setDefaults(); + } + + public String getName() { + return name; + } + + void setName(String name) { + this.name = name; + } + + public int getEnableVNC() { + return enableVNC; + } + + void setEnableVNC(int enableVNC) { + if (this.enableVNC != enableVNC) { + this.enableVNC = enableVNC; + setChanged(); + notifyChanged(MachineProperty.UI, enableVNC); + } + } + + public String getArch() { + return arch; + } + + void setArch(String arch) { + if (this.arch == null || !this.arch.equals(arch)) { + this.arch = arch; + setChanged(); + notifyChanged(MachineProperty.ARCH, arch); + } + } + + public String getMachineType() { + return machineType; + } + + void setMachineType(String machineType) { + if (this.machineType == null || !this.machineType.equals(machineType)) { + this.machineType = machineType; + setChanged(); + notifyChanged(MachineProperty.MACHINETYPE, machineType); + } + } + + public String getCpu() { + return cpu; + } + + void setCpu(String cpu) { + if (!this.cpu.equals(cpu)) { + this.cpu = cpu; + setChanged(); + notifyChanged(MachineProperty.CPU, cpu); + } + } + + public int getCpuNum() { + return cpuNum; + } + + void setCpuNum(int cpuNum) { + if (this.cpuNum != cpuNum) { + this.cpuNum = cpuNum; + setChanged(); + notifyChanged(MachineProperty.CPUNUM, cpuNum); + } + } + + public int getMemory() { + return memory; + } + + void setMemory(int memory) { + if (this.memory != memory) { + this.memory = memory; + setChanged(); + notifyChanged(MachineProperty.MEMORY, memory); + } + } + + public int getEnableMTTCG() { + return enableMTTCG; + } + + void setEnableMTTCG(int enableMTTCG) { + if (this.enableMTTCG != enableMTTCG) { + this.enableMTTCG = enableMTTCG; + setChanged(); + notifyChanged(MachineProperty.ENABLE_MTTCG, enableMTTCG); + } + } + + public int getEnableKVM() { + return enableKVM; + } + + void setEnableKVM(int enableKVM) { + if (this.enableKVM != enableKVM) { + this.enableKVM = enableKVM; + setChanged(); + notifyChanged(MachineProperty.ENABLE_KVM, enableKVM); + } + } + + public int getDisableACPI() { + return disableACPI; + } + + void setDisableACPI(int disableACPI) { + if (this.disableACPI != disableACPI) { + this.disableACPI = disableACPI; + setChanged(); + notifyChanged(MachineProperty.DISABLE_ACPI, disableACPI); + } + + } + + public int getDisableFdBootChk() { + return disableFdBootChk; + } + + void setDisableFdBootChk(int disableFdBootChk) { + if (this.disableFdBootChk != disableFdBootChk) { + this.disableFdBootChk = disableFdBootChk; + setChanged(); + notifyChanged(MachineProperty.DISABLE_FD_BOOT_CHK, disableFdBootChk); + } + + } + + public int getDisableTSC() { + return disableTSC; + } + + void setDisableTSC(int disableTSC) { + if (this.disableTSC != disableTSC) { + this.disableTSC = disableTSC; + setChanged(); + notifyChanged(MachineProperty.DISABLE_TSC, disableTSC); + } + + } + + public String getHdaImagePath() { + return hdaImagePath; + } + + void setHdaImagePath(String hdaImagePath) { + if (this.hdaImagePath == null || !this.hdaImagePath.equals(hdaImagePath)) { + this.hdaImagePath = hdaImagePath; + setChanged(); + notifyChanged(MachineProperty.HDA, hdaImagePath); + } + } + + public String getHdbImagePath() { + return hdbImagePath; + } + + void setHdbImagePath(String hdbImagePath) { + if (this.hdbImagePath == null || !this.hdbImagePath.equals(hdbImagePath)) { + this.hdbImagePath = hdbImagePath; + setChanged(); + notifyChanged(MachineProperty.HDB, hdbImagePath); + } + } + + public String getHdcImagePath() { + return hdcImagePath; + } + + void setHdcImagePath(String hdcImagePath) { + if (this.hdcImagePath == null || !this.hdcImagePath.equals(hdcImagePath)) { + this.hdcImagePath = hdcImagePath; + setChanged(); + notifyChanged(MachineProperty.HDC, hdcImagePath); + } + + } + + public String getHddImagePath() { + return hddImagePath; + } + + void setHddImagePath(String hddImagePath) { + if (this.hddImagePath == null || !this.hddImagePath.equals(hddImagePath)) { + this.hddImagePath = hddImagePath; + setChanged(); + notifyChanged(MachineProperty.HDD, hddImagePath); + } + + } + + public String getHdaInterface() { + return hdaInterface; + } + + void setHdaInterface(String hdInterface) { + if (this.hdaInterface == null || !this.hdaInterface.equals(hdInterface)) { + this.hdaInterface = hdInterface; + setChanged(); + notifyChanged(MachineProperty.HDA_INTERFACE, hdInterface); + } + } + + public String getHdbInterface() { + return hdbInterface; + } + + void setHdbInterface(String hdInterface) { + if (this.hdbInterface == null || !this.hdbInterface.equals(hdInterface)) { + this.hdbInterface = hdInterface; + setChanged(); + notifyChanged(MachineProperty.HDB_INTERFACE, hdInterface); + } + } + + public String getHdcInterface() { + return hdcInterface; + } + + void setHdcInterface(String hdInterface) { + if (this.hdcInterface == null || !this.hdcInterface.equals(hdInterface)) { + this.hdcInterface = hdInterface; + setChanged(); + notifyChanged(MachineProperty.HDC_INTERFACE, hdInterface); + } + } + + public String getHddInterface() { + return hddInterface; + } + + void setHddInterface(String hdInterface) { + if (this.hddInterface == null || !this.hddInterface.equals(hdInterface)) { + this.hddInterface = hdInterface; + setChanged(); + notifyChanged(MachineProperty.HDD_INTERFACE, hdInterface); + } + } + + public String getSharedFolderPath() { + return sharedFolderPath; + } + + void setSharedFolderPath(String sharedFolderPath) { + if (this.sharedFolderPath == null || !this.sharedFolderPath.equals(sharedFolderPath)) { + this.sharedFolderPath = sharedFolderPath; + setChanged(); + notifyChanged(MachineProperty.SHARED_FOLDER, sharedFolderPath); + } + + } + + public boolean isEnableCDROM() { + return enableCDROM; + } + + void setEnableCDROM(boolean enableCDROM) { + if (this.enableCDROM != enableCDROM) { + this.enableCDROM = enableCDROM; + setChanged(); + notifyChanged(MachineProperty.OTHER, enableCDROM); + } + } + + public boolean isEnableFDA() { + return enableFDA; + } + + void setEnableFDA(boolean enableFDA) { + if (this.enableFDA != enableFDA) { + this.enableFDA = enableFDA; + setChanged(); + notifyChanged(MachineProperty.OTHER, enableFDA); + } + + } + + public boolean isEnableFDB() { + return enableFDB; + } + + void setEnableFDB(boolean enableFDB) { + if (this.enableFDB != enableFDB) { + this.enableFDB = enableFDB; + setChanged(); + notifyChanged(MachineProperty.OTHER, enableFDB); + } + + } + + public boolean isEnableSD() { + return enableSD; + } + + void setEnableSD(boolean enableSD) { + if (this.enableSD != enableSD) { + this.enableSD = enableSD; + setChanged(); + notifyChanged(MachineProperty.OTHER, enableSD); + } + + } + + public String getCdImagePath() { + return cdImagePath; + } + + void setCdImagePath(String cdImagePath) { + + if (this.cdImagePath == null || !this.cdImagePath.equals(cdImagePath)) { + this.cdImagePath = cdImagePath; + setChanged(); + notifyChanged(MachineProperty.CDROM, cdImagePath); + } + } + + public String getFdaImagePath() { + return fdaImagePath; + } + + void setFdaImagePath(String fdaImagePath) { + if (this.fdaImagePath == null || !this.fdaImagePath.equals(fdaImagePath)) { + this.fdaImagePath = fdaImagePath; + setChanged(); + notifyChanged(MachineProperty.FDA, fdaImagePath); + } + + } + + public String getFdbImagePath() { + return fdbImagePath; + } + + void setFdbImagePath(String fdbImagePath) { + if (this.fdbImagePath == null || !this.fdbImagePath.equals(fdbImagePath)) { + this.fdbImagePath = fdbImagePath; + setChanged(); + notifyChanged(MachineProperty.FDB, fdbImagePath); + } + } + + public String getSdImagePath() { + return sdImagePath; + } + + void setSdImagePath(String sdImagePath) { + if (this.sdImagePath == null || !this.sdImagePath.equals(sdImagePath)) { + this.sdImagePath = sdImagePath; + setChanged(); + notifyChanged(MachineProperty.SD, sdImagePath); + } + + } + + public String getCDInterface() { + return cdInterface; + } + + void setCdInterface(String mediaInterface) { + if (this.cdInterface == null || !this.cdInterface.equals(mediaInterface)) { + this.cdInterface = mediaInterface; + setChanged(); + notifyChanged(MachineProperty.CDROM_INTERFACE, mediaInterface); + } + } + + public String getBootDevice() { + return bootDevice; + } + + void setBootDevice(String bootDevice) { + if (this.bootDevice == null || !this.bootDevice.equals(bootDevice)) { + this.bootDevice = bootDevice; + setChanged(); + notifyChanged(MachineProperty.BOOT_CONFIG, bootDevice); + } + } + + public String getKernel() { + return kernel; + } + + void setKernel(String kernel) { + if (this.kernel == null || !this.kernel.equals(kernel)) { + this.kernel = kernel; + setChanged(); + notifyChanged(MachineProperty.KERNEL, kernel); + } + + } + + public String getInitRd() { + return initRd; + } + + void setInitRd(String initRd) { + if (this.initRd == null || !this.initRd.equals(initRd)) { + this.initRd = initRd; + setChanged(); + notifyChanged(MachineProperty.INITRD, initRd); + } + + } + + public String getAppend() { + return append; + } + + void setAppend(String append) { + if (this.append == null || !this.append.equals(append)) { + this.append = append; + setChanged(); + notifyChanged(MachineProperty.APPEND, append); + } + + } + + public String getNetwork() { + return network; + } + + void setNetwork(String network) { + if (this.network == null || !this.network.equals(network)) { + this.network = network; + setChanged(); + notifyChanged(MachineProperty.NETCONFIG, network); + } + + } + + public String getNetworkCard() { + return networkCard; + } + + void setNetworkCard(String networkCard) { + if (this.networkCard == null || !this.networkCard.equals(networkCard)) { + this.networkCard = networkCard; + setChanged(); + notifyChanged(MachineProperty.NICCONFIG, networkCard); + } + + } + + public String getGuestFwd() { + return guestFwd; + } + + void setGuestFwd(String guestFwd) { + if (this.guestFwd == null || !this.guestFwd.equals(guestFwd)) { + this.guestFwd = guestFwd; + setChanged(); + notifyChanged(MachineProperty.GUESTFWD, guestFwd); + } + + } + + public String getHostFwd() { + return hostFwd; + } + + void setHostFwd(String hostFwd) { + if (this.hostFwd == null || !this.hostFwd.equals(hostFwd)) { + this.hostFwd = hostFwd; + setChanged(); + notifyChanged(MachineProperty.HOSTFWD, hostFwd); + } + + } + + public String getVga() { + return vga; + } + + void setVga(String vga) { + if (this.vga == null || !this.vga.equals(vga)) { + this.vga = vga; + setChanged(); + notifyChanged(MachineProperty.VGA, vga); + } + + } + + public String getExtraParams() { + return extraParams; + } + + void setExtraParams(String extraParams) { + if (this.extraParams == null || !this.extraParams.equals(extraParams)) { + this.extraParams = extraParams; + setChanged(); + notifyChanged(MachineProperty.EXTRA_PARAMS, extraParams); + } + + } + + public int getPaused() { + return paused; + } + + void setPaused(int value) { + if (this.paused != value) { + this.paused = value; + setChanged(); + notifyChanged(MachineProperty.PAUSED, paused); + } + } + + public int getShared_folder_mode() { + return sharedFolderMode; + } + + void setShared_folder_mode(int shared_folder_mode) { + if (this.sharedFolderMode != shared_folder_mode) { + this.sharedFolderMode = shared_folder_mode; + setChanged(); + notifyChanged(MachineProperty.SHARED_FOLDER_MODE, shared_folder_mode); + } + } + + public boolean hasRemovableDevices() { + return enableCDROM || enableFDA || enableFDB || enableSD; + } + + void setDefaults() { + if (LimboApplication.arch == Config.Arch.x86 || LimboApplication.arch == Config.Arch.x86_64) { + arch = "x86"; + cpu = "n270"; + machineType = "pc"; + networkCard = "Default"; + disableTSC = 1; + } else if (LimboApplication.arch == Config.Arch.arm || LimboApplication.arch == Config.Arch.arm64) { + arch = "ARM"; + machineType = "versatilepb"; + cpu = "Default"; + networkCard = "Default"; + } else if (LimboApplication.arch == Config.Arch.ppc || LimboApplication.arch == Config.Arch.ppc64) { + arch = "PPC"; + machineType = "g3beige"; + networkCard = "Default"; + } else if (LimboApplication.arch == Config.Arch.sparc || LimboApplication.arch == Config.Arch.sparc64) { + arch = "SPARC"; + vga = "cg3"; + machineType = "Default"; + networkCard = "Default"; + } + } + + public String getSoundCard() { + return soundCard; + } + + void setSoundCard(String soundCard) { + if (this.soundCard == null || !this.soundCard.equals(soundCard)) { + this.soundCard = soundCard; + setChanged(); + notifyChanged(MachineProperty.SOUNDCARD, soundCard); + } + } + + public String getKeyboard() { + return keyboard; + } + + void setKeyboard(String keyboard) { + if (this.keyboard == null || !this.keyboard.equals(keyboard)) { + this.keyboard = keyboard; + setChanged(); + notifyChanged(MachineProperty.MOUSE, mouse); + } + } + + public String getMouse() { + return mouse; + } + + void setMouse(String mouse) { + if (this.keyboard == null || this.mouse != mouse) { + this.mouse = mouse; + setChanged(); + notifyChanged(MachineProperty.MOUSE, mouse); + } + } + + public int getDisableAcpi() { + return disableACPI; + } + + public int getDisableHPET() { + return disableHPET; + } + + void setDisableHPET(int disableHPET) { + if (this.disableHPET != disableHPET) { + this.disableHPET = disableHPET; + setChanged(); + notifyChanged(MachineProperty.DISABLE_HPET, disableHPET); + } + + } + + private void notifyChanged(MachineProperty property, Object value) { + notifyObservers(new Object[]{property, value}); + } + + public enum FileType { + CDROM, FDA, FDB, SD, + HDA, HDB, HDC, HDD, SHARED_DIR, + KERNEL, INITRD, + EXPORT_DIR, IMAGE_DIR, LOG_DIR, IMPORT_FILE, IMPORT_BIOS_FILE + } + +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineAction.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineAction.java new file mode 100644 index 000000000..d262ed860 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineAction.java @@ -0,0 +1,25 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.machine; + +public enum MachineAction { + STOP_VM, IMPORT_VMS, LOAD_VM, CONTINUE_VM, RESET_VM, START_VM, SET_SDL_REFRESH_RATE, + SEND_MOUSE_EVENT, PAUSE_VM, DELETE_VM, INSERT_FAV, UPDATE_NOTIFICATION, DISPLAY_CHANGED, + FULLSCREEN, ENABLE_AAUDIO, IGNORE_BREAKPOINT_INVALIDATION, CREATE_VM +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineController.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineController.java new file mode 100644 index 000000000..4c4370aca --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineController.java @@ -0,0 +1,429 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.machine; + +import android.os.Handler; +import android.os.Looper; +import android.util.Log; + +import com.limbo.emu.lib.R; +import com.max2idea.android.limbo.files.FileUtils; +import com.max2idea.android.limbo.jni.MachineExecutorFactory; +import com.max2idea.android.limbo.main.LimboApplication; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Observer; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Class communicates to the qemu process via the jni bridge MachineExecutor. It is responsible for + * starting, stopping, and retrieving he status of the virtual machine. + */ +public class MachineController { + private static final String TAG = "MachineController"; + private static MachineController mSingleton; + private final ExecutorService saveVmStatusExecutor = Executors.newFixedThreadPool(1); + private final MachineExecutor machineExecutor; + private final HashSet onMachineStatusChangeListeners = new HashSet<>(); + private final HashSet onEventListeners = new HashSet<>(); + private final Class serviceClass; + private final IMachineDatabase machineDatabase; + private boolean saveVmStatusTimerQuit; + private boolean promptedPausedVM; + private Machine machine; + + private MachineController() { + machineExecutor = MachineExecutorFactory.createMachineExecutor(this, MachineExecutorFactory.MachineExecutorType.QEMU); + machineDatabase = MachineOpenHelper.getInstance(); + serviceClass = MachineService.class; + } + + public static MachineController getInstance() { + if (mSingleton == null) { + mSingleton = new MachineController(); + } + return mSingleton; + } + + + public MachineStatus getCurrStatus() { + if(getMachine() ==null) + return MachineStatus.Stopped; + else if (getMachine().getPaused() == 1) + return MachineStatus.Paused; + else if (MachineService.getService() == null) + return MachineController.MachineStatus.Ready; + else if (MachineService.getService().limboThread != null) + return MachineController.MachineStatus.Running; + else + return MachineController.MachineStatus.Stopped; + } + + void continueVM(final int delay) { + new Thread(new Runnable() { + @Override + public void run() { + if(delay > 0) { + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + machineExecutor.continueVM(); + notifyEventListeners(Event.MachineContinued, null); + } + }).start(); + } + + public void addOnStatusChangeListener(OnMachineStatusChangeListener listener) { + onMachineStatusChangeListeners.add(listener); + } + + public void removeOnStatusChangeListener(OnMachineStatusChangeListener listener) { + onMachineStatusChangeListeners.remove(listener); + } + + void removeOnStatusChangeListeners() { + onMachineStatusChangeListeners.clear(); + } + + public void addOnEventListener(OnEventListener listener) { + onEventListeners.add(listener); + } + + public void removeOnEventListener(OnEventListener listener) { + onEventListeners.remove(listener); + } + + void removeOnEventListeners() { + onEventListeners.clear(); + } + + void stopvm() { + machineExecutor.stopvm(0); + } + + private void notifyMachineStatusChangeListeners(Machine machine, MachineStatus status, Object o) { + for (OnMachineStatusChangeListener listener : onMachineStatusChangeListeners) + listener.onMachineStatusChanged(machine, status, o); + } + + private void notifyEventListeners(Event status, Object o) { + for (OnEventListener listener : onEventListeners) + listener.onEvent(machine, status, o); + } + + void startvm() { + machineExecutor.startService(); + } + + void restartvm() { + new Thread(new Runnable() { + public void run() { + if (machineExecutor != null) { + Log.d(TAG, "Restarting the VM..."); + machineExecutor.stopvm(1); + } + } + }).start(); + } + + private MachineStatus checkSaveVMStatus() { + final MachineStatus saveVmStatus = machineExecutor.getSaveVMStatus(); + if (saveVmStatus == MachineStatus.SaveCompleted) { + saveStateVMDB(); + if (promptedPausedVM) + stopSaveVmStatusTimer(); + getMachineExecutor().getMachine().setPaused(1); + } + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + if (saveVmStatus == MachineStatus.SaveCompleted) { + MachineController.getInstance().promptedPausedVM = true; + notifyMachineStatusChangeListeners(machine, saveVmStatus, null); + } else if (saveVmStatus == MachineStatus.SaveFailed) { + notifyMachineStatusChangeListeners(machine, saveVmStatus, null); + } + } + }, 1000); + return saveVmStatus; + } + + private void checkSaveStatus() { + while (!saveVmStatusTimerQuit) { + MachineStatus status = checkSaveVMStatus(); + Log.d(TAG, "State Status: " + status); + if (status == MachineStatus.Unknown + || status == MachineStatus.SaveCompleted + || status == MachineStatus.SaveFailed + ) { + Log.d(TAG, "Saving state is done: " + status); + stopSaveVmStatusTimer(); + return; + } + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } + Log.d(TAG, "Save state complete"); + } + + private void stopSaveVmStatusTimer() { + saveVmStatusTimerQuit = true; + } + + private void saveVmStatusTimerLoop() { + while (!saveVmStatusTimerQuit) { + checkSaveStatus(); + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } + } + + private void execSaveVmStatusTimer() { + saveVmStatusExecutor.submit(new Runnable() { + public void run() { + startSaveVmStatusTimer(); + } + }); + } + + private void startSaveVmStatusTimer() { + stopSaveVmStatusTimer(); + saveVmStatusTimerQuit = false; + try { + saveVmStatusTimerLoop(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + void pausevm() { + new Thread(new Runnable() { + public void run() { + String error = machineExecutor.saveVM(); + if (error != null) + MachineController.getInstance().notifyMachineStatusChangeListeners(machine, MachineStatus.SaveFailed, error); + MachineController.getInstance().execSaveVmStatusTimer(); + } + }).start(); + } + + private MachineExecutor getMachineExecutor() { + return machineExecutor; + } + + public String getMachineSaveDir() { + return LimboApplication.getMachineDir() + machineExecutor.getMachine().getName(); + } + + void changeRemovableDevice(MachineProperty property, String value) { + if (isRunning()) { + boolean res = getMachineExecutor().changeRemovableDevice(property, value); + if(!res) + value = null; + } + switch (property) { + case CDROM: + getMachine().setCdImagePath(value); + break; + case FDA: + getMachine().setFdaImagePath(value); + break; + case FDB: + getMachine().setFdbImagePath(value); + break; + case SHARED_FOLDER: + getMachine().setSharedFolderPath(value); + break; + } + } + + void sendMouseEvent(int button, int action, int relative, float x, float y) { + machineExecutor.sendMouseEvent(button, action, relative, x, y); + } + + public boolean isRunning() { + return getCurrStatus() == MachineStatus.Running; + } + + public boolean isPaused() { + return machineExecutor.getMachine().getPaused() == 1; + } + + public int getSdlRefreshRate(boolean idle) { + return machineExecutor.getSdlRefreshRate(idle); + } + + void setSdlRefreshRate(int refreshMs, boolean idle) { + machineExecutor.setSdlRefreshRate(refreshMs, idle); + } + + public String getMachineName() { + return machineExecutor.getMachine().getName(); + } + + public boolean isVNCEnabled() { + return getMachineExecutor().getMachine().getEnableVNC() == 1; + } + + String start() { + //TODO: figure out when the vm starts successfully and notify + // to unset the paused flag. For now we just wait a reasonable amount + // of time + setPaused(0, 5000); + return machineExecutor.start(); + } + + private void setPaused(final int value, final int delay) { + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + e.printStackTrace(); + } + machine.setPaused(value); + } + }).start(); + } + + public Machine getMachine() { + return machine; + } + + void setMachine(Machine machine) { + if (this.machine != machine) { + if (this.machine != null) { + this.machine.deleteObservers(); + } + this.machine = machine; + if (this.machine != null) + this.machine.addObserver((Observer) machineDatabase); + notifyEventListeners(Event.MachineLoaded, machine); + } + } + + void setStoredMachine(String value) { + setMachine(machineDatabase.getMachine(value)); + } + + //TODO: this should be accessible via a notifier and not directly + void saveStateVMDB() { + machineDatabase.updateMachineFieldAsync( + MachineController.getInstance().getMachine(), MachineProperty.PAUSED, 1 + ""); + } + + boolean createVM(String machineName) { + if (machineDatabase.getMachine(machineName) != null) { + notifyEventListeners(Event.MachineCreateFailed, (Integer) R.string.VMNameExistsChooseAnother); + return false; + } + Machine machine = new Machine(machineName, true); + notifyEventListeners(Event.MachineCreated, machineName); + MachineController.getInstance().setMachine(machine); + machineDatabase.insertMachine(getMachine()); + notifyMachineStatusChangeListeners(machine, MachineStatus.Ready, null); + return true; + } + + protected void importMachines(String importFilePath) { + setMachine(null); + ArrayList machines = MachineImporter.importMachines(importFilePath); + for (Machine machine : machines) { + MachineFilePaths.insertRecentFilePath(Machine.FileType.CDROM, machine.getCdImagePath()); + MachineFilePaths.insertRecentFilePath(Machine.FileType.HDA, machine.getHdaImagePath()); + MachineFilePaths.insertRecentFilePath(Machine.FileType.HDB, machine.getHdbImagePath()); + MachineFilePaths.insertRecentFilePath(Machine.FileType.HDC, machine.getHdcImagePath()); + MachineFilePaths.insertRecentFilePath(Machine.FileType.HDD, machine.getHddImagePath()); + MachineFilePaths.insertRecentFilePath(Machine.FileType.SHARED_DIR, machine.getSharedFolderPath()); + MachineFilePaths.insertRecentFilePath(Machine.FileType.FDA, machine.getFdaImagePath()); + MachineFilePaths.insertRecentFilePath(Machine.FileType.FDB, machine.getFdbImagePath()); + MachineFilePaths.insertRecentFilePath(Machine.FileType.SD, machine.getSdImagePath()); + MachineFilePaths.insertRecentFilePath(Machine.FileType.KERNEL, machine.getKernel()); + MachineFilePaths.insertRecentFilePath(Machine.FileType.INITRD, machine.getInitRd()); + } + notifyEventListeners(Event.MachinesImported, machines); + } + + public ArrayList getStoredMachines() { + return machineDatabase.getMachineNames(); + } + + protected boolean deleteMachine(Machine machine) { + return machineDatabase.deleteMachine(machine); + } + + public Class getServiceClass() { + return serviceClass; + } + + protected void onServiceStarted() { + notifyMachineStatusChangeListeners(machine, getCurrStatus(), null); + } + + protected void updateDisplay(int width, int height, int orientation) { + machineExecutor.updateDisplay(width, height, orientation); + } + + protected void onVMResolutionChanged(MachineExecutor machineExecutor, int vm_width, int vm_height) { + if(machineExecutor == this.machineExecutor) + notifyEventListeners(Event.MachineResolutionChanged, new Object[]{vm_width, vm_height}); + } + + public void setFullscreen() { + machineExecutor.setFullscreen(); + notifyEventListeners(Event.MachineFullscreen, null); + } + + public void enableAaudio(int value) { + machineExecutor.enableAaudio(value); + } + + public void ignoreBreakpointInvalidation(boolean value) { + machineExecutor.ignoreBreakpointInvalidation(value?1:0); + } + + public enum MachineStatus { + Ready, Stopped, Saving, Paused, SaveCompleted, SaveFailed, Unknown, Running + } + + public enum Event { + MachineCreated, MachineCreateFailed, MachineLoaded, MachineResolutionChanged, MachineContinued, MachineFullscreen, MachinesImported + } + + public interface OnMachineStatusChangeListener { + void onMachineStatusChanged(Machine machine, MachineStatus status, Object o); + } + + public interface OnEventListener { + void onEvent(Machine machine, Event event, Object o); + } + +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineExecutor.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineExecutor.java new file mode 100644 index 000000000..fb34e1b1c --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineExecutor.java @@ -0,0 +1,71 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.machine; + +/** Our emulation abstract bridge. It can be extended to implement bridges with other native + * emulators that support SDL. + */ +public abstract class MachineExecutor { + private static String TAG = "MachineExecutor"; + + private final MachineController machineController; + + public MachineExecutor(MachineController machineController) { + this.machineController = machineController; + } + + protected Machine getMachine() { + return machineController.getMachine(); + } + + protected void onResolutionChanged(int vm_width, int vm_height) { + machineController.onVMResolutionChanged(this, vm_width, vm_height); + } + + abstract public void startService(); + + // TODO: create int success code instead of string + abstract public String start(); + + abstract protected void stopvm(final int restart); + + public abstract int getSdlRefreshRate(boolean idle); + + public abstract void setSdlRefreshRate(int refreshMs, boolean idle); + + public abstract void sendMouseEvent(int button, int action, int relative, float x, float y); + + public abstract String saveVM(); + + public abstract void continueVM(); + + public abstract MachineController.MachineStatus getSaveVMStatus(); + + public abstract void enableAaudio(int value); + + public abstract boolean changeRemovableDevice(MachineProperty drive, String diskValue); + + public abstract String getDeviceName(MachineProperty driveProperty); + + public abstract void updateDisplay(int width, int height, int orientation); + + public abstract void setFullscreen(); + + public abstract void ignoreBreakpointInvalidation(int value); +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineExporter.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineExporter.java new file mode 100644 index 000000000..83b09fb62 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineExporter.java @@ -0,0 +1,167 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.machine; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.net.Uri; +import android.os.AsyncTask; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; + +import com.limbo.emu.lib.R; +import com.max2idea.android.limbo.files.FileUtils; +import com.max2idea.android.limbo.machine.Machine.FileType; +import com.max2idea.android.limbo.main.Config; +import com.max2idea.android.limbo.main.LimboFileManager; +import com.max2idea.android.limbo.main.LimboSettingsManager; +import com.max2idea.android.limbo.toast.ToastUtils; + +import java.io.File; + +public class MachineExporter extends AsyncTask { + private static final String TAG = "MachineExporter"; + public String exportFilePath; + private Activity activity; + private String displayName; + + public MachineExporter(Activity activity, String filePath) { + this.activity = activity; + this.exportFilePath = filePath; + } + + public static void promptExport(final Activity activity) { + + final AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(activity).create(); + alertDialog.setTitle(activity.getString(R.string.ExportFilename)); + + LinearLayout mLayout = new LinearLayout(activity); + mLayout.setPadding(20, 20, 20, 20); + mLayout.setOrientation(LinearLayout.VERTICAL); + + final EditText exportNameView = new EditText(activity); + exportNameView.setEnabled(true); + exportNameView.setVisibility(View.VISIBLE); + exportNameView.setSingleLine(); + LinearLayout.LayoutParams imageNameViewParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + mLayout.addView(exportNameView, imageNameViewParams); + + alertDialog.setView(mLayout); + + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.Export), + (DialogInterface.OnClickListener) null); + alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, activity.getString(R.string.ChangeDirectory), + (DialogInterface.OnClickListener) null); + + alertDialog.show(); + + Button positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE); + positiveButton.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View view) { + if (LimboSettingsManager.getExportDir(activity) == null) { + changeExportDir(activity); + return; + } + + String exportFilename = exportNameView.getText().toString(); + if (exportFilename.trim().equals("")) + ToastUtils.toastShort(activity, activity.getString(R.string.ExportFilenameCannotBeEmpty)); + else { + + if (!exportFilename.endsWith(".csv")) + exportFilename += ".csv"; + + exportMachines(activity, exportFilename); + alertDialog.dismiss(); + } + } + }); + + Button negativeButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE); + negativeButton.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View view) { + changeExportDir(activity); + } + }); + + } + + public static void exportMachines(Activity activity, String filePath) { + MachineExporter exporter = new MachineExporter(activity, filePath); + exporter.execute(); + } + + public static void changeExportDir(Activity activity) { + ToastUtils.toastLong(activity, activity.getString(R.string.ChooseDirForVMExport)); + LimboFileManager.browse(activity, FileType.EXPORT_DIR, Config.OPEN_EXPORT_DIR_REQUEST_CODE); + } + + public static String exportMachinesFile(Activity activity, String destDir, String destFile) { + String filePath = null; + File destFileF = new File(destDir, destFile); + + try { + String machinesToExport = MachineOpenHelper.getInstance().exportMachines(); + FileUtils.saveFileContents(destFileF.getAbsolutePath(), machinesToExport); + filePath = destFileF.getAbsolutePath(); + } catch (Exception ex) { + ToastUtils.toastShort(activity, activity.getString(R.string.FailedToExportFile) + + ": " + destFileF.getAbsolutePath() + ", " + activity.getString(R.string.Error) + ":" + ex.getMessage()); + } + return filePath; + } + + @Override + protected Void doInBackground(Void... arg0) { + displayName = exportMachinesToFile(exportFilePath); + return null; + } + + @Override + protected void onPostExecute(Void test) { + if (displayName != null) + ToastUtils.toastLong(activity, activity.getString(R.string.MachinesExported) + ": " + displayName); + } + + protected String exportMachinesToFile(String exportFileName) { + + String exportDir = LimboSettingsManager.getExportDir(activity); + String displayName = null; + if (exportDir.startsWith("content://")) { + Uri exportDirUri = Uri.parse(exportDir); + String machinesToExport = MachineOpenHelper.getInstance().exportMachines(); + byte [] contents = machinesToExport.getBytes(); + Uri fileCreatedUri = FileUtils.exportFileContents(activity, exportDirUri, exportFileName, contents); + displayName = FileUtils.getFullPathFromDocumentFilePath(fileCreatedUri.toString()); + } else { + String filePath = exportMachinesFile(activity, exportDir, exportFileName); + displayName = filePath; + } + return displayName; + } +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineFilePaths.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineFilePaths.java new file mode 100644 index 000000000..992321a1c --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineFilePaths.java @@ -0,0 +1,41 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.machine; + +import java.util.ArrayList; + +public class MachineFilePaths { + + public static void insertRecentFilePath(Machine.FileType fileType, String filePath) { + if (fileType == null || filePath == null || filePath.equals("")) + return; + if (!isRecentFilePathStored(fileType, filePath)) { + FavOpenHelper.getInstance().insertFav(fileType.name().toLowerCase(), filePath); + } + } + + public static boolean isRecentFilePathStored(Machine.FileType type, String filePath) { + return FavOpenHelper.getInstance().getFavSeq(type.toString().toLowerCase(), filePath) >= 0; + } + synchronized + public static ArrayList getRecentFilePaths(Machine.FileType fileType) { + return FavOpenHelper.getInstance().getFav(fileType.toString().toLowerCase()); + } + +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineImporter.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineImporter.java new file mode 100644 index 000000000..11e76b360 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineImporter.java @@ -0,0 +1,314 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.machine; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.util.Log; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.limbo.emu.lib.R; +import com.max2idea.android.limbo.files.FileUtils; +import com.max2idea.android.limbo.machine.Machine.FileType; +import com.max2idea.android.limbo.main.Config; +import com.max2idea.android.limbo.main.LimboFileManager; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Hashtable; + +public class MachineImporter { + private static final String TAG = "MachineImporter"; + + private static ArrayList getVMsFromFile(String importFilePath) { + ArrayList machines = new ArrayList<>(); + BufferedReader buffreader = null; + InputStream instream = null; + try { + Log.d(TAG, "Import file: " + importFilePath); + instream = FileUtils.getStreamFromFilePath(importFilePath); + if (instream != null) { + InputStreamReader inputreader = new InputStreamReader(instream); + buffreader = new BufferedReader(inputreader); + Hashtable attrs = new Hashtable<>(); + + String line = buffreader.readLine(); + String [] headers = line.split(","); + for (int i = 0; i < headers.length; i++) { + attrs.put(i, headers[i].replace("\"", "")); + } + + while (line != null) { + line = buffreader.readLine(); + if (line == null) + break; + // Parse + String [] machineAttr = line.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1); + Machine mach = new Machine(machineAttr[0], false); + for (int i = 0; i < machineAttr.length; i++) { + if (machineAttr[i].equals("\"null\"")) { + continue; + } + switch (attrs.get(i)) { + case "MACHINE_NAME": + mach.setName(machineAttr[i].replace("\"", "")); + break; + case "UI": + String ui = machineAttr[i].replace("\"", ""); + if (ui.equals("VNC")) + mach.setEnableVNC(1); + break; + case "PAUSED": + mach.setPaused(Integer.parseInt(machineAttr[i].replace("\"", ""))); + break; + // Arch + case "ARCH": + mach.setArch(machineAttr[i].replace("\"", "")); + break; + case "MACHINETYPE": + mach.setMachineType(machineAttr[i].replace("\"", "")); + break; + case "CPU": + mach.setCpu(machineAttr[i].replace("\"", "")); + break; + case "CPUNUM": + mach.setCpuNum(Integer.parseInt(machineAttr[i].replace("\"", ""))); + break; + case "MEMORY": + mach.setMemory(Integer.parseInt(machineAttr[i].replace("\"", ""))); + break; + + // Storage + case "HDA": + mach.setHdaImagePath(machineAttr[i].replace("\"", "")); + break; + case "HDB": + mach.setHdbImagePath(machineAttr[i].replace("\"", "")); + break; + case "HDC": + mach.setHdcImagePath(machineAttr[i].replace("\"", "")); + break; + case "HDD": + mach.setHddImagePath(machineAttr[i].replace("\"", "")); + break; + case "SHARED_FOLDER": + mach.setSharedFolderPath(machineAttr[i].replace("\"", "")); + break; + case "SHARED_FOLDER_MODE": + mach.setShared_folder_mode(Integer.parseInt(machineAttr[i].replace("\"", ""))); + break; + + // Removable Media + case "CDROM": + mach.setCdImagePath(machineAttr[i].replace("\"", "")); + break; + case "FDA": + mach.setFdaImagePath(machineAttr[i].replace("\"", "")); + break; + case "FDB": + mach.setFdbImagePath(machineAttr[i].replace("\"", "")); + break; + case "SD": + mach.setSdImagePath(machineAttr[i].replace("\"", "")); + break; + + case "HDA_INTERFACE": + mach.setHdaInterface(machineAttr[i].replace("\"", "")); + break; + case "HDB_INTERFACE": + mach.setHdbInterface(machineAttr[i].replace("\"", "")); + break; + case "HDC_INTERFACE": + mach.setHdcInterface(machineAttr[i].replace("\"", "")); + break; + case "HDD_INTERFACE": + mach.setHddInterface(machineAttr[i].replace("\"", "")); + break; + case "CDROM_INTERFACE": + mach.setCdInterface(machineAttr[i].replace("\"", "")); + break; + + // Misc + case "VGA": + mach.setVga(machineAttr[i].replace("\"", "")); + break; + case "SOUNDCARD": + mach.setSoundCard(machineAttr[i].replace("\"", "")); + break; + case "NETCONFIG": + mach.setNetwork(machineAttr[i].replace("\"", "")); + break; + case "NICCONFIG": + mach.setNetworkCard(machineAttr[i].replace("\"", "")); + break; + case "HOSTFWD": + mach.setHostFwd(machineAttr[i].replace("\"", "")); + break; + case "GUESTFWD": + mach.setGuestFwd(machineAttr[i].replace("\"", "")); + break; + + // Other + case "DISABLE_ACPI": + mach.setDisableACPI(Integer.parseInt(machineAttr[i].replace("\"", ""))); + break; + case "DISABLE_HPET": + mach.setDisableHPET(Integer.parseInt(machineAttr[i].replace("\"", ""))); + break; + case "DISABLE_TSC": + mach.setDisableTSC(Integer.parseInt(machineAttr[i].replace("\"", ""))); + break; + case "DISABLE_FD_BOOT_CHK": + mach.setDisableFdBootChk(Integer.parseInt(machineAttr[i].replace("\"", ""))); + break; + + // Boot Settings + case "BOOT_CONFIG": + mach.setBootDevice(machineAttr[i].replace("\"", "")); + break; + case "KERNEL": + mach.setKernel(machineAttr[i].replace("\"", "")); + break; + case "INITRD": + mach.setInitRd(machineAttr[i].replace("\"", "")); + break; + case "APPEND": + mach.setAppend(machineAttr[i].replace("\"", "")); + break; + + // Extra Params + case "EXTRA_PARAMS": + mach.setExtraParams(machineAttr[i].replace("\"", "")); + break; + + // Peripherals + case "MOUSE": + mach.setMouse(machineAttr[i].replace("\"", "")); + break; + case "KEYBOARD": + mach.setKeyboard(machineAttr[i].replace("\"", "")); + break; + case "ENABLE_MTTCG": + mach.setEnableMTTCG(Integer.parseInt(machineAttr[i].replace("\"", ""))); + break; + case "ENABLE_KVM": + mach.setEnableKVM(Integer.parseInt(machineAttr[i].replace("\"", ""))); + break; + } + + } + Log.d(TAG, "Adding Machine: " + mach.getName()); + machines.add(mach); + } + + } + } catch (Exception ex) { + ex.printStackTrace(); + } finally { + try { + if (buffreader != null) + buffreader.close(); + + } catch (IOException e) { + + e.printStackTrace(); + } + try { + if (instream != null) + instream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + try { + FileUtils.closeFileDescriptor(importFilePath); + } catch (IOException e) { + e.printStackTrace(); + } + } + + return machines; + } + + public static void promptImportMachines(final Activity activity) { + final AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(activity).create(); + alertDialog.setTitle(activity.getString(R.string.ImportMachines)); + + LinearLayout mLayout = new LinearLayout(activity); + mLayout.setOrientation(LinearLayout.VERTICAL); + mLayout.setPadding(20, 20, 20, 20); + + TextView imageNameView = new TextView(activity); + imageNameView.setVisibility(View.VISIBLE); + imageNameView.setText(activity.getResources().getString(R.string.importInstructions)); + + LinearLayout.LayoutParams searchViewParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + mLayout.addView(imageNameView, searchViewParams); + alertDialog.setView(mLayout); + + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.Ok), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + promptForImportDir(activity); + } + }); + alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, activity.getString(R.string.Cancel), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + alertDialog.dismiss(); + } + }); + alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + alertDialog.dismiss(); + } + }); + alertDialog.show(); + } + + private static void promptForImportDir(final Activity activity) { + new Thread(new Runnable() { + @Override + public void run() { + LimboFileManager.browse(activity, FileType.IMPORT_FILE, Config.OPEN_IMPORT_FILE_REQUEST_CODE); + } + }).start(); + + } + + public static ArrayList importMachines(String importFilePath) { + ArrayList machines = MachineImporter.getVMsFromFile(importFilePath); + for (Machine machine : machines) { + if (MachineOpenHelper.getInstance().getMachine(machine.getName()) != null) { + MachineOpenHelper.getInstance().deleteMachine(machine); + } + MachineOpenHelper.getInstance().insertMachine(machine); + } + return machines; + } + +} diff --git a/limbo-android-lib/src/main/jni/limbo/limbo.h b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineMediaInterface.java similarity index 84% rename from limbo-android-lib/src/main/jni/limbo/limbo.h rename to limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineMediaInterface.java index b0b7dada4..8740156c6 100644 --- a/limbo-android-lib/src/main/jni/limbo/limbo.h +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineMediaInterface.java @@ -1,5 +1,5 @@ /* - Copyright (C) Max Kastanas 2012 +Copyright (C) Max Kastanas 2012 * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,10 +16,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ +package com.max2idea.android.limbo.machine; -#ifndef LIMBO_H -#define LIMBO_H - -#include - -#endif +public enum MachineMediaInterface { + IDE, SCSI, VIRTIO +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineOpenHelper.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineOpenHelper.java new file mode 100644 index 000000000..c697ef618 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineOpenHelper.java @@ -0,0 +1,437 @@ +/* + Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.machine; + +import android.annotation.SuppressLint; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.util.Log; + +import com.max2idea.android.limbo.main.Config; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.Observable; +import java.util.Observer; + +/** + * DAO implementation for storing our machines into an SQLite database + */ +public class MachineOpenHelper extends SQLiteOpenHelper implements IMachineDatabase, Observer { + private static final String TAG = "MachineOpenHelper"; + + private static final int DATABASE_VERSION = 16; + private static final String DATABASE_NAME = "LIMBO"; + private static final String MACHINE_TABLE_NAME = "machines"; + + private static final String MACHINE_TABLE_CREATE = "CREATE TABLE IF NOT EXISTS " + MACHINE_TABLE_NAME + " (" + + MachineProperty.MACHINE_NAME.name() + " TEXT , " + MachineProperty.SNAPSHOT_NAME.name() + " TEXT , " + MachineProperty.CPU.name() + " TEXT, " + MachineProperty.ARCH.name() + " TEXT, " + MachineProperty.MEMORY.name() + + " TEXT, " + MachineProperty.FDA.name() + " TEXT, " + MachineProperty.FDB.name() + " TEXT, " + MachineProperty.CDROM.name() + " TEXT, " + MachineProperty.HDA.name() + " TEXT, " + MachineProperty.HDB.name() + " TEXT, " + + MachineProperty.HDC.name() + " TEXT, " + MachineProperty.HDD.name() + " TEXT, " + MachineProperty.BOOT_CONFIG.name() + " TEXT, " + MachineProperty.NETCONFIG.name() + " TEXT, " + MachineProperty.NICCONFIG.name() + + " TEXT, " + MachineProperty.VGA.name() + " TEXT, " + MachineProperty.SOUNDCARD.name() + " TEXT, " + MachineProperty.HDCONFIG.name() + " TEXT, " + MachineProperty.DISABLE_ACPI.name() + + " INTEGER, " + MachineProperty.DISABLE_HPET.name() + " INTEGER, " + MachineProperty.ENABLE_USBMOUSE.name() + " INTEGER, " + MachineProperty.STATUS.name() + " TEXT, " + + MachineProperty.LAST_UPDATED.name() + " DATE, " + MachineProperty.KERNEL.name() + " INTEGER, " + MachineProperty.INITRD.name() + " TEXT, " + MachineProperty.APPEND.name() + " TEXT, " + MachineProperty.CPUNUM.name() + + " INTEGER, " + MachineProperty.MACHINETYPE.name() + " TEXT, " + MachineProperty.DISABLE_FD_BOOT_CHK.name() + " INTEGER, " + MachineProperty.SD.name() + " TEXT, " + MachineProperty.PAUSED.name() + + " INTEGER, " + MachineProperty.SHARED_FOLDER.name() + " TEXT, " + MachineProperty.SHARED_FOLDER_MODE.name() + " INTEGER, " + MachineProperty.EXTRA_PARAMS.name() + " TEXT, " + + MachineProperty.HOSTFWD.name() + " TEXT, " + MachineProperty.GUESTFWD.name() + " TEXT, " + MachineProperty.UI.name() + " TEXT, " + MachineProperty.DISABLE_TSC.name() + " INTEGER, " + + MachineProperty.MOUSE.name() + " TEXT, " + MachineProperty.KEYBOARD.name() + " TEXT, " + MachineProperty.ENABLE_MTTCG.name() + " INTEGER, " + MachineProperty.ENABLE_KVM.name() + " INTEGER , " + + MachineProperty.HDA_INTERFACE.name() + " TEXT, " + MachineProperty.HDB_INTERFACE.name() + " TEXT, " + MachineProperty.HDC_INTERFACE.name() + " TEXT, " + MachineProperty.HDD_INTERFACE.name() + " TEXT , " + + MachineProperty.CDROM_INTERFACE.name() + " TEXT " + + ");"; + + private static MachineOpenHelper sInstance; + private SQLiteDatabase db; + + private MachineOpenHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + getDB(); + } + + static MachineOpenHelper getInstance() { + return sInstance; + } + + public static synchronized void initialize(Context context) { + if (sInstance == null) { + sInstance = new MachineOpenHelper(context.getApplicationContext()); + sInstance.setWriteAheadLoggingEnabled(true); + } + } + + private synchronized void getDB() { + if (db == null) + db = getWritableDatabase(); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL(MACHINE_TABLE_CREATE); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + Log.w("machineOpenHelper", "Upgrading database from version " + oldVersion + " to " + newVersion); + if (newVersion >= 3 && oldVersion <= 2) { + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.KERNEL + " TEXT;"); + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.INITRD + " TEXT;"); + } + + if (newVersion >= 4 && oldVersion <= 3) { + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.CPUNUM + " TEXT;"); + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.MACHINETYPE + " TEXT;"); + } + + if (newVersion >= 5 && oldVersion <= 4) { + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.HDC + " TEXT;"); + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.HDD + " TEXT;"); + } + + if (newVersion >= 6 && oldVersion <= 5) { + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.APPEND + " TEXT;"); + } + + if (newVersion >= 7 && oldVersion <= 6) { + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.DISABLE_FD_BOOT_CHK + " INTEGER;"); + } + + if (newVersion >= 8 && oldVersion <= 7) { + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.ARCH + " TEXT;"); + } + + if (newVersion >= 9 && oldVersion <= 8) { + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.SD + " TEXT;"); + } + + if (newVersion >= 10 && oldVersion <= 9) { + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.PAUSED + " INTEGER;"); + } + + if (newVersion >= 11 && oldVersion <= 10) { + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.SHARED_FOLDER + " TEXT;"); + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.SHARED_FOLDER_MODE + " INTEGER;"); + } + + if (newVersion >= 12 && oldVersion <= 11) { + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.EXTRA_PARAMS + " TEXT;"); + } + + if (newVersion >= 13 && oldVersion <= 12) { + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.HOSTFWD + " TEXT;"); + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.GUESTFWD + " TEXT;"); + } + + if (newVersion >= 14 && oldVersion <= 13) { + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.UI + " TEXT;"); + } + + if (newVersion >= 15 && oldVersion <= 14) { + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.DISABLE_TSC + " INTEGER;"); + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.MOUSE + " TEXT;"); + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.KEYBOARD + " TEXT;"); + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.ENABLE_MTTCG + " INTEGER;"); + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.ENABLE_KVM + " INTEGER;"); + } + + if (newVersion >= 16 && oldVersion <= 15) { + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.HDA_INTERFACE + " TEXT;"); + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.HDB_INTERFACE + " TEXT;"); + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.HDC_INTERFACE + " TEXT;"); + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.HDD_INTERFACE + " TEXT;"); + db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MachineProperty.CDROM_INTERFACE + " TEXT;"); + } + } + + public synchronized int insertMachine(Machine machine) { + int seqnum = -1; + SQLiteDatabase db = getWritableDatabase(); + + Log.d(TAG, "inserting machine: " + machine.getName()); + ContentValues stateValues = new ContentValues(); + stateValues.put(MachineProperty.MACHINE_NAME.name(), machine.getName()); //Legacy + stateValues.put(MachineProperty.CPU.name(), machine.getCpu()); + stateValues.put(MachineProperty.CPUNUM.name(), machine.getCpuNum()); + stateValues.put(MachineProperty.MEMORY.name(), machine.getMemory()); + stateValues.put(MachineProperty.HDA.name(), machine.getHdaImagePath()); + stateValues.put(MachineProperty.HDA_INTERFACE.name(), machine.getHdaInterface()); + stateValues.put(MachineProperty.HDB.name(), machine.getHdbImagePath()); + stateValues.put(MachineProperty.HDB_INTERFACE.name(), machine.getHdbInterface()); + stateValues.put(MachineProperty.HDC.name(), machine.getHdcImagePath()); + stateValues.put(MachineProperty.HDC_INTERFACE.name(), machine.getHdcInterface()); + stateValues.put(MachineProperty.HDD.name(), machine.getHddImagePath()); + stateValues.put(MachineProperty.HDD_INTERFACE.name(), machine.getHddInterface()); + stateValues.put(MachineProperty.CDROM.name(), machine.getCdImagePath()); + stateValues.put(MachineProperty.CDROM_INTERFACE.name(), machine.getCDInterface()); + stateValues.put(MachineProperty.FDA.name(), machine.getFdaImagePath()); + stateValues.put(MachineProperty.FDB.name(), machine.getFdbImagePath()); + stateValues.put(MachineProperty.SHARED_FOLDER.name(), machine.getSharedFolderPath()); + stateValues.put(MachineProperty.SHARED_FOLDER_MODE.name(), machine.getShared_folder_mode()); + stateValues.put(MachineProperty.BOOT_CONFIG.name(), machine.getBootDevice()); + stateValues.put(MachineProperty.NETCONFIG.name(), machine.getNetwork()); + stateValues.put(MachineProperty.NICCONFIG.name(), machine.getNetworkCard()); + stateValues.put(MachineProperty.VGA.name(), machine.getVga()); + stateValues.put(MachineProperty.DISABLE_ACPI.name(), machine.getDisableAcpi()); + stateValues.put(MachineProperty.DISABLE_HPET.name(), machine.getDisableHPET()); + stateValues.put(MachineProperty.DISABLE_TSC.name(), machine.getDisableTSC()); + stateValues.put(MachineProperty.DISABLE_FD_BOOT_CHK.name(), machine.getDisableFdBootChk()); + stateValues.put(MachineProperty.SOUNDCARD.name(), machine.getSoundCard()); + stateValues.put(MachineProperty.KERNEL.name(), machine.getKernel()); + stateValues.put(MachineProperty.INITRD.name(), machine.getInitRd()); + stateValues.put(MachineProperty.APPEND.name(), machine.getAppend()); + stateValues.put(MachineProperty.MACHINETYPE.name(), machine.getMachineType()); + stateValues.put(MachineProperty.ARCH.name(), machine.getArch()); + stateValues.put(MachineProperty.EXTRA_PARAMS.name(), machine.getExtraParams()); + stateValues.put(MachineProperty.HOSTFWD.name(), machine.getHostFwd()); + stateValues.put(MachineProperty.GUESTFWD.name(), machine.getGuestFwd()); + stateValues.put(MachineProperty.UI.name(), machine.getEnableVNC() == 1 ? "VNC" : "SDL"); + stateValues.put(MachineProperty.MOUSE.name(), machine.getMouse()); + stateValues.put(MachineProperty.KEYBOARD.name(), machine.getKeyboard()); + stateValues.put(MachineProperty.ENABLE_MTTCG.name(), machine.getEnableMTTCG()); + stateValues.put(MachineProperty.ENABLE_KVM.name(), machine.getEnableKVM()); + + @SuppressLint("SimpleDateFormat") + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + Date date = new Date(); + stateValues.put(MachineProperty.LAST_UPDATED.name(), dateFormat.format(date)); + stateValues.put(MachineProperty.STATUS.name(), Config.STATUS_CREATED); + + try { + seqnum = (int) db.insertOrThrow(MACHINE_TABLE_NAME, null, stateValues); + } catch (Exception e) { + Log.w(TAG, "Error while Insert machine: " + e.getMessage()); + e.printStackTrace(); + } + return seqnum; + } + + public void updateMachineFieldAsync(final Machine machine, final MachineProperty property, + final String value) { + Thread t = new Thread(new Runnable() { + public void run() { + updateMachineField(machine, property, value); + } + }); + t.start(); + } + + public void updateMachineField(Machine machine, MachineProperty property, String value) { + if (machine == null) + return; + ContentValues stateValues = new ContentValues(); + stateValues.put(property.name(), value); + try { + db.beginTransaction(); + db.update(MACHINE_TABLE_NAME, stateValues, + MachineProperty.MACHINE_NAME.name() + "=\"" + machine.getName() + "\" ", + null); + db.setTransactionSuccessful(); + } catch (Exception e) { + Log.w(TAG, "Error while Updating value: " + e.getMessage()); + if (Config.debug) + e.printStackTrace(); + } finally { + db.endTransaction(); + } + } + + public Machine getMachine(String machine) { + String qry = "select " + + MachineProperty.MACHINE_NAME + " , " + MachineProperty.CPU + " , " + MachineProperty.MEMORY + " , " + MachineProperty.CDROM + " , " + MachineProperty.FDA + + " , " + MachineProperty.FDB + " , " + MachineProperty.HDA + " , " + MachineProperty.HDB + " , " + MachineProperty.HDC + " , " + MachineProperty.HDD + " , " + + MachineProperty.NETCONFIG + " , " + MachineProperty.NICCONFIG + " , " + MachineProperty.VGA + " , " + MachineProperty.SOUNDCARD + " , " + + MachineProperty.HDCONFIG + " , " + MachineProperty.DISABLE_ACPI + " , " + MachineProperty.DISABLE_HPET + " , " + + MachineProperty.ENABLE_USBMOUSE + " , " + MachineProperty.SNAPSHOT_NAME + " , " + MachineProperty.BOOT_CONFIG + " , " + MachineProperty.KERNEL + + " , " + MachineProperty.INITRD + " , " + MachineProperty.APPEND + " , " + MachineProperty.CPUNUM + " , " + MachineProperty.MACHINETYPE + " , " + + MachineProperty.DISABLE_FD_BOOT_CHK + " , " + MachineProperty.ARCH + " , " + MachineProperty.PAUSED + " , " + MachineProperty.SD + " , " + + MachineProperty.SHARED_FOLDER + " , " + MachineProperty.SHARED_FOLDER_MODE + " , " + MachineProperty.EXTRA_PARAMS + " , " + + MachineProperty.HOSTFWD + " , " + MachineProperty.GUESTFWD + " , " + MachineProperty.UI + ", " + MachineProperty.DISABLE_TSC + ", " + + MachineProperty.MOUSE + ", " + MachineProperty.KEYBOARD + ", " + MachineProperty.ENABLE_MTTCG + ", " + MachineProperty.ENABLE_KVM + ", " + + MachineProperty.HDA_INTERFACE + ", " + MachineProperty.HDB_INTERFACE + ", " + MachineProperty.HDC_INTERFACE + ", " + MachineProperty.HDD_INTERFACE + ", " + + MachineProperty.CDROM_INTERFACE + " " + + " from " + MACHINE_TABLE_NAME + + " where " + MachineProperty.STATUS + " in ( " + Config.STATUS_CREATED + " , " + Config.STATUS_PAUSED + " " + + " ) " + " and " + MachineProperty.MACHINE_NAME + "=\"" + machine + "\"" + ";"; + + Machine myMachine = null; + + Cursor cur = db.rawQuery(qry, null); + + cur.moveToFirst(); + if (!cur.isAfterLast()) { + String machinename = cur.getString(0); + myMachine = new Machine(machinename, false); + + myMachine.setCpu(cur.getString(1)); + myMachine.setMemory(cur.getInt(2)); + myMachine.setCdImagePath(cur.getString(3)); + if (myMachine.getCdImagePath() != null) + myMachine.setEnableCDROM(true); + + myMachine.setFdaImagePath(cur.getString(4)); + if (myMachine.getFdaImagePath() != null) + myMachine.setEnableFDA(true); + myMachine.setFdbImagePath(cur.getString(5)); + if (myMachine.getFdbImagePath() != null) + myMachine.setEnableFDB(true); + + myMachine.setHdaImagePath(cur.getString(6)); + myMachine.setHdbImagePath(cur.getString(7)); + myMachine.setHdcImagePath(cur.getString(8)); + myMachine.setHddImagePath(cur.getString(9)); + + myMachine.setNetwork(cur.getString(10)); + myMachine.setNetworkCard(cur.getString(11)); + myMachine.setVga(cur.getString(12)); + myMachine.setSoundCard(cur.getString(13)); + myMachine.setDisableACPI(cur.getInt(15)); + myMachine.setDisableHPET(cur.getInt(16)); + myMachine.setBootDevice(cur.getString(19)); + myMachine.setKernel(cur.getString(20)); + myMachine.setInitRd(cur.getString(21)); + myMachine.setAppend(cur.getString(22)); + myMachine.setCpuNum(cur.getInt(23)); + myMachine.setMachineType(cur.getString(24)); + myMachine.setDisableFdBootChk(cur.getInt(25)); + myMachine.setArch(cur.getString(26)); + myMachine.setPaused(cur.getInt(27)); + + myMachine.setSdImagePath(cur.getString(28)); + if (myMachine.getSdImagePath() != null) + myMachine.setEnableSD(true); + + myMachine.setSharedFolderPath(cur.getString(29)); + myMachine.setShared_folder_mode(1); //hard drives are always Read/Write + myMachine.setExtraParams(cur.getString(31)); + myMachine.setHostFwd(cur.getString(32)); + myMachine.setGuestFwd(cur.getString(33)); + myMachine.setEnableVNC(cur.getString(34).equals("VNC") ? 1 : 0); + myMachine.setDisableTSC(cur.getInt(35)); + myMachine.setMouse(cur.getString(36)); + myMachine.setKeyboard(cur.getString(37)); + myMachine.setEnableMTTCG(cur.getInt(38)); + myMachine.setEnableKVM(cur.getInt(39)); + myMachine.setHdaInterface(cur.getString(40)); + myMachine.setHdbInterface(cur.getString(41)); + myMachine.setHdcInterface(cur.getString(42)); + myMachine.setHddInterface(cur.getString(43)); + myMachine.setCdInterface(cur.getString(44)); + } + cur.close(); + + return myMachine; + } + + public ArrayList getMachineNames() { + String qry = "select " + MachineProperty.MACHINE_NAME + " " + " from " + MACHINE_TABLE_NAME + + " where " + MachineProperty.STATUS + " in ( " + Config.STATUS_CREATED + " , " + + Config.STATUS_PAUSED + " " + " ) order by 1; "; + + ArrayList arrStr = new ArrayList<>(); + Cursor cur = db.rawQuery(qry, null); + cur.moveToFirst(); + while (!cur.isAfterLast()) { + String machinename = cur.getString(0); + cur.moveToNext(); + arrStr.add(machinename); + } + cur.close(); + + return arrStr; + } + + public boolean deleteMachine(Machine machine) { + int rowsAffected = 0; + try { + rowsAffected = db.delete(MACHINE_TABLE_NAME, MachineProperty.MACHINE_NAME + "=\"" + machine.getName() + "\"", null); + } catch (Exception e) { + Log.w(TAG, "Error while deleting VM: " + e.getMessage()); + if (Config.debug) + e.printStackTrace(); + } + return rowsAffected>0; + } + + String exportMachines() { + String qry = "select " + + MachineProperty.MACHINE_NAME + " , " + MachineProperty.CPU + " , " + MachineProperty.MEMORY + " , " + MachineProperty.CDROM + " , " + MachineProperty.FDA + + " , " + MachineProperty.FDB + " , " + MachineProperty.HDA + " , " + MachineProperty.HDB + " , " + MachineProperty.HDC + " , " + MachineProperty.HDD + " , " + + MachineProperty.NETCONFIG + " , " + MachineProperty.NICCONFIG + " , " + MachineProperty.VGA + " , " + MachineProperty.SOUNDCARD + " , " + + MachineProperty.HDCONFIG + " , " + MachineProperty.DISABLE_ACPI + " , " + MachineProperty.DISABLE_HPET + " , " + + MachineProperty.ENABLE_USBMOUSE + " , " + MachineProperty.SNAPSHOT_NAME + " , " + MachineProperty.BOOT_CONFIG + " , " + MachineProperty.KERNEL + + " , " + MachineProperty.INITRD + " , " + MachineProperty.APPEND + " , " + MachineProperty.CPUNUM + " , " + MachineProperty.MACHINETYPE + " , " + + MachineProperty.DISABLE_FD_BOOT_CHK + " , " + MachineProperty.ARCH + " , " + MachineProperty.PAUSED + " , " + MachineProperty.SD + " , " + + MachineProperty.SHARED_FOLDER + " , " + MachineProperty.SHARED_FOLDER_MODE + " , " + MachineProperty.EXTRA_PARAMS + " , " + + MachineProperty.HOSTFWD + " , " + MachineProperty.GUESTFWD + " , " + MachineProperty.UI + ", " + MachineProperty.DISABLE_TSC + ", " + + MachineProperty.MOUSE + ", " + MachineProperty.KEYBOARD + ", " + MachineProperty.ENABLE_MTTCG + ", " + MachineProperty.ENABLE_KVM +", " + + MachineProperty.HDA_INTERFACE + ", " + MachineProperty.HDB_INTERFACE + ", " + MachineProperty.HDC_INTERFACE + ", " + MachineProperty.HDD_INTERFACE + " " + + MachineProperty.CDROM_INTERFACE +" " + // Table + + " from " + MACHINE_TABLE_NAME + " order by 1; "; + + StringBuilder arrStr = new StringBuilder(); + Cursor cur = db.rawQuery(qry, null); + + cur.moveToFirst(); + StringBuilder headerline = new StringBuilder(); + for (int i = 0; i < cur.getColumnCount(); i++) { + headerline.append("\"").append(cur.getColumnName(i)).append("\""); + if (i < cur.getColumnCount() - 1) { + headerline.append(","); + } + } + arrStr.append(headerline).append("\n"); + while (!cur.isAfterLast()) { + StringBuilder line = new StringBuilder(); + for (int i = 0; i < cur.getColumnCount(); i++) { + line.append("\"").append(cur.getString(i)).append("\""); + if (i < cur.getColumnCount() - 1) { + line.append(","); + } + } + arrStr.append(line).append("\n"); + cur.moveToNext(); + } + cur.close(); + + return arrStr.toString(); + } + + @Override + public void update(Observable observable, Object o) { + Object[] params = (Object[]) o; + MachineProperty property = (MachineProperty) params[0]; + Object value = params[1]; + switch(property) { + case UI: + updateMachineField((Machine) observable, property, ((int) params[1]) == 1?"VNC":"SDL"); + return; + case OTHER: + return; + } + if(value instanceof Integer) + updateMachineField((Machine) observable, property, ((int) params[1])+""); + else + updateMachineField((Machine) observable, property, (String) params[1]); + + } +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineProperty.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineProperty.java new file mode 100644 index 000000000..f9dcfb99e --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineProperty.java @@ -0,0 +1,35 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.machine; + +public enum MachineProperty { + MACHINE_NAME, SNAPSHOT_NAME, + STATUS, PAUSED, LAST_UPDATED, + UI, MOUSE, ENABLE_USBMOUSE, KEYBOARD, + ARCH, MACHINETYPE, CPU, CPUNUM, MEMORY, ENABLE_MTTCG, ENABLE_KVM, + DISABLE_ACPI, DISABLE_HPET, DISABLE_TSC, DISABLE_FD_BOOT_CHK, + HDA, HDB, HDC, HDD, SHARED_FOLDER, SHARED_FOLDER_MODE, HDCONFIG, + CDROM, FDA, FDB, SD, + MEDIA_INTERFACE, HDA_INTERFACE, HDB_INTERFACE, HDC_INTERFACE, HDD_INTERFACE, CDROM_INTERFACE, + BOOT_CONFIG, KERNEL, INITRD, APPEND, + VGA, SOUNDCARD, NETCONFIG, HOSTFWD, GUESTFWD, NICCONFIG, + NON_REMOVABLE_DRIVE, REMOVABLE_DRIVE, DRIVE_ENABLED, + EXTRA_PARAMS, OTHER +} + diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineService.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineService.java new file mode 100644 index 000000000..24b9b6f42 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/machine/MachineService.java @@ -0,0 +1,245 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.machine; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.graphics.BitmapFactory; +import android.net.wifi.WifiManager; +import android.net.wifi.WifiManager.WifiLock; +import android.os.Build; +import android.os.Bundle; +import android.os.IBinder; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.util.Log; + +import androidx.core.app.NotificationCompat; + +import com.limbo.emu.lib.R; +import com.max2idea.android.limbo.files.FileUtils; +import com.max2idea.android.limbo.main.Config; +import com.max2idea.android.limbo.main.LimboActivity; +import com.max2idea.android.limbo.main.LimboSettingsManager; +import com.max2idea.android.limbo.network.NetworkUtils; +import com.max2idea.android.limbo.toast.ToastUtils; + +/** MachineService is responsible only for owning and starting the thread that executes the jni part + * This implementation is needed for Android to make sure that the process is started from a + * foreground service so it will remain alive but with its thread running in the background. + * All other communications with the native process will happen via MachineController -> MachineExecutor. + */ +public class MachineService extends Service { + public static final int notifID = 1000; + private static final String TAG = "MachineService"; + private static MachineService service; + public Thread limboThread; + private NotificationCompat.Builder builder; + private Notification mNotification; + private WifiLock mWifiLock; + private WakeLock mWakeLock; + + public static MachineService getService() { + return service; + } + @Override + public void onCreate() { + Log.d(TAG, "Creating Service"); + service = this; + } + + @Override + public IBinder onBind(Intent arg0) { + return null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + final String action = intent.getAction(); + final Bundle b = intent.getExtras(); + + if (limboThread != null) + return START_NOT_STICKY; + + if (action.equals(Config.ACTION_START)) { + if (MachineController.getInstance().getMachine() == null) + return START_NOT_STICKY; + + String text = MachineController.getInstance().getMachine().getName() + ": VM Running"; + if (MachineController.getInstance().isVNCEnabled()) { + text += " - " + service.getString(R.string.vncServer); + text += ": " + NetworkUtils.getVNCAddress(service) + ":" + Config.defaultVNCPort; + } + // we need to start the service in the foreground + setUpAsForeground(text); + + // start logging + FileUtils.startLogging(); + + // start the thread in the background + ThreadGroup group = new ThreadGroup("threadGroup"); + //FIXME: For now we set an abnormally very high stack size to overcome an issue with the + // SDL audio failing with StackOverflowError when resuming the vm. + limboThread = new Thread(group, new Runnable() { + public void run() { + startVM(); + } + }, "LimboThread", Config.stackSize); + if (LimboSettingsManager.getPrio(this)) + limboThread.setPriority(Thread.MAX_PRIORITY); + limboThread.start(); + } + + // Don't restart if killed + return START_NOT_STICKY; + } + + private void startVM() { + //XXX: wait till logging starts capturing + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + Log.d(TAG, "Starting VM: " + MachineController.getInstance().getMachine().getName()); + setupLocks(); + + // notify we started + MachineController.getInstance().onServiceStarted(); + + //set the exit code before we start + LimboSettingsManager.setExitCode(service, Config.EXIT_UNKNOWN); + + String res = MachineController.getInstance().start(); + + // If the vm exits with a use requested shutdown + // TODO: create int return codes instead of strings + if (res != null) { + if (!res.equals("VM shutdown")) { + ToastUtils.toastLong(service, res); + Log.e(TAG, res); + } else { + Log.d(TAG, res); + //set the exit code + LimboSettingsManager.setExitCode(service, Config.EXIT_SUCCESS); + } + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + ToastUtils.toastLong(service, res); + } + + cleanUp(); + stopService(); + + Log.d(TAG, "Exiting Limbo"); + //XXX: We exit here to force unload the native libs + System.exit(0); + } + + + public void cleanUp() { + //XXX flush and close all file descriptors if we haven't already + FileUtils.close_fds(); + } + + private void setUpAsForeground(String text) { + if (MachineController.getInstance().getMachine() == null) { + Log.w(TAG, "No Machine selected"); + return; + } + Intent intent = new Intent(service.getApplicationContext(), Config.clientClass); + PendingIntent pi = PendingIntent.getActivity(service.getApplicationContext(), 0, intent, + PendingIntent.FLAG_UPDATE_CURRENT); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel chan = new NotificationChannel(Config.notificationChannelID, Config.notificationChannelName, NotificationManager.IMPORTANCE_NONE); + NotificationManager notifService = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + notifService.createNotificationChannel(chan); + builder = new NotificationCompat.Builder(service, Config.notificationChannelID); + } else + builder = new NotificationCompat.Builder(service, ""); + mNotification = builder.setContentIntent(pi).setContentTitle(getString(R.string.app_name)).setContentText(text) + .setSmallIcon(R.drawable.limbo) + .setLargeIcon(BitmapFactory.decodeResource(service.getResources(), R.drawable.limbo)).build(); + mNotification.tickerText = text; + mNotification.flags |= Notification.FLAG_ONGOING_EVENT; + service.startForeground(notifID, mNotification); + } + + public void updateServiceNotification(String text) { + if (builder != null) { + builder.setContentText(text); + mNotification = builder.build(); + NotificationManager mNotificationManager = (NotificationManager) + service.getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE); + mNotificationManager.notify(notifID, mNotification); + } + } + + private void setupLocks() { + WifiManager wm = (WifiManager) service.getApplicationContext().getSystemService(Context.WIFI_SERVICE); + mWifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, Config.wifiLockTag); + mWifiLock.setReferenceCounted(false); + + PowerManager pm = (PowerManager) service.getApplicationContext().getSystemService(Context.POWER_SERVICE); + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Config.wakeLockTag); + mWakeLock.setReferenceCounted(false); + } + + public void onDestroy() { + Log.d(TAG, "Service destroyed"); + super.onDestroy(); + } + + private void releaseLocks() { + if (mWifiLock != null && mWifiLock.isHeld()) { + Log.d(TAG, "Release Wifi lock..."); + mWifiLock.release(); + } + + if (mWakeLock != null && mWakeLock.isHeld()) { + Log.d(TAG, "Release Wake lock..."); + mWakeLock.release(); + } + } + + private void stopService() { + releaseLocks(); + if (service != null) { + service.stopForeground(true); + service.stopSelf(); + } + } + + public void onTaskRemoved(Intent intent) { + LimboSettingsManager.setExitCode(this, Config.EXIT_SUCCESS); + } + + public interface OnStatusChangedListener { + void onStatusChanged(MachineService service, Machine machine, MachineController.MachineStatus status); + } +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/Config.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/Config.java index 3f1b9a099..fe1db8672 100644 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/Config.java +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/Config.java @@ -18,40 +18,23 @@ */ package com.max2idea.android.limbo.main; -import android.androidVNC.COLORMODEL; -import android.androidVNC.VncCanvasActivity; -import android.content.Context; -import android.graphics.Bitmap; -import android.os.Environment; -import android.widget.ImageView.ScaleType; +import com.max2idea.android.limbo.links.LinksManager; -import com.max2idea.android.limbo.utils.LinksManager; - -import java.util.Hashtable; import java.util.LinkedHashMap; /** - * - * @author dev + * Configuration */ public class Config { // Constants - public static final int UI_VNC = 0; - public static final int UI_SDL = 1; - public static final int UI_SPICE = 2; public static final int SDL_MOUSE_LEFT = 1; public static final int SDL_MOUSE_MIDDLE = 2; public static final int SDL_MOUSE_RIGHT = 3; public static final int SETTINGS_RETURN_CODE = 1000; - public static final int SETTINGS_REQUEST_CODE = 1001; public static final int FILEMAN_RETURN_CODE = 1002; - public static final int VNC_REQUEST_CODE = 1004; - public static final int VNC_RESULT_CODE = 1005; - public static final int VNC_RESET_RESULT_CODE = 1006; public static final int SDL_REQUEST_CODE = 1007; - public static final int SDL_RESULT_CODE = 1008; public static final int SDL_QUIT_RESULT_CODE = 1009; public static final int OPEN_IMAGE_FILE_REQUEST_CODE = 2001; @@ -72,6 +55,9 @@ public class Config { public static final int OPEN_LOG_FILE_DIR_REQUEST_CODE = 2011; public static final int OPEN_LOG_FILE_DIR_ASF_REQUEST_CODE = 2012; + public static final int OPEN_IMPORT_BIOS_FILE_REQUEST_CODE = 2013; + public static final int OPEN_IMPORT_BIOS_FILE_ASF_REQUEST_CODE = 2014; + public static final int STATUS_NULL = -1; public static final int STATUS_CREATED = 1000; public static final int STATUS_PAUSED = 1001; @@ -79,185 +65,129 @@ public class Config { // GUI Options public static final boolean enable_SDL = true; - public static boolean enable_SDL_sound = true; - public static final boolean enable_SPICE = false; - public static final int MAX_CPU_NUM = 8; - //Do not update these directly, see inherited project java files - public static boolean enable_X86; //Enable if you build QEMU with x86 softmmu - public static boolean enable_X86_64; //Enable if you build QEMU with x86_64 softmmu - public static boolean enable_ARM; //Enable if you build QEMU with Arm softmmu - public static boolean enable_ARM64; //Enable if you build QEMU with Arm64 softmmu - public static boolean enable_MIPS; //Enable if you build QEMU with Mips softmmu - public static boolean enable_PPC; //Enable if you build QEMU with PPC softmmu - public static boolean enable_PPC64; //Enable if you build QEMU with PPC64 softmmu - public static boolean enable_m68k ; - public static boolean enable_sparc ; - public static boolean enable_sparc64; - - //Enable if you build with KVM support, needes android-21 platform - public static boolean enable_KVM = false; - - public static final boolean enable_qemu_fullScreen = true; + // delay + static int keyDelay = 100; + static int mouseButtonDelay = 100; // App config public static final String APP_NAME = "Limbo Emulator"; - public static String storagedir = null; + public static final String defaultDNSServer = "8.8.8.8"; + // App Config + public static final String downloadLink = "https://github.com/limboemu/limbo/wiki/Downloads"; + public static final String guidesLink = "https://github.com/limboemu/limbo/wiki/Guides"; + public static final String kvmLink = "https://github.com/limboemu/limbo/wiki/KVM"; + public static final String faqLink = "https://github.com/limboemu/limbo/wiki/FAQ"; + public static final String toolsLink = "https://github.com/limboemu/limbo/wiki/Tools"; + public static final String newVersionLink = "https://raw.githubusercontent.com/limboemu/limbo/master/VERSION"; + public static final String otherOSLink = "https://github.com/limboemu/limbo/wiki/Other-Operating-Systems"; + + public static final boolean enableKeyboardLayoutOption = true; + public static final boolean enableMouseOption = true; + + // Debug + public static final boolean debug = false; + public static final boolean debugQmp = false; + public static final boolean debugStrictMode = false; + + public static final int EXIT_SUCCESS = 1; + public static final int EXIT_UNKNOWN = 2; + + + public static boolean enableSDLSound = true; + + // stack size to remove an issue with SDL Audio + public static long stackSize = 10 * 1024 * 1024; + // native alternative to audio track + public static String aaudioLibName = "libcompat-SDL2-addons.so"; + + // if you don't want to enable software updates set to false + public static boolean enableSoftwareUpdates = true; + + //TODO: enable immersive mode at some point in time + public static boolean enableImmersiveMode = false; + + //TODO: add in settings + public static boolean legacyDrives = false; + public static boolean enableDefaultDevices = false; + public static boolean syncFilesOnClose = true; + + public enum Arch { + x86, x86_64, arm, arm64, ppc, ppc64, sparc, sparc64 + } + + //Enable if you build with KVM support, needes android-21 platform + public static boolean enableKVM = false; + public static String storagedir = null; //Some OSes don't like emulated multi cores for QEMU 2.9.1 you can disable here /// thought there is also the Disable TSC feature so you don't have to do it here public static boolean enableSMPOnlyOnKVM = false; - //set to true if you need to debug native library loading - public static boolean loadNativeLibsEarly = false; - + public static boolean loadNativeLibsEarly = false; //XXX: QEMU 3.1.0 needs the libraries to be loaded from the main thread public static boolean loadNativeLibsMainThread = true; - public static String wakeLockTag = "limbo:wakelock"; public static String wifiLockTag = "limbo:wifilock"; - //this will be populated later - public static String cacheDir = null; - - //we disable mouse modes for now - public static boolean disableMouseModes = true; - - //double tap an hold is still buggy so we keep using the old-way trackpad - public static boolean enableDragOnLongPress = true; - - //we need to define the configuration for the VNC client since we replaced some deprecated - // functions - public static Bitmap.Config bitmapConfig = Bitmap.Config.RGB_565; - //XXX set scaling to linear it's a tad slower but it's worth it - public static int SDLHintScale=1; + public static int SDLHintScale = 1; public static boolean viewLogInternally = true; - - //XXX some archs don't support floppy or sd card public static boolean enableEmulatedFloppy = true; public static boolean enableEmulatedSDCard; public static String destLogFilename = "limbolog.txt"; - public static String notificationChannelID = "limbo"; public static String notificationChannelName = "limbo"; public static boolean showToast = false; public static boolean closeFileDescriptors = true; - //XXX: qemu vvfat is buggy so we disable public static boolean enableSharedFolder = false; - - public static final String getCacheDir(){ - return cacheDir.toString(); - } - public static final String getBasefileDir() { - return getCacheDir() + "/limbo/"; - } - - public static String getTmpFolder() { - return getBasefileDir() + "var/tmp"; // Do not modify - } public static String machineFolder = "machines/"; - public static String getMachineDir(){ - return getBasefileDir() + machineFolder; - } public static String logFilePath = null; - //TODO: future enhancement - public static boolean enableOpenSL; - - //hd cache is not quite applicable for Android - public static final boolean enableHDCache = false; - - - public static final String defaultDNSServer = "8.8.8.8"; - public static final String defaultUI = "VNC"; - public static String state_filename = "vm.state"; - + public static String stateFilename = "vm.state"; //QMP public static String QMPServer = "127.0.0.1"; public static int QMPPort = 4444; - public static int MAX_DISPLAY_REFRESH_RATE = 100; //Hz - - // App Config - public static final String downloadLink = "https://github.com/limboemu/limbo/wiki/Downloads"; - public static final String guidesLink = "https://github.com/limboemu/limbo/wiki/Guides"; - public static final String kvmLink = "https://github.com/limboemu/limbo/wiki/KVM"; - public static final String faqLink = "https://github.com/limboemu/limbo/wiki/FAQ"; - public static final String toolsLink = "https://github.com/limboemu/limbo/wiki/Tools"; - public static final String downloadUpdateLink = "https://raw.githubusercontent.com/limboemu/limbo/master/VERSION"; - // VNC Defaults public static String defaultVNCHost = "127.0.0.1"; - public static final String defaultVNCUsername = "limbo"; - public static final String defaultVNCPasswd = ""; - - //It seems that for new veersion of qemu it expectes a relative number + //It seems that new versions of qemu expect a relative number // so we stop using absolute port numbers public static final int defaultVNCPort = 1; - public static final String defaultVNCColorMode = COLORMODEL.C24bit.nameString(); - public static final ScaleType defaultFullscreenScaleMode = ScaleType.FIT_CENTER; - public static final ScaleType defaultScaleModeCenter = ScaleType.CENTER; - public static final String defaultInputMode = VncCanvasActivity.TOUCH_ZOOM_MODE; - //Keyboard Layout public static String defaultKeyboardLayout = "en-us"; - //a little nicer ui public static boolean collapseSections = true; - public static boolean enableFlashMemoryImages = true; public static boolean enableToggleKeyboard = false; //override this at the app level it dependes on the host arch public static boolean enableMTTCG = true; - - - - public static final boolean enableKeyboardLayoutOption = true; - public static final boolean enableMouseOption = true; - - // Features - protected static final boolean enableSaveVMmonitor = true; // we use the - // Monitor - // console to - // save vms - - // Debug - public static final boolean debug = false; - public static boolean debugQmp = false; - - //remove in production - public static boolean debugStrictMode = false; - - public static LinkedHashMap osImages = new LinkedHashMap(); - + public static LinkedHashMap osImages = new LinkedHashMap<>(); public static boolean processMouseHistoricalEvents = false; - public static String getLocalQMPSocketPath() { - return Config.getCacheDir()+"/qmpsocket"; - } + //Change to true in prod if you want to be notified by default for new versions + public static boolean defaultCheckNewVersion = false; - public static String getLocalVNCSocketPath() { - return Config.getCacheDir()+"/vncsocket"; - } + //enable tracing + // make sure you have access to the dir/files below + public static boolean enableTracingLog = false; + public static final String traceDir = "/sdcard/limbo/tmp/trace"; + public static final String traceEventsFile = "/sdcard/limbo/tmp/events"; + public static final String traceLogFile = "/sdcard/limbo/log.txt"; - public static enum MouseMode { - Trackpad, External - } - public static MouseMode mouseMode = MouseMode.Trackpad; + // override translation block size + public static boolean overrideTbSize; + public static String tbSize = "32M"; - //specify hd interface, alternative we don't need it right now - public static boolean enable_hd_if = false; - public static String hd_if_type = "ide"; + // Class that starts when user presses notification + public static Class clientClass = null; - //Change to true in prod if you want to be notified by default for new versions - public static boolean defaultCheckNewVersion = true; } diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/DrivesDialogBox.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/DrivesDialogBox.java new file mode 100644 index 000000000..9bab783c9 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/DrivesDialogBox.java @@ -0,0 +1,298 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.main; + +import android.app.Activity; +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; +import android.view.View; +import android.view.WindowManager; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.ArrayAdapter; +import android.widget.LinearLayout; +import android.widget.Spinner; + +import com.limbo.emu.lib.R; +import com.max2idea.android.limbo.machine.Machine; +import com.max2idea.android.limbo.machine.Machine.FileType; +import com.max2idea.android.limbo.machine.MachineAction; +import com.max2idea.android.limbo.machine.MachineController; +import com.max2idea.android.limbo.machine.MachineFilePaths; +import com.max2idea.android.limbo.machine.MachineProperty; +import com.max2idea.android.limbo.ui.SpinnerAdapter; + +import java.util.ArrayList; +import java.util.Observable; +import java.util.Observer; + +/** A simple custom dialog that serves as a way to change removable drives for Limbo. + * This class communicates passively with the ViewController which observes for changes on the ui. + */ +public class DrivesDialogBox extends Dialog implements Observer { + private static final String TAG = "DrivesDialogBox"; + + private final Machine currMachine; + public Spinner mCD; + public Spinner mFDA; + public Spinner mSD; + public Spinner mFDB; + public LinearLayout mCDLayout; + public LinearLayout mFDALayout; + public LinearLayout mFDBLayout; + public LinearLayout mSDLayout; + public FileType fileType; + private Activity activity; + private ViewListener viewListener; + + + public DrivesDialogBox(Activity activity, int theme, Machine currMachine) { + super(activity, theme); + this.activity = activity; + this.currMachine = currMachine; + getWindow().setFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND, WindowManager.LayoutParams.FLAG_DIM_BEHIND); + setContentView(R.layout.dev_dialog); + this.setTitle(R.string.RemovableDrives); + getWidgets(); + initUI(); + setupController(); + setOnDismissListener(new OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialogInterface) { + setViewListener(null); + MachineController.getInstance().getMachine().deleteObserver(DrivesDialogBox.this); + } + }); + MachineController.getInstance().getMachine().addObserver(this); + } + + private void setupController() { + setViewListener(LimboApplication.getViewListener()); + } + + public void setViewListener(ViewListener viewListener) { + this.viewListener = viewListener; + } + + @Override + public void onBackPressed() { + this.dismiss(); + } + + private void getWidgets() { + mCD = (Spinner) findViewById(R.id.cdromimgval); + mCDLayout = findViewById(R.id.cdromimgl); + + mFDA = (Spinner) findViewById(R.id.floppyimgval); + mFDALayout = findViewById(R.id.floppyimgl); + + mFDB = (Spinner) findViewById(R.id.floppybimgval); + mFDBLayout = findViewById(R.id.floppybimgl); + + mSD = (Spinner) findViewById(R.id.sdimgval); + mSDLayout = findViewById(R.id.sdimgl); + + } + + private void setupListeners() { + setupListener(mCD, MachineProperty.CDROM, FileType.CDROM); + setupListener(mFDA, MachineProperty.FDA, FileType.FDA); + setupListener(mFDB, MachineProperty.FDB, FileType.FDB); + setupListener(mSD, MachineProperty.SD, FileType.SD); + } + + private void setupListener(final Spinner spinner, final MachineProperty machineDrive, + final FileType fileType) { + spinner.setOnItemSelectedListener(new OnItemSelectedListener() { + public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { + setDriveValue(spinner, position, machineDrive, fileType); + } + + public void onNothingSelected(AdapterView parentView) { + } + }); + } + + private void setDriveValue(Spinner spinner, int position, MachineProperty driveLabel, + FileType filetype) { + String diskValue = (String) ((ArrayAdapter) spinner.getAdapter()).getItem(position); + if (position == 0) { + notifyFieldChange(MachineProperty.REMOVABLE_DRIVE, new Object[]{ driveLabel, ""}); + } else if (position == 1) { + this.fileType = filetype; + LimboFileManager.browse(activity, filetype, Config.OPEN_IMAGE_FILE_REQUEST_CODE); + spinner.setSelection(0); + } else if (position > 1) { + notifyFieldChange(MachineProperty.REMOVABLE_DRIVE, new Object[]{ driveLabel, diskValue}); + } + } + + public void populateDiskAdapter(final Spinner spinner, final FileType fileType, final boolean createOption, + final String value) { + Thread t = new Thread(new Runnable() { + public void run() { + ArrayList oldHDs = MachineFilePaths.getRecentFilePaths(fileType); + final ArrayList arraySpinner = new ArrayList<>(); + arraySpinner.add("None"); + if (createOption) + arraySpinner.add("New"); + arraySpinner.add(activity.getString(R.string.open)); + final int index = arraySpinner.size(); + for (String file : oldHDs) { + if (file != null) { + arraySpinner.add(file); + } + } + new Handler(Looper.getMainLooper()).post(new Runnable() { + public void run() { + SpinnerAdapter adapter = new SpinnerAdapter(activity, R.layout.custom_spinner_item, arraySpinner, index); + adapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); + spinner.setAdapter(adapter); + spinner.invalidate(); + setDiskValue(spinner, value); + } + }); + } + }); + t.start(); + } + + private void initUI() { + Thread thread = new Thread(new Runnable() { + public void run() { + if (currMachine.isEnableCDROM()) { + populateDiskAdapter(mCD, FileType.CDROM, false, currMachine.getCdImagePath()); + } else { + mCDLayout.setVisibility(View.GONE); + } + if (currMachine.isEnableFDA()) { + populateDiskAdapter(mFDA, FileType.FDA, false, currMachine.getFdaImagePath()); + } else { + mFDALayout.setVisibility(View.GONE); + } + if (currMachine.isEnableFDB()) { + populateDiskAdapter(mFDB, FileType.FDB, false, currMachine.getFdbImagePath()); + } else { + mFDBLayout.setVisibility(View.GONE); + } + if (currMachine.isEnableSD()) { + populateDiskAdapter(mSD, FileType.SD, false, currMachine.getCdImagePath()); + } else { + mSDLayout.setVisibility(View.GONE); + } + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + public void run() { + setupListeners(); + } + }, 500); + } + }); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); + } + + public void setDriveAttr(FileType fileType, final String file) { + notifyAction(MachineAction.INSERT_FAV, new Object[]{file, fileType}); + if (fileType == FileType.CDROM && file != null && !file.trim().equals("")) { + notifyFieldChange(MachineProperty.CDROM, file); + setSpinnerValue(mCD, file); + } else if (fileType == FileType.SD && file != null && !file.trim().equals("")) { + notifyFieldChange(MachineProperty.SD, file); + setSpinnerValue(mSD, file); + } else if (file != null && !file.trim().equals("") && fileType == FileType.FDA) { + notifyFieldChange(MachineProperty.FDA, file); + setSpinnerValue(mFDA, file); + } else if (file != null && !file.trim().equals("") && fileType == FileType.FDB) { + notifyFieldChange(MachineProperty.FDB, file); + setSpinnerValue(mFDB, file); + } + } + + private void setSpinnerValue(final Spinner spinner, final String value) { + new Handler(Looper.getMainLooper()).post(new Runnable() { + public void run() { + if (SpinnerAdapter.getItemPosition(spinner, value) < 0) { + SpinnerAdapter.addItem(spinner, value); + } + setDiskValue(spinner, value); + int res = spinner.getSelectedItemPosition(); + if (res == 1) { + spinner.setSelection(0); + } + } + }); + } + + + private void setDiskValue(final Spinner spinner, final String value) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + if (spinner != null) { + int pos = SpinnerAdapter.getItemPosition(spinner, value); + if (pos > 1) { + spinner.setSelection(pos); + } else { + spinner.setSelection(0); + } + } + } + }); + + } + + public void notifyFieldChange(MachineProperty property, Object value) { + if(viewListener !=null) + viewListener.onFieldChange(property, value); + } + + public void notifyAction(MachineAction action, Object value) { + if(viewListener !=null) + viewListener.onAction(action, value); + } + + @Override + public void update(Observable observable, Object o) { + Object[] params = (Object[]) o; + MachineProperty property = (MachineProperty) params[0]; + Object value = params[1]; + //XXX: if the executor is not able to change the drive then we reset the spinner + switch(property) { + case CDROM: + if(value == null) + setDiskValue(mCD, ""); + break; + case FDA: + if(value == null) + setDiskValue(mFDA, ""); + break; + case FDB: + if(value == null) + setDiskValue(mFDB, ""); + break; + case SD: + if(value == null) + setDiskValue(mSD, ""); + break; + } + } +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboActivity.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboActivity.java index 3f6345268..5d253f04f 100644 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboActivity.java +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboActivity.java @@ -18,38 +18,21 @@ */ package com.max2idea.android.limbo.main; -import android.androidVNC.ConnectionBean; -import android.androidVNC.RfbProto; -import android.androidVNC.VncCanvas; import android.app.Activity; import android.app.AlertDialog; import android.app.NotificationManager; -import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; -import android.net.Uri; -import android.net.wifi.WifiManager.WifiLock; -import android.os.AsyncTask; +import android.graphics.Point; +import android.os.Build; import android.os.Bundle; -import android.os.Environment; import android.os.Handler; import android.os.Looper; -import android.os.PowerManager.WakeLock; import android.os.StrictMode; - -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.widget.Toolbar; -import androidx.core.view.MenuItemCompat; -import android.text.InputType; -import android.text.method.HideReturnsTransformationMethod; -import android.text.method.PasswordTransformationMethod; import android.util.Log; +import android.view.Display; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; @@ -67,44 +50,52 @@ import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.ScrollView; import android.widget.Spinner; import android.widget.TextView; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.core.widget.NestedScrollView; + import com.limbo.emu.lib.R; -import com.max2idea.android.limbo.jni.VMExecutor; -import com.max2idea.android.limbo.utils.FavOpenHelper; -import com.max2idea.android.limbo.utils.FileInstaller; -import com.max2idea.android.limbo.utils.FileManager; -import com.max2idea.android.limbo.utils.FileUtils; -import com.max2idea.android.limbo.utils.LinksManager; -import com.max2idea.android.limbo.utils.Machine; -import com.max2idea.android.limbo.utils.MachineOpenHelper; -import com.max2idea.android.limbo.utils.OSDialogBox; -import com.max2idea.android.limbo.utils.QmpClient; -import com.max2idea.android.limbo.utils.UIUtils; -import com.max2idea.android.limbo.utils.UIUtils.LimboFileSpinnerAdapter; +import com.max2idea.android.limbo.dialog.DialogUtils; +import com.max2idea.android.limbo.files.FileInstaller; +import com.max2idea.android.limbo.files.FileUtils; +import com.max2idea.android.limbo.help.Help; +import com.max2idea.android.limbo.install.Installer; +import com.max2idea.android.limbo.keyboard.KeyboardUtils; +import com.max2idea.android.limbo.links.LinksManager; +import com.max2idea.android.limbo.log.Logger; +import com.max2idea.android.limbo.machine.ArchDefinitions; +import com.max2idea.android.limbo.machine.BIOSImporter; +import com.max2idea.android.limbo.machine.Machine; +import com.max2idea.android.limbo.machine.Machine.FileType; +import com.max2idea.android.limbo.machine.MachineAction; +import com.max2idea.android.limbo.machine.MachineController; +import com.max2idea.android.limbo.machine.MachineController.MachineStatus; +import com.max2idea.android.limbo.machine.MachineExporter; +import com.max2idea.android.limbo.machine.MachineFilePaths; +import com.max2idea.android.limbo.machine.MachineImporter; +import com.max2idea.android.limbo.machine.MachineProperty; +import com.max2idea.android.limbo.network.NetworkUtils; +import com.max2idea.android.limbo.toast.ToastUtils; +import com.max2idea.android.limbo.ui.SpinnerAdapter; +import com.max2idea.android.limbo.updates.UpdateChecker; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; import java.util.Hashtable; -import java.util.Iterator; -import java.util.logging.Level; -import java.util.logging.Logger; +import java.util.Observable; +import java.util.Observer; -import androidx.appcompat.app.AppCompatActivity; -import androidx.core.widget.NestedScrollView; +public class LimboActivity extends AppCompatActivity + implements MachineController.OnMachineStatusChangeListener, + MachineController.OnEventListener, Observer { -public class LimboActivity extends AppCompatActivity { - - public static final String TAG = "LIMBO"; + private static final String TAG = "LimboActivity"; private static final int HELP = 0; private static final int QUIT = 1; @@ -116,25 +107,18 @@ public class LimboActivity extends AppCompatActivity { private static final int LICENSE = 7; private static final int VIEWLOG = 8; private static final int CREATE = 9; - private static final int ISOSIMAGES = 10; private static final int DISCARD_VM_STATE = 11; - private static final int ENABLE_FILEMANAGER = 12; private static final int SETTINGS = 13; + private static final int TOOLS = 14; + private static final int IMPORT_BIOS_FILE = 15; + + // disk mapping + private static final Hashtable diskMapping = new Hashtable<>(); - public static VMStatus currStatus = VMStatus.Ready; - public static boolean vmStarted = false; - public static VMExecutor vmexecutor; - public static Machine currMachine = null; - private static LimboActivity activity = null; - private static String vnc_passwd = null; - private static int vnc_allow_external = 0; - private static int qmp_allow_external = 0; - public ProgressDialog progDialog; + private static boolean libLoaded; public View parent; - private InstallerTask installerTaskTask; private boolean machineLoaded; - private boolean timeQuit = false; - private Object lockTime = new Object(); + private FileType browseFileType = null; //Widgets private ImageView mStatus; @@ -143,89 +127,68 @@ public class LimboActivity extends AppCompatActivity { private EditText mAppend; private EditText mExtraParams; private TextView mStatusText; - private WakeLock mWakeLock; - private WifiLock wlock; - - private Spinner mMachine; private Spinner mCPU; - private Spinner mArch; private Spinner mMachineType; private Spinner mCPUNum; private Spinner mKernel; private Spinner mInitrd; - // HDD + private ImageView mHDAOptions; private Spinner mHDA; + private ImageView mHDBOptions; private Spinner mHDB; + private ImageView mHDCOptions; private Spinner mHDC; + private ImageView mHDDOptions; private Spinner mHDD; + private Spinner mSharedFolder; + + //removable private Spinner mCD; private Spinner mFDA; private Spinner mFDB; private Spinner mSD; - private Spinner mSharedFolder; - private CheckBox mHDAenable; - private CheckBox mHDBenable; - private CheckBox mHDCenable; - private CheckBox mHDDenable; private CheckBox mCDenable; private CheckBox mFDAenable; private CheckBox mFDBenable; private CheckBox mSDenable; - private CheckBox mSharedFolderenable; + private ImageView mCDOptions; + + // misc private Spinner mRamSize; private Spinner mBootDevices; - private Spinner mNicCard; + private Spinner mNetworkCard; private Spinner mNetConfig; private Spinner mVGAConfig; private Spinner mSoundCard; - private Spinner mHDCacheConfig; private Spinner mUI; private CheckBox mDisableACPI; private CheckBox mDisableHPET; private CheckBox mDisableTSC; - - //TODO: - // private CheckBox mSnapshot; - - private CheckBox mVNCAllowExternal; - private CheckBox mQMPAllowExternal; - private CheckBox mPrio; private CheckBox mEnableKVM; private CheckBox mEnableMTTCG; - - private CheckBox mToolBar; - private CheckBox mFullScreen; - private Spinner mSnapshot; - private Spinner mOrientation; private Spinner mKeyboard; private Spinner mMouse; + + // buttons private ImageButton mStart; + private ImageButton mPause; private ImageButton mStop; private ImageButton mRestart; - private ImageButton mSave; + //sections private LinearLayout mCPUSectionDetails; - private LinearLayout mCPUSectionHeader; private LinearLayout mStorageSectionDetails; - private LinearLayout mStorageSectionHeader; private LinearLayout mUserInterfaceSectionDetails; - private LinearLayout mUserInterfaceSectionHeader; private LinearLayout mAdvancedSectionDetails; - private LinearLayout mAdvancedSectionHeader; - private View mBootSectionHeader; private LinearLayout mBootSectionDetails; private LinearLayout mGraphicsSectionDetails; - private LinearLayout mGraphicsSectionHeader; private LinearLayout mRemovableStorageSectionDetails; - private LinearLayout mRemovableStorageSectionHeader; private LinearLayout mNetworkSectionDetails; - private View mNetworkSectionHeader; private LinearLayout mAudioSectionDetails; - private LinearLayout mAudioSectionHeader; - private ConnectionBean selected; - private FileType filetype; + + //summary private TextView mUISectionSummary; private TextView mCPUSectionSummary; private TextView mStorageSectionSummary; @@ -235,278 +198,69 @@ public class LimboActivity extends AppCompatActivity { private TextView mNetworkSectionSummary; private TextView mBootSectionSummary; private TextView mAdvancedSectionSummary; - private CheckBox mDesktopMode; - private FavOpenHelper favinstance; + + //layouts private NestedScrollView mScrollView; - private boolean libLoaded; - private OnClickListener resetClickListener; - private Hashtable diskMapping = new Hashtable<>(); - private String fixMouseDescr = " (Fixes Mouse)"; private boolean firstMTTCGCheck; + private ViewListener viewListener; - public static void quit() { - activity.finish(); - } - - static private void onInstall(boolean force) { - FileInstaller.installFiles(activity, force); - } - - public static String getVnc_passwd() { - return LimboActivity.vnc_passwd; - } - - public static void setVnc_passwd(String vnc_passwd) { - LimboActivity.vnc_passwd = vnc_passwd; - } - - public static String getLocalIpAddress() { - try { - for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) { - NetworkInterface intf = en.nextElement(); - for (Enumeration enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) { - InetAddress inetAddress = enumIpAddr.nextElement(); - if (!inetAddress.isLoopbackAddress() && inetAddress.getHostAddress().toString().contains(".")) { - return inetAddress.getHostAddress().toString(); - } - } - } - } catch (SocketException ex) { - ex.printStackTrace(); - } - return null; - } - - // Start calling the JNI interface - public static void startvm(Activity activity, int UI) { - QmpClient.allow_external = (qmp_allow_external == 1); - vmexecutor.qmp_allow_external = qmp_allow_external; - - if (UI == Config.UI_VNC) { - // disable sound card with VNC - vmexecutor.enablevnc = 1; - vmexecutor.enablespice = 0; - vmexecutor.sound_card = null; - vmexecutor.vnc_allow_external = vnc_allow_external; - RfbProto.allow_external = (vnc_allow_external == 1); - vmexecutor.vnc_passwd = vnc_passwd; - } else if (UI == Config.UI_SDL) { - vmexecutor.enablevnc = 0; - vmexecutor.enablespice = 0; - } else if (UI == Config.UI_SPICE) { - vmexecutor.vnc_allow_external = vnc_allow_external; - vmexecutor.vnc_passwd = vnc_passwd; - vmexecutor.enablevnc = 0; - vmexecutor.enablespice = 1; - } - vmexecutor.startvm(activity, UI); - - } - - public static LimboActivity getInstance() { - return activity; - } - - public static void cleanup() { - - if (getInstance() != null && getInstance().mMachine != null) { - vmStarted = false; - - //XXX flush and close all file descriptors if we haven't already - FileUtils.close_fds(); - - try { - MachineOpenHelper.getInstance(activity).close(); - } catch (Exception ex) { - Log.e(TAG, "Could not close machine db: " + ex); - } - - - try { - FavOpenHelper.getInstance(activity).close(); - } catch (Exception ex) { - Log.e(TAG, "Could not close fav db: " + ex); - } - - - ////XXX; we wait till fds flush and close - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - //set the exit code - LimboSettingsManager.setExitCode(activity, 1); - - //XXX: SDL seems to lock the keyboard events - // unless we finish the starting activity - activity.finish(); - - Log.v(TAG, "Exit"); - //XXX: We exit here to force unload the native libs - System.exit(0); - - } - } - - public static void saveStateVMDB() { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.PAUSED, - 1 + ""); - } - - public void changeStatus(final VMStatus status_changed) { + public void changeStatus(final MachineStatus status_changed) { runOnUiThread(new Runnable() { @Override public void run() { - if (status_changed == VMStatus.Running) { + if (MachineController.getInstance().isRunning() || status_changed == MachineStatus.Running) { mStatus.setImageResource(R.drawable.on); - mStatusText.setText("Running"); + if (mUI.getSelectedItemPosition() == 0) { + // VNC + mStatusText.setText(R.string.Running); + //XXX: we block the user from changing the drives + // from this activitybecause sdl is suspended and the thread will block + // so they have to change it from within the SDL Activity + enableRemovableDiskValues(true); + } else { + // SDL is always suspend in the background + mStatusText.setText(R.string.Suspended); + enableRemovableDiskValues(false); + } unlockRemovableDevices(false); - enableRemovableDiskValues(true); enableNonRemovableDeviceOptions(false); - vmStarted = true; - } else if (status_changed == VMStatus.Ready || status_changed == VMStatus.Stopped) { + mMachine.setEnabled(false); + } else if (status_changed == MachineStatus.Ready || status_changed == MachineStatus.Stopped) { mStatus.setImageResource(R.drawable.off); - mStatusText.setText("Stopped"); + mStatusText.setText(R.string.Stopped); unlockRemovableDevices(true); enableRemovableDiskValues(true); enableNonRemovableDeviceOptions(true); - } else if (status_changed == VMStatus.Saving) { + } else if (status_changed == MachineStatus.Saving) { mStatus.setImageResource(R.drawable.on); - mStatusText.setText("Saving State"); + mStatusText.setText(R.string.savingState); unlockRemovableDevices(false); enableRemovableDiskValues(false); enableNonRemovableDeviceOptions(false); - } else if (status_changed == VMStatus.Paused) { + } else if (status_changed == MachineStatus.Paused) { mStatus.setImageResource(R.drawable.on); - mStatusText.setText("Paused"); + mStatusText.setText(R.string.paused); unlockRemovableDevices(false); enableRemovableDiskValues(false); enableNonRemovableDeviceOptions(false); } } }); - - } - - private void install(boolean force) { - progDialog = ProgressDialog.show(activity, "Please Wait", "Installing BIOS...", true); - installerTaskTask = new InstallerTask(); - installerTaskTask.force = force; - installerTaskTask.execute(); - } - - public void UIAlertLicense(String title, String body, final Activity activity) { - - AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle(title); - - TextView textView = new TextView(activity); - textView.setText(body); - textView.setTextSize(10); - textView.setPadding(20, 20, 20, 20); - - ScrollView scrollView = new ScrollView(activity); - scrollView.addView(textView); - - alertDialog.setView(scrollView); - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "I Acknowledge", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - if (LimboSettingsManager.isFirstLaunch(activity)) { - install(true); - UIUtils.onHelp(activity); - UIUtils.onChangeLog(activity); - } - LimboSettingsManager.setFirstLaunch(activity); - return; - } - }); - alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - if (LimboSettingsManager.isFirstLaunch(activity)) { - if (activity.getParent() != null) { - activity.getParent().finish(); - } else { - activity.finish(); - } - } - } - }); - alertDialog.show(); } private void onTap() { - ApplicationInfo pInfo = null; - String userid = "None"; - try { - pInfo = activity.getPackageManager().getApplicationInfo(activity.getClass().getPackage().getName(), - PackageManager.GET_META_DATA); - userid = pInfo.uid + ""; - } catch (NameNotFoundException e) { - e.printStackTrace(); - } + String userid = LimboApplication.getUserId(this); if (!(new File("/dev/net/tun")).exists()) { - UIUtils.UIAlert(this,"TAP - User Id: " + userid, - "Your device doesn't support TAP, use \"User\" network mode instead "); + LimboActivityCommon.tapNotSupported(this, userid); return; } - - - DialogInterface.OnClickListener okListener = new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - updateSummary(false); - } - }; - - - DialogInterface.OnClickListener helpListener = - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - goToURL(Config.faqLink); - return; - } - }; - - UIUtils.UIAlert(activity, - "TAP Device found", - "Warning! Make sure device /dev/net/tun has appropriate permissions\nUser ID: " + userid - +"/n", - 16, false, "OK", okListener, - null, null, "TAP Help", helpListener); - } - - - private void onNetworkUser() { - ApplicationInfo pInfo = null; - - DialogInterface.OnClickListener okListener = new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - updateSummary(false); - } - }; - - DialogInterface.OnClickListener helpListener = - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - goToURL(Config.faqLink); - return; - } - }; - - UIUtils.UIAlert(activity, - "Network", - "Warning! Enabling external network for images you don't trust or containing old OSes is not recommended. " + - "If not use network \"None\" before running the virtual machine.\n", - 16, false, "OK", okListener, - null, null, "FAQ", helpListener); + LimboActivityCommon.promptTap(this, userid); } public void setUserPressed(boolean pressed) { - if (pressed) { - enableListeners(); + setupMiscOptions(); + setupNonRemovableDiskListeners(); enableRemovableDiskListeners(); } else { disableListeners(); @@ -515,5761 +269,2491 @@ public void setUserPressed(boolean pressed) { } private void disableRemovableDiskListeners() { + disableRemovableDiskListener(mCDenable, mCD); + disableRemovableDiskListener(mCDenable, mCD); + disableRemovableDiskListener(mCDenable, mCD); + disableRemovableDiskListener(mCDenable, mCD); + } - mCDenable.setOnCheckedChangeListener(null); - mFDAenable.setOnCheckedChangeListener(null); - mFDBenable.setOnCheckedChangeListener(null); - mSDenable.setOnCheckedChangeListener(null); - mCD.setOnItemSelectedListener(null); - mFDA.setOnItemSelectedListener(null); - mFDB.setOnItemSelectedListener(null); - mSD.setOnItemSelectedListener(null); + private void disableRemovableDiskListener(CheckBox enableDrive, Spinner spinner) { + enableDrive.setOnCheckedChangeListener(null); + spinner.setOnItemSelectedListener(null); } private void enableRemovableDiskListeners() { - mCD.setOnItemSelectedListener(new OnItemSelectedListener() { + enableRemovableDiskListener(mCD, mCDenable, mCDOptions, MachineProperty.CDROM, FileType.CDROM); + enableRemovableDiskListener(mFDA, mFDAenable, null, MachineProperty.FDA, FileType.FDA); + enableRemovableDiskListener(mFDB, mFDBenable, null, MachineProperty.FDB, FileType.FDB); + enableRemovableDiskListener(mSD, mSDenable, null, MachineProperty.SD, FileType.SD); + } + + private void enableRemovableDiskListener(final Spinner spinner, final CheckBox driveEnable, + final ImageView driveOptions, + final MachineProperty driveName, + final FileType fileType) { + spinner.setOnItemSelectedListener(new OnItemSelectedListener() { public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) + if (getMachine() == null) return; + String value = (String) ((ArrayAdapter) spinner.getAdapter()).getItem(position); + if (position == 1 && driveEnable.isChecked()) { + browseFileType = fileType; + LimboFileManager.browse(LimboActivity.this, browseFileType, Config.OPEN_IMAGE_FILE_REQUEST_CODE); + spinner.setSelection(0); + } else { + notifyFieldChange(MachineProperty.REMOVABLE_DRIVE, new Object[]{driveName, value}); + } + } + + public void onNothingSelected(AdapterView parentView) { + } + }); + driveEnable.setOnCheckedChangeListener( + new OnCheckedChangeListener() { + public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { + spinner.setEnabled(isChecked); + notifyFieldChange(MachineProperty.DRIVE_ENABLED, new Object[]{driveName, isChecked}); + triggerUpdateSpinner(spinner); + } - String cd = (String) ((ArrayAdapter) mCD.getAdapter()).getItem(position); - if ( - position == 0 && mCDenable.isChecked()) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.CDROM, ""); - currMachine.cd_iso_path = ""; - } else if ( - (position == 0 || !mCDenable.isChecked())) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.CDROM, - null); - currMachine.cd_iso_path = null; - } else if ( - position == 1 && mCDenable.isChecked()) { - filetype = FileType.CD; - FileManager.browse(activity, filetype, Config.OPEN_IMAGE_FILE_REQUEST_CODE); - mCD.setSelection(0); - } else if ( - position > 1 && mCDenable.isChecked()) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.CDROM, cd); - currMachine.cd_iso_path = cd; } - if ( - currStatus == VMStatus.Running && position > 1 && mCDenable.isChecked()) { - mCD.setEnabled(false); - vmexecutor.change_dev("ide1-cd0", currMachine.cd_iso_path); - mCD.setEnabled(true); - } else if ( - mCDenable.isChecked() && - currStatus == VMStatus.Running && position == 0) { - mCD.setEnabled(false); - vmexecutor.change_dev("ide1-cd0", null); // Eject - mCD.setEnabled(true); + ); + if(driveOptions!=null) { + driveOptions.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if(driveEnable.isChecked()) + promptDriveInterface(driveName); } - updateSummary(false); + }); + } + } + + private void setupMiscOptions() { + + mCPU.setOnItemSelectedListener(new OnItemSelectedListener() { + public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { + if (getMachine() == null) + return; + String cpu = (String) ((ArrayAdapter) mCPU.getAdapter()).getItem(position); + notifyFieldChange(MachineProperty.CPU, cpu); } public void onNothingSelected(AdapterView parentView) { - } }); - mFDA.setOnItemSelectedListener(new OnItemSelectedListener() { + mMachineType.setOnItemSelectedListener(new OnItemSelectedListener() { public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) + if (getMachine() == null) return; - - String fda = (String) ((ArrayAdapter) mFDA.getAdapter()).getItem(position); - if ( - position == 0 && mFDAenable.isChecked()) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.FDA, ""); - currMachine.fda_img_path = ""; - } else if ( - (position == 0 || !mFDAenable.isChecked())) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.FDA, null); - currMachine.fda_img_path = null; - } else if ( - position == 1 && mFDAenable.isChecked()) { - filetype = FileType.FDA; - FileManager.browse(activity, filetype, Config.OPEN_IMAGE_FILE_REQUEST_CODE); - mFDA.setSelection(0); - } else if ( - position > 1 && mFDAenable.isChecked()) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.FDA, fda); - currMachine.fda_img_path = fda; - } - if ( - currStatus == VMStatus.Running && position > 1 && mFDAenable.isChecked()) { - mFDA.setEnabled(false); - vmexecutor.change_dev("floppy0", currMachine.fda_img_path); - mFDA.setEnabled(true); - } else if ( - currStatus == VMStatus.Running && position == 0 && mFDAenable.isChecked()) { - mFDA.setEnabled(false); - vmexecutor.change_dev("floppy0", null); // Eject - mFDA.setEnabled(true); - } - updateSummary(false); - + String machineType = (String) ((ArrayAdapter) mMachineType.getAdapter()).getItem(position); + notifyFieldChange(MachineProperty.MACHINETYPE, machineType); } public void onNothingSelected(AdapterView parentView) { - } }); - mFDB.setOnItemSelectedListener(new OnItemSelectedListener() { + mUI.setOnItemSelectedListener(new OnItemSelectedListener() { public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) + if (getMachine() == null) return; - - String fdb = (String) ((ArrayAdapter) mFDB.getAdapter()).getItem(position); - if ( - position == 0 && mFDBenable.isChecked()) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.FDB, ""); - currMachine.fdb_img_path = ""; - } else if ( - (position == 0 || !mFDBenable.isChecked())) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.FDB, null); - currMachine.fdb_img_path = null; - } else if ( - position == 1 && mFDBenable.isChecked()) { - filetype = FileType.FDB; - FileManager.browse(activity, filetype, Config.OPEN_IMAGE_FILE_REQUEST_CODE); - mFDB.setSelection(0); - } else if ( - position > 1 && mFDBenable.isChecked()) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.FDB, fdb); - currMachine.fdb_img_path = fdb; - // TODO: If Machine is running eject and set floppy img - } - if ( - currStatus == VMStatus.Running && position > 1 && mFDBenable.isChecked()) { - mFDB.setEnabled(false); - vmexecutor.change_dev("floppy1", currMachine.fdb_img_path); - mFDB.setEnabled(true); - } else if ( - currStatus == VMStatus.Running && position == 0 && mFDBenable.isChecked()) { - mFDB.setEnabled(false); - vmexecutor.change_dev("floppy1", null); // Eject - mFDB.setEnabled(true); - } - updateSummary(false); + String ui = (String) ((ArrayAdapter) mUI.getAdapter()).getItem(position); + notifyFieldChange(MachineProperty.UI, ui); } public void onNothingSelected(AdapterView parentView) { - } }); - mSD.setOnItemSelectedListener(new OnItemSelectedListener() { + mCPUNum.setOnItemSelectedListener(new OnItemSelectedListener() { public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) + if (getMachine() == null) return; - - String sd = (String) ((ArrayAdapter) mSD.getAdapter()).getItem(position); - if ( - position == 0 && mSDenable.isChecked()) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.SD, ""); - currMachine.sd_img_path = ""; - } else if ( - (position == 0 || !mSDenable.isChecked())) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.SD, null); - currMachine.sd_img_path = null; - } else if ( - position == 1 && mSDenable.isChecked()) { - filetype = FileType.SD; - FileManager.browse(activity, filetype, Config.OPEN_IMAGE_FILE_REQUEST_CODE); - mSD.setSelection(0); - } else if ( - position > 1 && mSDenable.isChecked()) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.SD, sd); - currMachine.sd_img_path = sd; - // TODO: If Machine is running eject and set floppy img - } - if ( - currStatus == VMStatus.Running && position > 1 && mSDenable.isChecked()) { - } else if ( - currStatus == VMStatus.Running && position == 0 && mSDenable.isChecked()) { + final String cpuNum = (String) ((ArrayAdapter) mCPUNum.getAdapter()).getItem(position); + if (position > 0 && getMachine().getEnableMTTCG() != 1 && getMachine().getEnableKVM() != 1 && !firstMTTCGCheck) { + firstMTTCGCheck = true; + promptMultiCPU(cpuNum); + } else { + notifyFieldChange(MachineProperty.CPUNUM, cpuNum); } - updateSummary(false); + mDisableTSC.setChecked(position > 0 && (LimboApplication.arch == Config.Arch.x86 || + LimboApplication.arch == Config.Arch.x86_64)); } public void onNothingSelected(AdapterView parentView) { - } }); - mCDenable.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - if(currMachine == null) - return; + mRamSize.setOnItemSelectedListener(new OnItemSelectedListener() { + public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { + if (getMachine() == null) + return; + String ram = (String) ((ArrayAdapter) mRamSize.getAdapter()).getItem(position); + notifyFieldChange(MachineProperty.MEMORY, ram); + } - mCD.setEnabled(isChecked); + public void onNothingSelected(AdapterView parentView) { - currMachine.enableCDROM = isChecked; - if (isChecked) { - currMachine.cd_iso_path = ""; - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.CDROM, - ""); - mHDCenable.setChecked(false); - } else { - currMachine.cd_iso_path = null; - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.CDROM, - null); - } + } + }); - triggerUpdateSpinner(mCD); - updateSummary(false); - } + mKernel.setOnItemSelectedListener(new OnItemSelectedListener() { + public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { + if (getMachine() == null) + return; + String kernel = (String) ((ArrayAdapter) mKernel.getAdapter()).getItem(position); + if (position == 0) { + notifyFieldChange(MachineProperty.KERNEL, null); + } else if (position == 1) { + browseFileType = FileType.KERNEL; + LimboFileManager.browse(LimboActivity.this, browseFileType, Config.OPEN_IMAGE_FILE_REQUEST_CODE); + mKernel.setSelection(0); + } else if (position > 1) { + notifyFieldChange(MachineProperty.KERNEL, kernel); + } + } - } + public void onNothingSelected(AdapterView parentView) { + } + }); - ); - mFDAenable.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - if(currMachine == null) + mInitrd.setOnItemSelectedListener(new OnItemSelectedListener() { + public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { + if (getMachine() == null) return; + String initrd = (String) ((ArrayAdapter) mInitrd.getAdapter()).getItem(position); + if (position == 0) { + notifyFieldChange(MachineProperty.INITRD, initrd); + } else if (position == 1) { + browseFileType = FileType.INITRD; + LimboFileManager.browse(LimboActivity.this, browseFileType, Config.OPEN_IMAGE_FILE_REQUEST_CODE); + mInitrd.setSelection(0); + } else if (position > 1) { + notifyFieldChange(MachineProperty.INITRD, initrd); + } + } - mFDA.setEnabled(isChecked); + public void onNothingSelected(AdapterView parentView) { + } + }); - currMachine.enableFDA = isChecked; - if (isChecked) { - currMachine.fda_img_path = ""; - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.FDA, - ""); - } else { - currMachine.fda_img_path = null; - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.FDA, - null); - } + mBootDevices.setOnItemSelectedListener(new OnItemSelectedListener() { + public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { + if (getMachine() == null) + return; - triggerUpdateSpinner(mFDA); - updateSummary(false); + String bootDev = (String) ((ArrayAdapter) mBootDevices.getAdapter()).getItem(position); + notifyFieldChange(MachineProperty.BOOT_CONFIG, bootDev); } + public void onNothingSelected(AdapterView parentView) { + } }); - mFDBenable.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - if(currMachine == null) + + mNetConfig.setOnItemSelectedListener(new OnItemSelectedListener() { + public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { + if (getMachine() == null) return; - mFDB.setEnabled(isChecked); + String netcfg = (String) ((ArrayAdapter) mNetConfig.getAdapter()).getItem(position); + notifyFieldChange(MachineProperty.NETCONFIG, netcfg); + if (position > 0 && getMachine().getPaused() == 0 + && MachineController.getInstance().getCurrStatus() != MachineStatus.Running) { + mNetworkCard.setEnabled(true); + mDNS.setEnabled(true); + mHOSTFWD.setEnabled(true); + } else { + mNetworkCard.setEnabled(false); + mDNS.setEnabled(false); + mHOSTFWD.setEnabled(false); + } - currMachine.enableFDB = isChecked; - if (isChecked) { - currMachine.fdb_img_path = ""; - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.FDB, - ""); - } else { - currMachine.fdb_img_path = null; - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.FDB, - null); - } - triggerUpdateSpinner(mFDB); - updateSummary(false); + if (netcfg.equals("TAP")) { + onTap(); + } else if (netcfg.equals("User")) { + LimboActivityCommon.onNetworkUser(LimboActivity.this); + } } + public void onNothingSelected(AdapterView parentView) { + } }); - mSDenable.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - mSD.setEnabled(isChecked); - - currMachine.enableSD = isChecked; - if (isChecked) { - currMachine.sd_img_path = ""; - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.SD, ""); - } else { - currMachine.sd_img_path = null; - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.SD, - null); - } - triggerUpdateSpinner(mSD); - updateSummary(false); + mNetworkCard.setOnItemSelectedListener(new OnItemSelectedListener() { + public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { + if (getMachine() == null) + return; + if (position < 0 || position >= mNetworkCard.getCount()) { + mNetworkCard.setSelection(0); + return; + } + String niccfg = (String) ((ArrayAdapter) mNetworkCard.getAdapter()).getItem(position); + notifyFieldChange(MachineProperty.NICCONFIG, niccfg); } + public void onNothingSelected(final AdapterView parentView) { + } }); - } - - private void enableListeners() { - - - mArch.setOnItemSelectedListener(new OnItemSelectedListener() { - + mVGAConfig.setOnItemSelectedListener(new OnItemSelectedListener() { public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - // your code here - if(currMachine == null) + if (getMachine() == null) return; + String vgacfg = (String) ((ArrayAdapter) mVGAConfig.getAdapter()).getItem(position); + notifyFieldChange(MachineProperty.VGA, vgacfg); + } - String arch = (String) ((ArrayAdapter) mArch.getAdapter()).getItem(position); + public void onNothingSelected(AdapterView parentView) { + } + }); - currMachine.arch = arch; - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.ARCH, arch); + mSoundCard.setOnItemSelectedListener(new OnItemSelectedListener() { + public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { + if (getMachine() == null) + return; + String sndcfg = (String) ((ArrayAdapter) mSoundCard.getAdapter()).getItem(position); + notifyFieldChange(MachineProperty.SOUNDCARD, sndcfg); + } - if (currMachine.arch.equals("ARM") || currMachine.arch.equals("ARM64")) { + public void onNothingSelected(AdapterView parentView) { + } + }); - if (!machineLoaded) { - if(currMachine.machine_type==null) - currMachine.machine_type = "versatilepb"; - populateMachineType(currMachine.machine_type); - if(currMachine.cpu==null) - currMachine.cpu = "Default"; - populateCPUs(currMachine.cpu); - if(currMachine.nic_card==null) - currMachine.nic_card = "Default"; - populateNetDevices(currMachine.nic_card); - } + mDisableACPI.setOnCheckedChangeListener(new OnCheckedChangeListener() { + public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { + if (getMachine() == null) + return; + notifyFieldChange(MachineProperty.DISABLE_ACPI, isChecked); + } + }); - } else if (currMachine.arch.equals("MIPS")) { - - if (!machineLoaded) { - if(currMachine.machine_type==null) - currMachine.machine_type = "malta"; - populateMachineType(currMachine.machine_type); - if(currMachine.cpu==null) - currMachine.cpu = "Default"; - populateCPUs(currMachine.cpu); - - } - - } else if (currMachine.arch.equals("PPC") || currMachine.arch.equals("PPC64")) { - - if (!machineLoaded) { - if(currMachine.machine_type==null) { - if(currMachine.arch.equals("PPC")) - currMachine.machine_type = "g3beige"; - else if (currMachine.arch.equals("PPC64")){ - currMachine.machine_type = "Default"; - } - } - populateMachineType(currMachine.machine_type); - if(currMachine.cpu==null) - currMachine.cpu = "Default"; - populateCPUs(currMachine.cpu); - if(currMachine.nic_card==null) - currMachine.nic_card = "Default"; - populateNetDevices(currMachine.nic_card); - - } - - } else if (currMachine.arch.equals("x86") || currMachine.arch.equals("x64")) { - - if (!machineLoaded) { - if(currMachine.machine_type==null) - currMachine.machine_type = "pc"; - populateMachineType(currMachine.machine_type); - if(currMachine.cpu==null) { - if(currMachine.arch.equals("x86")) - currMachine.cpu = "n270"; - else if(currMachine.arch.equals("x64")) - currMachine.cpu = "phenom"; - } - populateCPUs(currMachine.cpu); - if(currMachine.nic_card==null) - currMachine.nic_card = "Default"; - populateNetDevices(currMachine.nic_card); - } - - } else if (currMachine.arch.equals("m68k")) { - - if (!machineLoaded) { - if(currMachine.machine_type==null) - currMachine.machine_type = "Default"; - populateMachineType(currMachine.machine_type); - if(currMachine.cpu==null) - currMachine.cpu = "Default"; - populateCPUs(currMachine.cpu); - - } - - } else if (currMachine.arch.equals("SPARC") || currMachine.arch.equals("SPARC64") ) { - - if (!machineLoaded) { - if(currMachine.machine_type==null) - currMachine.machine_type = "Default"; - populateMachineType(currMachine.machine_type); - if(currMachine.cpu==null) - currMachine.cpu = "Default"; - populateCPUs(currMachine.cpu); - if(currMachine.nic_card==null) - currMachine.nic_card = "Default"; - populateNetDevices(currMachine.nic_card); - - } - - } - - if (currStatus == VMStatus.Running - || currMachine.arch.equals("ARM") - || currMachine.arch.equals("ARM64") - || currMachine.arch.equals("MIPS") - || currMachine.arch.equals("m68k") || currMachine.arch.equals("PPC") - || currMachine.arch.equals("SPARC") || currMachine.arch.equals("SPARC64") ) { - mDisableACPI.setEnabled(false); - mDisableHPET.setEnabled(false); - mDisableTSC.setEnabled(false); - - } else if (currMachine.arch.equals("x86") || currMachine.arch.equals("x64")) { - - mDisableACPI.setEnabled(true); - mDisableHPET.setEnabled(true); - mDisableTSC.setEnabled(true); - - } - - updateSummary(false); - - machineLoaded = false; + mDisableHPET.setOnCheckedChangeListener(new OnCheckedChangeListener() { + public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { + if (getMachine() == null) + return; + notifyFieldChange(MachineProperty.DISABLE_HPET, isChecked); } + }); - @Override - public void onNothingSelected(AdapterView adapterView) { - + mDisableTSC.setOnCheckedChangeListener(new OnCheckedChangeListener() { + public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { + if (getMachine() == null) + return; + notifyFieldChange(MachineProperty.DISABLE_TSC, isChecked); } - }); - mSnapshot.setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) + mDNS.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View view, boolean hasFocus) { + if (getMachine() == null) return; - - String snapshot_name = (String) ((ArrayAdapter) mSnapshot.getAdapter()).getItem(position); - if ( - position == 0) { - currMachine.snapshot_name = ""; - - Thread thread = new Thread(new Runnable() { - public void run() { - loadMachine(currMachine.machinename, currMachine.snapshot_name); - } - }); - thread.setPriority(Thread.MIN_PRIORITY); - thread.start(); - - mStart.setImageResource(R.drawable.play); - } else if ( - position > 0) { - currMachine.snapshot_name = snapshot_name; - - Thread thread = new Thread(new Runnable() { - public void run() { - loadMachine(currMachine.machinename, currMachine.snapshot_name); - } - }); - thread.setPriority(Thread.MIN_PRIORITY); - thread.start(); - - mStart.setImageResource(R.drawable.play); - enableNonRemovableDeviceOptions(false); - enableRemovableDeviceOptions(false); - mSnapshot.setEnabled(true); + if (!hasFocus) { + setDNSServer(mDNS.getText().toString()); + LimboSettingsManager.setDNSServer(LimboActivity.this, mDNS.getText().toString()); } - updateSummary(false); - } - - public void onNothingSelected(AdapterView parentView) { - } }); - - - mCPU.setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) + mHOSTFWD.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View view, boolean hasFocus) { + if (getMachine() == null) return; - - String cpu = (String) ((ArrayAdapter) mCPU.getAdapter()).getItem(position); - - - currMachine.cpu = cpu; - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.CPU, cpu); - - updateSummary(false); - } - - public void onNothingSelected(AdapterView parentView) { + if (!hasFocus) { + notifyFieldChange(MachineProperty.HOSTFWD, mHOSTFWD.getText().toString()); + } } }); - mMachineType.setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) + mAppend.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View view, boolean hasFocus) { + if (getMachine() == null) return; - - String machineType = (String) ((ArrayAdapter) mMachineType.getAdapter()).getItem(position); - currMachine.machine_type = machineType; - MachineOpenHelper.getInstance(activity).update(currMachine, - MachineOpenHelper.MACHINE_TYPE, machineType); - - updateSummary(false); - - } - - public void onNothingSelected(AdapterView parentView) { + if (!hasFocus) { + notifyFieldChange(MachineProperty.APPEND, mAppend.getText().toString()); + } } }); - mUI.setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) + mExtraParams.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View view, boolean hasFocus) { + if (getMachine() == null) return; - - String ui = (String) ((ArrayAdapter) mUI.getAdapter()).getItem(position); - currMachine.ui = ui; - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.UI, ui); - - if (position == 0) { - mVNCAllowExternal.setEnabled(true); - if (mSnapshot.getSelectedItemPosition() == 0) - mSoundCard.setEnabled(false); - } else { - mVNCAllowExternal.setEnabled(false); - if (mSnapshot.getSelectedItemPosition() == 0) { - if (Config.enable_SDL_sound) - mSoundCard.setEnabled(true); - } + if (!hasFocus) { + notifyFieldChange(MachineProperty.EXTRA_PARAMS, mExtraParams.getText().toString()); } - updateSummary(false); } + }); - public void onNothingSelected(AdapterView parentView) { + OnClickListener resetClickListener = new OnClickListener() { + @Override + public void onClick(View view) { + view.setFocusableInTouchMode(true); + view.setFocusable(true); } - }); + }; - mCPUNum.setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) + mDNS.setOnClickListener(resetClickListener); + mAppend.setOnClickListener(resetClickListener); + mHOSTFWD.setOnClickListener(resetClickListener); + mExtraParams.setOnClickListener(resetClickListener); + mEnableKVM.setOnCheckedChangeListener(new OnCheckedChangeListener() { + public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { + if (getMachine() == null) return; - - final String cpuNum = (String) ((ArrayAdapter) mCPUNum.getAdapter()).getItem(position); - - if (position>0 && currMachine.enableMTTCG!=1 && currMachine.enableKVM!=1 && !firstMTTCGCheck) { - firstMTTCGCheck = true; - DialogInterface.OnClickListener okListener = new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - currMachine.cpuNum = Integer.parseInt(cpuNum); - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.CPUNUM, - cpuNum); - updateSummary(false); - } - }; - - DialogInterface.OnClickListener cancelListener = - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - mCPUNum.setSelection(0); - return; - } - }; - - DialogInterface.OnClickListener helpListener = - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - mCPUNum.setSelection(0); - goToURL(Config.faqLink); - return; - } - }; - - UIUtils.UIAlert(activity, - "Multiple vCPUs", - "Warning! Setting Multiple Virtual CPUs will NOT result in additional performance unless you use KVM or MTTCG. " + - "Your device might even be slow or the Guest OS might hang so it is advised to use 1 CPU. " + - "\n\n" + ((Config.enable_X86 || Config.enable_X86_64) ? - "If your guest OS is not able to boot check option 'Disable TSC' and restart the VM. ": "") - +"Do you want to continue?", - 16, false, "OK", okListener, "Cancel", cancelListener, "vCPU Help", helpListener); - + if (isChecked) { + promptKVM(); } else { - currMachine.cpuNum = Integer.parseInt(cpuNum); - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.CPUNUM, - cpuNum); - + notifyFieldChange(MachineProperty.ENABLE_KVM, isChecked); } - if(position>0 && (Config.enable_X86 || Config.enable_X86_64)) - mDisableTSC.setChecked(true); - else - mDisableTSC.setChecked(false); - - updateSummary(false); } - public void onNothingSelected(AdapterView parentView) { - - } }); - mRamSize.setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) + mEnableMTTCG.setOnCheckedChangeListener(new OnCheckedChangeListener() { + public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { + if (getMachine() == null) return; - - String ram = (String) ((ArrayAdapter) mRamSize.getAdapter()).getItem(position); - currMachine.memory = Integer.parseInt(ram); - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.MEMORY, - ram); - - - - updateSummary(false); - } - - public void onNothingSelected(AdapterView parentView) { - + if (isChecked) { + promptEnableMTTCG(); + } else { + notifyFieldChange(MachineProperty.ENABLE_MTTCG, isChecked); + } } }); - mKernel.setOnItemSelectedListener(new OnItemSelectedListener() { + mKeyboard.setOnItemSelectedListener(new OnItemSelectedListener() { public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) + if (getMachine() == null) return; - - String kernel = (String) ((ArrayAdapter) mKernel.getAdapter()).getItem(position); - if ( - position == 0) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.KERNEL, - null); - currMachine.kernel = null; - } else if ( - position == 1) { - filetype = FileType.KERNEL; - FileManager.browse(activity, filetype, Config.OPEN_IMAGE_FILE_REQUEST_CODE); - mKernel.setSelection(0); - } else if ( - position > 1) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.KERNEL, - kernel); - currMachine.kernel = kernel; - // TODO: If Machine is running eject and set floppy img - } - updateSummary(false); - + String keyboardCfg = (String) ((ArrayAdapter) mKeyboard.getAdapter()).getItem(position); + notifyFieldChange(MachineProperty.KEYBOARD, keyboardCfg); } public void onNothingSelected(AdapterView parentView) { } }); - mInitrd.setOnItemSelectedListener(new OnItemSelectedListener() { + mMouse.setOnItemSelectedListener(new OnItemSelectedListener() { public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) - return; - - String initrd = (String) ((ArrayAdapter) mInitrd.getAdapter()).getItem(position); - if ( - position == 0) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.INITRD, - null); - currMachine.initrd = null; - } else if ( - position == 1) { - filetype = FileType.INITRD; - FileManager.browse(activity, filetype, Config.OPEN_IMAGE_FILE_REQUEST_CODE); - mInitrd.setSelection(0); - } else if ( - position > 1) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.INITRD, - initrd); - currMachine.initrd = initrd; - - } - updateSummary(false); - + String mouseCfg = (String) ((ArrayAdapter) mMouse.getAdapter()).getItem(position); + notifyFieldChange(MachineProperty.MOUSE, mouseCfg); } public void onNothingSelected(AdapterView parentView) { - } }); + } - mHDA.setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - - if(currMachine == null) - return; - - String hda = (String) ((ArrayAdapter) mHDA.getAdapter()).getItem(position); - if ( - position == 0 && mHDAenable.isChecked()) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.HDA, ""); - currMachine.hda_img_path = ""; - } else if ( - (position == 0 || !mHDAenable.isChecked())) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.HDA, null); - currMachine.hda_img_path = null; - } else if ( - position == 1 && mHDAenable.isChecked()) { - promptImageName(activity, FileType.HDA); - mHDA.setSelection(0); - } else if ( - position == 2 && mHDAenable.isChecked()) { - filetype =FileType.HDA; - FileManager.browse(activity, filetype, Config.OPEN_IMAGE_FILE_REQUEST_CODE); - mHDA.setSelection(0); - } else if ( - position > 2 && mHDAenable.isChecked()) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.HDA, hda); - currMachine.hda_img_path = hda; - } - updateSummary(false); + private void setCPUOptions() { + if (MachineController.getInstance().getCurrStatus() != MachineStatus.Running && + (LimboApplication.arch == Config.Arch.x86 || LimboApplication.arch == Config.Arch.x86_64)) { + mDisableACPI.setEnabled(true); + mDisableHPET.setEnabled(true); + mDisableTSC.setEnabled(true); + } else { + mDisableACPI.setEnabled(false); + mDisableHPET.setEnabled(false); + mDisableTSC.setEnabled(false); + } + } + private void setArchOptions() { + if (!machineLoaded) { + populateMachineType(getMachine().getMachineType()); + populateCPUs(getMachine().getCpu()); + populateNetDevices(getMachine().getNetworkCard()); + } + } + private void promptKVM() { + DialogInterface.OnClickListener okListener = new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + notifyFieldChange(MachineProperty.ENABLE_KVM, true); + mEnableMTTCG.setChecked(false); } + }; - public void onNothingSelected(AdapterView parentView) { - - } - }); + DialogInterface.OnClickListener cancelListener = + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + mEnableKVM.setChecked(false); + notifyFieldChange(MachineProperty.ENABLE_KVM, false); + } + }; - mHDB.setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) - return; + DialogInterface.OnClickListener helpListener = + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + mEnableKVM.setChecked(false); + notifyFieldChange(MachineProperty.ENABLE_KVM, false); + LimboActivityCommon.goToURL(LimboActivity.this, Config.kvmLink); + } + }; - String hdb = (String) ((ArrayAdapter) mHDB.getAdapter()).getItem(position); - - if ( - position == 0 && mHDBenable.isChecked()) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.HDB, ""); - currMachine.hdb_img_path = ""; - } else if ( - (position == 0 || !mHDBenable.isChecked())) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.HDB, null); - currMachine.hdb_img_path = null; - } else if ( - position == 1 && mHDBenable.isChecked()) { - promptImageName(activity, FileType.HDB); - mHDB.setSelection(0); - } else if ( - position == 2 && mHDBenable.isChecked()) { - filetype = FileType.HDB; - FileManager.browse(activity, filetype, Config.OPEN_IMAGE_FILE_REQUEST_CODE); - mHDB.setSelection(0); - } else if ( - position > 2 && mHDBenable.isChecked()) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.HDB, hdb); - currMachine.hdb_img_path = hdb; - } - updateSummary(false); + DialogUtils.UIAlert(LimboActivity.this, getString(R.string.EnableKVM), + getString(R.string.EnableKVMWarning), + 16, false, getString(android.R.string.ok), + okListener, getString(android.R.string.cancel), + cancelListener, getString(R.string.KVMHelp), helpListener); + } + private void promptEnableMTTCG() { + DialogInterface.OnClickListener okListener = new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + notifyFieldChange(MachineProperty.ENABLE_MTTCG, true); + mEnableKVM.setChecked(false); } + }; + DialogInterface.OnClickListener cancelListener = + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + notifyFieldChange(MachineProperty.ENABLE_MTTCG, false); + mEnableMTTCG.setChecked(false); + } + }; + DialogInterface.OnClickListener helpListener = + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + mEnableMTTCG.setChecked(false); + notifyFieldChange(MachineProperty.ENABLE_MTTCG, false); + LimboActivityCommon.goToURL(LimboActivity.this, Config.faqLink); + } + }; + DialogUtils.UIAlert(LimboActivity.this, getString(R.string.enableMTTCG), + getString(R.string.enableMTTCGWarning), + 16, false, getString(android.R.string.ok), okListener, + getString(android.R.string.cancel) + , cancelListener, getString(R.string.mttcgHelp), helpListener); + } - public void onNothingSelected(AdapterView parentView) { - + private void promptMultiCPU(final String cpuNum) { + DialogInterface.OnClickListener okListener = new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + notifyFieldChange(MachineProperty.CPUNUM, cpuNum); } - }); - - mHDC.setOnItemSelectedListener(new OnItemSelectedListener() { + }; + DialogInterface.OnClickListener cancelListener = + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + mCPUNum.setSelection(0); + } + }; + DialogInterface.OnClickListener helpListener = + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + mCPUNum.setSelection(0); + LimboActivityCommon.goToURL(LimboActivity.this, Config.faqLink); + } + }; + DialogUtils.UIAlert(LimboActivity.this, getString(R.string.multipleVCPU), + getString(R.string.multipleVCPUWarning) + + ((LimboApplication.arch == Config.Arch.x86_64) ? + getString(R.string.disableTSCInstructions) : "") + + " " + getString(R.string.DoYouWantToContinue), + 16, false, getString(android.R.string.ok), okListener, + getString(android.R.string.cancel), cancelListener, getString(R.string.vCPUHelp), helpListener); + } + + private void setupNonRemovableDiskListeners() { + setupNonRemovableDiskListener(mHDA, mHDAOptions, MachineProperty.HDA, FileType.HDA); + setupNonRemovableDiskListener(mHDB, mHDBOptions, MachineProperty.HDB, FileType.HDB); + setupNonRemovableDiskListener(mHDC, mHDCOptions, MachineProperty.HDC, FileType.HDC); + setupNonRemovableDiskListener(mHDD, mHDDOptions, MachineProperty.HDD, FileType.HDD); + setupSharedFolderDisk(); + } + + private void setupNonRemovableDiskListener(final Spinner diskSpinner, final ImageView diskImage, + final MachineProperty machineDriveName, + final FileType diskFileType) { + diskSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) + if (getMachine() == null) return; - - String hdc = (String) ((ArrayAdapter) mHDC.getAdapter()).getItem(position); - if ( - position == 0 && mHDCenable.isChecked()) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.HDC, ""); - currMachine.hdc_img_path = ""; - } else if ( - (position == 0 || !mHDCenable.isChecked())) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.HDC, null); - currMachine.hdc_img_path = null; - } else if ( - position == 1 && mHDCenable.isChecked()) { - promptImageName(activity, FileType.HDC); - mHDC.setSelection(0); - } else if ( - position == 2 && mHDCenable.isChecked()) { - filetype = FileType.HDC; - FileManager.browse(activity, filetype, Config.OPEN_IMAGE_FILE_REQUEST_CODE); - mHDC.setSelection(0); - } else if ( - position > 2 && mHDCenable.isChecked()) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.HDC, hdc); - currMachine.hdc_img_path = hdc; + String hdName = (String) ((ArrayAdapter) diskSpinner.getAdapter()).getItem(position); + if (position == 1) { + promptImageName(LimboActivity.this, diskFileType); + diskSpinner.setSelection(0); + } else if (position == 2) { + browseFileType = diskFileType; + LimboFileManager.browse(LimboActivity.this, browseFileType, Config.OPEN_IMAGE_FILE_REQUEST_CODE); + diskSpinner.setSelection(0); + } else { + notifyFieldChange(MachineProperty.NON_REMOVABLE_DRIVE, new Object[]{machineDriveName, hdName}); } - updateSummary(false); - } public void onNothingSelected(AdapterView parentView) { - } }); - mHDD.setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) - return; - - String hdd = (String) ((ArrayAdapter) mHDD.getAdapter()).getItem(position); - if ( - position == 0 && mHDDenable.isChecked()) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.HDD, ""); - currMachine.hdd_img_path = ""; - } else if ( - (position == 0 || !mHDDenable.isChecked())) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.HDD, null); - currMachine.hdd_img_path = null; - } else if ( - position == 1 && mHDDenable.isChecked()) { - promptImageName(activity, FileType.HDD); - mHDD.setSelection(0); - } else if ( - position == 2 && mHDDenable.isChecked()) { - filetype = FileType.HDD; - FileManager.browse(activity, filetype, Config.OPEN_IMAGE_FILE_REQUEST_CODE); - mHDD.setSelection(0); - } else if ( - position > 2 && mHDDenable.isChecked()) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.HDD, hdd); - currMachine.hdd_img_path = hdd; - } - updateSummary(false); + diskImage.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + promptDriveInterface(machineDriveName); } + }); + } - public void onNothingSelected(AdapterView parentView) { + private void promptDriveInterface(final MachineProperty machineDriveName) { + if(getMachine() == null) + return; + final String[] items = { + "ide", + "scsi", + "virtio" + }; + final AlertDialog.Builder mBuilder = new AlertDialog.Builder(this); + mBuilder.setTitle(machineDriveName + " " + getString(R.string.Interface)); + int driveInterface = getMachineInterface(machineDriveName, items); + mBuilder.setSingleChoiceItems(items, driveInterface, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int i) { + notifyFieldChange(MachineProperty.MEDIA_INTERFACE, new Object[] {machineDriveName, items[i]}); + dialog.dismiss(); } }); + final AlertDialog alertDialog = mBuilder.create(); + alertDialog.show(); + } + private int getMachineInterface(MachineProperty machineDriveName, String[] items) { + String hdInterfaceStr = null; + switch(machineDriveName) { + case HDA: + hdInterfaceStr = getMachine().getHdaInterface(); + break; + case HDB: + hdInterfaceStr = getMachine().getHdbInterface(); + break; + case HDC: + hdInterfaceStr = getMachine().getHdcInterface(); + break; + case HDD: + hdInterfaceStr = getMachine().getHddInterface(); + break; + case CDROM: + hdInterfaceStr = getMachine().getCDInterface(); + break; + } + for(int i=0; i parentView, View selectedItemView, int position, long id) { - if(currMachine == null) + if (getMachine() == null) return; String shared_folder = (String) ((ArrayAdapter) mSharedFolder.getAdapter()).getItem(position); - if ( - position == 0 && mSharedFolderenable.isChecked()) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.SHARED_FOLDER, ""); - currMachine.shared_folder = ""; - } else if ( - (position == 0 || !mSharedFolderenable.isChecked())) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.SHARED_FOLDER, null); - currMachine.shared_folder = null; - } else if ( - position == 1 && mSharedFolderenable.isChecked()) { - filetype = FileType.SHARED_DIR; - FileManager.browse(activity, filetype, Config.OPEN_SHARED_DIR_REQUEST_CODE); + if (position == 0) { + notifyFieldChange(MachineProperty.SHARED_FOLDER, shared_folder); + } else if (position == 1) { + browseFileType = FileType.SHARED_DIR; + LimboFileManager.browse(LimboActivity.this, browseFileType, Config.OPEN_SHARED_DIR_REQUEST_CODE); mSharedFolder.setSelection(0); - } else if ( - position > 1 && mSharedFolderenable.isChecked()) { - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.SHARED_FOLDER, shared_folder); - currMachine.shared_folder = shared_folder; + } else if (position > 1) { + notifyFieldChange(MachineProperty.SHARED_FOLDER, shared_folder); } - updateSummary(false); } public void onNothingSelected(AdapterView parentView) { - } }); + } + protected synchronized void setDNSServer(String string) { - mHDAenable.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - if(currMachine == null) - return; - - mHDA.setEnabled(isChecked); - if (isChecked) { - currMachine.hda_img_path = ""; - } else { - currMachine.hda_img_path = null; - } - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.HDA, - currMachine.hda_img_path); - - triggerUpdateSpinner(mHDA); - updateSummary(false); - } - - }); - mHDBenable.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - if(currMachine == null) - return; - - mHDB.setEnabled(isChecked); - if (isChecked) { - currMachine.hdb_img_path = ""; - } else { - currMachine.hdb_img_path = null; - } - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.HDB, - currMachine.hdb_img_path); - - triggerUpdateSpinner(mHDB); - updateSummary(false); - } - - }); - mHDCenable.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - if(currMachine == null) - return; - - mHDC.setEnabled(isChecked); - if (isChecked) { - currMachine.hdc_img_path = ""; - mCDenable.setChecked(false); - } else { - currMachine.hdc_img_path = null; - } - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.HDC, - currMachine.hdc_img_path); - - triggerUpdateSpinner(mHDC); - updateSummary(false); - } - - }); - mHDDenable.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - if(currMachine == null) - return; - - mHDD.setEnabled(isChecked); - if (isChecked) { - currMachine.hdd_img_path = ""; - mSharedFolderenable.setChecked(false); - } else { - currMachine.hdd_img_path = null; - } - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.HDD, - currMachine.hdd_img_path); - - triggerUpdateSpinner(mHDD); - updateSummary(false); - } - - }); - - - mSharedFolderenable.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - if(currMachine == null) - return; - - if(isChecked) { - promptSharedFolder(activity); - } - mSharedFolder.setEnabled(isChecked); - if (isChecked) { - currMachine.shared_folder = ""; - mHDDenable.setChecked(false); - } else { - currMachine.shared_folder = null; - } - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.SHARED_FOLDER, - currMachine.shared_folder); - - triggerUpdateSpinner(mSharedFolder); - updateSummary(false); - } - - }); - - - - mBootDevices.setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) - return; - - String bootDev = (String) ((ArrayAdapter) mBootDevices.getAdapter()).getItem(position); - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.BOOT_CONFIG, - bootDev); - currMachine.bootdevice = bootDev; - updateSummary(false); - } - - public void onNothingSelected(AdapterView parentView) { - } - }); - - this.mNetConfig.setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) - return; - - String netfcg = (String) ((ArrayAdapter) mNetConfig.getAdapter()).getItem(position); - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.NET_CONFIG, - netfcg); - currMachine.net_cfg = netfcg; - if (position > 0 && currMachine.paused == 0 - && currStatus != VMStatus.Running) { - mNicCard.setEnabled(true); - mDNS.setEnabled(true); - mHOSTFWD.setEnabled(true); - } else { - mNicCard.setEnabled(false); - mDNS.setEnabled(false); - mHOSTFWD.setEnabled(false); - } - - ApplicationInfo pInfo = null; - - if (netfcg.equals("TAP")) { - onTap(); - } else if (netfcg.equals("User")) { - onNetworkUser(); - } - - updateSummary(false); - - } - - public void onNothingSelected(AdapterView parentView) { - - } - }); - - this.mNicCard.setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) - return; - - if (position < 0 || position >= mNicCard.getCount()) { - mNicCard.setSelection(0); - updateSummary(false); - return; - } - String niccfg = (String) ((ArrayAdapter) mNicCard.getAdapter()).getItem(position); - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.NIC_CONFIG, - niccfg); - currMachine.nic_card = niccfg; - - updateSummary(false); - } - - public void onNothingSelected(final AdapterView parentView) { - - } - }); - - mVGAConfig.setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) - return; - - String vgacfg = (String) ((ArrayAdapter) mVGAConfig.getAdapter()).getItem(position); - - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.VGA, - vgacfg); - currMachine.vga_type = vgacfg; - updateSummary(false); - } - - public void onNothingSelected(AdapterView parentView) { - } - }); - - this.mSoundCard.setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) - return; - - String sndcfg = (String) ((ArrayAdapter) mSoundCard.getAdapter()).getItem(position); - - MachineOpenHelper.getInstance(activity).update(currMachine, - MachineOpenHelper.SOUNDCARD_CONFIG, sndcfg); - currMachine.soundcard = sndcfg; - updateSummary(false); - } - - public void onNothingSelected(AdapterView parentView) { - } - }); - - mHDCacheConfig.setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) - return; - - String hdcfg = (String) ((ArrayAdapter) mHDCacheConfig.getAdapter()).getItem(position); - - MachineOpenHelper.getInstance(activity).update(currMachine, - MachineOpenHelper.HDCACHE_CONFIG, hdcfg); - currMachine.hd_cache = hdcfg; - updateSummary(false); - } - - public void onNothingSelected(AdapterView parentView) { - } - }); - - mDisableACPI.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - if(currMachine == null) - return; - - MachineOpenHelper.getInstance(activity).update(currMachine, - MachineOpenHelper.DISABLE_ACPI, ((isChecked ? 1 : 0) + "")); - currMachine.disableacpi = (isChecked ? 1 : 0); - updateSummary(false); - } - - public void onNothingSelected(AdapterView parentView) { - - } - }); - - mDisableHPET.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - if(currMachine == null) - return; - - MachineOpenHelper.getInstance(activity).update(currMachine, - MachineOpenHelper.DISABLE_HPET, ((isChecked ? 1 : 0) + "")); - currMachine.disablehpet = (isChecked ? 1 : 0); - updateSummary(false); - } - - public void onNothingSelected(AdapterView parentView) { - } - }); - - mDisableTSC.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - if(currMachine == null) - return; - - MachineOpenHelper.getInstance(activity).update(currMachine, - MachineOpenHelper.DISABLE_TSC, ((isChecked ? 1 : 0) + "")); - currMachine.disabletsc = (isChecked ? 1 : 0); - updateSummary(false); - } - - public void onNothingSelected(AdapterView parentView) { - } - }); - - mDNS.setOnFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View view, boolean hasFocus) { - if(currMachine == null) - return; - - if (!hasFocus) { - setDNSServer(mDNS.getText().toString()); - LimboSettingsManager.setDNSServer(activity, mDNS.getText().toString()); - updateSummary(false); - } - } - }); - - mHOSTFWD.setOnFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View view, boolean hasFocus) { - if(currMachine == null) - return; - - if (!hasFocus) { - - currMachine.hostfwd = mHOSTFWD.getText().toString(); - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.HOSTFWD, - mHOSTFWD.getText().toString()); - - updateSummary(false); - } - } - }); - - mAppend.setOnFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View view, boolean hasFocus) { - if(currMachine == null) - return; - - if (!hasFocus) { - currMachine.append = mAppend.getText() + ""; - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.APPEND, - mAppend.getText() + ""); - - updateSummary(false); - } - } - }); - - mExtraParams.setOnFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View view, boolean hasFocus) { - if(currMachine == null) - return; - - if (!hasFocus) { - currMachine.extra_params = mExtraParams.getText() + ""; - MachineOpenHelper.getInstance(activity).update(currMachine, - MachineOpenHelper.EXTRA_PARAMS, mExtraParams.getText() + ""); - - updateSummary(false); - } - } - }); - - - resetClickListener = new OnClickListener() { - @Override - public void onClick(View view) { - view.setFocusableInTouchMode(true); - view.setFocusable(true); - } - }; - - mDNS.setOnClickListener(resetClickListener); - mAppend.setOnClickListener(resetClickListener); - mHOSTFWD.setOnClickListener(resetClickListener); - mExtraParams.setOnClickListener(resetClickListener); - - - mVNCAllowExternal.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - - if (isChecked) { - promptVNCAllowExternal(activity); - } else { - vnc_passwd = null; - vnc_allow_external = 0; - } - updateSummary(false); - - } - - public void onNothingSelected(AdapterView parentView) { - } - }); - - mQMPAllowExternal.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - - if (isChecked) { - promptQMPAllowExternal(activity); - } else { - qmp_allow_external = 0; - } - updateSummary(false); - - } - - public void onNothingSelected(AdapterView parentView) { - } - }); - - mDesktopMode.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - - if (isChecked) { - LimboSettingsManager.setDesktopMode(activity, true); - Config.mouseMode = Config.MouseMode.External; - } else { - LimboSettingsManager.setDesktopMode(activity, false); - Config.mouseMode = Config.MouseMode.Trackpad; - } - updateSummary(false); - - } - - public void onNothingSelected(AdapterView parentView) { - } - }); - - mPrio.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - - if (isChecked) { - promptPrio(activity); - } else { - LimboSettingsManager.setPrio(activity, false); - } - updateSummary(false); - } - }); - mEnableKVM.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - if(currMachine == null) - return; - - if (isChecked) { - DialogInterface.OnClickListener okListener = new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - MachineOpenHelper.getInstance(activity).update(currMachine, - MachineOpenHelper.ENABLE_KVM, 1 + ""); - currMachine.enableKVM = 1; - updateSummary(false); - mEnableMTTCG.setChecked(false); - } - }; - - DialogInterface.OnClickListener cancelListener = - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - MachineOpenHelper.getInstance(activity).update(currMachine, - MachineOpenHelper.ENABLE_KVM, 0 + ""); - mEnableKVM.setChecked(false); - currMachine.enableKVM = 0; - updateSummary(false); - return; - } - }; - - DialogInterface.OnClickListener helpListener = - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - MachineOpenHelper.getInstance(activity).update(currMachine, - MachineOpenHelper.ENABLE_KVM, 0 + ""); - mEnableKVM.setChecked(false); - currMachine.enableKVM = 0; - updateSummary(false); - goToURL(Config.kvmLink); - return; - } - }; - - UIUtils.UIAlert(activity, - "Enable KVM", - "Warning! You'll need an Android Device with the same architecture as the emulated Guest. " + - "Make sure you have installed an Android kernel with KVM support." + - "If you don't know what this is press Cancel now.\n\nIf you experience crashes disable this option. Do you want to continue?", - 16, false, "OK", okListener, "Cancel", cancelListener, "KVM Help", helpListener); - - } else { - MachineOpenHelper.getInstance(activity).update(currMachine, - MachineOpenHelper.ENABLE_KVM, 0 + ""); - currMachine.enableKVM = 0; - - } - updateSummary(false); - } - - }); - - mEnableMTTCG.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - if(currMachine == null) - return; - - if (isChecked) { - DialogInterface.OnClickListener okListener = new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - MachineOpenHelper.getInstance(activity).update(currMachine, - MachineOpenHelper.ENABLE_MTTCG, 1 + ""); - currMachine.enableMTTCG = 1; - updateSummary(false); - mEnableKVM.setChecked(false); - } - }; - - DialogInterface.OnClickListener cancelListener = - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - MachineOpenHelper.getInstance(activity).update(currMachine, - MachineOpenHelper.ENABLE_MTTCG, 0 + ""); - mEnableMTTCG.setChecked(false); - currMachine.enableMTTCG = 0; - updateSummary(false); - return; - } - }; - - DialogInterface.OnClickListener helpListener = - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - MachineOpenHelper.getInstance(activity).update(currMachine, - MachineOpenHelper.ENABLE_MTTCG, 0 + ""); - mEnableMTTCG.setChecked(false); - currMachine.enableMTTCG = 0; - updateSummary(false); - goToURL(Config.faqLink); - return; - } - }; - - - UIUtils.UIAlert(activity, - "Enable MTTCG", - "Warning! Enabling MTTCG under Limbo is not fully tested. " + - "\nYou need to have an Android Devices with multi core CPU. " + - "If you don't know what this is press Cancel." + - "\n\nIf you experience app crashing or freezing disable this option. Do you want to continue?", - 16, false,"OK", okListener, "Cancel", cancelListener, "MTTCG Help", helpListener); - } else { - MachineOpenHelper.getInstance(activity).update(currMachine, - MachineOpenHelper.ENABLE_MTTCG, 0 + ""); - currMachine.enableMTTCG = 1; - - } - - updateSummary(false); - } - - public void onNothingSelected(AdapterView parentView) { - } - }); - - mToolBar.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - - if (isChecked) { - LimboSettingsManager.setAlwaysShowMenuToolbar(activity, true); - } else { - LimboSettingsManager.setAlwaysShowMenuToolbar(activity, false); - } - updateSummary(false); - } - - public void onNothingSelected(AdapterView parentView) { - } - }); - - mFullScreen.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton viewButton, boolean isChecked) { - - if (isChecked) { - LimboSettingsManager.setFullscreen(activity, true); - } else { - LimboSettingsManager.setFullscreen(activity, false); - } - updateSummary(false); - } - - public void onNothingSelected(AdapterView parentView) { - } - }); - - mOrientation.setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - String orientationCfg = (String) ((ArrayAdapter) mOrientation.getAdapter()).getItem(position); - LimboSettingsManager.setOrientationSetting(activity, position); - updateSummary(false); - } - - public void onNothingSelected(AdapterView parentView) { - } - }); - - mKeyboard.setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) - return; - - String keyboardCfg = (String) ((ArrayAdapter) mKeyboard.getAdapter()).getItem(position); - - currMachine.keyboard = keyboardCfg; - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.KEYBOARD, keyboardCfg); - - updateSummary(false); - } - - public void onNothingSelected(AdapterView parentView) { - } - }); - - mMouse.setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(currMachine == null) - return; - - String mouseCfg = (String) ((ArrayAdapter) mMouse.getAdapter()).getItem(position); - String mouseDB = mouseCfg.split(" ")[0]; - currMachine.mouse = mouseDB; - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.MOUSE, mouseDB); - updateSummary(false); - } - - public void onNothingSelected(AdapterView parentView) { - } - }); - } - - protected synchronized void setDNSServer(String string) { - - File resolvConf = new File(Config.getBasefileDir() + "/etc/resolv.conf"); - FileOutputStream fileStream = null; - try { - fileStream = new FileOutputStream(resolvConf); - String str = "nameserver " + string + "\n\n"; - byte[] data = str.getBytes(); - fileStream.write(data); - } catch (Exception ex) { - Log.e(TAG, "Could not write DNS to file: " + ex); - } finally { - if (fileStream != null) - try { - fileStream.close(); - } catch (IOException e) { - - e.printStackTrace(); - } - } - } - - private void disableListeners() { - - if (mMachine == null) - return; - - - mSnapshot.setOnItemSelectedListener(null); - - mUI.setOnItemSelectedListener(null); - mKeyboard.setOnItemSelectedListener(null); - mMouse.setOnItemSelectedListener(null); - mOrientation.setOnItemSelectedListener(null); - mVNCAllowExternal.setOnCheckedChangeListener(null); - mQMPAllowExternal.setOnCheckedChangeListener(null); - mDesktopMode.setOnCheckedChangeListener(null); - mToolBar.setOnCheckedChangeListener(null); - mFullScreen.setOnCheckedChangeListener(null); - - mArch.setOnItemSelectedListener(null); - mMachineType.setOnItemSelectedListener(null); - mCPU.setOnItemSelectedListener(null); - mCPUNum.setOnItemSelectedListener(null); - mRamSize.setOnItemSelectedListener(null); - mDisableACPI.setOnCheckedChangeListener(null); - mDisableHPET.setOnCheckedChangeListener(null); - mDisableTSC.setOnCheckedChangeListener(null); - mEnableKVM.setOnCheckedChangeListener(null); - mEnableMTTCG.setOnCheckedChangeListener(null); - - mHDAenable.setOnCheckedChangeListener(null); - mHDBenable.setOnCheckedChangeListener(null); - mHDCenable.setOnCheckedChangeListener(null); - mHDDenable.setOnCheckedChangeListener(null); - mSharedFolderenable.setOnCheckedChangeListener(null); - mHDA.setOnItemSelectedListener(null); - mHDB.setOnItemSelectedListener(null); - mHDC.setOnItemSelectedListener(null); - mHDD.setOnItemSelectedListener(null); - mSharedFolder.setOnItemSelectedListener(null); - mHDCacheConfig.setOnItemSelectedListener(null); - - - mBootDevices.setOnItemSelectedListener(null); - mKernel.setOnItemSelectedListener(null); - mInitrd.setOnItemSelectedListener(null); - mAppend.setOnFocusChangeListener(null); - - mVGAConfig.setOnItemSelectedListener(null); - - mSoundCard.setOnItemSelectedListener(null); - - mNetConfig.setOnItemSelectedListener(null); - mNicCard.setOnItemSelectedListener(null); - mDNS.setOnFocusChangeListener(null); - mHOSTFWD.setOnFocusChangeListener(null); - - mPrio.setOnCheckedChangeListener(null); - mExtraParams.setOnFocusChangeListener(null); - - } - - /** - * Called when the activity is first created. - */ - @Override - public void onCreate(Bundle savedInstanceState) { - activity = this; - super.onCreate(savedInstanceState); - - clearNotifications(); - setupStrictMode(); - setupFolders(); - setContentView(R.layout.limbo_main); - setupWidgets(); - setupDiskMapping(); - createListeners(); - populateAttributes(); - execTimer(); - checkFirstLaunch(); - setupToolbar(); - checkUpdate(); - checkLog(); - checkAndLoadLibs(); - setupLinks(); - - } - - private void setupLinks() { - - Config.osImages.put("Advanced Tools", new LinksManager.LinkInfo("Advanced Tools", - "Get Full Qwerty Keyboard, Download Manager for ISO and virtual disks images, SSH/FTP Client for network connection", - Config.toolsLink, - LinksManager.LinkType.TOOL)); - } - - private void checkAndLoadLibs() { - if (Config.loadNativeLibsEarly) - if (Config.loadNativeLibsMainThread) - setupNativeLibs(); - else - setupNativeLibsAsync(); - } - - private void clearNotifications() { - NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.cancelAll(); - } - - private void setupDiskMapping() { - - diskMapping.clear(); - addDiskMapping(FileType.HDA, mHDA, mHDAenable, MachineOpenHelper.HDA); - addDiskMapping(FileType.HDB, mHDB, mHDBenable, MachineOpenHelper.HDB); - addDiskMapping(FileType.HDC, mHDC, mHDCenable, MachineOpenHelper.HDC); - addDiskMapping(FileType.HDD, mHDD, mHDDenable, MachineOpenHelper.HDD); - addDiskMapping(FileType.SHARED_DIR, mSharedFolder, mSharedFolderenable, MachineOpenHelper.SHARED_FOLDER); - - addDiskMapping(FileType.CD, mCD, mCDenable, MachineOpenHelper.CDROM); - addDiskMapping(FileType.FDA, mFDA, mFDAenable, MachineOpenHelper.FDA); - addDiskMapping(FileType.FDB, mFDB, mFDBenable, MachineOpenHelper.FDB); - addDiskMapping(FileType.SD, mSD, mSDenable, MachineOpenHelper.SD); - - addDiskMapping(FileType.KERNEL, mKernel, null, MachineOpenHelper.KERNEL); - addDiskMapping(FileType.INITRD, mInitrd, null, MachineOpenHelper.INITRD); - } - - private void addDiskMapping(FileType fileType, Spinner spinner, CheckBox enableCheckBox, String dbColName) { - spinner.setTag(fileType); - - diskMapping.put(fileType, new DiskInfo(spinner, enableCheckBox, dbColName)); - } - - private void setupNativeLibsAsync() { - - Thread thread = new Thread(new Runnable() { - public void run() { - setupNativeLibs(); - } - }); - thread.setPriority(Thread.MIN_PRIORITY); - thread.start(); - - } - - private void createListeners() { - - mMachine.setOnItemSelectedListener(new OnItemSelectedListener() { - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - - if (position == 0) { - enableNonRemovableDeviceOptions(false); - enableRemovableDeviceOptions(false); - mVNCAllowExternal.setEnabled(false); - mQMPAllowExternal.setEnabled(false); - currMachine = null; - } else if (position == 1) { - mMachine.setSelection(0); - promptMachineName(activity); - mVNCAllowExternal.setEnabled(true); - mQMPAllowExternal.setEnabled(true); - - } else { - final String machine = (String) ((ArrayAdapter) mMachine.getAdapter()).getItem(position); - setUserPressed(false); - Thread thread = new Thread(new Runnable() { - public void run() { - loadMachine(machine, ""); - } - }); - thread.setPriority(Thread.MIN_PRIORITY); - thread.start(); - populateSnapshot(); - mVNCAllowExternal.setEnabled(true); - mQMPAllowExternal.setEnabled(true); - - } - updateSummary(false); - } - - public void onNothingSelected(AdapterView parentView) { - - } - }); - - mScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() { - @Override - public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { - savePendingEditText(); - } - }); - - mStart.setOnClickListener(new OnClickListener() { - public void onClick(View view) { - - if (!Config.loadNativeLibsEarly && Config.loadNativeLibsMainThread) { - setupNativeLibs(); - } - - Thread thread = new Thread(new Runnable() { - public void run() { - - if (!Config.loadNativeLibsEarly && !Config.loadNativeLibsMainThread) { - setupNativeLibs(); - } - onStartButton(); - } - }); - thread.setPriority(Thread.MIN_PRIORITY); - thread.start(); - - } - }); - mStop.setOnClickListener(new OnClickListener() { - public void onClick(View view) { - onStopButton(false); - - } - }); - mRestart.setOnClickListener(new OnClickListener() { - public void onClick(View view) { - - onRestartButton(); - - } - }); - } - - private void savePendingEditText() { - View currentView = getCurrentFocus(); - if (currentView != null && currentView instanceof EditText) { - ((EditText) currentView).setFocusable(false); - } - } - - private void checkFirstLaunch() { - Thread t = new Thread(new Runnable() { - public void run() { - - if (LimboSettingsManager.isFirstLaunch(activity)) { - onFirstLaunch(); - } - - } - }); - t.start(); - } - - private void checkLog() { - - Thread t = new Thread(new Runnable() { - public void run() { - - if (LimboSettingsManager.getExitCode(activity) != 1) { - LimboSettingsManager.setExitCode(activity, 1); - runOnUiThread(new Runnable() { - @Override - public void run() { - UIUtils.promptShowLog(activity); - } - }); - } - } - }); - t.start(); - } - - private void setupFolders() { - Thread t = new Thread(new Runnable() { - public void run() { - - Config.cacheDir = getCacheDir().getAbsolutePath(); - Config.storagedir = Environment.getExternalStorageDirectory().toString(); - - // Create Temp folder - File folder = new File(Config.getTmpFolder()); - if (!folder.exists()) - folder.mkdirs(); - - - } - }); - t.start(); - } - - //XXX: sometimes this needs to be called from the main thread otherwise - // qemu crashes when it is started later - public void setupNativeLibs() { - - if (libLoaded) - return; - - //Some devices need stl loaded upfront - //System.loadLibrary("stlport_shared"); - - //Compatibility lib - System.loadLibrary("compat-limbo"); - - //Glib deps - System.loadLibrary("compat-musl"); - - - //Glib - System.loadLibrary("glib-2.0"); - - //Pixman for qemu - System.loadLibrary("pixman-1"); - - //Spice server - if (Config.enable_SPICE) { - System.loadLibrary("crypto"); - System.loadLibrary("ssl"); - System.loadLibrary("spice"); - } - - // //Load SDL library - if (Config.enable_SDL) { - System.loadLibrary("SDL2"); - } - - System.loadLibrary("compat-SDL2-ext"); - - //Limbo needed for vmexecutor - System.loadLibrary("limbo"); - - loadQEMULib(); - - libLoaded = true; - } - - protected void loadQEMULib() { - - } - - public void setupToolbar() { - Toolbar tb = (Toolbar) findViewById(R.id.toolbar); - setSupportActionBar(tb); - - // Get the ActionBar here to configure the way it behaves. - final ActionBar ab = getSupportActionBar(); - ab.setHomeAsUpIndicator(R.drawable.limbo); // set a custom icon for the - // default home button - ab.setDisplayShowHomeEnabled(true); // show or hide the default home - // button - ab.setDisplayHomeAsUpEnabled(true); - ab.setDisplayShowCustomEnabled(true); // enable overriding the default - // toolbar layout - ab.setDisplayShowTitleEnabled(true); // disable the default title - // element here (for centered - // title) - - ab.setTitle(R.string.app_name); - } - - public void checkUpdate() { - Thread tsdl = new Thread(new Runnable() { - public void run() { - // Check for a new Version - - UIUtils.checkNewVersion(activity); - - } - }); - tsdl.start(); - } - - private void setupStrictMode() { - - if (Config.debugStrictMode) { - StrictMode.setThreadPolicy( - new StrictMode.ThreadPolicy.Builder().detectDiskReads().detectDiskWrites().detectNetwork() - //.penaltyDeath() - .penaltyLog().build()); - StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectLeakedSqlLiteObjects() - .detectLeakedClosableObjects().penaltyLog() - // .penaltyDeath() - .build()); - } - - } - - - private void populateAttributes() { - - - Thread t = new Thread(new Runnable() { - public void run() { - favinstance = FavOpenHelper.getInstance(activity); - updateGlobalSettings(); - - runOnUiThread(new Runnable() { - @Override - public void run() { - populateAttributesUI(); - } - }); - } - }); - t.start(); - } - - private void populateAttributesUI() { - - this.populateMachines(null); - this.populateArch(); - this.populateMachineType(null); - this.populateCPUs(null); - this.populateCPUNum(); - this.populateRAM(); - this.populateDisks(); - this.populateBootDevices(); - this.populateNet(); - this.populateNetDevices(null); - this.populateVGA(); - this.populateSoundcardConfig(); - this.populateHDCacheConfig(); - this.populateSnapshot(); - this.populateUI(); - this.populateOrientation(); - this.populateKeyboardLayout(); - this.populateMouse(); - } - - private void populateDisks() { - - //disks - this.populateDiskAdapter(mHDA, FileType.HDA, true); - this.populateDiskAdapter(mHDB, FileType.HDB, true); - this.populateDiskAdapter(mHDC, FileType.HDC, true); - this.populateDiskAdapter(mHDD, FileType.HDD, true); - this.populateDiskAdapter(mSharedFolder, FileType.SHARED_DIR, false); - - //removable - this.populateDiskAdapter(mCD, FileType.CD, false); - this.populateDiskAdapter(mFDA, FileType.FDA, false); - this.populateDiskAdapter(mFDB, FileType.FDB, false); - this.populateDiskAdapter(mSD, FileType.SD, false); - - //boot - this.populateDiskAdapter(mKernel, FileType.KERNEL, false); - this.populateDiskAdapter(mInitrd, FileType.INITRD, false); - - } - - public void onFirstLaunch() { - onLicense(); - } - - private void createMachine(String machineValue) { - - if (MachineOpenHelper.getInstance(activity).getMachine(machineValue, "") != null) { - UIUtils.toastShort(activity, "VM Name \"" + machineValue + "\" exists please choose another name!"); - return; - } - - showDownloadLinks(); - - currMachine = new Machine(machineValue); - - // We set SDL as default interface - currMachine.ui = "SDL"; - - // default settings for each architecture, this helps user with the most common setup - if (Config.enable_X86 || Config.enable_X86_64) { - currMachine.arch = "x86"; - currMachine.cpu = "n270"; - currMachine.machine_type = "pc"; - currMachine.nic_card = "Default"; - currMachine.disabletsc = 1; - } else if (Config.enable_ARM || Config.enable_ARM64) { - currMachine.arch = "ARM"; - currMachine.machine_type = "versatilepb"; - currMachine.cpu = "Default"; - currMachine.nic_card = "Default"; - } else if (Config.enable_MIPS) { - currMachine.arch = "MIPS"; - currMachine.machine_type = "malta"; - } else if (Config.enable_PPC || Config.enable_PPC64) { - currMachine.arch = "PPC"; - currMachine.machine_type = "g3beige"; - currMachine.nic_card = "Default"; - } else if (Config.enable_m68k) { - currMachine.arch = "m68k"; - currMachine.machine_type = "Default"; - } else if (Config.enable_sparc || Config.enable_sparc64) { - currMachine.arch = "SPARC"; - currMachine.vga_type="cg3"; - currMachine.machine_type = "Default"; - currMachine.nic_card = "Default"; - } - - MachineOpenHelper.getInstance(activity).insertMachine(currMachine); - - populateMachines(machineValue); - - if (LimboActivity.currMachine != null && currMachine.cpu != null - && (currMachine.cpu.endsWith("(arm)") || currMachine.arch.startsWith("MIPS") - || currMachine.arch.startsWith("PPC") || currMachine.arch.startsWith("m68k")) - - ) { - mMachineType.setEnabled(true); // Disabled for now - } - enableNonRemovableDeviceOptions(true); - enableRemovableDeviceOptions(true); - this.mVNCAllowExternal.setEnabled(true); - this.mQMPAllowExternal.setEnabled(true); - - } - - protected void showDownloadLinks() { - - if (!Config.osImages.isEmpty()) { - OSDialogBox oses = new OSDialogBox(activity); - oses.setCanceledOnTouchOutside(false); - oses.setCancelable(false); - oses.show(); - } - } - - private void onDeleteMachine() { - if (currMachine == null) { - UIUtils.toastShort(this, "Select a machine first!"); - return; - } - Thread t = new Thread(new Runnable() { - public void run() { - MachineOpenHelper.getInstance(activity).deleteMachineDB(currMachine); - final String name = currMachine.machinename; - runOnUiThread(new Runnable() { - @Override - public void run() { - disableListeners(); - disableRemovableDiskListeners(); - mMachine.setSelection(0); - currMachine = null; - populateAttributes(); - UIUtils.toastShort(activity, "Machine " + name + " deleted"); - enableListeners(); - enableRemovableDiskListeners(); - } - }); - - } - }); - t.start(); - - } - - private void onExportMachines() { - promptExportName(this); - } - - public void exportMachines(String filePath) { - progDialog = ProgressDialog.show(activity, "Please Wait", "Exporting Machines...", true); - ExportMachines exporter = new ExportMachines(); - exporter.exportFilePath = filePath; - exporter.execute(); - - } - - private void onImportMachines() { - - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("Import Machines"); - - LinearLayout mLayout = new LinearLayout(this); - mLayout.setOrientation(LinearLayout.VERTICAL); - mLayout.setPadding(20, 20, 20, 20); - - TextView imageNameView = new TextView(activity); - imageNameView.setVisibility(View.VISIBLE); - imageNameView.setText( - "Choose the .csv file you want to import.\n" - + "WARNING: Any machine with the same name will be replaced!\n" - ); - - LinearLayout.LayoutParams searchViewParams = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - mLayout.addView(imageNameView, searchViewParams); - alertDialog.setView(mLayout); - - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - promptForImportDir(); - } - }); - alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - - return; - } - }); - alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - - return; - - } - }); - alertDialog.show(); - - } - - private void promptForImportDir() { - FileManager.browse(this, FileType.IMPORT_FILE, Config.OPEN_IMPORT_FILE_REQUEST_CODE); - } - - - public void importFile(String importFilePath) { - // For each line create a Machine - progDialog = ProgressDialog.show(activity, "Please Wait", "Importing Machines...", true); - disableListeners(); - disableRemovableDiskListeners(); - currMachine = null; - mMachine.setSelection(0); - ImportMachines importer = new ImportMachines(); - importer.importFilePath = importFilePath; - importer.execute(); - } - - private void onLicense() { - PackageInfo pInfo = null; - - try { - pInfo = getPackageManager().getPackageInfo(getClass().getPackage().getName(), PackageManager.GET_META_DATA); - } catch (NameNotFoundException e) { - e.printStackTrace(); - return; - } - - final PackageInfo finalPInfo = pInfo; - runOnUiThread(new Runnable() { - @Override - public void run() { - try { - UIAlertLicense(Config.APP_NAME + " v" + finalPInfo.versionName, - FileUtils.LoadFile(activity, "LICENSE", false), - activity); - } catch (IOException e) { - - e.printStackTrace(); - } - } - }); - - } - - public void exit() { - onStopButton(true); - } - - private void unlockRemovableDevices(boolean flag) { - - mCDenable.setEnabled(flag); - mFDAenable.setEnabled(flag); - mFDBenable.setEnabled(flag); - mSDenable.setEnabled(flag); - - } - - private void enableRemovableDeviceOptions(boolean flag) { - - unlockRemovableDevices(flag); - enableRemovableDiskValues(flag); - } - - private void enableRemovableDiskValues(boolean flag) { - mCD.setEnabled(flag && mCDenable.isChecked()); - mFDA.setEnabled(flag && mFDAenable.isChecked()); - mFDB.setEnabled(flag && mFDBenable.isChecked()); - mSD.setEnabled(flag && mSDenable.isChecked()); - } - - private void enableNonRemovableDeviceOptions(boolean flag) { - - //snapshot - this.mSnapshot.setEnabled(flag); - - - //ui - this.mUI.setEnabled(flag); - this.mKeyboard.setEnabled(Config.enableKeyboardLayoutOption && flag); - this.mMouse.setEnabled(Config.enableMouseOption && flag); - //XXX: disabled for now, use the Emulated mouse property to fix mouse -// this.mDesktopMode.setEnabled(false); - - // Everything Except removable devices - this.mArch.setEnabled(flag); - this.mMachineType.setEnabled(flag); - this.mCPU.setEnabled(flag); - this.mCPUNum.setEnabled(flag); - this.mRamSize.setEnabled(flag); - this.mEnableKVM.setEnabled(flag && (Config.enable_KVM || !flag)); - this.mEnableMTTCG.setEnabled(flag && (Config.enableMTTCG || !flag)); - - //lock drives - mHDAenable.setEnabled(flag); - mHDBenable.setEnabled(flag); - mHDCenable.setEnabled(flag); - mHDDenable.setEnabled(flag); - mSharedFolderenable.setEnabled(flag); - - //drives - mHDA.setEnabled(flag && mHDAenable.isChecked()); - mHDB.setEnabled(flag && mHDBenable.isChecked()); - mHDC.setEnabled(flag && mHDCenable.isChecked()); - mHDD.setEnabled(flag && mHDDenable.isChecked()); - mSharedFolder.setEnabled(flag && mSharedFolderenable.isChecked()); - mHDCacheConfig.setEnabled(flag && (Config.enableHDCache || !flag)); - - //boot - this.mBootDevices.setEnabled(flag); - this.mKernel.setEnabled(flag); - this.mInitrd.setEnabled(flag); - this.mAppend.setEnabled(flag); - - //graphics - this.mVGAConfig.setEnabled(flag); - - //audio - if (Config.enable_SDL_sound - && currMachine != null && currMachine.ui != null - && currMachine.ui.equals("SDL") && currMachine.paused == 0) - this.mSoundCard.setEnabled(flag); - else - this.mSoundCard.setEnabled(false); - - //net - this.mNetConfig.setEnabled(flag); - this.mNicCard.setEnabled(flag && mNetConfig.getSelectedItemPosition() > 0); - this.mDNS.setEnabled(flag && mNetConfig.getSelectedItemPosition() > 0); - this.mHOSTFWD.setEnabled(flag && mNetConfig.getSelectedItemPosition() > 0); - - //advanced - this.mDisableACPI.setEnabled(flag); - this.mDisableHPET.setEnabled(flag); - this.mDisableTSC.setEnabled(flag); - this.mPrio.setEnabled(flag); - this.mExtraParams.setEnabled(flag); - - } - - // Main event function - // Retrives values from saved preferences - private void onStartButton() { - - if (this.mMachine.getSelectedItemPosition() == 0 || this.currMachine == null ) { - UIUtils.toastShort(activity, "Select or Create a Virtual Machine first"); - return; - } - try { - validateFiles(); - } catch (Exception ex) { - UIUtils.toastLong(this, ex.toString()); - return; - } - if (currMachine.snapshot_name != null && !currMachine.snapshot_name.toLowerCase().equals("none") - && !currMachine.snapshot_name.toLowerCase().equals("") && currMachine.soundcard != null - && !currMachine.soundcard.toLowerCase().equals("none") && mUI.getSelectedItemPosition() != 1) { - UIUtils.toastLong(getApplicationContext(), - "Snapshot was saved with soundcard enabled please use SDL \"User Interface\""); - return; - } - - if (currMachine != null && currMachine.cpu != null && currMachine.cpu.endsWith("(arm)") - && (currMachine.kernel == null || currMachine.kernel.equals(""))) { - UIUtils.toastLong(LimboActivity.this, "Couldn't find a Kernel image\nPlease attach a Kernel image first!"); - return; - } - - if (currMachine != null && currMachine.machine_type != null && currMachine.cpu.endsWith("(arm)") - && currMachine.machine_type.equals("None")) { - UIUtils.toastLong(LimboActivity.this, "Please select an ARM machine type first!"); - return; - } - - - if (vmexecutor == null) { - - try { - vmexecutor = new VMExecutor(currMachine, this); - } catch (Exception ex) { - UIUtils.toastLong(activity, "Error: " + ex); - return; - - } - } - if (mCDenable.isChecked() && vmexecutor.cd_iso_path == null) - vmexecutor.cd_iso_path = ""; - if (mFDAenable.isChecked() && vmexecutor.fda_img_path == null) - vmexecutor.fda_img_path = ""; - if (mFDBenable.isChecked() && vmexecutor.fdb_img_path == null) - vmexecutor.fdb_img_path = ""; - if (Config.enableFlashMemoryImages && mSDenable.isChecked() && vmexecutor.sd_img_path == null) - vmexecutor.sd_img_path = ""; - - // Global settings - - // dns - vmexecutor.dns_addr = mDNS.getText().toString(); - - // Append only when kernel is set - - if (currMachine.kernel != null && !currMachine.kernel.equals("")) - vmexecutor.append = mAppend.getText().toString(); - - vmexecutor.paused = currMachine.paused; - - if (!vmStarted) { - UIUtils.toastShort(LimboActivity.this, "Starting VM"); - //XXX: make sure that bios files are installed in case we ran out of space in the last - // run - FileInstaller.installFiles(activity, false); - } else { - UIUtils.toastShort(LimboActivity.this, "Connecting to VM"); - } - - if (mUI.getSelectedItemPosition() == 0) { // VNC - vmexecutor.enableqmp = 1; // We enable qemu monitor - startVNC(); - //we don't flag the VNC as started yet because we need - // to send cont via QEMU console first - } else if (mUI.getSelectedItemPosition() == 1) { // SDL - // XXX: We need to enable qmp server to be able to save the state - // We could do it via the Monitor but SDL for Android - // doesn't support multiple Windows - vmexecutor.enableqmp = 1; - startSDL(); - currMachine.paused = 0; - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.PAUSED, - 0 + ""); - } else if (mUI.getSelectedItemPosition() == 2) { // SPICE - startSPICE(); - currMachine.paused = 0; - MachineOpenHelper.getInstance(activity).update(currMachine, MachineOpenHelper.PAUSED, - 0 + ""); - } - - runOnUiThread(new Runnable() { - @Override - public void run() { - mQMPAllowExternal.setEnabled(false); - - if (vmStarted) { - //do nothing - - } else if (vmexecutor.paused == 1) { - UIUtils.toastShort(LimboActivity.this, "VM Resuming"); - } else { -// if (!vmStarted) { -// UIUtils.toastLong(LimboActivity.this, "VM Started\nPause the VM instead so you won't have to boot again!"); -// } - enableNonRemovableDeviceOptions(false); - mStart.setImageResource(R.drawable.play); - } - } - }); - - new Handler(Looper.getMainLooper()).post(new Runnable() { - public void run() { - mMachine.setEnabled(false); - } - }); - - } - - private String getLanguageCode(int index) { - // TODO: Add more languages from /assets/roms/keymaps - switch (index) { - case 0: - return "en-us"; - case 1: - return "es"; - case 2: - return "fr"; - } - return null; - } - - public void startSDL() { - - Thread tsdl = new Thread(new Runnable() { - public void run() { - startsdl(); - } - }); - if (mPrio.isChecked()) - tsdl.setPriority(Thread.MAX_PRIORITY); - tsdl.start(); - } - - private void startVNC() { - - VncCanvas.retries = 0; - if (!vmStarted) { - - Thread tvm = new Thread(new Runnable() { - public void run() { - startvm(LimboActivity.this, Config.UI_VNC); - } - }); - if (mPrio.isChecked()) - tvm.setPriority(Thread.MAX_PRIORITY); - tvm.start(); - } else { - activity.startvnc(); - } - - - } - - private void startSPICE() { - - if (!vmStarted) { - - Thread tvm = new Thread(new Runnable() { - public void run() { - startvm(LimboActivity.this, Config.UI_SPICE); - } - }); - if (mPrio.isChecked()) - tvm.setPriority(Thread.MAX_PRIORITY); - tvm.start(); - } - - } - - private boolean validateFiles() { - - - if (!FileUtils.fileValid(this, currMachine.hda_img_path) - || !FileUtils.fileValid(this, currMachine.hdb_img_path) - || !FileUtils.fileValid(this, currMachine.hdc_img_path) - || !FileUtils.fileValid(this, currMachine.hdd_img_path) - || !FileUtils.fileValid(this, currMachine.fda_img_path) - || !FileUtils.fileValid(this, currMachine.fdb_img_path) - || !FileUtils.fileValid(this, currMachine.sd_img_path) - || !FileUtils.fileValid(this, currMachine.cd_iso_path) - || !FileUtils.fileValid(this, currMachine.kernel) - || !FileUtils.fileValid(this, currMachine.initrd) - ) - return false; - - return true; - } - - private void onStopButton(boolean exit) { - stopVM(exit); - } - - private void onRestartButton() { - - execTimer(); - - if (vmexecutor == null) { - if (this.currMachine != null && this.currMachine.paused == 1) { - promptDiscardVMState(); - return; - } else { - UIUtils.toastShort(LimboActivity.this, "VM not running"); - return; - } - } - - Machine.resetVM(activity); - } - - private void onSaveButton() { - - // TODO: This probably has no effect - Thread t = new Thread(new Runnable() { - public void run() { - promptStateName(activity); - } - }); - t.start(); - } - - private void onResumeButton() { - - // TODO: This probably has no effect - Thread t = new Thread(new Runnable() { - public void run() { - resumevm(); - } - }); - t.start(); - } - - public 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); - } - } - - // Setting up the UI - public void setupWidgets() { - - setupSections(); - - this.mScrollView = findViewById(R.id.scroll_view); - - this.mStatus = (ImageView) findViewById(R.id.statusVal); - this.mStatus.setImageResource(R.drawable.off); - this.mStatusText = (TextView) findViewById(R.id.statusStr); - - mStart = (ImageButton) findViewById(R.id.startvm); - mStop = (ImageButton) findViewById(R.id.stopvm); - mRestart = (ImageButton) findViewById(R.id.restartvm); - - //Machine - this.mMachine = (Spinner) findViewById(R.id.machineval); - - //snapshots not used currently - this.mSnapshot = (Spinner) findViewById(R.id.snapshotval); - - //ui - if (!Config.enable_SDL) - this.mUI.setEnabled(false); - this.mVNCAllowExternal = (CheckBox) findViewById(R.id.vncexternalval); - mVNCAllowExternal.setChecked(false); - this.mQMPAllowExternal = (CheckBox) findViewById(R.id.qmpexternalval); - mQMPAllowExternal.setChecked(false); - - this.mDesktopMode = (CheckBox) findViewById(R.id.desktopmodeval); - this.mKeyboard = (Spinner) findViewById(R.id.keyboardval); - this.mMouse = (Spinner) findViewById(R.id.mouseval); - this.mOrientation = (Spinner) findViewById(R.id.orientationval); - this.mToolBar = (CheckBox) findViewById(R.id.showtoolbarval); - this.mFullScreen = (CheckBox) findViewById(R.id.fullscreenval); - - //cpu/board - this.mCPU = (Spinner) findViewById(R.id.cpuval); - this.mArch = (Spinner) findViewById(R.id.archtypeval); - this.mMachineType = (Spinner) findViewById(R.id.machinetypeval); - this.mCPUNum = (Spinner) findViewById(R.id.cpunumval); - this.mUI = (Spinner) findViewById(R.id.uival); - this.mRamSize = (Spinner) findViewById(R.id.rammemval); - this.mEnableKVM = (CheckBox) findViewById(R.id.enablekvmval); - this.mEnableMTTCG = (CheckBox) findViewById(R.id.enablemttcgval); - this.mDisableACPI = (CheckBox) findViewById(R.id.acpival); - this.mDisableHPET = (CheckBox) findViewById(R.id.hpetval); - this.mDisableTSC = (CheckBox) findViewById(R.id.tscval); - - //disks - this.mHDAenable = (CheckBox) findViewById(R.id.hdimgcheck); - this.mHDBenable = (CheckBox) findViewById(R.id.hdbimgcheck); - this.mHDCenable = (CheckBox) findViewById(R.id.hdcimgcheck); - this.mHDDenable = (CheckBox) findViewById(R.id.hddimgcheck); - this.mHDA = (Spinner) findViewById(R.id.hdimgval); - this.mHDB = (Spinner) findViewById(R.id.hdbimgval); - this.mHDC = (Spinner) findViewById(R.id.hdcimgval); - this.mHDD = (Spinner) findViewById(R.id.hddimgval); - - LinearLayout sharedFolderLayout = (LinearLayout) findViewById(R.id.sharedfolderl); - if(!Config.enableSharedFolder) - sharedFolderLayout.setVisibility(View.GONE); - this.mSharedFolderenable = (CheckBox) findViewById(R.id.sharedfoldercheck); - this.mSharedFolder = (Spinner) findViewById(R.id.sharedfolderval); - - - this.mHDCacheConfig = (Spinner) findViewById(R.id.hdcachecfgval); - this.mHDCacheConfig.setEnabled(false); // Disabled for now - - //Removable storage - this.mCD = (Spinner) findViewById(R.id.cdromimgval); - this.mFDA = (Spinner) findViewById(R.id.floppyimgval); - this.mFDB = (Spinner) findViewById(R.id.floppybimgval); - if(!Config.enableEmulatedFloppy) { - LinearLayout mFDALayout = (LinearLayout) findViewById(R.id.floppyimgl); - mFDALayout.setVisibility(View.GONE); - LinearLayout mFDBLayout = (LinearLayout) findViewById(R.id.floppybimgl); - mFDBLayout.setVisibility(View.GONE); - } - this.mSD = (Spinner) findViewById(R.id.sdcardimgval); - if(!Config.enableEmulatedSDCard) { - LinearLayout mSDCardLayout = (LinearLayout) findViewById(R.id.sdcardimgl); - mSDCardLayout.setVisibility(View.GONE); - } - this.mCDenable = (CheckBox) findViewById(R.id.cdromimgcheck); - this.mFDAenable = (CheckBox) findViewById(R.id.floppyimgcheck); - this.mFDBenable = (CheckBox) findViewById(R.id.floppybimgcheck); - this.mSDenable = (CheckBox) findViewById(R.id.sdcardimgcheck); - - //boot - this.mBootDevices = (Spinner) findViewById(R.id.bootfromval); - this.mKernel = (Spinner) findViewById(R.id.kernelval); - this.mInitrd = (Spinner) findViewById(R.id.initrdval); - this.mAppend = (EditText) findViewById(R.id.appendval); - - //display - this.mVGAConfig = (Spinner) findViewById(R.id.vgacfgval); - - //sound - this.mSoundCard = (Spinner) findViewById(R.id.soundcfgval); - - //network - this.mNetConfig = (Spinner) findViewById(R.id.netcfgval); - this.mNicCard = (Spinner) findViewById(R.id.netDevicesVal); - this.mDNS = (EditText) findViewById(R.id.dnsval); - setDefaultDNServer(); - this.mHOSTFWD = (EditText) findViewById(R.id.hostfwdval); - - // advanced - this.mPrio = (CheckBox) findViewById(R.id.prioval); - this.mExtraParams = (EditText) findViewById(R.id.extraparamsval); - - disableFeatures(); - enableRemovableDeviceOptions(false); - enableNonRemovableDeviceOptions(false); - - } - - private void disableFeatures() { - - LinearLayout mAudioSectionLayout = (LinearLayout) findViewById(R.id.audiosectionl); - if(!Config.enable_SDL_sound) { - mAudioSectionLayout.setVisibility(View.GONE); - } - - LinearLayout mDisableTSCLayout = (LinearLayout) findViewById(R.id.tscl); - LinearLayout mDisableACPILayout = (LinearLayout) findViewById(R.id.acpil); - LinearLayout mDisableHPETLayout = (LinearLayout) findViewById(R.id.hpetl); - LinearLayout mEnableKVMLayout = (LinearLayout) findViewById(R.id.kvml); - - if(!Config.enable_X86 && !Config.enable_X86_64) { - mDisableTSCLayout.setVisibility(View.GONE); - mDisableACPILayout.setVisibility(View.GONE); - mDisableHPETLayout.setVisibility(View.GONE); - } - if(!Config.enable_X86 && !Config.enable_X86_64 - && !Config.enable_ARM && !Config.enable_ARM64) { - mEnableKVMLayout.setVisibility(View.GONE); - } - } - - private void setDefaultDNServer() { - // TODO Auto-generated method stub - Thread thread = new Thread(new Runnable() { - public void run() { - final String defaultDNSServer = LimboSettingsManager.getDNSServer(activity); - new Handler(Looper.getMainLooper()).post(new Runnable() { - public void run() { - // Code here will run in UI thread - mDNS.setText(defaultDNSServer); - } - }); - } - }); - thread.setPriority(Thread.MIN_PRIORITY); - thread.start(); - - } - - private void setupSections() { - - if (Config.collapseSections) { - mCPUSectionDetails = (LinearLayout) findViewById(R.id.cpusectionDetails); - mCPUSectionDetails.setVisibility(View.GONE); - mCPUSectionSummary = (TextView) findViewById(R.id.cpusectionsummaryStr); - mCPUSectionHeader = (LinearLayout) findViewById(R.id.cpusectionheaderl); - mCPUSectionHeader.setOnClickListener(new OnClickListener() { - public void onClick(View view) { - disableListeners(); - disableRemovableDiskListeners(); - toggleVisibility(mCPUSectionDetails); - enableListenersDelayed(500); - } - }); - - mStorageSectionDetails = (LinearLayout) findViewById(R.id.storagesectionDetails); - mStorageSectionDetails.setVisibility(View.GONE); - mStorageSectionSummary = (TextView) findViewById(R.id.storagesectionsummaryStr); - mStorageSectionHeader = (LinearLayout) findViewById(R.id.storageheaderl); - mStorageSectionHeader.setOnClickListener(new OnClickListener() { - public void onClick(View view) { - disableListeners(); - disableRemovableDiskListeners(); - toggleVisibility(mStorageSectionDetails); - enableListenersDelayed(500); - } - }); - - mUserInterfaceSectionDetails = (LinearLayout) findViewById(R.id.userInterfaceDetails); - mUserInterfaceSectionDetails.setVisibility(View.GONE); - mUISectionSummary = (TextView) findViewById(R.id.uisectionsummaryStr); - mUserInterfaceSectionHeader = (LinearLayout) findViewById(R.id.userinterfaceheaderl); - mUserInterfaceSectionHeader.setOnClickListener(new OnClickListener() { - public void onClick(View view) { - disableListeners(); - disableRemovableDiskListeners(); - toggleVisibility(mUserInterfaceSectionDetails); - enableListenersDelayed(500); - } - }); - - - mRemovableStorageSectionDetails = (LinearLayout) findViewById(R.id.removableStoragesectionDetails); - mRemovableStorageSectionDetails.setVisibility(View.GONE); - mRemovableStorageSectionSummary = (TextView) findViewById(R.id.removablesectionsummaryStr); - mRemovableStorageSectionHeader = (LinearLayout) findViewById(R.id.removablestorageheaderl); - mRemovableStorageSectionHeader.setOnClickListener(new OnClickListener() { - public void onClick(View view) { - disableListeners(); - disableRemovableDiskListeners(); - toggleVisibility(mRemovableStorageSectionDetails); - enableListenersDelayed(500); - } - }); - - mGraphicsSectionDetails = (LinearLayout) findViewById(R.id.graphicssectionDetails); - mGraphicsSectionDetails.setVisibility(View.GONE); - mGraphicsSectionSummary = (TextView) findViewById(R.id.graphicssectionsummaryStr); - mGraphicsSectionHeader = (LinearLayout) findViewById(R.id.graphicsheaderl); - mGraphicsSectionHeader.setOnClickListener(new OnClickListener() { - public void onClick(View view) { - disableListeners(); - disableRemovableDiskListeners(); - toggleVisibility(mGraphicsSectionDetails); - enableListenersDelayed(500); - } - }); - mAudioSectionDetails = (LinearLayout) findViewById(R.id.audiosectionDetails); - mAudioSectionDetails.setVisibility(View.GONE); - mAudioSectionSummary = (TextView) findViewById(R.id.audiosectionsummaryStr); - mAudioSectionHeader = (LinearLayout) findViewById(R.id.audioheaderl); - mAudioSectionHeader.setOnClickListener(new OnClickListener() { - public void onClick(View view) { - disableListeners(); - disableRemovableDiskListeners(); - toggleVisibility(mAudioSectionDetails); - enableListenersDelayed(500); - } - }); - - mNetworkSectionDetails = (LinearLayout) findViewById(R.id.networksectionDetails); - mNetworkSectionDetails.setVisibility(View.GONE); - mNetworkSectionSummary = (TextView) findViewById(R.id.networksectionsummaryStr); - mNetworkSectionHeader = (LinearLayout) findViewById(R.id.networkheaderl); - mNetworkSectionHeader.setOnClickListener(new OnClickListener() { - public void onClick(View view) { - disableListeners(); - disableRemovableDiskListeners(); - toggleVisibility(mNetworkSectionDetails); - enableListenersDelayed(500); - } - }); - - mBootSectionDetails = (LinearLayout) findViewById(R.id.bootsectionDetails); - mBootSectionDetails.setVisibility(View.GONE); - mBootSectionSummary = (TextView) findViewById(R.id.bootsectionsummaryStr); - mBootSectionHeader = (LinearLayout) findViewById(R.id.bootheaderl); - mBootSectionHeader.setOnClickListener(new OnClickListener() { - public void onClick(View view) { - disableListeners(); - disableRemovableDiskListeners(); - toggleVisibility(mBootSectionDetails); - enableListenersDelayed(500); - } - }); - - mAdvancedSectionDetails = (LinearLayout) findViewById(R.id.advancedSectionDetails); - mAdvancedSectionDetails.setVisibility(View.GONE); - mAdvancedSectionSummary = (TextView) findViewById(R.id.advancedsectionsummaryStr); - mAdvancedSectionHeader = (LinearLayout) findViewById(R.id.advancedheaderl); - mAdvancedSectionHeader.setOnClickListener(new OnClickListener() { - public void onClick(View view) { - disableListeners(); - disableRemovableDiskListeners(); - toggleVisibility(mAdvancedSectionDetails); - enableListenersDelayed(500); - } - }); - } - } - - private void enableListenersDelayed(int msecs) { - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - enableListeners(); - enableRemovableDiskListeners(); - } - },msecs); - } - - public void updateUISummary(boolean clear) { - if (clear || currMachine == null || mMachine.getSelectedItemPosition() < 2) - mUISectionSummary.setText(""); - else { - String text = currMachine.ui - + ", Orientation: " + mOrientation.getSelectedItem().toString() - + ", Keyboard: " + mKeyboard.getSelectedItem().toString() - + ", Mouse: " + mMouse.getSelectedItem().toString() - + (mToolBar.isChecked() ? ", Toolbar On" : "") - + (mFullScreen.isChecked() ? ", Fullscreen" : "") - + (mVNCAllowExternal.isChecked() ? ", External VNC Server: On" : "") - + (mQMPAllowExternal.isChecked() ? ", External QMP Server: On" : "") - + (mDesktopMode.isChecked() ? ", Desktop Mode" : ""); - - mUISectionSummary.setText(text); - } - } - - public void updateCPUSummary(boolean clear) { - if (clear || currMachine == null || mMachine.getSelectedItemPosition() < 2) - mCPUSectionSummary.setText(""); - else { - String text = "Arch: " + currMachine.arch - + ", Machine Type: " + currMachine.machine_type - + ", CPU: " + currMachine.cpu - + ", " + currMachine.cpuNum + " CPU" + ((currMachine.cpuNum > 1) ? "s" : "") - + ", " + currMachine.memory + " MB"; - if (mEnableMTTCG.isChecked()) - text = appendOption("Enable MTTCG", text); - if (mEnableKVM.isChecked()) - text = appendOption("Enable KVM", text); - if (mDisableACPI.isChecked()) - text = appendOption("Disable ACPI", text); - if (mDisableHPET.isChecked()) - text = appendOption("Disable HPET", text); - if (mDisableTSC.isChecked()) - text = appendOption("Disable TSC", text); - mCPUSectionSummary.setText(text); - } - } - - public void updateStorageSummary(boolean clear) { - if (clear || currMachine == null || mMachine.getSelectedItemPosition() < 2) - mStorageSectionSummary.setText(""); - else { - String text = null; - text = appendDriveFilename(currMachine.hda_img_path, text, "HDA", false); - text = appendDriveFilename(currMachine.hdb_img_path, text, "HDB", false); - text = appendDriveFilename(currMachine.hdc_img_path, text, "HDC", false); - text = appendDriveFilename(currMachine.hdd_img_path, text, "HDD", false); - - if(Config.enableSharedFolder) - text = appendDriveFilename(currMachine.shared_folder, text, "Shared Folder", false); - - if (text == null || text.equals("'")) - text = "None"; - mStorageSectionSummary.setText(text); - } - } - - public void updateRemovableStorageSummary(boolean clear) { - if (clear || currMachine == null || mMachine.getSelectedItemPosition() < 2) - mRemovableStorageSectionSummary.setText(""); - else { - String text = null; - - text = appendDriveFilename(currMachine.cd_iso_path, text, "CDROM", true); - text = appendDriveFilename(currMachine.fda_img_path, text, "FDA", true); - text = appendDriveFilename(currMachine.fdb_img_path, text, "FDB", true); - text = appendDriveFilename(currMachine.sd_img_path, text, "SD", true); - - - if (text == null || text.equals("")) - text = "None"; - - mRemovableStorageSectionSummary.setText(text); - } - } - - public void updateBootSummary(boolean clear) { - if (clear || currMachine == null || mMachine.getSelectedItemPosition() < 2) - mBootSectionSummary.setText(""); - else { - String text = "Boot from: " + currMachine.bootdevice; - text = appendDriveFilename(currMachine.kernel, text, "kernel", false); - text = appendDriveFilename(currMachine.initrd, text, "initrd", false); - text = appendDriveFilename(currMachine.append, text, "append", false); - - mBootSectionSummary.setText(text); - } - } - - private String appendDriveFilename(String driveFile, String text, String drive, boolean allowEmptyDrive) { - - String file = null; - if (driveFile != null) { - if((driveFile.equals("") || driveFile.equals("None") ) && allowEmptyDrive){ - file = drive + ": Empty"; - } else if (!driveFile.equals("") && !driveFile.equals("None")) - file = drive + ": " + FileUtils.getFilenameFromPath(driveFile); - } - if (text == null && file != null) - text = file; - else if (file != null) - text += (", " + file); - return text; - } - - public void updateGraphicsSummary(boolean clear) { - if (clear || currMachine == null || mMachine.getSelectedItemPosition() < 2) - mGraphicsSectionSummary.setText(""); - else { - String text = "Video Card: " + currMachine.vga_type; - mGraphicsSectionSummary.setText(text); - } - } - - public void updateAudioSummary(boolean clear) { - if (clear || currMachine == null || mMachine.getSelectedItemPosition() < 2) - mAudioSectionSummary.setText(""); - else { - String text = mUI.getSelectedItemPosition() == 1 ? ("Audio Card: " + currMachine.soundcard) : "Audio Card: None"; + File resolvConf = new File(LimboApplication.getBasefileDir() + "/etc/resolv.conf"); + FileOutputStream fileStream = null; + try { + fileStream = new FileOutputStream(resolvConf); + String str = "nameserver " + string + "\n\n"; + byte[] data = str.getBytes(); + fileStream.write(data); + } catch (Exception ex) { + Log.e(TAG, "Could not write DNS to file: " + ex); + } finally { + if (fileStream != null) + try { + fileStream.close(); + } catch (IOException e) { - mAudioSectionSummary.setText(text); + e.printStackTrace(); + } } } - public void updateNetworkSummary(boolean clear) { - if (clear || currMachine == null || mMachine.getSelectedItemPosition() < 2) - mNetworkSectionSummary.setText(""); - else { - String text = "Net: " + currMachine.net_cfg; - if (!currMachine.net_cfg.equals("None")) { - text = text + ", NIC Card: " + currMachine.nic_card - + ", DNS Server: " + mDNS.getText(); - if (!(mHOSTFWD.getText() + "").equals("")) - text += (", Host Forward: " + mHOSTFWD.getText()); - } - mNetworkSectionSummary.setText(text); - } + private void disableListeners() { + if (mMachine == null) + return; + mUI.setOnItemSelectedListener(null); + mKeyboard.setOnItemSelectedListener(null); + mMouse.setOnItemSelectedListener(null); + mMachineType.setOnItemSelectedListener(null); + mCPU.setOnItemSelectedListener(null); + mCPUNum.setOnItemSelectedListener(null); + mRamSize.setOnItemSelectedListener(null); + mDisableACPI.setOnCheckedChangeListener(null); + mDisableHPET.setOnCheckedChangeListener(null); + mDisableTSC.setOnCheckedChangeListener(null); + mEnableKVM.setOnCheckedChangeListener(null); + mEnableMTTCG.setOnCheckedChangeListener(null); + mHDA.setOnItemSelectedListener(null); + mHDB.setOnItemSelectedListener(null); + mHDC.setOnItemSelectedListener(null); + mHDD.setOnItemSelectedListener(null); + mSharedFolder.setOnItemSelectedListener(null); + mBootDevices.setOnItemSelectedListener(null); + mKernel.setOnItemSelectedListener(null); + mInitrd.setOnItemSelectedListener(null); + mAppend.setOnFocusChangeListener(null); + mVGAConfig.setOnItemSelectedListener(null); + mSoundCard.setOnItemSelectedListener(null); + mNetConfig.setOnItemSelectedListener(null); + mNetworkCard.setOnItemSelectedListener(null); + mDNS.setOnFocusChangeListener(null); + mHOSTFWD.setOnFocusChangeListener(null); + mExtraParams.setOnFocusChangeListener(null); } - - public void updateAdvancedSummary(boolean clear) { - if (clear || currMachine == null || mMachine.getSelectedItemPosition() < 2) - mAdvancedSectionSummary.setText(""); - else { - String text = null; - if (mPrio.isChecked()) - text = appendOption("High Priority", text); - if (currMachine.extra_params != null && !currMachine.extra_params.equals("")) - text = appendOption("Extra Params: " + currMachine.extra_params, text); - mAdvancedSectionSummary.setText(text); - } + /** + * Called when the activity is first created. + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setupAppEnvironment(); + clearNotifications(); + setupStrictMode(); + setContentView(R.layout.limbo_main); + setupWidgets(); + setupController(); + setupDiskMapping(); + createListeners(); + populateAttributesUI(); + checkFirstLaunch(); + setupToolbar(); + checkUpdate(); + checkLog(); + checkAndLoadLibs(); + restore(); + setupListeners(); + addGenericOperatingSystems(); } - private String appendOption(String option, String text) { - - if (text == null && option != null) - text = option; - else if (option != null) - text += (", " + option); - return text; + private void setupAppEnvironment() { + LimboApplication.setupEnv(this); } - private void triggerUpdateSpinner(final Spinner spinner) { - - final int position = (int) spinner.getSelectedItemId(); - spinner.setSelection(0); - - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - spinner.setSelection(position); - } - }, 100); - + private void setupController() { + setViewListener(LimboApplication.getViewListener()); } - private void promptPrio(final Activity activity) { - - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("Enable High Priority!"); - - TextView textView = new TextView(activity); - textView.setVisibility(View.VISIBLE); - textView.setPadding(20, 20, 20, 20); - textView.setText("Warning! High Priority might increase emulation speed but " + "will slow your phone down!"); - - alertDialog.setView(textView); - - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - LimboSettingsManager.setPrio(activity, true); - } - }); - alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - mPrio.setChecked(false); - return; - } - }); - alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - mPrio.setChecked(false); - } - }); - alertDialog.show(); + public void setViewListener(ViewListener viewListener) { + this.viewListener = viewListener; } - - private void promptSharedFolder(final Activity activity) { - - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("Enable Shared Folder!"); - - TextView textView = new TextView(activity); - textView.setVisibility(View.VISIBLE); - textView.setPadding(20, 20, 20, 20); - textView.setText( - "Warning! Enabling Shared folder is buggy under Limbo. " + - "\nIf you use Storage Cleaning apps exclude this folder. " + - "\nMake sure you keep a backup of your files. " + - "\nPausing the Virtual Machine is not supported with this feature." + - "\nIf you experience crashes disable this option. " + - "\nDo you want to continue?"); - - ScrollView scrollView = new ScrollView(activity); - scrollView.addView(textView); - - alertDialog.setView(scrollView); - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - - } - }); - alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - mSharedFolderenable.setChecked(false); - return; - } - }); - alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - mSharedFolderenable.setChecked(false); - } - }); - alertDialog.setCancelable(false); - alertDialog.show(); + private void setupListeners() { + MachineController.getInstance().addOnStatusChangeListener(this); + MachineController.getInstance().addOnEventListener(this); } - public void promptVNCAllowExternal(final Activity activity) { - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("Enable VNC server"); - - TextView textView = new TextView(activity); - textView.setVisibility(View.VISIBLE); - textView.setPadding(20, 20, 20, 20); - textView.setText("VNC Server: " + this.getLocalIpAddress() + ":" + Config.defaultVNCPort + "\n" - + "Warning: Allowing VNC to serve external connections is not Recommended! " + - "VNC connections are Unencrypted therefore NOT secure. " + - "Make sure you're on a private network and you trust other apps installed in your device!\n"); - - final EditText passwdView = new EditText(activity); - passwdView.setInputType(InputType.TYPE_CLASS_TEXT | - InputType.TYPE_TEXT_VARIATION_PASSWORD | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); - passwdView.setSelection(passwdView.getText().length()); - passwdView.setHint("Password"); - passwdView.setEnabled(true); - passwdView.setVisibility(View.VISIBLE); - passwdView.setSingleLine(); - - LinearLayout mLayout = new LinearLayout(this); - mLayout.setPadding(20, 20, 20, 20); - mLayout.setOrientation(LinearLayout.VERTICAL); - - LinearLayout.LayoutParams textViewParams = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - mLayout.addView(textView, textViewParams); - - LinearLayout.LayoutParams passwordViewParams = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - mLayout.addView(passwdView, passwordViewParams); - - alertDialog.setView(mLayout); - passwdView.setTag(false); - passwdView.setTransformationMethod(new PasswordTransformationMethod()); - passwdView.setSelection(passwdView.getText().length()); - - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Set", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - - - if (passwdView.getText().toString().trim().equals("")) { - UIUtils.toastShort(getApplicationContext(), "Password cannot be empty!"); - vnc_passwd = null; - vnc_allow_external = 0; - mVNCAllowExternal.setChecked(false); - return; - } else { - vnc_passwd = passwdView.getText().toString(); - vnc_allow_external = 1; - } - - } - }); - alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - vnc_passwd = null; - vnc_allow_external = 0; - mVNCAllowExternal.setChecked(false); - return; - } - }); - - - - alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - mVNCAllowExternal.setChecked(false); - vnc_passwd = null; - vnc_allow_external = 0; - } - }); - alertDialog.show(); - -// alertDialog.setButton(DialogInterface.BUTTON_NEUTRAL, " ", new DialogInterface.OnClickListener() { -// public void onClick(DialogInterface dialog, int which) { -// } -// }); - try { - Button passwordButton = alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL); -// passwordButton.setWidth(24); -// passwordButton.setHeight(24); - passwordButton.setVisibility(View.VISIBLE); - passwordButton.setBackgroundResource(android.R.drawable.ic_menu_view); - passwordButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if ((boolean) passwdView.getTag()) { - passwdView.setTransformationMethod(new PasswordTransformationMethod()); - } else - passwdView.setTransformationMethod(new HideReturnsTransformationMethod()); - passwdView.setTag(!(boolean) passwdView.getTag()); - passwdView.setSelection(passwdView.getText().length()); - } - }); - } finally{} + private void restoreUI(final String machine) { + int position = SpinnerAdapter.getItemPosition(mMachine, machine); + mMachine.setSelection(position); } - - public void promptQMPAllowExternal(final Activity activity) { - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("Enable QMP server"); - - TextView textView = new TextView(activity); - textView.setVisibility(View.VISIBLE); - textView.setPadding(20, 20, 20, 20); - textView.setText("QMP Server: " + this.getLocalIpAddress() + ":" + Config.QMPPort + "\n" - + "Warning: Allowing QMP to serve external connections is NOT Recommended! " + - "QMP connections are Passwordless and Unencrypted therefore NOT secure. " + - "Make sure you're on a private network and you trust other apps installed in your device!\n"); - - LinearLayout mLayout = new LinearLayout(this); - mLayout.setPadding(20, 20, 20, 20); - mLayout.setOrientation(LinearLayout.VERTICAL); - - LinearLayout.LayoutParams textViewParams = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - mLayout.addView(textView, textViewParams); - - alertDialog.setView(mLayout); - - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Continue", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - qmp_allow_external = 1; - } - }); - alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - qmp_allow_external = 0; - mQMPAllowExternal.setChecked(false); - return; - } - }); - - - - alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + private void restore() { + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { @Override - public void onCancel(DialogInterface dialog) { - mQMPAllowExternal.setChecked(false); - vnc_allow_external = 0; - } - }); - alertDialog.show(); - - } - - private void loadMachine(String machine, String snapshot) { - - - machineLoaded = true; - this.setUserPressed(false); - - // Load machine from DB - this.currMachine = MachineOpenHelper.getInstance(activity).getMachine(machine, snapshot); - - if (currMachine == null) { - return; - } - new Handler(Looper.getMainLooper()).post(new Runnable() { public void run() { - - mVNCAllowExternal.setChecked(false); - mQMPAllowExternal.setChecked(false); - - populateMachineType(currMachine.machine_type); - populateCPUs(currMachine.cpu); - populateNetDevices(currMachine.nic_card); - - setAdapter(mArch, currMachine.arch); - setCPUNum(currMachine.cpuNum); - setRAM(currMachine.memory); - setDiskValue(FileType.KERNEL, currMachine.kernel); - setDiskValue(FileType.INITRD, currMachine.initrd); - if (currMachine.append != null) - mAppend.setText(currMachine.append); - else - mAppend.setText(""); - - if (currMachine.hostfwd != null) - mHOSTFWD.setText(currMachine.hostfwd); - else - mHOSTFWD.setText(""); - - if (currMachine.extra_params != null) - mExtraParams.setText(currMachine.extra_params); - else - mExtraParams.setText(""); - - // CDROM - setDiskValue(FileType.CD, currMachine.cd_iso_path); - - // Floppy - setDiskValue(FileType.FDA, currMachine.fda_img_path); - setDiskValue(FileType.FDB, currMachine.fdb_img_path); - - // SD Card - setDiskValue(FileType.SD, currMachine.sd_img_path); - - // HDD - setDiskValue(FileType.HDA, currMachine.hda_img_path); - setDiskValue(FileType.HDB, currMachine.hdb_img_path); - setDiskValue(FileType.HDC, currMachine.hdc_img_path); - setDiskValue(FileType.HDD, currMachine.hdd_img_path); - - //sharedfolder - setDiskValue(FileType.SHARED_DIR, currMachine.shared_folder); - - // Advance - setBootDevice(currMachine.bootdevice); - setNetCfg(currMachine.net_cfg, false); - // this.setNicDevice(currMachine.nic_card, false); - setVGA(currMachine.vga_type); - setHDCache(currMachine.hd_cache); - setSoundcard(currMachine.soundcard); - setUI(currMachine.ui); - setMouse(currMachine.mouse); - setKeyboard(currMachine.keyboard); - mDisableACPI.setChecked(currMachine.disableacpi == 1 ? true : false); - mDisableHPET.setChecked(currMachine.disablehpet == 1 ? true : false); - if(Config.enable_X86 || Config.enable_X86_64) - mDisableTSC.setChecked(currMachine.disabletsc == 1 ? true : false); - mEnableKVM.setChecked(currMachine.enableKVM == 1 ? true : false); - mEnableMTTCG.setChecked(currMachine.enableMTTCG == 1 ? true : false); - -// userPressedBluetoothMouse = false; - - - // We finished loading now when a user change a setting it will - // fire an - // event - - enableNonRemovableDeviceOptions(true); - enableRemovableDeviceOptions(true); - - if (Config.enable_SDL_sound) { - if (currMachine.ui != null && currMachine.ui.equals("SDL") && currMachine.paused == 0) { - mSoundCard.setEnabled(true); - } else - mSoundCard.setEnabled(false); - } else - mSoundCard.setEnabled(false); - - mMachine.setEnabled(false); - - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - - - if (currMachine.fda_img_path != null) { - mFDAenable.setChecked(true); - } else - mFDAenable.setChecked(false); - - if (currMachine.fdb_img_path != null) { - mFDBenable.setChecked(true); - } else - mFDBenable.setChecked(false); - - if (currMachine.hda_img_path != null) { - mHDAenable.setChecked(true); - } else - mHDAenable.setChecked(false); - - if (currMachine.hdb_img_path != null) { - mHDBenable.setChecked(true); - } else - mHDBenable.setChecked(false); - - if (currMachine.hdc_img_path != null) { - mHDCenable.setChecked(true); - } else - mHDCenable.setChecked(false); - - if (currMachine.hdd_img_path != null) { - mHDDenable.setChecked(true); - } else - mHDDenable.setChecked(false); - - if (currMachine.cd_iso_path != null) { - mCDenable.setChecked(true); - } else - mCDenable.setChecked(false); - - if (currMachine.sd_img_path != null) { - mSDenable.setChecked(true); - } else - mSDenable.setChecked(false); - - if (currMachine.shared_folder != null) { - mSharedFolderenable.setChecked(true); - } else - mSharedFolderenable.setChecked(false); - - if (currMachine.paused == 1) { - changeStatus(VMStatus.Paused); - enableNonRemovableDeviceOptions(false); - enableRemovableDeviceOptions(false); - } else { - changeStatus(VMStatus.Ready); - enableNonRemovableDeviceOptions(true); - enableRemovableDeviceOptions(true); - } - - setUserPressed(true); - machineLoaded = false; - mMachine.setEnabled(true); - updateSummary(false); - - } - }, 1000); - + if (MachineController.getInstance().isRunning()) { + restoreUI(MachineController.getInstance().getMachineName()); + } } - }); - + }, 1000); } - private void updateSummary(boolean clear) { - - updateUISummary(clear); - updateCPUSummary(clear); - updateStorageSummary(clear); - updateRemovableStorageSummary(clear); - updateGraphicsSummary(clear); - updateAudioSummary(clear); - updateNetworkSummary(clear); - updateBootSummary(clear); - updateAdvancedSummary(clear); + private void checkAndLoadLibs() { + if (Config.loadNativeLibsEarly) + if (Config.loadNativeLibsMainThread) + setupNativeLibs(); + else + setupNativeLibsAsync(); } - public void promptMachineName(final Activity activity) { - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("New Machine Name"); - final EditText vmNameTextView = new EditText(activity); - vmNameTextView.setPadding(20, 20, 20, 20); - vmNameTextView.setEnabled(true); - vmNameTextView.setVisibility(View.VISIBLE); - vmNameTextView.setSingleLine(); - alertDialog.setView(vmNameTextView); - alertDialog.setCanceledOnTouchOutside(false); - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Create", (DialogInterface.OnClickListener) null); - - alertDialog.show(); - - Button button = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); - button.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - if (vmNameTextView.getText().toString().trim().equals("")) - UIUtils.toastShort(activity, "Machine name cannot be empty"); - else { - createMachine(vmNameTextView.getText().toString()); - alertDialog.dismiss(); - } - } - }); - alertDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(DialogInterface dialog) { - InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(vmNameTextView.getWindowToken(), 0); - } - }); + private void clearNotifications() { + NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancelAll(); } - public void promptImageName(final Activity activity, final FileType fileType) { + private void setupDiskMapping() { + diskMapping.clear(); + addDiskMapping(FileType.HDA, mHDA, null, MachineProperty.HDA); + addDiskMapping(FileType.HDB, mHDB, null, MachineProperty.HDB); + addDiskMapping(FileType.HDC, mHDC, null, MachineProperty.HDC); + addDiskMapping(FileType.HDD, mHDD, null, MachineProperty.HDD); + addDiskMapping(FileType.SHARED_DIR, mSharedFolder, null, MachineProperty.SHARED_FOLDER); + + addDiskMapping(FileType.CDROM, mCD, mCDenable, MachineProperty.CDROM); + addDiskMapping(FileType.FDA, mFDA, mFDAenable, MachineProperty.FDA); + addDiskMapping(FileType.FDB, mFDB, mFDBenable, MachineProperty.FDB); + addDiskMapping(FileType.SD, mSD, mSDenable, MachineProperty.SD); - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("Image Name"); + addDiskMapping(FileType.KERNEL, mKernel, null, MachineProperty.KERNEL); + addDiskMapping(FileType.INITRD, mInitrd, null, MachineProperty.INITRD); + } - LinearLayout mLayout = new LinearLayout(this); - mLayout.setPadding(20, 20, 20, 20); - mLayout.setOrientation(LinearLayout.VERTICAL); + private void addDiskMapping(FileType fileType, Spinner spinner, + CheckBox enableCheckBox, MachineProperty dbColName) { + spinner.setTag(fileType); - final EditText imageNameView = new EditText(activity); - imageNameView.setEnabled(true); - imageNameView.setVisibility(View.VISIBLE); - imageNameView.setSingleLine(); - LinearLayout.LayoutParams imageNameViewParams = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - mLayout.addView(imageNameView, imageNameViewParams); + diskMapping.put(fileType, new DiskInfo(spinner, enableCheckBox, dbColName)); + } - final Spinner size = new Spinner(this); - LinearLayout.LayoutParams spinnerParams = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + private void setupNativeLibsAsync() { - String[] arraySpinner = new String[5]; - arraySpinner[0] = "1GB (Growable)"; - arraySpinner[1] = "2GB (Growable)"; - arraySpinner[2] = "4GB (Growable)"; - arraySpinner[3] = "10 GB (Growable)"; - arraySpinner[4] = "20 GB (Growable)"; + Thread thread = new Thread(new Runnable() { + public void run() { + setupNativeLibs(); + } + }); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); - ArrayAdapter sizeAdapter = new ArrayAdapter(this, R.layout.custom_spinner_item, arraySpinner); - sizeAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - size.setAdapter(sizeAdapter); - mLayout.addView(size, spinnerParams); + } - alertDialog.setView(mLayout); + private void createListeners() { - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Create", (DialogInterface.OnClickListener) null); - alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Change Directory", (DialogInterface.OnClickListener) null); + mMachine.setOnItemSelectedListener(new OnItemSelectedListener() { + public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - alertDialog.show(); + if (position == 0) { + enableNonRemovableDeviceOptions(false); + enableRemovableDeviceOptions(false); + if (!MachineController.getInstance().isRunning()) + notifyAction(MachineAction.LOAD_VM, null); + } else if (position == 1) { + mMachine.setSelection(0); + promptMachineName(LimboActivity.this); + } else { + final String machine = (String) ((ArrayAdapter) mMachine.getAdapter()).getItem(position); + setUserPressed(false); + machineLoaded = true; + notifyAction(MachineAction.LOAD_VM, machine); + } + } - Button positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE); - positiveButton.setOnClickListener(new View.OnClickListener() { + public void onNothingSelected(AdapterView parentView) { + } + }); + mScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() { @Override - public void onClick(View view) { - if (LimboSettingsManager.getImagesDir(LimboActivity.this) == null) { - changeImagesDir(); - return; - } + public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { + savePendingEditText(); + } + }); - int sizeSel = size.getSelectedItemPosition(); - String templateImage = "hd1g.qcow2"; - if (sizeSel == 0) { - templateImage = "hd1g.qcow2"; - } else if (sizeSel == 1) { - templateImage = "hd2g.qcow2"; - } else if (sizeSel == 2) { - templateImage = "hd4g.qcow2"; - } else if (sizeSel == 3) { - templateImage = "hd10g.qcow2"; - }else if (sizeSel == 4) { - templateImage = "hd20g.qcow2"; + mStart.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + if (!Config.loadNativeLibsEarly && Config.loadNativeLibsMainThread) { + setupNativeLibs(); } - - String image = imageNameView.getText().toString(); - if (image.trim().equals("")) - UIUtils.toastShort(activity, "Image filename cannot be empty"); - else { - if (!image.endsWith(".qcow2")) { - image += ".qcow2"; - } - boolean res = createImgFromTemplate(templateImage, image, fileType); - if (res) { - alertDialog.dismiss(); + Thread thread = new Thread(new Runnable() { + public void run() { + if (!Config.loadNativeLibsEarly && !Config.loadNativeLibsMainThread) { + setupNativeLibs(); + } + onStartButton(); } - - } + }); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); } }); - - Button negativeButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE); - negativeButton.setOnClickListener(new View.OnClickListener() { - - @Override + mPause.setOnClickListener(new OnClickListener() { public void onClick(View view) { - changeImagesDir(); - + onPauseButton(); + } + }); + mStop.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + onStopButton(false); + } + }); + mRestart.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + onRestartButton(); } }); - } + private void onPauseButton() { + if (MachineController.getInstance().isRunning()) { + if (MachineController.getInstance().isVNCEnabled()) + LimboActivityCommon.promptPause(this, viewListener); + else { + LimboSDLActivity.pendingPause = true; + startSDL(); + } + } + } - public void promptExportName(final Activity activity) { + private void savePendingEditText() { + View currentView = getCurrentFocus(); + if (currentView instanceof EditText) { + currentView.setFocusable(false); + } + } - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("Export Filename"); + private void checkFirstLaunch() { + Thread t = new Thread(new Runnable() { + public void run() { + if (LimboSettingsManager.isFirstLaunch(LimboActivity.this)) { + onFirstLaunch(); + } + } + }); + t.start(); + } - LinearLayout mLayout = new LinearLayout(this); - mLayout.setPadding(20, 20, 20, 20); - mLayout.setOrientation(LinearLayout.VERTICAL); + private void checkLog() { + Thread t = new Thread(new Runnable() { + public void run() { + if (LimboSettingsManager.getExitCode(LimboActivity.this) != Config.EXIT_SUCCESS) { + if (MachineController.getInstance().isRunning()) + LimboSettingsManager.setExitCode(LimboActivity.this, Config.EXIT_UNKNOWN); + else + LimboSettingsManager.setExitCode(LimboActivity.this, Config.EXIT_SUCCESS); + runOnUiThread(new Runnable() { + @Override + public void run() { + com.max2idea.android.limbo.log.Logger.promptShowLog(LimboActivity.this); + } + }); + } + } + }); + t.start(); + } - final EditText exportNameView = new EditText(activity); - exportNameView.setEnabled(true); - exportNameView.setVisibility(View.VISIBLE); - exportNameView.setSingleLine(); - LinearLayout.LayoutParams imageNameViewParams = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - mLayout.addView(exportNameView, imageNameViewParams); + //XXX: this needs to be called from the main thread otherwise + // qemu crashes when it is started later + public void setupNativeLibs() { + if (libLoaded) + return; + //Compatibility lib + System.loadLibrary("compat-limbo"); - alertDialog.setView(mLayout); + //Glib deps + System.loadLibrary("compat-musl"); - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Export", (DialogInterface.OnClickListener) null); - alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Change Directory", (DialogInterface.OnClickListener) null); + //Glib + System.loadLibrary("glib-2.0"); - alertDialog.show(); + //Pixman for qemu + System.loadLibrary("pixman-1"); - Button positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE); - positiveButton.setOnClickListener(new View.OnClickListener() { + // SDL library + if (Config.enable_SDL) { + if (Build.VERSION.SDK_INT >= 26) + System.loadLibrary("compat-SDL2-addons"); + System.loadLibrary("SDL2"); + } - @Override - public void onClick(View view) { - if (LimboSettingsManager.getExportDir(LimboActivity.this) == null) { - changeExportDir(); - return; - } + System.loadLibrary("compat-SDL2-ext"); - String exportFilename = exportNameView.getText().toString(); - if (exportFilename.trim().equals("")) - UIUtils.toastShort(activity, "Export filename cannot be empty"); - else { + //Limbo needed for vmexecutor + System.loadLibrary("limbo"); - if(!exportFilename.endsWith(".csv")) - exportFilename += ".csv"; + // qemu arch specific lib + loadQEMULib(); - exportMachines(exportFilename); - alertDialog.dismiss(); - } - } - }); + libLoaded = true; + } - Button negativeButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE); - negativeButton.setOnClickListener(new View.OnClickListener() { + protected void loadQEMULib() { - @Override - public void onClick(View view) { - changeExportDir(); + } - } - }); + public void setupToolbar() { + Toolbar tb = findViewById(R.id.toolbar); + setSupportActionBar(tb); + final ActionBar ab = getSupportActionBar(); + if (ab != null) { + ab.setHomeAsUpIndicator(R.drawable.limbo); + ab.setDisplayShowHomeEnabled(true); + ab.setDisplayHomeAsUpEnabled(true); + ab.setDisplayShowCustomEnabled(true); + ab.setDisplayShowTitleEnabled(true); + ab.setTitle(R.string.app_name); + } } - public void changeExportDir() { - UIUtils.toastLong(LimboActivity.this, "Choose a directory to export your VMs"); - FileManager.browse(LimboActivity.this, FileType.EXPORT_DIR, Config.OPEN_EXPORT_DIR_REQUEST_CODE); + public void checkUpdate() { + Thread tsdl = new Thread(new Runnable() { + public void run() { + UpdateChecker.checkNewVersion(LimboActivity.this); + } + }); + tsdl.start(); } - public void changeImagesDir() { - UIUtils.toastLong(LimboActivity.this, "Choose a directory to create your image"); - FileManager.browse(LimboActivity.this, FileType.IMAGE_DIR, Config.OPEN_IMAGE_DIR_REQUEST_CODE); + private void setupStrictMode() { + if (Config.debugStrictMode) { + StrictMode.setThreadPolicy( + new StrictMode.ThreadPolicy.Builder().detectDiskReads().detectDiskWrites().detectNetwork() + .penaltyLog().build()); + StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectLeakedSqlLiteObjects() + .detectLeakedClosableObjects().penaltyLog() + .build()); + } } - public void changeSharedDir() { - UIUtils.toastLong(LimboActivity.this, "Choose a directory to shared files with the VM"); - FileManager.browse(LimboActivity.this, FileType.SHARED_DIR, Config.OPEN_SHARED_DIR_REQUEST_CODE); + private void populateAttributesUI() { + populateMachines(null); + populateMachineType(null); + populateCPUs(null); + populateCPUNum(); + populateRAM(); + populateDisks(); + populateBootDevices(); + populateNet(); + populateNetDevices(null); + populateVGA(); + populateSoundcardConfig(); + populateUI(); + populateKeyboardLayout(); + populateMouse(); } + private void populateDisks() { - protected boolean createImgFromTemplate(String templateImage, String destImage, FileType imgType) { + //disks + populateDiskAdapter(mHDA, FileType.HDA, true); + populateDiskAdapter(mHDB, FileType.HDB, true); + populateDiskAdapter(mHDC, FileType.HDC, true); + populateDiskAdapter(mHDD, FileType.HDD, true); + populateDiskAdapter(mSharedFolder, FileType.SHARED_DIR, false); + + //removables drives + populateDiskAdapter(mCD, FileType.CDROM, false); + populateDiskAdapter(mFDA, FileType.FDA, false); + populateDiskAdapter(mFDB, FileType.FDB, false); + populateDiskAdapter(mSD, FileType.SD, false); - String imagesDir = LimboSettingsManager.getImagesDir(this); - String displayName = null; - String filePath = null; - if(imagesDir.startsWith("content://")) { - Uri imagesDirUri = Uri.parse(imagesDir); - Uri fileCreatedUri = FileInstaller.installImageTemplateToSDCard(activity, templateImage, - imagesDirUri, "hdtemplates", destImage); - displayName = FileUtils.getFullPathFromDocumentFilePath(fileCreatedUri.toString()); - filePath = fileCreatedUri.toString(); - } else { - filePath = FileInstaller.installImageTemplateToExternalStorage(activity, templateImage, imagesDir, "hdtemplates", destImage); - displayName = filePath; - } + //boot + populateDiskAdapter(mKernel, FileType.KERNEL, false); + populateDiskAdapter(mInitrd, FileType.INITRD, false); - if (progDialog != null && progDialog.isShowing()) { - progDialog.dismiss(); - } + } - if (displayName != null) { - UIUtils.toastShort(activity, "Image Created: " + displayName); - updateDrive(imgType, filePath); + public void onFirstLaunch() { + promptLicense(); + } - return true; - } - return false; + private void createMachine(String machineName) { + notifyAction(MachineAction.CREATE_VM, machineName); } - protected String exportMachinesToFile(String exportFileName) { + private void machineCreated() { + runOnUiThread(new Runnable() { + @Override + public void run() { + showOperatingSystems(); + populateMachines(getMachine().getName()); + enableNonRemovableDeviceOptions(true); + enableRemovableDeviceOptions(true); + setArchOptions(); + } + }); + } - String exportDir = LimboSettingsManager.getExportDir(this); - String displayName = null; - if(exportDir.startsWith("content://")) { - Uri exportDirUri = Uri.parse(exportDir); - Uri fileCreatedUri = FileUtils.exportFileSDCard(activity, - exportDirUri, exportFileName); - displayName = FileUtils.getFullPathFromDocumentFilePath(fileCreatedUri.toString()); - } else { - String filePath = FileUtils.exportFileLegacy(this, exportDir, exportFileName); - displayName = filePath; + protected void showOperatingSystems() { + if (!Config.osImages.isEmpty()) { + LinksManager manager = new LinksManager(this); + manager.show(); } + } - if (progDialog != null && progDialog.isShowing()) { - progDialog.dismiss(); + private void onDeleteMachine() { + if (getMachine() == null) { + ToastUtils.toastShort(this, getString(R.string.SelectAMachineFirst)); + return; } + Thread t = new Thread(new Runnable() { + public void run() { + final String name = getMachine().getName(); + notifyAction(MachineAction.DELETE_VM, getMachine()); + runOnUiThread(new Runnable() { + @Override + public void run() { + disableListeners(); + disableRemovableDiskListeners(); + mMachine.setSelection(0); + notifyAction(MachineAction.LOAD_VM, null); + populateAttributesUI(); + ToastUtils.toastShort(LimboActivity.this, getString(R.string.MachineDeleted) + ": " + name); + setupMiscOptions(); + setupNonRemovableDiskListeners(); + enableRemovableDiskListeners(); + } + }); + } + }); + t.start(); - return displayName; } + public void importMachines(String importFilePath) { + disableListeners(); + disableRemovableDiskListeners(); + mMachine.setSelection(0); + notifyAction(MachineAction.IMPORT_VMS, importFilePath); + } + private void promptLicense() { + runOnUiThread(new Runnable() { + @Override + public void run() { + try { + LimboActivityCommon.promptLicense(LimboActivity.this, + Config.APP_NAME + " " + LimboApplication.getLimboVersionString() + + " " + "QEMU" + " " + LimboApplication.getQemuVersionString() , + FileUtils.LoadFile(LimboActivity.this, "LICENSE", false)); + } catch (IOException e) { - public boolean onKeyDown(int keyCode, KeyEvent event) { + e.printStackTrace(); + } + } + }); - if (keyCode == KeyEvent.KEYCODE_BACK) { - moveTaskToBack(true); - return true; // return - } + } - return false; + public void exit() { + if (MachineController.getInstance().isRunning()) + onStopButton(true); + else + System.exit(0); } - public void promptStateName(final Activity activity) { - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("Snapshot/State Name"); - final EditText stateNameView = new EditText(activity); - stateNameView.setPadding(20, 20, 20, 20); - stateNameView.setEnabled(true); - stateNameView.setVisibility(View.VISIBLE); - stateNameView.setSingleLine(); - alertDialog.setView(stateNameView); - - // alertDialog.setMessage(body); - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Create", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - savevm(stateNameView.getText() + ""); + private void unlockRemovableDevices(boolean flag) { + mCDenable.setEnabled(flag); + mFDAenable.setEnabled(flag); + mFDBenable.setEnabled(flag); + mSDenable.setEnabled(flag); + } - return; - } - }); - alertDialog.show(); + private void enableRemovableDeviceOptions(boolean flag) { + unlockRemovableDevices(flag); + enableRemovableDiskValues(flag); + } + private void enableRemovableDiskValues(boolean flag) { + mCD.setEnabled(flag && mCDenable.isChecked()); + mFDA.setEnabled(flag && mFDAenable.isChecked()); + mFDB.setEnabled(flag && mFDBenable.isChecked()); + mSD.setEnabled(flag && mSDenable.isChecked()); } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); + private void enableNonRemovableDeviceOptions(boolean flag) { + if (MachineController.getInstance().isRunning()) + flag = false; - if (resultCode == Config.VNC_RESET_RESULT_CODE) { - Log.d(TAG, "Reconnecting"); - this.startvnc(); + //ui + mUI.setEnabled(flag); + mKeyboard.setEnabled(Config.enableKeyboardLayoutOption && flag); + mMouse.setEnabled(Config.enableMouseOption && flag); + + // Enable everything except removable devices + mMachineType.setEnabled(flag); + mCPU.setEnabled(flag); + mCPUNum.setEnabled(flag); + mRamSize.setEnabled(flag); + mEnableKVM.setEnabled(flag && Config.enableKVM); + mEnableMTTCG.setEnabled(flag && Config.enableMTTCG); - } else if (resultCode == Config.SDL_QUIT_RESULT_CODE) { - if (activity.getParent() != null) { - activity.getParent().finish(); - } - activity.finish(); + //drives + mHDA.setEnabled(flag); + mHDAOptions.setEnabled(flag); + mHDB.setEnabled(flag); + mHDBOptions.setEnabled(flag); + mHDC.setEnabled(flag); + mHDCOptions.setEnabled(flag); + mHDD.setEnabled(flag); + mHDDOptions.setEnabled(flag); + mSharedFolder.setEnabled(flag); - UIUtils.toastShort(getApplicationContext(), "SDL Quit"); - if (LimboActivity.vmexecutor != null) { - LimboActivity.vmexecutor.stopvm(0); - } - } else if (requestCode == Config.OPEN_IMPORT_FILE_REQUEST_CODE || requestCode == Config.OPEN_IMPORT_FILE_ASF_REQUEST_CODE) { - String file = null; - if(requestCode == Config.OPEN_IMPORT_FILE_ASF_REQUEST_CODE) { - file = FileUtils.getFileUriFromIntent(this, data, false); - } else { - file = FileUtils.getFilePathFromIntent(this, data); - } - if(file!=null) - importFile(file); - } else if (requestCode == Config.OPEN_EXPORT_DIR_REQUEST_CODE || requestCode == Config.OPEN_EXPORT_DIR_ASF_REQUEST_CODE) { - String exportDir = null; - if(requestCode == Config.OPEN_EXPORT_DIR_ASF_REQUEST_CODE) { - exportDir = FileUtils.getFileUriFromIntent(this, data, true); - } else { - exportDir = FileUtils.getDirPathFromIntent(this, data); - } - if(exportDir != null) - LimboSettingsManager.setExportDir(this, exportDir); + //boot + mBootDevices.setEnabled(flag); + mKernel.setEnabled(flag); + mInitrd.setEnabled(flag); + mAppend.setEnabled(flag); - } else if (requestCode == Config.OPEN_IMAGE_FILE_REQUEST_CODE || requestCode == Config.OPEN_IMAGE_FILE_ASF_REQUEST_CODE) { - String file = null; - if(requestCode == Config.OPEN_IMAGE_FILE_ASF_REQUEST_CODE) { - file = FileUtils.getFileUriFromIntent(this, data, true); - } else { - filetype = FileUtils.getFileTypeFromIntent(this, data); - file = FileUtils.getFilePathFromIntent(this, data); - } - if(file!=null) - updateDrive(filetype, file); + //graphics + mVGAConfig.setEnabled(flag); + //audio + if (Config.enableSDLSound && getMachine() != null + && getMachine().getEnableVNC() != 1 + && getMachine().getPaused() == 0) + mSoundCard.setEnabled(flag); + else + mSoundCard.setEnabled(false); - } else if (requestCode == Config.OPEN_IMAGE_DIR_REQUEST_CODE || requestCode == Config.OPEN_IMAGE_DIR_ASF_REQUEST_CODE) { - String imageDir = null; - if(requestCode == Config.OPEN_IMAGE_DIR_ASF_REQUEST_CODE) { - imageDir = FileUtils.getFileUriFromIntent(this, data, true); - } else { - imageDir = FileUtils.getDirPathFromIntent(this, data); - } - if(imageDir != null) - LimboSettingsManager.setImagesDir(this, imageDir); + //net + mNetConfig.setEnabled(flag); + mNetworkCard.setEnabled(flag && mNetConfig.getSelectedItemPosition() > 0); + mDNS.setEnabled(flag && mNetConfig.getSelectedItemPosition() > 0); + mHOSTFWD.setEnabled(flag && mNetConfig.getSelectedItemPosition() > 0); - } else if (requestCode == Config.OPEN_SHARED_DIR_REQUEST_CODE || requestCode == Config.OPEN_SHARED_DIR_ASF_REQUEST_CODE) { - String file = null; - if(requestCode == Config.OPEN_SHARED_DIR_ASF_REQUEST_CODE) { - file = FileUtils.getFileUriFromIntent(this, data, true); - } else { - filetype = FileUtils.getFileTypeFromIntent(this, data); - file = FileUtils.getDirPathFromIntent(this, data); - } - if(file!=null) { - updateDrive(filetype, file); - LimboSettingsManager.setSharedDir(this, file); - } - } else if (requestCode == Config.OPEN_LOG_FILE_DIR_REQUEST_CODE|| requestCode == Config.OPEN_LOG_FILE_DIR_ASF_REQUEST_CODE) { - String file = null; - if(requestCode == Config.OPEN_LOG_FILE_DIR_ASF_REQUEST_CODE) { - file = FileUtils.getFileUriFromIntent(this, data, true); - } else { - file = FileUtils.getDirPathFromIntent(this, data); - } - if(file!=null) { - FileUtils.saveLogToFile(activity, file); - } - } + //advanced + mDisableACPI.setEnabled(flag); + mDisableHPET.setEnabled(flag); + mDisableTSC.setEnabled(flag); + mExtraParams.setEnabled(flag); } - private void updateDrive(FileType fileType, String diskValue) { + // Main event function + // Retrives values from saved preferences + private void onStartButton() { - //FIXME: sometimes the array adapters try to set invalid values - if (fileType == null || diskValue == null) { + if (mMachine.getSelectedItemPosition() == 0 || getMachine() == null) { + ToastUtils.toastShort(LimboActivity.this, getString(R.string.SelectOrCreateVirtualMachineFirst)); return; } + // focus out of edit texts to make sure they are applied to the db + mStart.requestFocus(); - Spinner spinner = getSpinner(fileType); - ArrayAdapter adapter = getAdapter(fileType); - - if (fileType != null && diskValue != null && !diskValue.trim().equals("")) { - String colName = getColName(fileType); - - MachineOpenHelper.getInstance(activity).update(currMachine, colName, diskValue); - if (adapter.getPosition(diskValue) < 0) { - adapter.add(diskValue); - } - this.addDriveToList(diskValue, fileType); - this.setDiskValue(fileType, diskValue); + if (!validateFiles()) { + return; } - int res = spinner.getSelectedItemPosition(); - if (res == 1) { - spinner.setSelection(0); + try { + createMachineDir(MachineController.getInstance().getMachineSaveDir()); + } catch (Exception ex) { + ToastUtils.toastLong(LimboActivity.this, getString(R.string.Error) + ": " + ex); + return; } - } + // XXX: save the user defined dns server before we start the vm + LimboSettingsManager.setDNSServer(this, mDNS.getText().toString()); - private String getColName(FileType fileType) { - if (diskMapping.containsKey(fileType)) - return diskMapping.get(fileType).colName; - return null; - } + //XXX: make sure that bios files are installed in case we ran out of space in the last run + FileInstaller.installFiles(LimboActivity.this, false); - private ArrayAdapter getAdapter(FileType fileType) { - Spinner spinner = getSpinner(fileType); - return (ArrayAdapter) spinner.getAdapter(); + if (getMachine().getEnableVNC() == 1) { + startVNC(); + } else { + startSDL(); + } } - private Spinner getSpinner(FileType fileType) { - if (diskMapping.containsKey(fileType)) - return diskMapping.get(fileType).spinner; - return null; + private void createMachineDir(String dir) throws Exception { + File destDir = new File(dir); + if (!destDir.exists()) { + if (!destDir.mkdirs()) + throw new Exception(getString(R.string.failToCreateMachineDirError)); + } } - @Override - public void onStop() { - super.onStop(); + /** + * Starts the SDL Activity that will later start the native process via the service. + * This is done so that the java SDL part is initialized prior to starting the vm. + */ + public void startSDL() { + Intent intent = new Intent(LimboActivity.this, LimboSDLActivity.class); + startActivityForResult(intent, Config.SDL_REQUEST_CODE); } - @Override - public void onDestroy() { - savePendingEditText(); - super.onDestroy(); - this.stopTimeListener(); - + /** + * Start the vm with VNC Suport via the Controller which will later call the native process + * via the service. We do this since we don't have a built-in VNC client anymore. + */ + public void startVNC() { + if (LimboSettingsManager.getEnableExternalVNC(this)) { + // VNC external connections + LimboActivityCommon.promptVNCServer(this, + getString(R.string.ExternalVNCEnabledWarning), viewListener); + } else if (!LimboSettingsManager.getVNCEnablePassword(this)) { + // VNC Password is not enabled + LimboActivityCommon.promptVNCServer(this, + getString(R.string.VNCPasswordNotEnabledWarning), viewListener); + } else if (LimboSettingsManager.getVNCEnablePassword(this) + && LimboSettingsManager.getVNCPass(this) == null) { + // VNC Password is missing + ToastUtils.toastShort(this, getString(R.string.VNCPasswordMissing)); + } else { + notifyAction(MachineAction.START_VM, null); + } } - public void startvnc() { - - // Wait till Qemu settles - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - Logger.getLogger(LimboActivity.class.getName()).log(Level.SEVERE, null, ex); + private boolean validateFiles() { + return FileUtils.fileValid(getMachine().getHdaImagePath()) + && FileUtils.fileValid(getMachine().getHdbImagePath()) + && FileUtils.fileValid(getMachine().getHdcImagePath()) + && FileUtils.fileValid(getMachine().getHddImagePath()) + && FileUtils.fileValid(getMachine().getFdaImagePath()) + && FileUtils.fileValid(getMachine().getFdbImagePath()) + && FileUtils.fileValid(getMachine().getSdImagePath()) + && FileUtils.fileValid(getMachine().getCdImagePath()) + && FileUtils.fileValid(getMachine().getKernel()) + && FileUtils.fileValid(getMachine().getInitRd()); + } + + private void onStopButton(boolean exitApp) { + KeyboardUtils.hideKeyboard(this, mScrollView); + if (MachineController.getInstance().isRunning()) { + if (MachineController.getInstance().isVNCEnabled()) + LimboActivityCommon.promptStopVM(this, viewListener); + else { + LimboSDLActivity.pendingStop = true; + startSDL(); + } + } else { + if (getMachine() != null + && MachineController.getInstance().isPaused() && !exitApp) { + promptDiscardVMState(); + } else { + ToastUtils.toastShort(LimboActivity.this, getString(R.string.vmNotRunning)); + } } + } - runOnUiThread(new Runnable() { - @Override - public void run() { - mVNCAllowExternal.setEnabled(false); + private void onRestartButton() { + if (!MachineController.getInstance().isRunning()) { + if (getMachine() != null && getMachine().getPaused() == 1) { + promptDiscardVMState(); + } else { + ToastUtils.toastShort(LimboActivity.this, getString(R.string.VMNotRunning)); } - }); + } + LimboActivityCommon.promptResetVM(this, viewListener); + } + public void toggleSectionVisibility(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); + } + } - if (this.mVNCAllowExternal.isChecked() - && vnc_passwd != null && !vnc_passwd.equals("")) { - vmexecutor.change_vnc_password(); + public void setupWidgets() { + setupSections(); + mScrollView = findViewById(R.id.scroll_view); + mStatus = findViewById(R.id.statusVal); + mStatus.setImageResource(R.drawable.off); + mStatusText = findViewById(R.id.statusStr); - if (currMachine.paused != 1) - promptConnectLocally(activity); - else - connectLocally(); - } else { - connectLocally(); - } + mStart = findViewById(R.id.startvm); + mPause = findViewById(R.id.pausevm); + mStop = findViewById(R.id.stopvm); + mRestart = findViewById(R.id.restartvm); - } + //Machine + mMachine = findViewById(R.id.machineval); + if (MachineController.getInstance().isRunning()) + mMachine.setEnabled(false); - public void promptConnectLocally(final Activity activity) { + //ui + if (!Config.enable_SDL) + mUI.setEnabled(false); - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("VNC Started"); - TextView stateView = new TextView(activity); - stateView.setText("VNC Server started: " + getLocalIpAddress() + ":" + Config.defaultVNCPort + "\n" - + "Warning: VNC Connection is Unencrypted and not secure make sure you're on a private network!\n"); + mKeyboard = findViewById(R.id.keyboardval); + mMouse = findViewById(R.id.mouseval); - stateView.setPadding(20, 20, 20, 20); - alertDialog.setView(stateView); + //cpu/board + mCPU = findViewById(R.id.cpuval); + mMachineType = findViewById(R.id.machinetypeval); + mCPUNum = findViewById(R.id.cpunumval); + mUI = findViewById(R.id.uival); + mRamSize = findViewById(R.id.rammemval); + mEnableKVM = findViewById(R.id.enablekvmval); + mEnableMTTCG = findViewById(R.id.enablemttcgval); + mDisableACPI = findViewById(R.id.acpival); + mDisableHPET = findViewById(R.id.hpetval); + mDisableTSC = findViewById(R.id.tscval); - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - alertDialog.dismiss(); - } - }); - alertDialog.setButton(DialogInterface.BUTTON_NEUTRAL, "Connect Locally", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - connectLocally(); - } - }); - alertDialog.show(); - } - }, 100); + //disks + mHDA = findViewById(R.id.hdaimgval); + mHDAOptions = findViewById(R.id.hdaoptions); + mHDB = findViewById(R.id.hdbimgval); + mHDBOptions = findViewById(R.id.hdboptions); + mHDC = findViewById(R.id.hdcimgval); + mHDCOptions = findViewById(R.id.hdcoptions); + mHDD = findViewById(R.id.hddimgval); + mHDDOptions = findViewById(R.id.hddoptions); + + LinearLayout sharedFolderLayout = findViewById(R.id.sharedfolderl); + if (!Config.enableSharedFolder) + sharedFolderLayout.setVisibility(View.GONE); + mSharedFolder = findViewById(R.id.sharedfolderval); + //Removable storage + mCD = findViewById(R.id.cdromimgval); + mFDA = findViewById(R.id.floppyimgval); + mFDB = findViewById(R.id.floppybimgval); + mCDOptions = findViewById(R.id.cdromoptions); + if (!Config.enableEmulatedFloppy) { + LinearLayout mFDALayout = findViewById(R.id.floppyimgl); + mFDALayout.setVisibility(View.GONE); + LinearLayout mFDBLayout = findViewById(R.id.floppybimgl); + mFDBLayout.setVisibility(View.GONE); + } + mSD = findViewById(R.id.sdcardimgval); + if (!Config.enableEmulatedSDCard) { + LinearLayout mSDCardLayout = findViewById(R.id.sdcardimgl); + mSDCardLayout.setVisibility(View.GONE); + } + mCDenable = findViewById(R.id.cdromimgcheck); + mFDAenable = findViewById(R.id.floppyimgcheck); + mFDBenable = findViewById(R.id.floppybimgcheck); + mSDenable = findViewById(R.id.sdcardimgcheck); - } + //boot + mBootDevices = findViewById(R.id.bootfromval); + mKernel = findViewById(R.id.kernelval); + mInitrd = findViewById(R.id.initrdval); + mAppend = findViewById(R.id.appendval); - public void connectLocally() { - //UIUtils.toastShort(LimboActivity.this, "Connecting to VM Display"); - Intent intent = getVNCIntent(); - startActivityForResult(intent, Config.VNC_REQUEST_CODE); - } + //display + mVGAConfig = findViewById(R.id.vgacfgval); - public void startsdl() { + //sound + mSoundCard = findViewById(R.id.soundcfgval); - Intent intent = null; + //network + mNetConfig = findViewById(R.id.netcfgval); + mNetworkCard = findViewById(R.id.netDevicesVal); + mDNS = findViewById(R.id.dnsval); + setDefaultDNServer(); + mHOSTFWD = findViewById(R.id.hostfwdval); - intent = new Intent(this, LimboSDLActivity.class); + // advanced + mExtraParams = findViewById(R.id.extraparamsval); - android.content.ContentValues values = new android.content.ContentValues(); - startActivityForResult(intent, Config.SDL_REQUEST_CODE); + disableFeatures(); + enableRemovableDeviceOptions(false); + enableNonRemovableDeviceOptions(false); } - public void savevm(String name) { - if (vmexecutor != null) { - if (// - (currMachine.hda_img_path == null || currMachine.hda_img_path.equals("")) - && (currMachine.hdb_img_path == null || currMachine.hdb_img_path.equals("")) - && (currMachine.hdc_img_path == null || currMachine.hdc_img_path.equals("")) - && (currMachine.hdd_img_path == null || currMachine.hdd_img_path.equals(""))) { - UIUtils.toastLong(LimboActivity.this, "Couldn't find a QCOW2 image\nPlease attach an HDA or HDB image first!"); - } else { - vmexecutor.savevm("test_snapshot"); - UIUtils.toastShort(LimboActivity.this, "VM Saved"); - } - } else { + private void disableFeatures() { - UIUtils.toastShort(LimboActivity.this, "VM not running"); + LinearLayout mAudioSectionLayout = findViewById(R.id.audiosectionl); + if (!Config.enableSDLSound) { + mAudioSectionLayout.setVisibility(View.GONE); } + LinearLayout mDisableTSCLayout = findViewById(R.id.tscl); + LinearLayout mDisableACPILayout = findViewById(R.id.acpil); + LinearLayout mDisableHPETLayout = findViewById(R.id.hpetl); + LinearLayout mEnableKVMLayout = findViewById(R.id.kvml); + + if (LimboApplication.arch != Config.Arch.x86 && LimboApplication.arch != Config.Arch.x86_64) { + mDisableTSCLayout.setVisibility(View.GONE); + mDisableACPILayout.setVisibility(View.GONE); + mDisableHPETLayout.setVisibility(View.GONE); + } + if (LimboApplication.arch != Config.Arch.x86 && LimboApplication.arch != Config.Arch.x86_64 + && LimboApplication.arch != Config.Arch.arm && LimboApplication.arch != Config.Arch.arm64) { + mEnableKVMLayout.setVisibility(View.GONE); + } } - public void resumevm() { - if (vmexecutor != null) { - vmexecutor.resumevm(); - UIUtils.toastShort(LimboActivity.this, "VM Reset"); - } else { + private void setDefaultDNServer() { - UIUtils.toastShort(LimboActivity.this, "VM not running"); - } + Thread thread = new Thread(new Runnable() { + public void run() { + final String defaultDNSServer = LimboSettingsManager.getDNSServer(LimboActivity.this); + new Handler(Looper.getMainLooper()).post(new Runnable() { + public void run() { + // Code here will run in UI thread + mDNS.setText(defaultDNSServer); + } + }); + } + }); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); } - // Set Hard Disk - private void populateRAM() { - - String[] arraySpinner = new String[256]; + private void setupSections() { - arraySpinner[0] = 4 + ""; - for (int i = 1; i < arraySpinner.length; i++) { - arraySpinner[i] = i * 8 + ""; - } - ; + if (Config.collapseSections) { + mCPUSectionDetails = findViewById(R.id.cpusectionDetails); + mCPUSectionDetails.setVisibility(View.GONE); + mCPUSectionSummary = findViewById(R.id.cpusectionsummaryStr); + LinearLayout mCPUSectionHeader = findViewById(R.id.cpusectionheaderl); + mCPUSectionHeader.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + disableListeners(); + disableRemovableDiskListeners(); + toggleSectionVisibility(mCPUSectionDetails); + enableListenersDelayed(); + } + }); - ArrayAdapter ramAdapter = new ArrayAdapter(this, R.layout.custom_spinner_item, arraySpinner); - ramAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - this.mRamSize.setAdapter(ramAdapter); - this.mRamSize.invalidate(); - } + mStorageSectionDetails = findViewById(R.id.storagesectionDetails); + mStorageSectionDetails.setVisibility(View.GONE); + mStorageSectionSummary = findViewById(R.id.storagesectionsummaryStr); + LinearLayout mStorageSectionHeader = findViewById(R.id.storageheaderl); + mStorageSectionHeader.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + disableListeners(); + disableRemovableDiskListeners(); + toggleSectionVisibility(mStorageSectionDetails); + enableListenersDelayed(); + } + }); - private void populateCPUNum() { - String[] arraySpinner = new String[Config.MAX_CPU_NUM]; + mUserInterfaceSectionDetails = findViewById(R.id.userInterfaceDetails); + mUserInterfaceSectionDetails.setVisibility(View.GONE); + mUISectionSummary = findViewById(R.id.uisectionsummaryStr); + LinearLayout mUserInterfaceSectionHeader = findViewById(R.id.userinterfaceheaderl); + mUserInterfaceSectionHeader.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + disableListeners(); + disableRemovableDiskListeners(); + toggleSectionVisibility(mUserInterfaceSectionDetails); + enableListenersDelayed(); + } + }); - for (int i = 0; i < arraySpinner.length; i++) { - arraySpinner[i] = (i + 1) + ""; - } + mRemovableStorageSectionDetails = findViewById(R.id.removableStoragesectionDetails); + mRemovableStorageSectionDetails.setVisibility(View.GONE); + mRemovableStorageSectionSummary = findViewById(R.id.removablesectionsummaryStr); + LinearLayout mRemovableStorageSectionHeader = findViewById(R.id.removablestorageheaderl); + mRemovableStorageSectionHeader.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + disableListeners(); + disableRemovableDiskListeners(); + toggleSectionVisibility(mRemovableStorageSectionDetails); + enableListenersDelayed(); + } + }); - ArrayAdapter cpuNumAdapter = new ArrayAdapter(this, R.layout.custom_spinner_item, arraySpinner); - cpuNumAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - this.mCPUNum.setAdapter(cpuNumAdapter); - this.mCPUNum.invalidate(); - } + mGraphicsSectionDetails = findViewById(R.id.graphicssectionDetails); + mGraphicsSectionDetails.setVisibility(View.GONE); + mGraphicsSectionSummary = findViewById(R.id.graphicssectionsummaryStr); + LinearLayout mGraphicsSectionHeader = findViewById(R.id.graphicsheaderl); + mGraphicsSectionHeader.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + disableListeners(); + disableRemovableDiskListeners(); + toggleSectionVisibility(mGraphicsSectionDetails); + enableListenersDelayed(); + } + }); + mAudioSectionDetails = findViewById(R.id.audiosectionDetails); + mAudioSectionDetails.setVisibility(View.GONE); + mAudioSectionSummary = findViewById(R.id.audiosectionsummaryStr); + LinearLayout mAudioSectionHeader = findViewById(R.id.audioheaderl); + mAudioSectionHeader.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + disableListeners(); + disableRemovableDiskListeners(); + toggleSectionVisibility(mAudioSectionDetails); + enableListenersDelayed(); + } + }); - // Set Hard Disk - private void setRAM(final int ram) { + mNetworkSectionDetails = findViewById(R.id.networksectionDetails); + mNetworkSectionDetails.setVisibility(View.GONE); + mNetworkSectionSummary = findViewById(R.id.networksectionsummaryStr); + View mNetworkSectionHeader = findViewById(R.id.networkheaderl); + mNetworkSectionHeader.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + disableListeners(); + disableRemovableDiskListeners(); + toggleSectionVisibility(mNetworkSectionDetails); + enableListenersDelayed(); + } + }); - this.mRamSize.post(new Runnable() { - public void run() { - if (ram != 0) { - int pos = ((ArrayAdapter) mRamSize.getAdapter()).getPosition(ram + ""); - mRamSize.setSelection(pos); + mBootSectionDetails = findViewById(R.id.bootsectionDetails); + mBootSectionDetails.setVisibility(View.GONE); + mBootSectionSummary = findViewById(R.id.bootsectionsummaryStr); + View mBootSectionHeader = findViewById(R.id.bootheaderl); + mBootSectionHeader.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + disableListeners(); + disableRemovableDiskListeners(); + toggleSectionVisibility(mBootSectionDetails); + enableListenersDelayed(); } - } - }); + }); + mAdvancedSectionDetails = findViewById(R.id.advancedSectionDetails); + mAdvancedSectionDetails.setVisibility(View.GONE); + mAdvancedSectionSummary = findViewById(R.id.advancedsectionsummaryStr); + LinearLayout mAdvancedSectionHeader = findViewById(R.id.advancedheaderl); + mAdvancedSectionHeader.setOnClickListener(new OnClickListener() { + public void onClick(View view) { + disableListeners(); + disableRemovableDiskListeners(); + toggleSectionVisibility(mAdvancedSectionDetails); + enableListenersDelayed(); + } + }); + } } - private void setCPUNum(final int cpuNum) { - - this.mCPUNum.post(new Runnable() { + private void enableListenersDelayed() { + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override public void run() { - if (cpuNum != 0) { - int pos = ((ArrayAdapter) mCPUNum.getAdapter()).getPosition(cpuNum + ""); - mCPUNum.setSelection(pos); - } + setupMiscOptions(); + setupNonRemovableDiskListeners(); + enableRemovableDiskListeners(); } - }); - + }, 500); } - // Set Hard Disk - private void populateBootDevices() { - ArrayList bootDevicesList = new ArrayList(); - bootDevicesList.add("Default"); - bootDevicesList.add("CD Rom"); - bootDevicesList.add("Hard Disk"); - if(Config.enableEmulatedFloppy) - bootDevicesList.add("Floppy"); - - String[] arraySpinner = bootDevicesList.toArray(new String[bootDevicesList.size()]); - - ArrayAdapter bootDevAdapter = new ArrayAdapter(this, R.layout.custom_spinner_item, arraySpinner); - bootDevAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - this.mBootDevices.setAdapter(bootDevAdapter); - this.mBootDevices.invalidate(); + public void updateUISummary(boolean clear) { + if (clear || getMachine() == null || mMachine.getSelectedItemPosition() < 2) + mUISectionSummary.setText(""); + else { + String text = getString(R.string.display) + ": " + (getMachine().getEnableVNC() == 1 ? "VNC" : "SDL"); + if (getMachine().getEnableVNC() == 1) { + text += ", " + getString(R.string.server); + text += ": " + NetworkUtils.getVNCAddress(this) + ":" + Config.defaultVNCPort; + } + if (getMachine().getKeyboard() != null) { + text += ", " + getString(R.string.keyboard) + ": " + getMachine().getKeyboard(); + } + if (getMachine().getMouse() != null) { + text += ", " + getString(R.string.mouse) + ": " + getMachine().getMouse(); + } + mUISectionSummary.setText(text); + } } - // Set Net Cfg - private void populateNet() { - String[] arraySpinner = {"None", "User", "TAP"}; - ArrayAdapter netAdapter = new ArrayAdapter(this, R.layout.custom_spinner_item, arraySpinner); - netAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - this.mNetConfig.setAdapter(netAdapter); - this.mNetConfig.invalidate(); + private Machine getMachine() { + return MachineController.getInstance().getMachine(); } - // Set VGA Cfg - private void populateVGA() { - ArrayList arrList = new ArrayList(); - - - if (Config.enable_X86 || Config.enable_X86_64 - || Config.enable_ARM || Config.enable_ARM64 - || Config.enable_PPC || Config.enable_PPC64 - ) { - arrList.add("std"); + public void updateCPUSummary(boolean clear) { + if (clear || getMachine() == null || mMachine.getSelectedItemPosition() < 2) + mCPUSectionSummary.setText(""); + else { + String text = "Machine Type: " + getMachine().getMachineType() + + ", CPU: " + getMachine().getCpu() + + ", " + getMachine().getCpuNum() + " CPU" + ((getMachine().getCpuNum() > 1) ? "s" : "") + + ", " + getMachine().getMemory() + " MB"; + if (mEnableMTTCG.isChecked()) + text = appendOption("Enable MTTCG", text); + if (mEnableKVM.isChecked()) + text = appendOption("Enable KVM", text); + if (mDisableACPI.isChecked()) + text = appendOption("Disable ACPI", text); + if (mDisableHPET.isChecked()) + text = appendOption("Disable HPET", text); + if (mDisableTSC.isChecked()) + text = appendOption("Disable TSC", text); + mCPUSectionSummary.setText(text); } + } - if (Config.enable_X86 || Config.enable_X86_64 - ) { - arrList.add("cirrus"); - arrList.add("vmware"); - } + public void updateStorageSummary(boolean clear) { + if (clear || getMachine() == null || mMachine.getSelectedItemPosition() < 2) + mStorageSectionSummary.setText(""); + else { + String text = null; + text = appendDriveFilename(getMachine().getHdaImagePath(), text, "HDA", false); + text = appendDriveFilename(getMachine().getHdbImagePath(), text, "HDB", false); + text = appendDriveFilename(getMachine().getHdcImagePath(), text, "HDC", false); + text = appendDriveFilename(getMachine().getHddImagePath(), text, "HDD", false); - //override vga for sun4m (32bit) machines to cg3 - if (Config.enable_sparc) { - arrList.add("cg3"); - } + if (Config.enableSharedFolder) + text = appendDriveFilename(getMachine().getSharedFolderPath(), text, + getString(R.string.SharedFolder), false); - if (Config.enable_ARM || Config.enable_ARM64) { - arrList.add("virtio-gpu-pci"); + if (text == null || text.equals("'")) + text = "None"; + mStorageSectionSummary.setText(text); } - - if (Config.enable_SPICE) - arrList.add("qxl"); - - //XXX: some archs don't support vga on QEMU like SPARC64 - arrList.add("nographic"); - - //TODO: Add XEN??? - // "xenfb" - - ArrayAdapter vgaAdapter = new ArrayAdapter(this, R.layout.custom_spinner_item, arrList); - vgaAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - this.mVGAConfig.setAdapter(vgaAdapter); - this.mVGAConfig.invalidate(); } - private void populateOrientation() { + public void updateRemovableStorageSummary(boolean clear) { + if (clear || getMachine() == null || mMachine.getSelectedItemPosition() < 2) + mRemovableStorageSectionSummary.setText(""); + else { + String text = null; - ArrayList arrList = new ArrayList(); - arrList.add("Auto"); - arrList.add("Landscape"); - arrList.add("Landscape Reverse"); - arrList.add("Portrait"); - arrList.add("Portrait Reverse"); + text = appendDriveFilename(getMachine().getCdImagePath(), text, "CDROM", true); + text = appendDriveFilename(getMachine().getFdaImagePath(), text, "FDA", true); + text = appendDriveFilename(getMachine().getFdbImagePath(), text, "FDB", true); + text = appendDriveFilename(getMachine().getSdImagePath(), text, "SD", true); - ArrayAdapter orientationAdapter = new ArrayAdapter(this, R.layout.custom_spinner_item, arrList); - orientationAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - this.mOrientation.setAdapter(orientationAdapter); - this.mOrientation.invalidate(); - int pos = LimboSettingsManager.getOrientationSetting(activity); - if (pos >= 0) { - this.mOrientation.setSelection(pos); + if (text == null || text.equals("")) + text = "None"; + + mRemovableStorageSectionSummary.setText(text); } } - private void populateKeyboardLayout() { - ArrayList arrList = new ArrayList(); - arrList.add("en-us"); - - - ArrayAdapter keyboardAdapter = new ArrayAdapter(this, R.layout.custom_spinner_item, arrList); - keyboardAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - this.mKeyboard.setAdapter(keyboardAdapter); - this.mKeyboard.invalidate(); - - //TODO: for now we use only English keyboard, add more layouts - int pos = 0; - if (pos >= 0) { - this.mKeyboard.setSelection(pos); + public void updateBootSummary(boolean clear) { + if (clear || getMachine() == null || mMachine.getSelectedItemPosition() < 2) + mBootSectionSummary.setText(""); + else { + String text = "Boot from: " + getMachine().getBootDevice(); + text = appendDriveFilename(getMachine().getKernel(), text, "kernel", false); + text = appendDriveFilename(getMachine().getInitRd(), text, "initrd", false); + text = appendDriveFilename(getMachine().getAppend(), text, "append", false); + mBootSectionSummary.setText(text); } } - private void populateMouse() { - ArrayList arrList = new ArrayList(); - arrList.add("ps2"); - arrList.add("usb-mouse"); - arrList.add("usb-tablet" + fixMouseDescr); + private String appendDriveFilename(String driveFile, String text, String drive, boolean allowEmptyDrive) { - ArrayAdapter mouseAdapter = new ArrayAdapter(this, R.layout.custom_spinner_item, arrList); - mouseAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - this.mMouse.setAdapter(mouseAdapter); - this.mMouse.invalidate(); -// -// int pos = LimboSettingsManager.getMouseSetting(activity); -// if (pos >= 0) { -// this.mMouse.setSelection(pos); -// } + String file = null; + if (driveFile != null) { + if ((driveFile.equals("") || driveFile.equals("None")) && allowEmptyDrive) { + file = drive + ": Empty"; + } else if (!driveFile.equals("") && !driveFile.equals("None")) + file = drive + ": " + FileUtils.getFilenameFromPath(driveFile); + } + if (text == null && file != null) + text = file; + else if (file != null) + text += (", " + file); + return text; } - private void populateSoundcardConfig() { - - String[] arraySpinner = {"None", "sb16", "ac97", "adlib", "cs4231a", "gus", "es1370", "hda", "pcspk", "all"}; - - ArrayAdapter sndAdapter = new ArrayAdapter(this, R.layout.custom_spinner_item, arraySpinner); - sndAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - this.mSoundCard.setAdapter(sndAdapter); - this.mSoundCard.invalidate(); + public void updateGraphicsSummary(boolean clear) { + if (clear || getMachine() == null || mMachine.getSelectedItemPosition() < 2) + mGraphicsSectionSummary.setText(""); + else { + String text = "Video Card: " + getMachine().getVga(); + mGraphicsSectionSummary.setText(text); + } } - // Set Cache Cfg - private void populateHDCacheConfig() { - - String[] arraySpinner = {"default", "none", "writeback", "writethrough"}; - - ArrayAdapter hdCacheAdapter = new ArrayAdapter(this, R.layout.custom_spinner_item, arraySpinner); - hdCacheAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - this.mHDCacheConfig.setAdapter(hdCacheAdapter); - this.mHDCacheConfig.invalidate(); + public void updateAudioSummary(boolean clear) { + if (clear || getMachine() == null + || mMachine.getSelectedItemPosition() < 2) + mAudioSectionSummary.setText(""); + else { + String soundCard = getMachine().getSoundCard(); + String text = getString(R.string.AudioCard) + ": " + (soundCard != null ? soundCard : "None"); + mAudioSectionSummary.setText(text); + } } - // Set Hard Disk - private void populateNetDevices(String nic) { - - String[] arraySpinner = {"Default","e1000", "pcnet", "rtl8139", "ne2k_pci", "i82551", "i82557b", - "i82559er", "virtio"}; - - ArrayList arrList = new ArrayList(); - arrList.add("Default"); - - if (currMachine != null && currMachine.arch != null) { - if (currMachine.arch.equals("x86")) { - - arrList = new ArrayList(Arrays.asList(arraySpinner)); - } else if (currMachine.arch.equals("x64")) { - arrList = new ArrayList(Arrays.asList(arraySpinner)); - } else if (currMachine.arch.equals("ARM") - || currMachine.arch.equals("ARM64")) { - arrList = new ArrayList(Arrays.asList(arraySpinner)); - arrList.add("smc91c111"); - arrList.add("xgmac"); - arrList.add("lan9118"); - arrList.add("cadence_gem"); - arrList.add("allwinner-emac"); - arrList.add("mv88w8618"); - } else if (currMachine.arch.equals("PPC") || currMachine.arch.equals("PPC64")) { - arrList = new ArrayList(Arrays.asList(arraySpinner)); - } else if (currMachine.arch.equals("SPARC") || currMachine.arch.equals("SPARC64")) { - arrList.add("lance"); + public void updateNetworkSummary(boolean clear) { + if (clear || getMachine() == null + || mMachine.getSelectedItemPosition() < 2) + mNetworkSectionSummary.setText(""); + else { + String netCfg = getMachine().getNetwork(); + String text = getString(R.string.Network) + ": " + (netCfg != null ? netCfg : "None"); + if (netCfg != null && !netCfg.equals("None")) { + String nicCard = getMachine().getNetworkCard(); + text += ", " + getString(R.string.NicCard) + ": " + (nicCard != null ? nicCard : "None"); + text += ", " + getString(R.string.DNSServer) + ": " + mDNS.getText(); + String hostFWD = getMachine().getHostFwd(); + if (hostFWD != null && !hostFWD.equals("")) + text += ", " + getString(R.string.HostForward) + ": " + hostFWD; } - } else { - arrList = new ArrayList(Arrays.asList(arraySpinner)); + mNetworkSectionSummary.setText(text); } + } - ArrayAdapter nicCfgAdapter = new ArrayAdapter(this, R.layout.custom_spinner_item, arrList); - nicCfgAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - this.mNicCard.setAdapter(nicCfgAdapter); - - - this.mNicCard.invalidate(); - - int pos = nicCfgAdapter.getPosition(nic); - if (pos >= 0) { - this.mNicCard.setSelection(pos); + public void updateAdvancedSummary(boolean clear) { + if (clear || getMachine() == null || mMachine.getSelectedItemPosition() < 2) + mAdvancedSectionSummary.setText(""); + else { + String text = ""; + if (getMachine().getExtraParams() != null + && !getMachine().getExtraParams().equals("")) + text = getString(R.string.ExtraParams) + ": " + getMachine().getExtraParams(); + mAdvancedSectionSummary.setText(text); } } - // Set Hard Disk - private void populateMachines(final String machineValue) { - - Thread thread = new Thread(new Runnable() { - public void run() { - - ArrayList machines = MachineOpenHelper.getInstance(activity).getMachines(); - int length = 0; - if (machines == null || machines.size() == 0) { - length = 0; - } else { - length = machines.size(); - } - - final String[] arraySpinner = new String[machines.size() + 2]; - arraySpinner[0] = "None"; - arraySpinner[1] = "New"; - int index = 2; - Iterator i = machines.iterator(); - while (i.hasNext()) { - String file = (String) i.next(); - if (file != null) { - arraySpinner[index++] = file; - } - } - - new Handler(Looper.getMainLooper()).post(new Runnable() { - public void run() { - ArrayAdapter machineAdapter = new ArrayAdapter(activity, R.layout.custom_spinner_item, arraySpinner); - machineAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - mMachine.setAdapter(machineAdapter); - mMachine.invalidate(); - if (machineValue != null) - setAdapter(mMachine, machineValue); - } - }); - - } - }); - thread.setPriority(Thread.MIN_PRIORITY); - thread.start(); + private String appendOption(String option, String text) { + if (text == null && option != null) + text = option; + else if (option != null) + text += (", " + option); + return text; } - // Set Hard Disk - private void setCPU(final String cpu) { - - this.mCPU.post(new Runnable() { - public void run() { - if (cpu != null) { - int pos = ((ArrayAdapter) mCPU.getAdapter()).getPosition(cpu); - - mCPU.setSelection(pos); - } - } - }); - - } + private void triggerUpdateSpinner(final Spinner spinner) { - private void setAdapter(final Spinner spinner, final String value) { + final int position = (int) spinner.getSelectedItemId(); + spinner.setSelection(0); - spinner.post(new Runnable() { + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override public void run() { - if (value != null) { - int pos = ((ArrayAdapter) spinner.getAdapter()).getPosition(value); - spinner.setSelection(pos); - } + spinner.setSelection(position); } - }); - - } - - private void setMachineFieldValue(FileType type, final String diskValue) { - if (type == FileType.HDA) { - currMachine.hda_img_path = diskValue; - } else if (type == FileType.HDB) { - currMachine.hdb_img_path = diskValue; - } else if (type == FileType.HDC) { - currMachine.hdc_img_path = diskValue; - } else if (type == FileType.HDD) { - currMachine.hdd_img_path = diskValue; - } else if (type == FileType.SHARED_DIR) { - currMachine.shared_folder = diskValue; - } else if (type == FileType.CD) { - currMachine.cd_iso_path = diskValue; - } else if (type == FileType.FDA) { - currMachine.fda_img_path = diskValue; - } else if (type == FileType.FDB) { - currMachine.fdb_img_path = diskValue; - } else if (type == FileType.SD) { - currMachine.sd_img_path = diskValue; - } else if (type == FileType.KERNEL) { - currMachine.kernel = diskValue; - } else if (type == FileType.INITRD) { - currMachine.initrd = diskValue; - } + }, 100); } - private void setDiskValue(FileType fileType, final String diskValue) { - Spinner spinner = getSpinner(fileType); - ArrayAdapter adapter = getAdapter(fileType); - - setMachineFieldValue(fileType, diskValue); - setDiskAdapter(spinner, diskValue); - } + private void loadMachine() { - private void setDiskAdapter(final Spinner spinner, final String value) { - spinner.post(new Runnable() { + setUserPressed(false); + if (getMachine() == null) { + return; + } + new Handler(Looper.getMainLooper()).post(new Runnable() { public void run() { - if (value != null) { - int pos = ((ArrayAdapter) spinner.getAdapter()).getPosition(value); - - if (pos >= 0) { - spinner.setSelection(pos); - } else { - spinner.setSelection(0); + loadMachineUI(); + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + postLoadMachineUI(); } - } else { - spinner.setSelection(0); - } + }, 1000); + setCPUOptions(); + getMachine().addObserver(LimboActivity.this); } }); } - private void setHDA(final String hda) { - currMachine.hda_img_path = hda; - setDiskAdapter(mHDA, hda); - } + private void postLoadMachineUI() { - private void setHDB(final String hdb) { - this.currMachine.hdb_img_path = hdb; - setDiskAdapter(mHDB, hdb); - } + mFDAenable.setChecked(getMachine().getFdaImagePath() != null); + mFDBenable.setChecked(getMachine().getFdbImagePath() != null); + mCDenable.setChecked(getMachine().getCdImagePath() != null); + mSDenable.setChecked(getMachine().getSdImagePath() != null); - private void setHDC(final String hdc) { + changeStatus(MachineController.getInstance().getCurrStatus()); + if (getMachine().getPaused() == 1) { + enableNonRemovableDeviceOptions(false); + enableRemovableDeviceOptions(false); + } else { + enableNonRemovableDeviceOptions(true); + enableRemovableDeviceOptions(true); + } + setUserPressed(true); + machineLoaded = false; + mMachine.setEnabled(!MachineController.getInstance().isRunning()); + } + + private void loadMachineUI() { + populateMachineType(getMachine().getMachineType()); + populateCPUs(getMachine().getCpu()); + populateNetDevices(getMachine().getNetworkCard()); + SpinnerAdapter.setDiskAdapterValue(mCPUNum, getMachine().getCpuNum() + ""); + SpinnerAdapter.setDiskAdapterValue(mRamSize, getMachine().getMemory() + ""); + seMachineDriveValue(FileType.KERNEL, getMachine().getKernel()); + seMachineDriveValue(FileType.INITRD, getMachine().getInitRd()); + if (getMachine().getAppend() != null) + mAppend.setText(getMachine().getAppend()); + else + mAppend.setText(""); - this.currMachine.hdc_img_path = hdc; - setDiskAdapter(mHDC, hdc); - } + if (getMachine().getHostFwd() != null) + mHOSTFWD.setText(getMachine().getHostFwd()); + else + mHOSTFWD.setText(""); - private void setHDD(final String hdd) { - this.currMachine.hdd_img_path = hdd; - setDiskAdapter(mHDD, hdd); - } + if (getMachine().getExtraParams() != null) + mExtraParams.setText(getMachine().getExtraParams()); + else + mExtraParams.setText(""); + + // CDROM + seMachineDriveValue(FileType.CDROM, getMachine().getCdImagePath()); + + // Floppy + seMachineDriveValue(FileType.FDA, getMachine().getFdaImagePath()); + seMachineDriveValue(FileType.FDB, getMachine().getFdbImagePath()); + + // SD Card + seMachineDriveValue(FileType.SD, getMachine().getSdImagePath()); + + // HDD + seMachineDriveValue(FileType.HDA, getMachine().getHdaImagePath()); + seMachineDriveValue(FileType.HDB, getMachine().getHdbImagePath()); + seMachineDriveValue(FileType.HDC, getMachine().getHdcImagePath()); + seMachineDriveValue(FileType.HDD, getMachine().getHddImagePath()); + + //sharedfolder + seMachineDriveValue(FileType.SHARED_DIR, getMachine().getSharedFolderPath()); + + // Advance + SpinnerAdapter.setDiskAdapterValue(mBootDevices, getMachine().getBootDevice()); + SpinnerAdapter.setDiskAdapterValue(mNetConfig, getMachine().getNetwork()); + SpinnerAdapter.setDiskAdapterValue(mVGAConfig, getMachine().getVga()); + SpinnerAdapter.setDiskAdapterValue(mSoundCard, getMachine().getSoundCard()); + SpinnerAdapter.setDiskAdapterValue(mUI, getMachine().getEnableVNC() == 1 ? "VNC" : "SDL"); + SpinnerAdapter.setDiskAdapterValue(mMouse, fixMouseValue(getMachine().getMouse())); + SpinnerAdapter.setDiskAdapterValue(mKeyboard, getMachine().getKeyboard()); + + // motherboard settings + mDisableACPI.setChecked(getMachine().getDisableAcpi() == 1); + mDisableHPET.setChecked(getMachine().getDisableHPET() == 1); + if (LimboApplication.arch == Config.Arch.x86 || LimboApplication.arch == Config.Arch.x86_64) + mDisableTSC.setChecked(getMachine().getDisableTSC() == 1); + mEnableKVM.setChecked(getMachine().getEnableKVM() == 1); + mEnableMTTCG.setChecked(getMachine().getEnableMTTCG() == 1); - private void setFDA(final String fda) { - this.currMachine.fda_img_path = fda; - setDiskAdapter(mFDA, fda); - } + enableNonRemovableDeviceOptions(true); + enableRemovableDeviceOptions(!MachineController.getInstance().isRunning()); - private void setFDB(final String fdb) { - this.currMachine.fdb_img_path = fdb; - setDiskAdapter(mFDB, fdb); - } + if (Config.enableSDLSound) { + mSoundCard.setEnabled(getMachine().getEnableVNC() != 1 && getMachine().getPaused() == 0); + } else + mSoundCard.setEnabled(false); - private void setSD(final String sd) { - this.currMachine.sd_img_path = sd; - setDiskAdapter(mSD, sd); + mMachine.setEnabled(false); } + private String fixMouseValue(String mouse) { + if (mouse != null) { + if (mouse.startsWith("usb-tablet")) + mouse += " " + getString(R.string.fixesMouseParen); + } + return mouse; + } - //XXX: Not supported - private void setHDCache(final String hdcache) { + private synchronized void updateSummary() { + updateUISummary(false); + updateCPUSummary(false); + updateStorageSummary(false); + updateRemovableStorageSummary(false); + updateGraphicsSummary(false); + updateAudioSummary(false); + updateNetworkSummary(false); + updateBootSummary(false); + updateAdvancedSummary(false); + } - mHDCacheConfig.post(new Runnable() { - public void run() { - if (hdcache != null) { - int pos = ((ArrayAdapter) mHDCacheConfig.getAdapter()).getPosition(hdcache); + public void promptMachineName(final Activity activity) { + final AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(activity).create(); + alertDialog.setTitle(getString(R.string.NewMachineName)); + final EditText vmNameTextView = new EditText(activity); + vmNameTextView.setPadding(20, 20, 20, 20); + vmNameTextView.setEnabled(true); + vmNameTextView.setVisibility(View.VISIBLE); + vmNameTextView.setSingleLine(); + alertDialog.setView(vmNameTextView); + alertDialog.setCanceledOnTouchOutside(false); + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.Create), (DialogInterface.OnClickListener) null); - if (pos >= 0) { - mHDCacheConfig.setSelection(pos); - } else { - mHDCacheConfig.setSelection(0); - } - } else { - mHDCacheConfig.setSelection(0); + alertDialog.show(); + Button button = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); + button.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + if (vmNameTextView.getText().toString().trim().equals("")) + ToastUtils.toastShort(activity, getString(R.string.MachineNameCannotBeEmpty)); + else { + createMachine(vmNameTextView.getText().toString()); + alertDialog.dismiss(); } } }); - + alertDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(vmNameTextView.getWindowToken(), 0); + } + }); } - private void setSoundcard(final String soundcard) { + public void promptImageName(final Activity activity, final FileType fileType) { - this.mSoundCard.post(new Runnable() { - public void run() { - if (soundcard != null) { - int pos = ((ArrayAdapter) mSoundCard.getAdapter()).getPosition(soundcard); + final AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(activity).create(); + alertDialog.setTitle(getString(R.string.ImageName)); - if (pos >= 0) { - mSoundCard.setSelection(pos); - } else { - mSoundCard.setSelection(0); - } - } else { - mSoundCard.setSelection(0); - } - } - }); + LinearLayout mLayout = new LinearLayout(this); + mLayout.setPadding(20, 20, 20, 20); + mLayout.setOrientation(LinearLayout.VERTICAL); - } + final EditText imageNameView = new EditText(activity); + imageNameView.setEnabled(true); + imageNameView.setVisibility(View.VISIBLE); + imageNameView.setSingleLine(); + LinearLayout.LayoutParams imageNameViewParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + mLayout.addView(imageNameView, imageNameViewParams); - private void setUI(final String ui) { + final Spinner size = new Spinner(this); + LinearLayout.LayoutParams spinnerParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - this.mUI.post(new Runnable() { - public void run() { - if (ui != null) { - int pos = ((ArrayAdapter) mUI.getAdapter()).getPosition(ui); + String[] arraySpinner = new String[5]; + arraySpinner[0] = "1GB (Growable)"; + arraySpinner[1] = "2GB (Growable)"; + arraySpinner[2] = "4GB (Growable)"; + arraySpinner[3] = "10 GB (Growable)"; + arraySpinner[4] = "20 GB (Growable)"; - if (pos >= 0) { - mUI.setSelection(pos); - } else { - mUI.setSelection(0); - } - } else { - mUI.setSelection(0); + ArrayAdapter sizeAdapter = new ArrayAdapter(this, R.layout.custom_spinner_item, arraySpinner); + sizeAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); + size.setAdapter(sizeAdapter); + mLayout.addView(size, spinnerParams); - } - } - }); + alertDialog.setView(mLayout); - } + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.Create), (DialogInterface.OnClickListener) null); + alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.ChangeDirectory), (DialogInterface.OnClickListener) null); - private void setVGA(final String vga) { + alertDialog.show(); - this.mVGAConfig.post(new Runnable() { - public void run() { - if (vga != null) { - int pos = ((ArrayAdapter) mVGAConfig.getAdapter()).getPosition(vga); + Button positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE); + positiveButton.setOnClickListener(new View.OnClickListener() { - if (pos >= 0) { - mVGAConfig.setSelection(pos); - } else { - mVGAConfig.setSelection(0); + @Override + public void onClick(View view) { + if (LimboSettingsManager.getImagesDir(LimboActivity.this) == null) { + changeImagesDir(); + return; + } + + int sizeSel = size.getSelectedItemPosition(); + String templateImage = "hd1g.qcow2"; + if (sizeSel == 0) { + templateImage = "hd1g.qcow2"; + } else if (sizeSel == 1) { + templateImage = "hd2g.qcow2"; + } else if (sizeSel == 2) { + templateImage = "hd4g.qcow2"; + } else if (sizeSel == 3) { + templateImage = "hd10g.qcow2"; + } else if (sizeSel == 4) { + templateImage = "hd20g.qcow2"; + } + + String image = imageNameView.getText().toString(); + if (image.trim().equals("")) + ToastUtils.toastShort(activity, getString(R.string.ImageFilenameCannotBeEmpty)); + else { + if (!image.endsWith(".qcow2")) { + image += ".qcow2"; + } + String filePath = FileUtils.createImgFromTemplate(LimboActivity.this, templateImage, image, fileType); + if (filePath!=null) { + updateDrive(fileType, filePath); + alertDialog.dismiss(); } - } else { - mVGAConfig.setSelection(0); } } }); - } - - - private void setMouse(final String mouse) { + Button negativeButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE); + negativeButton.setOnClickListener(new View.OnClickListener() { - this.mMouse.post(new Runnable() { - public void run() { - if (mouse!= null) { - String mouseStr = mouse; - if(mouseStr.startsWith("usb-tablet")) - mouseStr+= fixMouseDescr; - int pos = ((ArrayAdapter) mMouse.getAdapter()).getPosition(mouseStr); - - if (pos >= 0) { - mMouse.setSelection(pos); - } else { - mMouse.setSelection(0); - } - } else { - mMouse.setSelection(0); + @Override + public void onClick(View view) { + changeImagesDir(); - } } }); + } + public void changeImagesDir() { + ToastUtils.toastLong(LimboActivity.this, getString(R.string.chooseDirToCreateImage)); + LimboFileManager.browse(LimboActivity.this, FileType.IMAGE_DIR, Config.OPEN_IMAGE_DIR_REQUEST_CODE); } - private void setKeyboard(final String keyboard) { + public boolean onKeyDown(int keyCode, KeyEvent event) { - this.mKeyboard.post(new Runnable() { - public void run() { - if (keyboard!= null) { - String mouseStr = keyboard; - int pos = ((ArrayAdapter) mKeyboard.getAdapter()).getPosition(keyboard); + if (keyCode == KeyEvent.KEYCODE_BACK) { + moveTaskToBack(true); + return true; // return + } - if (pos >= 0) { - mKeyboard.setSelection(pos); - } else { - mKeyboard.setSelection(0); - } - } else { - mKeyboard.setSelection(0); + return false; + } - } + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (resultCode == Config.SDL_QUIT_RESULT_CODE) { + if (getParent() != null) { + getParent().finish(); } - }); + finish(); + if (MachineController.getInstance().isRunning()) { + notifyAction(MachineAction.STOP_VM, null); + } + } else if (requestCode == Config.OPEN_IMPORT_FILE_REQUEST_CODE || requestCode == Config.OPEN_IMPORT_FILE_ASF_REQUEST_CODE) { + String file; + if (requestCode == Config.OPEN_IMPORT_FILE_ASF_REQUEST_CODE) { + file = FileUtils.getFileUriFromIntent(this, data, false); + } else { + file = FileUtils.getFilePathFromIntent(this, data); + } + if (file != null) + importMachines(file); + } else if (requestCode == Config.OPEN_EXPORT_DIR_REQUEST_CODE || requestCode == Config.OPEN_EXPORT_DIR_ASF_REQUEST_CODE) { + String exportDir; + if (requestCode == Config.OPEN_EXPORT_DIR_ASF_REQUEST_CODE) { + exportDir = FileUtils.getFileUriFromIntent(this, data, true); + } else { + exportDir = FileUtils.getDirPathFromIntent(this, data); + } + if (exportDir != null) + LimboSettingsManager.setExportDir(this, exportDir); + } else if (requestCode == Config.OPEN_IMAGE_FILE_REQUEST_CODE || requestCode == Config.OPEN_IMAGE_FILE_ASF_REQUEST_CODE) { + String file; + if (requestCode == Config.OPEN_IMAGE_FILE_ASF_REQUEST_CODE) { + file = FileUtils.getFileUriFromIntent(this, data, true); + } else { + browseFileType = FileUtils.getFileTypeFromIntent(this, data); + file = FileUtils.getFilePathFromIntent(this, data); + } + if (file != null) + updateDrive(browseFileType, file); + } else if (requestCode == Config.OPEN_IMAGE_DIR_REQUEST_CODE || requestCode == Config.OPEN_IMAGE_DIR_ASF_REQUEST_CODE) { + String imageDir; + if (requestCode == Config.OPEN_IMAGE_DIR_ASF_REQUEST_CODE) { + imageDir = FileUtils.getFileUriFromIntent(this, data, true); + } else { + imageDir = FileUtils.getDirPathFromIntent(this, data); + } + if (imageDir != null) + LimboSettingsManager.setImagesDir(this, imageDir); + } else if (requestCode == Config.OPEN_SHARED_DIR_REQUEST_CODE || requestCode == Config.OPEN_SHARED_DIR_ASF_REQUEST_CODE) { + String file; + if (requestCode == Config.OPEN_SHARED_DIR_ASF_REQUEST_CODE) { + file = FileUtils.getFileUriFromIntent(this, data, true); + } else { + browseFileType = FileUtils.getFileTypeFromIntent(this, data); + file = FileUtils.getDirPathFromIntent(this, data); + } + if (file != null) { + updateDrive(browseFileType, file); + LimboSettingsManager.setSharedDir(this, file); + } + } else if (requestCode == Config.OPEN_LOG_FILE_DIR_REQUEST_CODE || requestCode == Config.OPEN_LOG_FILE_DIR_ASF_REQUEST_CODE) { + String file; + if (requestCode == Config.OPEN_LOG_FILE_DIR_ASF_REQUEST_CODE) { + file = FileUtils.getFileUriFromIntent(this, data, true); + } else { + file = FileUtils.getDirPathFromIntent(this, data); + } + if (file != null) { + FileUtils.saveLogToFile(LimboActivity.this, file); + } + } else if (requestCode == Config.OPEN_IMPORT_BIOS_FILE_REQUEST_CODE || requestCode == Config.OPEN_IMPORT_BIOS_FILE_ASF_REQUEST_CODE) { + String file; + if (requestCode == Config.OPEN_IMPORT_BIOS_FILE_ASF_REQUEST_CODE) { + file = FileUtils.getFileUriFromIntent(this, data, false); + } else { + file = FileUtils.getFilePathFromIntent(this, data); + } + if (file != null) + BIOSImporter.importBIOSFile(this, file); + } } + private void updateDrive(FileType fileType, String diskValue) { + //FIXME: sometimes the array adapters try to set invalid values + if (fileType == null || diskValue == null) { + return; + } + Spinner spinner = getSpinner(fileType); + if (!diskValue.trim().isEmpty()) { + if (SpinnerAdapter.getPositionFromSpinner(spinner, diskValue) < 0) { + android.widget.SpinnerAdapter adapter = spinner.getAdapter(); + if (adapter instanceof ArrayAdapter) { + SpinnerAdapter.addItem(spinner, diskValue); + } + } + notifyAction(MachineAction.INSERT_FAV, new Object[]{diskValue, fileType}); + seMachineDriveValue(fileType, diskValue); + } + int res = spinner.getSelectedItemPosition(); + if (res == 1) { + spinner.setSelection(0); + } + } - private void setNetCfg(final String net, boolean userPressed) { -// this.userPressedNetCfg = userPressed; + private ArrayAdapter getAdapter(FileType fileType) { + Spinner spinner = getSpinner(fileType); + return (ArrayAdapter) spinner.getAdapter(); + } - this.mNetConfig.post(new Runnable() { - public void run() { - if (net != null) { - int pos = ((ArrayAdapter) mNetConfig.getAdapter()).getPosition(net); + private Spinner getSpinner(FileType fileType) { + if (diskMapping.containsKey(fileType)) + return diskMapping.get(fileType).spinner; + return null; + } - if (pos >= 0) { - mNetConfig.setSelection(pos); - } else { - mNetConfig.setSelection(0); - } - } else { - mNetConfig.setSelection(0); + private MachineProperty getProperty(FileType fileType) { + if (diskMapping.containsKey(fileType)) + return diskMapping.get(fileType).colName; + return null; + } - } - } - }); + @Override + public void onStop() { + super.onStop(); + } + @Override + public void onDestroy() { + savePendingEditText(); + MachineController.getInstance().removeOnStatusChangeListener(this); + getMachine().deleteObserver(LimboActivity.this); + setViewListener(null); + super.onDestroy(); } - private void setBootDevice(final String bootDevice) { + private void populateRAM() { + String[] arraySpinner = new String[4 * 256]; + arraySpinner[0] = 4 + ""; + for (int i = 1; i < arraySpinner.length; i++) { + arraySpinner[i] = i * 8 + ""; + } + ArrayAdapter ramAdapter = new ArrayAdapter<>(this, R.layout.custom_spinner_item, arraySpinner); + ramAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); + mRamSize.setAdapter(ramAdapter); + mRamSize.invalidate(); + } - this.mBootDevices.post(new Runnable() { - public void run() { - if (bootDevice != null) { - int pos = ((ArrayAdapter) mBootDevices.getAdapter()).getPosition(bootDevice); + private void populateCPUNum() { + String[] arraySpinner = new String[Config.MAX_CPU_NUM]; + for (int i = 0; i < arraySpinner.length; i++) { + arraySpinner[i] = (i + 1) + ""; + } + ArrayAdapter cpuNumAdapter = new ArrayAdapter<>(this, R.layout.custom_spinner_item, arraySpinner); + cpuNumAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); + mCPUNum.setAdapter(cpuNumAdapter); + mCPUNum.invalidate(); + } - if (pos >= 0) { - mBootDevices.setSelection(pos); - } else { - mBootDevices.setSelection(0); - } - } else { - mBootDevices.setSelection(0); + private void populateBootDevices() { + ArrayList bootDevicesList = new ArrayList<>(); + bootDevicesList.add("Default"); + bootDevicesList.add("CDROM"); + bootDevicesList.add("Hard Disk"); + if (Config.enableEmulatedFloppy) + bootDevicesList.add("Floppy"); - } - } - }); + String[] arraySpinner = bootDevicesList.toArray(new String[0]); + ArrayAdapter bootDevAdapter = new ArrayAdapter<>(this, R.layout.custom_spinner_item, arraySpinner); + bootDevAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); + mBootDevices.setAdapter(bootDevAdapter); + mBootDevices.invalidate(); } - private void setSnapshot(final String snapshot) { - - this.mSnapshot.post(new Runnable() { - public void run() { - if (snapshot != null && !snapshot.equals("")) { - int pos = ((ArrayAdapter) mSnapshot.getAdapter()).getPosition(snapshot); + private void populateNet() { + String[] arraySpinner = {"None", "User", "TAP"}; + ArrayAdapter netAdapter = new ArrayAdapter<>(this, R.layout.custom_spinner_item, arraySpinner); + netAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); + mNetConfig.setAdapter(netAdapter); + mNetConfig.invalidate(); + } - if (pos >= 0) { - mSnapshot.setSelection(pos); - mSnapshot.invalidate(); - } else { - mSnapshot.setSelection(0); - } - } else { - mSnapshot.setSelection(0); + private void populateVGA() { + ArrayList arrList = ArchDefinitions.getVGAValues(this); + ArrayAdapter vgaAdapter = new ArrayAdapter<>(this, R.layout.custom_spinner_item, arrList); + vgaAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); + mVGAConfig.setAdapter(vgaAdapter); + mVGAConfig.invalidate(); + } - } + private void populateKeyboardLayout() { + ArrayList arrList = ArchDefinitions.getKeyboardValues(this); + ArrayAdapter keyboardAdapter = new ArrayAdapter<>(this, R.layout.custom_spinner_item, arrList); + keyboardAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); + mKeyboard.setAdapter(keyboardAdapter); + mKeyboard.invalidate(); + //TODO: for now we use only English keyboard, add more layouts + mKeyboard.setSelection(0); + } - } - }); + private void populateMouse() { + ArrayList arrList = ArchDefinitions.getMouseValues(this); + ArrayAdapter mouseAdapter = new ArrayAdapter<>(this, R.layout.custom_spinner_item, arrList); + mouseAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); + mMouse.setAdapter(mouseAdapter); + mMouse.invalidate(); + } + private void populateSoundcardConfig() { + ArrayList soundCards = new ArrayList<>(); + soundCards.add("None"); + soundCards.addAll(ArchDefinitions.getSoundcards(this)); + ArrayAdapter sndAdapter = new ArrayAdapter<>(this, R.layout.custom_spinner_item, soundCards); + sndAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); + mSoundCard.setAdapter(sndAdapter); + mSoundCard.invalidate(); } - private void setNicDevice(final String nic) { + private void populateNetDevices(String nic) { + ArrayList networkCards = ArchDefinitions.getNetworkDevices(this); + ArrayAdapter nicCfgAdapter = new ArrayAdapter<>(this, R.layout.custom_spinner_item, networkCards); + nicCfgAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); + mNetworkCard.setAdapter(nicCfgAdapter); + mNetworkCard.invalidate(); - this.mNicCard.post(new Runnable() { - public void run() { - if (nic != null) { - int pos = ((ArrayAdapter) mNicCard.getAdapter()).getPosition(nic); + int pos = nicCfgAdapter.getPosition(nic); + if (pos >= 0) { + mNetworkCard.setSelection(pos); + } + } - if (pos >= 0) { - mNicCard.setSelection(pos); - } else { - mNicCard.setSelection(3); + private void populateMachines(final String machineValue) { + Thread thread = new Thread(new Runnable() { + public void run() { + final ArrayList machinesList = ArchDefinitions.getMachineValues(LimboActivity.this); + ArrayList machinesDB = MachineController.getInstance().getStoredMachines(); + machinesList.addAll(machinesDB); + new Handler(Looper.getMainLooper()).post(new Runnable() { + public void run() { + ArrayAdapter machineAdapter = new ArrayAdapter<>(LimboActivity.this, R.layout.custom_spinner_item, machinesList); + machineAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); + mMachine.setAdapter(machineAdapter); + mMachine.invalidate(); + if (machineValue != null) + SpinnerAdapter.setDiskAdapterValue(mMachine, machineValue); } - } else { - mNicCard.setSelection(3); - - } + }); } }); + thread.setPriority(Thread.MIN_PRIORITY); + thread.start(); + } + private void seMachineDriveValue(FileType fileType, final String diskValue) { + Spinner spinner = getSpinner(fileType); + if (spinner != null) + SpinnerAdapter.setDiskAdapterValue(spinner, diskValue); } private void populateCPUs(String cpu) { - - ArrayList arrList = new ArrayList(); - - // x86 cpus 32 bit - ArrayList arrX86 = new ArrayList(); - arrX86.add("Default"); - arrX86.add("qemu32");// QEMU Virtual CPU version 2.3.0 - arrX86.add("kvm32");// Common 32-bit KVM processor - arrX86.add("coreduo");// Genuine Intel(R) CPU T2600 @ 2.16GHz - arrX86.add("486"); - arrX86.add("pentium"); - arrX86.add("pentium2"); - arrX86.add("pentium3"); - arrX86.add("athlon"); // QEMU Virtual CPU version 2.3.0 - arrX86.add("n270"); // Intel(R) Atom(TM) CPU N270 @ 1.60GHz - - // x86 cpus 64 bit - ArrayList arrX86_64 = new ArrayList(); - arrX86_64.add("Default"); - arrX86_64.add("qemu64");// QEMU Virtual CPU version 2.3.0 - arrX86_64.add("kvm64");// Common KVM processor - arrX86_64.add("phenom");// AMD Phenom(tm) 9550 Quad-Core Processor - arrX86_64.add("core2duo");// Intel(R) Core(TM)2 Duo CPU T7700 @ - // 2.40GHz - arrX86_64.add("Conroe");// Intel Celeron_4x0 (Conroe/Merom Class Core - // 2) - arrX86_64.add("Penryn");// Intel Core 2 Duo P9xxx (Penryn Class Core - // 2) - arrX86_64.add("Nehalem");// Intel Core i7 9xx (Nehalem Class Core i7) - arrX86_64.add("Westmere");// e Westmere E56xx/L56xx/X56xx (Nehalem-C) - arrX86_64.add("SandyBridge");// Intel Xeon E312xx (Sandy Bridge) - arrX86_64.add("IvyBridge");// Intel Xeon E3-12xx v2 (Ivy Bridge) - arrX86_64.add("Haswell-noTSX");// Intel Core Processor (Haswell, no - // TSX) - arrX86_64.add("Haswell");// Intel Core Processor (Haswell) - arrX86_64.add("Broadwell-noTSX");// Intel Core Processor (Broadwell, - // no TSX) - arrX86_64.add("Broadwell");// Intel Core Processor (Broadwell) - arrX86_64.add("Opteron_G1");// AMD Opteron 240 (Gen 1 Class Opteron) - arrX86_64.add("Opteron_G2");// AMD Opteron 22xx (Gen 2 Class Opteron) - arrX86_64.add("Opteron_G3");// AMD Opteron 23xx (Gen 3 Class Opteron) - arrX86_64.add("Opteron_G4");// AMD Opteron 62xx class CPU - arrX86_64.add("Opteron_G5");// AMD Opteron 63xx class CPU - - // ARM cpus - ArrayList arrARM = new ArrayList(); - arrARM.add("Default"); - arrARM.add("arm926"); - arrARM.add("arm920t"); - arrARM.add("arm946"); - arrARM.add("arm1026"); - arrARM.add("arm1136"); - arrARM.add("arm1136-r2"); - arrARM.add("arm1176"); - arrARM.add("arm11mpcore"); - arrARM.add("cortex-m3"); - arrARM.add("cortex-m4"); - arrARM.add("cortex-m5"); - arrARM.add("cortex-a7"); - arrARM.add("cortex-a8"); - arrARM.add("cortex-a9"); - arrARM.add("cortex-a15"); - arrARM.add("cortex-r5"); - arrARM.add("pxa250"); - arrARM.add("pxa255"); - arrARM.add("pxa260"); - arrARM.add("pxa261"); - arrARM.add("pxa262"); - arrARM.add("pxa270-a0"); - arrARM.add("pxa270-a1"); - arrARM.add("pxa270"); - arrARM.add("pxa270-b0"); - arrARM.add("pxa270-b1"); - arrARM.add("pxa270-c0"); - arrARM.add("pxa270-c5"); - arrARM.add("sa1100"); - arrARM.add("sa1110"); - arrARM.add("ti925t"); - - ArrayList arrARM64 = new ArrayList(); - arrARM64.add("Default"); - arrARM64.add("cortex-a53"); - arrARM64.add("cortex-a57"); - - // Mips cpus - ArrayList arrMips = new ArrayList(); - arrMips.add("Default"); - - ArrayList arrPpc = new ArrayList(); - arrPpc.add("Default"); - arrPpc.add("601");// (alias for 601_v2) - arrPpc.add("603");// PVR 00030100 - arrPpc.add("Vanilla");// (alias for 603) - arrPpc.add("604");// PVR 00040103 - arrPpc.add("ppc32");// (alias for 604) - arrPpc.add("ppc");// (alias for 604) - arrPpc.add("default");// (alias for 604) - arrPpc.add("602");// PVR 00050100 - arrPpc.add("Goldeneye");// (alias for 603e7t) - arrPpc.add("740e");// PVR 00080100 - arrPpc.add("G3");// (alias for 750_v3.1) - arrPpc.add("Arthur");// (alias for 740_v3.1) - arrPpc.add("745");// (alias for 745_v2.8) - arrPpc.add("Goldfinger");// (alias for 755_v2.8) - arrPpc.add("LoneStar");// (alias for 750l_v3.2) - arrPpc.add("Mach5");// (alias for 604r) - arrPpc.add("G4");// (alias for 7400_v2.9) - arrPpc.add("403");// (alias for 403GC) - arrPpc.add("G2");// PVR 00810011 - arrPpc.add("Cobra");// PVR 10100000 - arrPpc.add("STB03");// PVR 40310000 - arrPpc.add("STB04");// PVR 41810000 - arrPpc.add("405");// (alias for 405D4) - arrPpc.add("STB25");// PVR 51510950 - arrPpc.add("750fl");// PVR 70000203 - arrPpc.add("750gl");// PVR 70020102 - arrPpc.add("7450");// (alias for 7450_v2.1) - arrPpc.add("Vger");// (alias for 7450_v2.1) - arrPpc.add("7441");// (alias for 7441_v2.3) - arrPpc.add("7451");// (alias for 7451_v2.3) - arrPpc.add("7445");// (alias for 7445_v3.2) - arrPpc.add("7455");// (alias for 7455_v3.2) - arrPpc.add("7457");// (alias for 7457_v1.2) - arrPpc.add("e600");// PVR 80040010 - arrPpc.add("7448");// (alias for 7448_v2.1) - arrPpc.add("7410");// (alias for 7410_v1.4) - arrPpc.add("Nitro");// (alias for 7410_v1.4) - arrPpc.add("e500");// (alias for e500v2_v22) - - ArrayList arrPpc64 = new ArrayList(); - arrPpc64.add("Default"); - arrPpc64.add("601_v1"); - arrPpc64.add("601_v0"); - arrPpc64.add("601_v2"); - arrPpc64.add("601"); - arrPpc64.add("601v"); - arrPpc64.add("603"); - arrPpc64.add("mpc8240"); - arrPpc64.add("vanilla"); - arrPpc64.add("604"); - arrPpc64.add("ppc32"); - arrPpc64.add("ppc"); - arrPpc64.add("default"); - arrPpc64.add("602"); - arrPpc64.add("603e_v1.1"); - arrPpc64.add("603e_v1.2"); - arrPpc64.add("603e_v1.3"); - arrPpc64.add("603e_v1.4"); - arrPpc64.add("603e_v2.2"); - arrPpc64.add("603e_v3"); - arrPpc64.add("603e_v4"); - arrPpc64.add("603e_v4.1"); - arrPpc64.add("603e"); - arrPpc64.add("stretch"); - arrPpc64.add("603p"); - arrPpc64.add("603e7v"); - arrPpc64.add("vaillant"); - arrPpc64.add("603e7v1"); - arrPpc64.add("6030000000"); - arrPpc64.add("603e7v2"); - arrPpc64.add("603e7t"); - arrPpc64.add("603r"); - arrPpc64.add("goldeneye"); - arrPpc64.add("750_v1.0"); - arrPpc64.add("740_v1.0"); - arrPpc64.add("740e"); - arrPpc64.add("750e"); - arrPpc64.add("750_v2.0"); - arrPpc64.add("740_v2.0"); - arrPpc64.add("750_v2.1"); - arrPpc64.add("740_v2.1"); - arrPpc64.add("740_v2.2"); - arrPpc64.add("750_v2.2"); - arrPpc64.add("750_v3.0"); - arrPpc64.add("740_v3.0"); - arrPpc64.add("750_v3.1"); - arrPpc64.add("750"); - arrPpc64.add("typhoon"); - arrPpc64.add("g3"); - arrPpc64.add("740_v3.1"); - arrPpc64.add("740"); - arrPpc64.add("arthur"); - arrPpc64.add("750cx_v1.0"); - arrPpc64.add("750cx_v2.0"); - arrPpc64.add("750cx_v2.1"); - arrPpc64.add("750cx_v2.2"); - arrPpc64.add("750cx"); - arrPpc64.add("750cxe_v2.1"); - arrPpc64.add("750cxe_v2.2"); - arrPpc64.add("750cxe_v2.3"); - arrPpc64.add("750cxe_v2.4"); - arrPpc64.add("750cxe_v3.0"); - arrPpc64.add("750cxe_v3.1"); - arrPpc64.add("755_v1.0"); - arrPpc64.add("745_v1.0"); - arrPpc64.add("755_v1.1"); - arrPpc64.add("745_v1.1"); - arrPpc64.add("755_v2.0"); - arrPpc64.add("745_v2.0"); - arrPpc64.add("755_v2.1"); - arrPpc64.add("745_v2.1"); - arrPpc64.add("745_v2.2"); - arrPpc64.add("755_v2.2"); - arrPpc64.add("755_v2.3"); - arrPpc64.add("745_v2.3"); - arrPpc64.add("755_v2.4"); - arrPpc64.add("745_v2.4"); - arrPpc64.add("745_v2.5"); - arrPpc64.add("755_v2.5"); - arrPpc64.add("755_v2.6"); - arrPpc64.add("745_v2.6"); - arrPpc64.add("755_v2.7"); - arrPpc64.add("745_v2.7"); - arrPpc64.add("745_v2.8"); - arrPpc64.add("745"); - arrPpc64.add("755_v2.8"); - arrPpc64.add("755"); - arrPpc64.add("goldfinger"); - arrPpc64.add("750cxe_v2.4b"); - arrPpc64.add("750cxe_v3.1b"); - arrPpc64.add("750cxe"); - arrPpc64.add("750cxr"); - arrPpc64.add("750cl_v1.0"); - arrPpc64.add("750cl_v2.0"); - arrPpc64.add("750cl"); - arrPpc64.add("750l_v2.0"); - arrPpc64.add("750l_v2.1"); - arrPpc64.add("750l_v2.2"); - arrPpc64.add("750l_v3.0"); - arrPpc64.add("750l_v3.2"); - arrPpc64.add("750l"); - arrPpc64.add("lonestar"); - arrPpc64.add("604e_v1.0"); - arrPpc64.add("604e_v2.2"); - arrPpc64.add("604e_v2.4"); - arrPpc64.add("604e"); - arrPpc64.add("sirocco"); - arrPpc64.add("604r"); - arrPpc64.add("mach5"); - arrPpc64.add("7400_v1.0"); - arrPpc64.add("7400_v1.1"); - arrPpc64.add("7400_v2.0"); - arrPpc64.add("7400_v2.1"); - arrPpc64.add("7400_v2.2"); - arrPpc64.add("7400_v2.6"); - arrPpc64.add("7400_v2.7"); - arrPpc64.add("7400_v2.8"); - arrPpc64.add("7400_v2.9"); - arrPpc64.add("7400"); - arrPpc64.add("max"); - arrPpc64.add("g4"); - arrPpc64.add("403ga"); - arrPpc64.add("403gb"); - arrPpc64.add("403gc"); - arrPpc64.add("403"); - arrPpc64.add("403gcx"); - arrPpc64.add("401a1"); - arrPpc64.add("401b2"); - arrPpc64.add("iop480"); - arrPpc64.add("401c2"); - arrPpc64.add("401d2"); - arrPpc64.add("40100"); - arrPpc64.add("401f2"); - arrPpc64.add("401g2"); - arrPpc64.add("401"); - arrPpc64.add("g2"); - arrPpc64.add("mpc603"); - arrPpc64.add("g2hip3"); - arrPpc64.add("mpc8250_hip3"); - arrPpc64.add("mpc8255_hip3"); - arrPpc64.add("mpc8260_hip3"); - arrPpc64.add("mpc8264_hip3"); - arrPpc64.add("mpc8265_hip3"); - arrPpc64.add("mpc8266_hip3"); - arrPpc64.add("mpc8349a"); - arrPpc64.add("mpc8347ap"); - arrPpc64.add("mpc8347eap"); - arrPpc64.add("mpc8347p"); - arrPpc64.add("mpc8349"); - arrPpc64.add("mpc8343e"); - arrPpc64.add("mpc8343ea"); - arrPpc64.add("mpc8343"); - arrPpc64.add("mpc8347et"); - arrPpc64.add("mpc8347e"); - arrPpc64.add("mpc8349e"); - arrPpc64.add("mpc8347at"); - arrPpc64.add("mpc8347a"); - arrPpc64.add("mpc8349ea"); - arrPpc64.add("mpc8347eat"); - arrPpc64.add("mpc8347ea"); - arrPpc64.add("mpc8343a"); - arrPpc64.add("mpc8347t"); - arrPpc64.add("mpc8347"); - arrPpc64.add("mpc8347ep"); - arrPpc64.add("e300c1"); - arrPpc64.add("e300c2"); - arrPpc64.add("e300c3"); - arrPpc64.add("e300"); - arrPpc64.add("mpc8379"); - arrPpc64.add("mpc8378e"); - arrPpc64.add("mpc8379e"); - arrPpc64.add("mpc8378"); - arrPpc64.add("mpc8377"); - arrPpc64.add("e300c4"); - arrPpc64.add("mpc8377e"); - arrPpc64.add("750p"); - arrPpc64.add("conan/doyle"); - arrPpc64.add("740p"); - arrPpc64.add("cobra"); - arrPpc64.add("460exb"); - arrPpc64.add("460ex"); - arrPpc64.add("440epx"); - arrPpc64.add("405d2"); - arrPpc64.add("x2vp4"); - arrPpc64.add("x2vp7"); - arrPpc64.add("x2vp20"); - arrPpc64.add("x2vp50"); - arrPpc64.add("405gpa"); - arrPpc64.add("405gpb"); - arrPpc64.add("405cra"); - arrPpc64.add("405gpc"); - arrPpc64.add("405gpd"); - arrPpc64.add("405gp"); - arrPpc64.add("405crb"); - arrPpc64.add("405crc"); - arrPpc64.add("405cr"); - arrPpc64.add("405gpe"); - arrPpc64.add("stb03"); - arrPpc64.add("npe4gs3"); - arrPpc64.add("npe405h"); - arrPpc64.add("npe405h2"); - arrPpc64.add("405ez"); - arrPpc64.add("npe405l"); - arrPpc64.add("405d4"); - arrPpc64.add("405"); - arrPpc64.add("stb04"); - arrPpc64.add("405lp"); - arrPpc64.add("440epa"); - arrPpc64.add("440epb"); - arrPpc64.add("440ep"); - arrPpc64.add("405gpr"); - arrPpc64.add("405ep"); - arrPpc64.add("stb25"); - arrPpc64.add("750fx_v1.0"); - arrPpc64.add("750fx_v2.0"); - arrPpc64.add("750fx_v2.1"); - arrPpc64.add("750fx_v2.2"); - arrPpc64.add("750fl"); - arrPpc64.add("750fx_v2.3"); - arrPpc64.add("750fx"); - arrPpc64.add("750gx_v1.0"); - arrPpc64.add("750gx_v1.1"); - arrPpc64.add("750gx_v1.2"); - arrPpc64.add("750gx"); - arrPpc64.add("750gl"); - arrPpc64.add("440-xilinx"); - arrPpc64.add("440-xilinx-w-dfpu"); - arrPpc64.add("7450_v1.0"); - arrPpc64.add("7450_v1.1"); - arrPpc64.add("7450_v1.2"); - arrPpc64.add("7450_v2.0"); - arrPpc64.add("7450_v2.1"); - arrPpc64.add("7450"); - arrPpc64.add("vger"); - arrPpc64.add("7441_v2.1"); - arrPpc64.add("7441_v2.3"); - arrPpc64.add("7441"); - arrPpc64.add("7451_v2.3"); - arrPpc64.add("7451"); - arrPpc64.add("7451_v2.10"); - arrPpc64.add("7441_v2.10"); - arrPpc64.add("7455_v1.0"); - arrPpc64.add("7445_v1.0"); - arrPpc64.add("7445_v2.1"); - arrPpc64.add("7455_v2.1"); - arrPpc64.add("7445_v3.2"); - arrPpc64.add("7445"); - arrPpc64.add("7455_v3.2"); - arrPpc64.add("7455"); - arrPpc64.add("apollo6"); - arrPpc64.add("7455_v3.3"); - arrPpc64.add("7445_v3.3"); - arrPpc64.add("7455_v3.4"); - arrPpc64.add("7445_v3.4"); - arrPpc64.add("7447_v1.0"); - arrPpc64.add("7457_v1.0"); - arrPpc64.add("7457_v1.1"); - arrPpc64.add("7447_v1.1"); - arrPpc64.add("7447"); - arrPpc64.add("7457_v1.2"); - arrPpc64.add("7457"); - arrPpc64.add("apollo7"); - arrPpc64.add("7457a_v1.0"); - arrPpc64.add("apollo7pm"); - arrPpc64.add("7447a_v1.0"); - arrPpc64.add("7447a_v1.1"); - arrPpc64.add("7457a_v1.1"); - arrPpc64.add("7447a_v1.2"); - arrPpc64.add("7447a"); - arrPpc64.add("7457a_v1.2"); - arrPpc64.add("7457a"); - arrPpc64.add("e600"); - arrPpc64.add("mpc8610"); - arrPpc64.add("mpc8641d"); - arrPpc64.add("mpc8641"); - arrPpc64.add("7448_v1.0"); - arrPpc64.add("7448_v1.1"); - arrPpc64.add("7448_v2.0"); - arrPpc64.add("7448_v2.1"); - arrPpc64.add("7448"); - arrPpc64.add("7410_v1.0"); - arrPpc64.add("7410_v1.1"); - arrPpc64.add("7410_v1.2"); - arrPpc64.add("7410_v1.3"); - arrPpc64.add("7410_v1.4"); - arrPpc64.add("7410"); - arrPpc64.add("nitro"); - arrPpc64.add("e500_v10"); - arrPpc64.add("mpc8540_v10"); - arrPpc64.add("mpc8540_v21"); - arrPpc64.add("mpc8540"); - arrPpc64.add("e500_v20"); - arrPpc64.add("e500v1"); - arrPpc64.add("mpc8541_v10"); - arrPpc64.add("mpc8541e_v11"); - arrPpc64.add("mpc8541e"); - arrPpc64.add("mpc8540_v20"); - arrPpc64.add("mpc8541e_v10"); - arrPpc64.add("mpc8541_v11"); - arrPpc64.add("mpc8541"); - arrPpc64.add("mpc8555_v10"); - arrPpc64.add("mpc8548_v10"); - arrPpc64.add("mpc8543_v10"); - arrPpc64.add("mpc8543e_v10"); - arrPpc64.add("mpc8548e_v10"); - arrPpc64.add("mpc8555e_v10"); - arrPpc64.add("e500v2_v10"); - arrPpc64.add("mpc8560_v10"); - arrPpc64.add("mpc8543e_v11"); - arrPpc64.add("mpc8548e_v11"); - arrPpc64.add("mpc8555e_v11"); - arrPpc64.add("mpc8555e"); - arrPpc64.add("mpc8555_v11"); - arrPpc64.add("mpc8555"); - arrPpc64.add("mpc8548_v11"); - arrPpc64.add("mpc8543_v11"); - arrPpc64.add("mpc8547e_v20"); - arrPpc64.add("e500v2_v20"); - arrPpc64.add("mpc8560_v20"); - arrPpc64.add("mpc8545e_v20"); - arrPpc64.add("mpc8545_v20"); - arrPpc64.add("mpc8548_v20"); - arrPpc64.add("mpc8543_v20"); - arrPpc64.add("mpc8543e_v20"); - arrPpc64.add("mpc8548e_v20"); - arrPpc64.add("mpc8545_v21"); - arrPpc64.add("mpc8545"); - arrPpc64.add("mpc8548_v21"); - arrPpc64.add("mpc8548"); - arrPpc64.add("mpc8543_v21"); - arrPpc64.add("mpc8543"); - arrPpc64.add("mpc8544_v10"); - arrPpc64.add("mpc8543e_v21"); - arrPpc64.add("mpc8543e"); - arrPpc64.add("mpc8544e_v10"); - arrPpc64.add("mpc8533_v10"); - arrPpc64.add("mpc8548e_v21"); - arrPpc64.add("mpc8548e"); - arrPpc64.add("mpc8547e_v21"); - arrPpc64.add("mpc8547e"); - arrPpc64.add("mpc8560_v21"); - arrPpc64.add("mpc8560"); - arrPpc64.add("e500v2_v21"); - arrPpc64.add("mpc8533e_v10"); - arrPpc64.add("mpc8545e_v21"); - arrPpc64.add("mpc8545e"); - arrPpc64.add("mpc8533_v11"); - arrPpc64.add("mpc8533"); - arrPpc64.add("mpc8567e"); - arrPpc64.add("e500v2_v22"); - arrPpc64.add("e500"); - arrPpc64.add("e500v2"); - arrPpc64.add("mpc8533e_v11"); - arrPpc64.add("mpc8533e"); - arrPpc64.add("mpc8568e"); - arrPpc64.add("mpc8568"); - arrPpc64.add("mpc8567"); - arrPpc64.add("mpc8544e_v11"); - arrPpc64.add("mpc8544e"); - arrPpc64.add("mpc8544_v11"); - arrPpc64.add("mpc8544"); - arrPpc64.add("e500v2_v30"); - arrPpc64.add("mpc8572e"); - arrPpc64.add("mpc8572"); - arrPpc64.add("e500mc"); - arrPpc64.add("g2h4"); - arrPpc64.add("g2hip4"); - arrPpc64.add("mpc8241"); - arrPpc64.add("mpc8245"); - arrPpc64.add("mpc8250"); - arrPpc64.add("mpc8250_hip4"); - arrPpc64.add("mpc8255"); - arrPpc64.add("mpc8255_hip4"); - arrPpc64.add("mpc8260"); - arrPpc64.add("mpc8260_hip4"); - arrPpc64.add("mpc8264"); - arrPpc64.add("mpc8264_hip4"); - arrPpc64.add("mpc8265"); - arrPpc64.add("mpc8265_hip4"); - arrPpc64.add("mpc8266"); - arrPpc64.add("mpc8266_hip4"); - arrPpc64.add("g2le"); - arrPpc64.add("g2gp"); - arrPpc64.add("g2legp"); - arrPpc64.add("mpc5200_v10"); - arrPpc64.add("mpc5200_v12"); - arrPpc64.add("mpc52xx"); - arrPpc64.add("mpc5200"); - arrPpc64.add("g2legp1"); - arrPpc64.add("mpc5200_v11"); - arrPpc64.add("mpc5200b_v21"); - arrPpc64.add("mpc5200b"); - arrPpc64.add("mpc5200b_v20"); - arrPpc64.add("g2legp3"); - arrPpc64.add("mpc82xx"); - arrPpc64.add("powerquicc-ii"); - arrPpc64.add("mpc8247"); - arrPpc64.add("mpc8248"); - arrPpc64.add("mpc8270"); - arrPpc64.add("mpc8271"); - arrPpc64.add("mpc8272"); - arrPpc64.add("mpc8275"); - arrPpc64.add("mpc8280"); - arrPpc64.add("e200z5"); - arrPpc64.add("e200z6"); - arrPpc64.add("e200"); - arrPpc64.add("g2ls"); - arrPpc64.add("g2lels"); - - - // m68k cpus - ArrayList arrM68k = new ArrayList(); - arrM68k.add("Default"); - arrM68k.add("cfv4e"); - arrM68k.add("m5206"); - arrM68k.add("m5208"); - arrM68k.add("m68000"); - arrM68k.add("m68020"); - arrM68k.add("m68030"); - arrM68k.add("m68040"); - arrM68k.add("m68060"); - arrM68k.add("any"); - - // Sparc - ArrayList arrSparc = new ArrayList(); - arrSparc.add("Default"); - //XXX: something is wrong with quoting and doesn't let qemu find the cpu - // when it contains a space for now we don't add any cpus -// arrSparc.add("Fujitsu MB86904"); -// arrSparc.add("Fujitsu MB86907"); -// arrSparc.add("TI MicroSparc I"); -// arrSparc.add("TI MicroSparc II"); -// arrSparc.add("TI MicroSparc IIep"); -// arrSparc.add("TI SuperSparc 40"); -// arrSparc.add("TI SuperSparc 50"); -// arrSparc.add("TI SuperSparc 51"); -// arrSparc.add("TI SuperSparc 60"); -// arrSparc.add("TI SuperSparc 61"); -// arrSparc.add("TI SuperSparc II"); -// arrSparc.add("LEON2"); -// arrSparc.add("LEON3"); - - // Sparc - ArrayList arrSparc64 = new ArrayList(); - arrSparc64.add("Default"); - //XXX: something is wrong with quoting and doesn't let qemu find the cpu - // when it contains a space for now we don't add any cpus -// arrSparc64.add("Fujitsu Sparc64"); -// arrSparc64.add("Fujitsu Sparc64 III"); -// arrSparc64.add("Fujitsu Sparc64 IV"); -// arrSparc64.add("Fujitsu Sparc64 V"); -// arrSparc64.add("TI UltraSparc I"); -// arrSparc64.add("TI UltraSparc II"); -// arrSparc64.add("TI UltraSparc IIi"); -// arrSparc64.add("TI UltraSparc IIe"); -// arrSparc64.add("Sun UltraSparc III"); -// arrSparc64.add("Sun UltraSparc III"); -// arrSparc64.add("Sun UltraSparc IIIi"); -// arrSparc64.add("Sun UltraSparc IV"); -// arrSparc64.add("Sun UltraSparc IV+"); -// arrSparc64.add("Sun UltraSparc IIIi+"); -// arrSparc64.add("Sun UltraSparc T1"); -// arrSparc64.add("Sun UltraSparc T2"); -// arrSparc64.add("NEC UltraSparc I"); - - - if (currMachine != null && currMachine.arch != null) { - if (currMachine.arch.equals("x86")) { - arrList.addAll(arrX86); - } else if (currMachine.arch.equals("x64")) { - arrList.addAll(arrX86_64); - } else if (currMachine.arch.equals("ARM")) { - arrList.addAll(arrARM); - } else if (currMachine.arch.equals("ARM64")) { - arrList.addAll(arrARM64); - } else if (currMachine.arch.equals("MIPS")) { - arrList.addAll(arrMips); - } else if (currMachine.arch.equals("PPC")) { - arrList.addAll(arrPpc); - } else if (currMachine.arch.equals("PPC64")) { - arrList.addAll(arrPpc64); - } else if (currMachine.arch.equals("m68k")) { - arrList.addAll(arrM68k); - } else if (currMachine.arch.equals("SPARC")) { - arrList.addAll(arrSparc); - } else if (currMachine.arch.equals("SPARC64")) { - arrList.addAll(arrSparc64); - } - } else { - arrList.addAll(arrX86); - } - - if(Config.enable_X86 || Config.enable_X86_64 || Config.enable_ARM || Config.enable_ARM64) - arrList.add("host"); - - ArrayAdapter cpuAdapter = new ArrayAdapter(this, R.layout.custom_spinner_item, arrList); + ArrayList arrList = ArchDefinitions.getCpuValues(this); + ArrayAdapter cpuAdapter = new ArrayAdapter<>(this, R.layout.custom_spinner_item, arrList); cpuAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - this.mCPU.setAdapter(cpuAdapter); - - this.mCPU.invalidate(); - + mCPU.setAdapter(cpuAdapter); + mCPU.invalidate(); int pos = cpuAdapter.getPosition(cpu); if (pos >= 0) { - this.mCPU.setSelection(pos); + mCPU.setSelection(pos); } - - } - - private void populateArch() { - - ArrayList arrList = new ArrayList(); - - if (Config.enable_X86) - arrList.add("x86"); - if (Config.enable_X86_64) - arrList.add("x64"); - - if (Config.enable_ARM) - arrList.add("ARM"); - if (Config.enable_ARM64) - arrList.add("ARM64"); - - if (Config.enable_MIPS) - arrList.add("MIPS"); - - if (Config.enable_PPC) - arrList.add("PPC"); - - if (Config.enable_PPC) - arrList.add("PPC64"); - - if (Config.enable_m68k) - arrList.add("m68k"); - - if (Config.enable_sparc) - arrList.add("SPARC"); - - if (Config.enable_sparc64) - arrList.add("SPARC64"); - - ArrayAdapter archAdapter = new ArrayAdapter(this, R.layout.custom_spinner_item, arrList); - archAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - - this.mArch.setAdapter(archAdapter); - - this.mArch.invalidate(); - } private void populateMachineType(String machineType) { + ArrayList arrList = ArchDefinitions.getMachineTypeValues(this); - ArrayList arrList = new ArrayList(); - - if (currMachine != null && currMachine.arch!=null) { - if (currMachine.arch.equals("x86") || currMachine.arch.equals("x64")) { - arrList.add("Default"); - arrList.add("pc"); - arrList.add("pc-i440fx-2.9"); - arrList.add("pc-i440fx-2.8"); - arrList.add("pc-i440fx-2.7"); - arrList.add("pc-i440fx-2.6"); - arrList.add("pc-i440fx-2.5"); - arrList.add("pc-i440fx-2.4"); - arrList.add("pc-i440fx-2.3"); - arrList.add("pc-i440fx-2.2"); - arrList.add("pc-i440fx-2.1"); - arrList.add("pc-i440fx-2.0"); - arrList.add("pc-i440fx-1.7"); - arrList.add("pc-i440fx-1.6"); - arrList.add("pc-i440fx-1.5"); - arrList.add("pc-i440fx-1.4"); - arrList.add("pc-1.3"); - arrList.add("pc-1.2"); - arrList.add("pc-1.1"); - arrList.add("pc-1.0"); - arrList.add("pc-0.15"); - arrList.add("pc-0.14"); - arrList.add("pc-0.13"); - arrList.add("pc-0.12"); - arrList.add("pc-0.11"); - arrList.add("pc-0.10"); - arrList.add("q35"); - arrList.add("pc-q35-2.9"); - arrList.add("pc-q35-2.8"); - arrList.add("pc-q35-2.7"); - arrList.add("pc-q35-2.6"); - arrList.add("pc-q35-2.5"); - arrList.add("pc-q35-2.4"); - arrList.add("isapc"); - arrList.add("none"); - - } else if (currMachine.arch.equals("ARM") || currMachine.arch.equals("ARM64")) { -// arrList.add("Default"); //no default for ARM - arrList.add("akita"); - arrList.add("ast2500-evb"); - arrList.add("borzoi"); - arrList.add("canon-a1100"); - arrList.add("cheetah"); - arrList.add("collie"); - arrList.add("connex"); - arrList.add("cubieboard"); - arrList.add("highbank"); - arrList.add("imx25-pdk"); - arrList.add("integratorcp"); - arrList.add("kzm"); - arrList.add("lm3s6965evb"); - arrList.add("lm3s811evb"); - arrList.add("mainstone"); - arrList.add("midway"); - arrList.add("musicpal"); - arrList.add("n800"); - arrList.add("n810"); - arrList.add("netduino2"); - arrList.add("none"); - arrList.add("nuri"); - arrList.add("palmetto-bmc"); - arrList.add("raspi2"); - arrList.add("realview-eb"); - arrList.add("realview-eb-mpcore"); - arrList.add("realview-pb-a8"); - arrList.add("realview-pbx-a9"); - arrList.add("romulus-bmc"); - arrList.add("sabrelite"); - arrList.add("smdkc210"); - arrList.add("spitz"); - arrList.add("sx1"); - arrList.add("sx1-v1"); - arrList.add("terrier"); - arrList.add("tosa"); - arrList.add("verdex"); - arrList.add("versatileab"); - arrList.add("versatilepb"); - arrList.add("vexpress-a15"); - arrList.add("vexpress-a9"); - arrList.add("virt-2.6"); - arrList.add("virt-2.7"); - arrList.add("virt-2.8"); - arrList.add("virt"); - arrList.add("virt-2.9"); - arrList.add("xilinx-zynq-a9"); - arrList.add("xlnx-ep108"); - arrList.add("xlnx-zcu102"); - arrList.add("z2"); - - } else if (currMachine.arch.equals("MIPS")) { - arrList.add("Default"); - arrList.add("malta"); - arrList.add("mips"); - arrList.add("mipssim"); - arrList.add("none"); - } else if (currMachine.arch.equals("PPC")) { - arrList.add("Default"); - arrList.add("40p"); - arrList.add("bamboo"); - arrList.add("g3beige"); - arrList.add("mac99"); - arrList.add("mpc8544ds"); - arrList.add("none"); - arrList.add("ppce500"); - arrList.add("prep"); - arrList.add("ref405ep"); - arrList.add("taihu"); - arrList.add("virtex-ml507"); - } else if (currMachine.arch.equals("PPC64")) { - arrList.add("Default"); - arrList.add("none"); - arrList.add("powernv"); - arrList.add("pseries-2.1"); - arrList.add("pseries-2.10"); - arrList.add("pseries"); - arrList.add("pseries-2.11"); - arrList.add("pseries-2.2"); - arrList.add("pseries-2.3"); - arrList.add("pseries-2.4"); - arrList.add("pseries-2.5"); - arrList.add("pseries-2.6"); - arrList.add("pseries-2.7"); - arrList.add("pseries-2.8"); - arrList.add("pseries-2.9"); - - - - } else if (currMachine.arch.equals("m68k")) { - arrList.add("Default"); - arrList.add("an5206"); // Arnewsh 5206 - arrList.add("mcf5208evb"); // MCF5206EVB (default0 - arrList.add("none"); - } else if (currMachine.arch.equals("SPARC")) { - arrList.add("Default"); - arrList.add("LX"); // Arnewsh 5206 - arrList.add("SPARCClassic"); // MCF5206EVB (default0 - arrList.add("SS-10"); - arrList.add("SS-20"); - arrList.add("SS-4"); - arrList.add("SS-5"); - arrList.add("SS-600MP"); - arrList.add("Voyager"); - arrList.add("leon3_generic"); - - } if (currMachine.arch.equals("SPARC64")) { - arrList.add("Default"); - arrList.add("niagara"); - arrList.add("sun4u"); - arrList.add("sun4v"); - arrList.add("none"); - - } - - } - - ArrayAdapter machineTypeAdapter = new ArrayAdapter(this, R.layout.custom_spinner_item, arrList); + ArrayAdapter machineTypeAdapter = new ArrayAdapter<>(this, R.layout.custom_spinner_item, arrList); machineTypeAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - this.mMachineType.setAdapter(machineTypeAdapter); + mMachineType.setAdapter(machineTypeAdapter); - this.mMachineType.invalidate(); + mMachineType.invalidate(); int pos = machineTypeAdapter.getPosition(machineType); - if (pos >= 0) { - this.mMachineType.setSelection(pos); - } else { - this.mMachineType.setSelection(0); - } + mMachineType.setSelection(Math.max(pos, 0)); } private void populateUI() { - - - ArrayList arrList = new ArrayList(); - arrList.add("VNC"); - if (Config.enable_SDL) - arrList.add("SDL"); - if (Config.enable_SPICE) - arrList.add("SPICE"); - - ArrayAdapter uiAdapter = new ArrayAdapter(this, R.layout.custom_spinner_item, arrList); + ArrayList arrList = ArchDefinitions.getUIValues(); + ArrayAdapter uiAdapter = new ArrayAdapter<>(this, R.layout.custom_spinner_item, arrList); uiAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - this.mUI.setAdapter(uiAdapter); - this.mUI.invalidate(); + mUI.setAdapter(uiAdapter); + mUI.invalidate(); } - // Set File Adapters public void populateDiskAdapter(final Spinner spinner, final FileType fileType, final boolean createOption) { - Thread t = new Thread(new Runnable() { public void run() { - - ArrayList oldHDs = FavOpenHelper.getInstance(activity).getFav(fileType.toString().toLowerCase()); - int length = 0; - if (oldHDs == null || oldHDs.size() == 0) { - length = 0; - } else { - length = oldHDs.size(); - } - - final ArrayList arraySpinner = new ArrayList(); + ArrayList oldHDs = MachineFilePaths.getRecentFilePaths(fileType); + final ArrayList arraySpinner = new ArrayList<>(); arraySpinner.add("None"); if (createOption) arraySpinner.add("New"); - arraySpinner.add("Open"); + arraySpinner.add(getString(R.string.open)); final int index = arraySpinner.size(); - Iterator i = oldHDs.iterator(); - while (i.hasNext()) { - String file = (String) i.next(); - if (file != null) { - arraySpinner.add(file); - } - } - - new Handler(Looper.getMainLooper()).post(new Runnable() { - public void run() { - LimboFileSpinnerAdapter adapter = new LimboFileSpinnerAdapter(activity, R.layout.custom_spinner_item, arraySpinner, index); - adapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - spinner.setAdapter(adapter); - spinner.invalidate(); - } - }); - } - }); - t.start(); - } - - private void populateSnapshot() { - Thread t = new Thread(new Runnable() { - public void run() { - ArrayList oldSnapshots = null; - if (currMachine != null) { - oldSnapshots = MachineOpenHelper.getInstance(activity).getSnapshots(currMachine); - } - - int length = 0; - if (oldSnapshots == null) { - length = 0; - } else { - length = oldSnapshots.size(); - } - - final ArrayList arraySpinner = new ArrayList(); - arraySpinner.add("None"); - if (oldSnapshots != null) { - Iterator i = oldSnapshots.iterator(); - while (i.hasNext()) { - String file = (String) i.next(); + if (oldHDs != null) { + for (String file : oldHDs) { if (file != null) { arraySpinner.add(file); } } } - - final ArrayList finalOldSnapshots = oldSnapshots; new Handler(Looper.getMainLooper()).post(new Runnable() { public void run() { - ArrayAdapter snapshotAdapter = new ArrayAdapter(activity, R.layout.custom_spinner_item, arraySpinner); - snapshotAdapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - mSnapshot.setAdapter(snapshotAdapter); - mSnapshot.invalidate(); - - if (finalOldSnapshots == null) { - mSnapshot.setEnabled(false); - } else { - mSnapshot.setEnabled(true); - } + SpinnerAdapter adapter = new SpinnerAdapter(LimboActivity.this, R.layout.custom_spinner_item, arraySpinner, index); + adapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); + spinner.setAdapter(adapter); + spinner.invalidate(); } }); - - } }); t.start(); - - - } - - - public Intent getVNCIntent() { - return new Intent(LimboActivity.this, com.max2idea.android.limbo.main.LimboVNCActivity.class); - - } - - public enum FileType { - HDA, HDB, HDC,HDD, CD,FDA,FDB,SD,KERNEL, INITRD, SHARED_DIR, EXPORT_DIR, IMAGE_DIR, LOG_DIR, IMPORT_FILE - } - - private void addDriveToList(String file, FileType type) { - - if (file == null) - return; - - int res = FavOpenHelper.getInstance(activity).getFavSeq(file, type.toString().toLowerCase()); - if (res == -1) { - if (type == FileType.HDA) { - this.mHDA.getAdapter().getCount(); - } else if (type == FileType.HDB) { - this.mHDB.getAdapter().getCount(); - } else if (type == FileType.HDC) { - this.mHDC.getAdapter().getCount(); - } else if (type == FileType.HDD) { - this.mHDD.getAdapter().getCount(); - } else if (type == FileType.SHARED_DIR) { - this.mSharedFolder.getAdapter().getCount(); - } else if (type == FileType.CD) { - this.mCD.getAdapter().getCount(); - } else if (type == FileType.FDA) { - this.mFDA.getAdapter().getCount(); - } else if (type == FileType.FDB) { - this.mFDB.getAdapter().getCount(); - } else if (type == FileType.SD) { - this.mSD.getAdapter().getCount(); - } - if (file != null && !file.equals("")) { - FavOpenHelper.getInstance(activity).insertFav(file, type.toString().toLowerCase()); - } - } - } - @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - this.invalidateOptionsMenu(); + invalidateOptionsMenu(); } @Override public boolean onPrepareOptionsMenu(Menu menu) { menu.clear(); - return this.setupMenu(menu); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - menu.clear(); - return this.setupMenu(menu); - } - - public boolean setupMenu(Menu menu) { - - menu.add(0, HELP, 0, "Help").setIcon(R.drawable.help); - menu.add(0, INSTALL, 0, "Install Roms").setIcon(R.drawable.install); - menu.add(0, CREATE, 0, "Create machine").setIcon(R.drawable.machinetype); - menu.add(0, DELETE, 0, "Delete Machine").setIcon(R.drawable.delete); - menu.add(0, SETTINGS, 0, "Settings").setIcon(R.drawable.settings); - if (currMachine != null && currMachine.paused == 1) - menu.add(0, DISCARD_VM_STATE, 0, "Discard Saved State").setIcon(R.drawable.close); - menu.add(0, EXPORT, 0, "Export Machines").setIcon(R.drawable.exportvms); - menu.add(0, IMPORT, 0, "Import Machines").setIcon(R.drawable.importvms); - menu.add(0, VIEWLOG, 0, "View Log").setIcon(android.R.drawable.ic_menu_view); - menu.add(0, HELP, 0, "Help").setIcon(R.drawable.help); - menu.add(0, CHANGELOG, 0, "Changelog").setIcon(android.R.drawable.ic_menu_help); - menu.add(0, LICENSE, 0, "License").setIcon(android.R.drawable.ic_menu_help); - menu.add(0, QUIT, 0, "Exit").setIcon(android.R.drawable.ic_lock_power_off); - - - int maxMenuItemsShown = 3; - int actionShow = MenuItemCompat.SHOW_AS_ACTION_IF_ROOM; - if(UIUtils.isLandscapeOrientation(this)) { - maxMenuItemsShown = 4; - actionShow = MenuItemCompat.SHOW_AS_ACTION_ALWAYS; - } - - for (int i = 0; i < menu.size() && i < maxMenuItemsShown; i++) { - MenuItemCompat.setShowAsAction(menu.getItem(i), actionShow); + menu.add(0, HELP, 0, R.string.help).setIcon(R.drawable.help); + menu.add(0, INSTALL, 0, R.string.InstallRoms).setIcon(R.drawable.install); + if(!MachineController.getInstance().isRunning()) { + menu.add(0, CREATE, 0, R.string.CreateMachine).setIcon(R.drawable.machinetype); + menu.add(0, DELETE, 0, R.string.DeleteMachine).setIcon(R.drawable.delete); + if (getMachine() != null && getMachine().getPaused() == 1) + menu.add(0, DISCARD_VM_STATE, 0, R.string.DiscardSavedState).setIcon(R.drawable.close); + menu.add(0, EXPORT, 0, R.string.ExportMachines).setIcon(R.drawable.exportvms); + menu.add(0, IMPORT, 0, R.string.ImportMachines).setIcon(R.drawable.importvms); + } + menu.add(0, IMPORT_BIOS_FILE, 0, R.string.ImportBIOSFile).setIcon(R.drawable.importvms); + menu.add(0, SETTINGS, 0, R.string.Settings).setIcon(R.drawable.settings); + menu.add(0, TOOLS, 0, R.string.advancedTools).setIcon(R.drawable.advanced); + menu.add(0, VIEWLOG, 0, R.string.ViewLog).setIcon(android.R.drawable.ic_menu_view); + menu.add(0, HELP, 0, R.string.help).setIcon(R.drawable.help); + menu.add(0, CHANGELOG, 0, R.string.Changelog).setIcon(android.R.drawable.ic_menu_help); + menu.add(0, LICENSE, 0, R.string.License).setIcon(android.R.drawable.ic_menu_help); + menu.add(0, QUIT, 0, R.string.Exit).setIcon(android.R.drawable.ic_lock_power_off); + + for (int i = 0; i < 2; i++) { + menu.getItem(i).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); } - return true; - } @Override public boolean onOptionsItemSelected(final MenuItem item) { super.onOptionsItemSelected(item); - if (item.getItemId() == this.INSTALL) { - this.install(true); - } else if (item.getItemId() == this.DELETE) { - this.promptDeleteMachine(); - } else if (item.getItemId() == this.DISCARD_VM_STATE) { - promptDiscardVMState(); - } else if (item.getItemId() == this.CREATE) { - this.promptMachineName(this); - } else if (item.getItemId() == this.SETTINGS) { - this.goToSettings(); - } else if (item.getItemId() == this.EXPORT) { - this.onExportMachines(); - } else if (item.getItemId() == this.IMPORT) { - this.onImportMachines(); - } else if (item.getItemId() == this.HELP) { - UIUtils.onHelp(this); - } else if (item.getItemId() == this.VIEWLOG) { - this.onViewLog(); - } else if (item.getItemId() == this.CHANGELOG) { - UIUtils.onChangeLog(activity); - } else if (item.getItemId() == this.LICENSE) { - this.onLicense(); - } else if (item.getItemId() == this.QUIT) { - this.exit(); + if (item.getItemId() == INSTALL) { + Installer.installFiles(this, true); + } else if (item.getItemId() == DELETE) { + promptDeleteMachine(); + } else if (item.getItemId() == DISCARD_VM_STATE) { + promptDiscardVMState(); + } else if (item.getItemId() == CREATE) { + promptMachineName(this); + } else if (item.getItemId() == SETTINGS) { + showSettings(); + } else if (item.getItemId() == TOOLS) { + LimboActivityCommon.goToURL(this, Config.toolsLink); + } else if (item.getItemId() == EXPORT) { + MachineExporter.promptExport(this); + } else if (item.getItemId() == IMPORT) { + MachineImporter.promptImportMachines(this); + } else if (item.getItemId() == IMPORT_BIOS_FILE) { + BIOSImporter.promptImportBIOSFile(this); + } else if (item.getItemId() == HELP) { + Help.showHelp(this); + } else if (item.getItemId() == VIEWLOG) { + Logger.viewLimboLog(LimboActivity.this); + } else if (item.getItemId() == CHANGELOG) { + LimboActivityCommon.showChangelog(LimboActivity.this); + } else if (item.getItemId() == LICENSE) { + promptLicense(); + } else if (item.getItemId() == QUIT) { + exit(); } return true; } - private void goToSettings() { + private void showSettings() { Intent i = new Intent(this, LimboSettingsManager.class); - activity.startActivity(i); - } - - public void onViewLog() { - - Thread t = new Thread(new Runnable() { - public void run() { - FileUtils.viewLimboLog(activity); - } - }); - t.start(); - } - - private void goToURL(String url) { - - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse(url)); - activity.startActivity(i); - + startActivity(i); } public void promptDeleteMachine() { - if(currMachine == null) { - UIUtils.toastShort(this, "No Machine selected"); + if (getMachine() == null) { + ToastUtils.toastShort(this, getString(R.string.NoMachineSelected)); return; } - new AlertDialog.Builder(this).setTitle("Delete VM: " + currMachine.machinename) - .setMessage("Delete VM? Only machine definition and state will be deleted, disk images files will not be deleted") - .setPositiveButton("Yes", new DialogInterface.OnClickListener() { + new AlertDialog.Builder(this).setTitle(getString(R.string.DeleteVM) + ": " + getMachine().getName()) + .setMessage(R.string.deleteVMWarning) + .setPositiveButton(getString(android.R.string.yes), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { onDeleteMachine(); } - }).setNegativeButton("No", new DialogInterface.OnClickListener() { + }).setNegativeButton(getString(android.R.string.no), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { } }).show(); } - public void promptDiscardVMState() { - new AlertDialog.Builder(this).setTitle("Discard VM State") - .setMessage("The VM is Paused. If you discard the state you might lose data. Continue?") - .setPositiveButton("Yes", new DialogInterface.OnClickListener() { + new AlertDialog.Builder(this).setTitle(R.string.discardVMState) + .setMessage(R.string.discardVMInstructions) + .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { - currMachine.paused = 0; - MachineOpenHelper.getInstance(activity).update(currMachine, - MachineOpenHelper.PAUSED, 0 + ""); - changeStatus(VMStatus.Ready); + notifyFieldChange(MachineProperty.PAUSED, 0); + changeStatus(MachineStatus.Ready); enableNonRemovableDeviceOptions(true); enableRemovableDeviceOptions(true); } - }).setNegativeButton("No", new DialogInterface.OnClickListener() { + }).setNegativeButton(getString(android.R.string.no), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { } }).show(); } - public void stopVM(boolean exit) { - execTimer(); - if (vmexecutor == null && !exit) { - if (this.currMachine != null && this.currMachine.paused == 1) { - promptDiscardVMState(); - return; - } else { - - UIUtils.toastShort(LimboActivity.this, "VM not running"); - return; - } - } - UIUtils.hideKeyboard(this, mScrollView); - Machine.stopVM(activity); - } - - public void saveSnapshotDB(String snapshot_name) { - currMachine.snapshot_name = snapshot_name; - MachineOpenHelper.getInstance(activity).deleteMachineDB(currMachine); - MachineOpenHelper.getInstance(activity).insertMachine(currMachine); - if (((ArrayAdapter) mSnapshot.getAdapter()).getPosition(snapshot_name) < 0) { - ((ArrayAdapter) mSnapshot.getAdapter()).add(snapshot_name); - } - } - - public void stopTimeListener() { - - synchronized (this.lockTime) { - this.timeQuit = true; - this.lockTime.notifyAll(); - } - } - public void onPause() { View currentView = getCurrentFocus(); - if (currentView != null && currentView instanceof EditText) { - ((EditText) currentView).setFocusable(false); + if (currentView instanceof EditText) { + currentView.setFocusable(false); } super.onPause(); - this.stopTimeListener(); } public void onResume() { - super.onResume(); - updateValues(); - execTimer(); + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + updateValues(); + if(libLoaded) + notifyAction(MachineAction.IGNORE_BREAKPOINT_INVALIDATION, LimboSettingsManager.getIgnoreBreakpointInvalidation(LimboActivity.this)); + } + }, 1000); + } private void updateValues() { - Thread t = new Thread(new Runnable() { public void run() { - checkAndUpdateStatus(true); runOnUiThread(new Runnable() { @Override public void run() { + changeStatus(MachineController.getInstance().getCurrStatus()); updateRemovableDiskValues(); - updateGlobalSettings(); - updateSummary(false); + updateSummary(); } }); } @@ -6277,207 +2761,150 @@ public void run() { t.start(); } - private void updateGlobalSettings() { - final boolean enableFullscreen = LimboSettingsManager.getFullscreen(activity); - final boolean enablehighPriority = LimboSettingsManager.getPrio(activity); - final boolean enableDesktopMode = LimboSettingsManager.getDesktopMode(activity); - final boolean enableShowToolbar = LimboSettingsManager.getAlwaysShowMenuToolbar(activity); - - runOnUiThread(new Runnable() { - @Override - public void run() { - mFullScreen.setChecked(enableFullscreen); - mPrio.setChecked(enablehighPriority); - mDesktopMode.setChecked(enableDesktopMode); - if(enableDesktopMode) { - Config.mouseMode = Config.MouseMode.External; - } else - Config.mouseMode = Config.MouseMode.Trackpad; - mToolBar.setChecked(enableShowToolbar); - - } - }); - - } - private void updateRemovableDiskValues() { - if(currMachine!=null) { + if (getMachine() != null) { disableRemovableDiskListeners(); - this.updateDrive(FileType.CD, currMachine.cd_iso_path); - this.updateDrive(FileType.FDA, currMachine.fda_img_path); - this.updateDrive(FileType.FDB, currMachine.fdb_img_path); - this.updateDrive(FileType.SD, currMachine.sd_img_path); + updateDrive(FileType.CDROM, getMachine().getCdImagePath()); + updateDrive(FileType.FDA, getMachine().getFdaImagePath()); + updateDrive(FileType.FDB, getMachine().getFdbImagePath()); + updateDrive(FileType.SD, getMachine().getSdImagePath()); enableRemovableDiskListeners(); } } - public void timer() { - //XXX: No timers just ping a few times - for (int i = 0; i < 3; i++) { - checkAndUpdateStatus(false); - - try { - Thread.sleep(1000); - } catch (InterruptedException ex) { - ex.printStackTrace(); - } - } - + public boolean isLandscapeOrientation(Activity activity) { + Display display = activity.getWindowManager().getDefaultDisplay(); + Point screenSize = new Point(); + display.getSize(screenSize); + return screenSize.x >= screenSize.y; } - private void checkAndUpdateStatus(boolean force) { - if (vmexecutor != null) { - VMStatus status = checkStatus(); - if (force || status != currStatus) { - currStatus = status; + @Override + public void onMachineStatusChanged(Machine machine, MachineStatus status, Object o) { + switch (status) { + case SaveFailed: + LimboActivityCommon.promptPausedErrorVM(this, (String) o, viewListener); + break; + case SaveCompleted: + LimboActivityCommon.promptPausedVM(this, viewListener); + break; + default: changeStatus(status); - } } } - void execTimer() { - - Thread t = new Thread(new Runnable() { + @Override + public void onEvent(Machine machine, MachineController.Event event, Object o) { + switch (event) { + case MachineCreateFailed: + if (o instanceof Integer) { + ToastUtils.toastShort(LimboActivity.this, getString((int) o)); + } else if (o instanceof String) { + ToastUtils.toastShort(LimboActivity.this, (String) o); + } + break; + case MachineCreated: + machineCreated(); + break; + case MachineLoaded: + loadMachine(); + break; + case MachineContinued: + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + invalidateOptionsMenu(); + changeStatus(MachineController.getInstance().getCurrStatus()); + } + }, 1000); + break; + case MachinesImported: + onMachinesImported((ArrayList) o); + break; + } + runOnUiThread(new Runnable() { + @Override public void run() { - startTimer(); + updateSummary(); } }); - t.start(); } - public void startTimer() { - this.stopTimeListener(); - - timeQuit = false; - try { - timer(); - } catch (Exception ex) { - ex.printStackTrace(); - - } + private void onMachinesImported(ArrayList machines) { + populateAttributesUI(); + setupMiscOptions(); + setupNonRemovableDiskListeners(); + enableRemovableDiskListeners(); + updateFavAdapters(); + LimboActivityCommon.promptMachinesImported(this, machines); } - - public enum VMStatus { - Ready, Stopped, Saving, Paused, Completed, Failed, Unknown, Running + private void updateFavAdapters() { + mHDA.getAdapter().getCount(); + mHDB.getAdapter().getCount(); + mHDC.getAdapter().getCount(); + mHDD.getAdapter().getCount(); + mCD.getAdapter().getCount(); + mFDA.getAdapter().getCount(); + mFDB.getAdapter().getCount(); + mSD.getAdapter().getCount(); + mKernel.getAdapter().getCount(); + mInitrd.getAdapter().getCount(); } - private VMStatus checkStatus() { - VMStatus state = VMStatus.Ready; - if (vmexecutor != null && libLoaded && vmexecutor.get_state().toUpperCase().equals("RUNNING")) { - state = VMStatus.Running; - } - return state; + private void addGenericOperatingSystems() { + Config.osImages.put(getString(R.string.other), new LinksManager.LinkInfo("Other", + getString(R.string.otherOperatingSystem), + Config.otherOSLink, + LinksManager.LinkType.OTHER)); + Config.osImages.put(getString(R.string.custom), new LinksManager.LinkInfo("Custom", + getString(R.string.customOperatingSystem), + null, + LinksManager.LinkType.OTHER)); } - private class InstallerTask extends AsyncTask { - public boolean force; - - @Override - protected Void doInBackground(Void... arg0) { - onInstall(force); - if (progDialog.isShowing()) { - progDialog.dismiss(); + @Override + public void update(Observable observable, final Object o) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Object [] params = (Object[] ) o; + if (params[0] instanceof MachineProperty) { + MachineProperty property = (MachineProperty) params[0]; + if (property == MachineProperty.UI) { + if (getMachine().getEnableVNC() != 1) + mSoundCard.setEnabled(true); + else + mSoundCard.setEnabled(true); + } + } + updateSummary(); } - return null; - } - - @Override - protected void onPostExecute(Void test) { + }); - } } - private class ExportMachines extends AsyncTask { - - public String exportFilePath; - private String displayName; - - @Override - protected Void doInBackground(Void... arg0) { - - // Export - displayName = exportMachinesToFile(exportFilePath); - - return null; - } - - @Override - protected void onPostExecute(Void test) { - if (progDialog.isShowing()) { - progDialog.dismiss(); - } - if(displayName != null) - UIUtils.toastLong(LimboActivity.this, "Machines are exported in " + displayName); - - } + public void notifyFieldChange(MachineProperty property, Object value) { + if (viewListener != null) + viewListener.onFieldChange(property, value); } - private class ImportMachines extends AsyncTask { - - private String importFilePath; - private ArrayList machines; - private String displayImportFilePath; - - @Override - protected Void doInBackground(Void... arg0) { - // Import - displayImportFilePath = FileUtils.getFullPathFromDocumentFilePath(importFilePath); - machines = FileUtils.getVMsFromFile(activity, importFilePath); - UIUtils.toastLong(LimboActivity.this, "Import machines: " + machines.size()); - - if (machines == null) { - return null; - } - - for (int i = 0; i < machines.size(); i++) { - Machine machine = machines.get(i); - if (MachineOpenHelper.getInstance(activity).getMachine(machine.machinename, "") != null) { - MachineOpenHelper.getInstance(activity).deleteMachineDB(machine); - } - MachineOpenHelper.getInstance(activity).insertMachine(machine); - addDriveToList(machine.cd_iso_path, FileType.CD); - addDriveToList(machine.hda_img_path,FileType.HDA ); - addDriveToList(machine.hdb_img_path, FileType.HDB); - addDriveToList(machine.hdc_img_path, FileType.HDC); - addDriveToList(machine.hdd_img_path, FileType.HDD); - addDriveToList(machine.shared_folder, FileType.SHARED_DIR); - addDriveToList(machine.fda_img_path, FileType.FDA); - addDriveToList(machine.fdb_img_path, FileType.FDB); - addDriveToList(machine.sd_img_path, FileType.SD); - addDriveToList(machine.kernel, FileType.KERNEL); - addDriveToList(machine.initrd, FileType.INITRD); - } - - return null; - } - - @Override - protected void onPostExecute(Void test) { - if (progDialog.isShowing()) { - progDialog.dismiss(); - } - if(machines!=null) { - UIUtils.toastLong(LimboActivity.this, "Machines are imported from " + displayImportFilePath); - populateAttributes(); - enableListeners(); - enableRemovableDiskListeners(); - } - - } + public void notifyAction(MachineAction action, Object value) { + if (viewListener != null) + viewListener.onAction(action, value); } - private class DiskInfo { + static class DiskInfo { public CheckBox enableCheckBox; public Spinner spinner; - public String colName; - ; + public MachineProperty colName; - public DiskInfo(Spinner spinner, CheckBox enableCheckbox, String dbColName) { + public DiskInfo(Spinner spinner, CheckBox enableCheckbox, MachineProperty dbColName) { this.spinner = spinner; this.enableCheckBox = enableCheckbox; this.colName = dbColName; } } + } diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboActivityCommon.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboActivityCommon.java new file mode 100644 index 000000000..d8874f87c --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboActivityCommon.java @@ -0,0 +1,343 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.main; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Color; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.widget.ScrollView; +import android.widget.TextView; + +import com.limbo.emu.lib.R; +import com.max2idea.android.limbo.dialog.DialogUtils; +import com.max2idea.android.limbo.files.FileUtils; +import com.max2idea.android.limbo.help.Help; +import com.max2idea.android.limbo.install.Installer; +import com.max2idea.android.limbo.machine.Machine; +import com.max2idea.android.limbo.machine.MachineAction; +import com.max2idea.android.limbo.network.NetworkUtils; +import com.max2idea.android.limbo.toast.ToastUtils; + +import java.io.IOException; +import java.util.ArrayList; + +public class LimboActivityCommon { + private static final String TAG = "LimboActivityCommon"; + + public static void promptStopVM(final Activity activity, final ViewListener viewListener) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + new AlertDialog.Builder(activity).setTitle(R.string.ShutdownVM) + .setMessage(R.string.ShutdownVMWarning) + .setPositiveButton(activity.getString(android.R.string.ok), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + viewListener.onAction(MachineAction.STOP_VM, null); + } + }).setNegativeButton(activity.getString(android.R.string.cancel), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + } + }).show(); + } + }); + } + + public static void promptPausedErrorVM(final Activity activity, final String msg, final ViewListener viewListener) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + + String message = msg != null ? msg : activity.getString(R.string.CouldNotPauseVMViewLogFileDetails); + new AlertDialog.Builder(activity).setTitle(R.string.Error).setMessage(message) + .setPositiveButton(activity.getString(android.R.string.ok), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + Thread t = new Thread(new Runnable() { + public void run() { + viewListener.onAction(MachineAction.CONTINUE_VM, 0); + } + }); + t.start(); + } + }).show(); + + } + }); + } + + public static void promptPause(final Activity activity, final ViewListener viewListener) { + if (!LimboSettingsManager.getEnableQmp(activity)) { + ToastUtils.toastShort(activity, activity.getString(R.string.EnableQMPForSavingVMState)); + return; + } + + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + + final AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(activity).create(); + alertDialog.setTitle(activity.getString(R.string.PauseVM)); + TextView stateView = new TextView(activity); + stateView.setText(R.string.pauseVMWarning); + stateView.setPadding(20, 20, 20, 20); + alertDialog.setView(stateView); + + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.Pause), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + viewListener.onAction(MachineAction.PAUSE_VM, null); + } + }); + alertDialog.show(); + } + }); + } + + public static void promptResetVM(final Activity activity, final ViewListener viewListener) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + + new AlertDialog.Builder(activity).setTitle(R.string.ResetVM) + .setMessage(R.string.ResetVMWarning) + .setPositiveButton(activity.getString(android.R.string.yes), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + viewListener.onAction(MachineAction.RESET_VM, null); + } + }).setNegativeButton(activity.getString(android.R.string.no), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + } + }).show(); + + } + }); + } + + public static void promptPausedVM(final Activity activity, final ViewListener viewListener) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + + new AlertDialog.Builder(activity).setCancelable(false).setTitle(R.string.Paused) + .setMessage(R.string.VMPausedPressOkToExit) + .setPositiveButton(R.string.Ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + viewListener.onAction(MachineAction.STOP_VM, null); + } + }).show(); + + } + }); + } + + public static void promptVNCServer(final Context context, final String msg, final ViewListener viewListener) { + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + final AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(context).create(); + alertDialog.setTitle(context.getString(R.string.vncServer)); + + alertDialog.setMessage(context.getString(R.string.vncServer) + ": " + + NetworkUtils.getVNCAddress(context) + + ":" + Config.defaultVNCPort + "\n" + + "\n" + msg); + + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, context.getString(android.R.string.ok), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + viewListener.onAction(MachineAction.START_VM, null); + } + }); + alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(R.string.Cancel), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + alertDialog.dismiss(); + } + }); + + alertDialog.show(); + } + }); + } + + public static void promptMachinesImported(final Activity activity, final ArrayList machines) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + + DialogUtils.UIAlert(activity, activity.getString(R.string.importMachines), + activity.getString(R.string.machinesImported) + + ": " + machines.size() + "\n" + activity.getString(R.string.reassignDiskFilesInstructions), + 16, false, + activity.getString(android.R.string.ok), new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialogInterface, int i) { + } + }, null, null, null, null); + + } + }); + } + + public static void promptLicense(final Activity activity, final String title, final String body) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(activity).create(); + alertDialog.setTitle(title); + + TextView textView = new TextView(activity); + textView.setText(body); + textView.setTextSize(10); + textView.setPadding(20, 20, 20, 20); + + ScrollView scrollView = new ScrollView(activity); + scrollView.addView(textView); + + alertDialog.setView(scrollView); + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, + activity.getString(R.string.IAcknowledge), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + if (LimboSettingsManager.isFirstLaunch(activity)) { + Installer.installFiles(activity, true); + Help.showHelp(activity); + showChangelog(activity); + } + LimboSettingsManager.setFirstLaunch(activity); + } + }); + alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + if (LimboSettingsManager.isFirstLaunch(activity)) { + if (activity.getParent() != null) { + activity.getParent().finish(); + } else { + activity.finish(); + } + } + } + }); + alertDialog.show(); + } + }); + } + + public static void tapNotSupported(final Activity activity, final String userid) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + + DialogUtils.UIAlert(activity, activity.getString(R.string.tapUserId) + ": " + userid, + activity.getString(R.string.tapNotSupportInstructions)); + + } + }); + } + + public static void promptTap(final Activity activity, final String userid) { + + final DialogInterface.OnClickListener okListener = new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + + } + }; + + final DialogInterface.OnClickListener helpListener = + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + goToURL(activity, Config.faqLink); + } + }; + + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + DialogUtils.UIAlert(activity, + activity.getString(R.string.TapDeviceFound), + activity.getString(R.string.tunDeviceWarning) + ": " + userid + "\n", + 16, false, activity.getString(android.R.string.ok), okListener, + null, null, activity.getString(R.string.TAPHelp), helpListener); + } + }); + } + + public static void goToURL(Context context, String url) { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(url)); + context.startActivity(i); + } + + public static void onNetworkUser(final Activity activity) { + final DialogInterface.OnClickListener okListener = new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + + } + }; + + final DialogInterface.OnClickListener helpListener = + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + LimboActivityCommon.goToURL(activity, Config.faqLink); + } + }; + + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + DialogUtils.UIAlert(activity, + activity.getString(R.string.network), + activity.getString(R.string.externalNetworkWarning), + 16, false, activity.getString(android.R.string.ok), okListener, + null, null, activity.getString(R.string.faq), helpListener); + } + }); + } + + + public static void showChangelog(Activity activity) { + try { + AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(activity).create(); + alertDialog.setTitle(activity.getString(R.string.CHANGELOG)); + alertDialog.setCanceledOnTouchOutside(false); + TextView textView = new TextView(activity); + textView.setPadding(20, 20, 20, 20); + textView.setText(FileUtils.LoadFile(activity, "CHANGELOG", false)); + textView.setBackgroundColor(Color.WHITE); + textView.setTextColor(Color.BLACK); + ScrollView view = new ScrollView(activity); + view.addView(textView); + alertDialog.setView(view); + alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, activity.getString(android.R.string.ok), + (DialogInterface.OnClickListener) null); + alertDialog.show(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboApplication.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboApplication.java index 43fbe10fe..0031550eb 100644 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboApplication.java +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboApplication.java @@ -1,20 +1,193 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ package com.max2idea.android.limbo.main; import android.app.Application; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Environment; +import android.util.Log; + +import com.max2idea.android.limbo.files.FileUtils; +import com.max2idea.android.limbo.machine.Dispatcher; +import com.max2idea.android.limbo.machine.FavOpenHelper; +import com.max2idea.android.limbo.machine.MachineOpenHelper; +import com.max2idea.android.limbo.toast.ToastUtils; + +import java.io.File; +import java.io.IOException; +/** + * We use the application context for the initiliazation of some of the Storage and + * Controller implementations. + */ public class LimboApplication extends Application { + private static final String TAG = "LimboApplication"; + //Do not update these directly, see inherited project java files + public static Config.Arch arch; + private static Context sInstance; + private static String qemuVersionString; + private static int qemuVersion; + private static String limboVersionString; + private static int limboVersion; - @Override - public void onCreate() { - super.onCreate(); - try { - Class.forName("android.os.AsyncTask"); - } catch (Throwable ignore) { - // ignored - } + public static Context getInstance() { + return sInstance; + } + + public static void setupEnv(Context context) { + try { + PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getClass().getPackage().getName(), + PackageManager.GET_META_DATA); + limboVersion = packageInfo.versionCode; + limboVersionString = packageInfo.versionName; + Log.d(TAG, "Limbo Version: " + limboVersion); + Log.d(TAG, "Limbo Version Code: " + limboVersionString); + + qemuVersionString = FileUtils.LoadFile(context, "QEMU_VERSION", false); + String [] qemuVersionParts = qemuVersionString.trim().split("\\."); + qemuVersion = Integer.parseInt(qemuVersionParts[0]) * 10000 + + Integer.parseInt(qemuVersionParts[1]) * 100 + + Integer.parseInt(qemuVersionParts[2]); + Log.d(TAG, "Qemu Version: " + qemuVersionString); + Log.d(TAG, "Qemu Version Number: " + qemuVersion); + } catch (Exception e) { + e.printStackTrace(); + ToastUtils.toastShort(context, "Could not load version information: " + e); + } + } + + public static String getUserId(Context context) { + String userid = "None"; + try { + ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(context.getClass().getPackage().getName(), + PackageManager.GET_META_DATA); + userid = appInfo.uid + ""; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return userid; + } + + public static boolean isHost64Bit() { + return Build.SUPPORTED_64_BIT_ABIS != null && Build.SUPPORTED_64_BIT_ABIS.length > 0; + } + + // Legacy + public static boolean isHostX86_64() { + if (Build.SUPPORTED_64_BIT_ABIS != null) { + for (int i = 0; i < Build.SUPPORTED_64_BIT_ABIS.length; i++) + if (Build.SUPPORTED_64_BIT_ABIS[i].equals("x86_64")) + return true; + } + return false; + } + + // Legacy + public static boolean isHostX86() { + if (Build.SUPPORTED_32_BIT_ABIS != null) { + for (int i = 0; i < Build.SUPPORTED_32_BIT_ABIS.length; i++) + if (Build.SUPPORTED_32_BIT_ABIS[i].equals("x86")) + return true; + } + return false; + } + + public static boolean isHostArm() { + if (Build.SUPPORTED_32_BIT_ABIS != null) { + for (int i = 0; i < Build.SUPPORTED_32_BIT_ABIS.length; i++) + if (Build.SUPPORTED_32_BIT_ABIS[i].equals("armeabi-v7a")) + return true; + } + return false; + } - + public static boolean isHostArmv8() { + if (Build.SUPPORTED_64_BIT_ABIS != null) { + for (int i = 0; i < Build.SUPPORTED_64_BIT_ABIS.length; i++) + if (Build.SUPPORTED_64_BIT_ABIS[i].equals("arm64-v8a")) + return true; + } + return false; + } - } + public static ViewListener getViewListener() { + return Dispatcher.getInstance(); + } + + public static String getBasefileDir() { + return getInstance().getCacheDir() + "/limbo/"; + } + + public static String getTmpFolder() { + return getBasefileDir() + "var/tmp"; // Do not modify + } + + public static String getMachineDir() { + return getBasefileDir() + Config.machineFolder; + } + + public static String getLocalQMPSocketPath() { + return getInstance().getCacheDir() + "/qmpsocket"; + } + + public static String getQemuVersionString() { + return qemuVersionString; + } + + public static int getQemuVersion() { + return qemuVersion; + } + + public static String getLimboVersionString() { + return limboVersionString; + } + + public static int getLimboVersion() { + return limboVersion; + } + + @Override + public void onCreate() { + super.onCreate(); + sInstance = this; + try { + Class.forName("android.os.AsyncTask"); + } catch (Throwable ignore) { + // ignored + } + MachineOpenHelper.initialize(this); + FavOpenHelper.initialize(this); + setupFolders(); + } + private void setupFolders() { + Config.storagedir = Environment.getExternalStorageDirectory().toString(); + File folder = new File(LimboApplication.getTmpFolder()); + if (!folder.exists()) { + boolean res = folder.mkdirs(); + if (!res) { + Log.e(TAG, "Could not create temp folder: " + folder.getPath()); + } + } + } } diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboFileManager.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboFileManager.java index 79d9be05e..9640f375c 100644 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboFileManager.java +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboFileManager.java @@ -16,15 +16,523 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ - package com.max2idea.android.limbo.main; -import com.max2idea.android.limbo.utils.FileManager; +import android.Manifest; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.ListActivity; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.Looper; +import android.provider.DocumentsContract; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.MimeTypeMap; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; + +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import com.limbo.emu.lib.R; +import com.max2idea.android.limbo.dialog.DialogUtils; +import com.max2idea.android.limbo.files.FileUtils; +import com.max2idea.android.limbo.machine.Machine.FileType; +import com.max2idea.android.limbo.toast.ToastUtils; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; /** - * - * @author dev + * Legacy File Manager Activity for older devices and devices that don't support + * Android Storage Framework. This requires android:requestLegacyExternalStorage="true" in + * AndroidManifest.xml. */ -public class LimboFileManager extends FileManager{ +public class LimboFileManager extends ListActivity { + + private static final int REQUEST_WRITE_PERMISSION = 1001; + private static String TAG = "FileManager"; + private final int SELECT_DIR = 1; + private final int CREATE_DIR = 2; + private final int CANCEL = 3; + public Comparator comparator = new Comparator() { + + public int compare(File object1, File object2) { + if (object1.getName().startsWith("..")) + return -1; + else if (object2.getName().startsWith("..")) + return 1; + else if (object1.getName().endsWith("/") && !object2.getName().endsWith("/")) + return -1; + else if (!object1.getName().endsWith("/") && object2.getName().endsWith("/")) + return 1; + return object1.toString().compareToIgnoreCase(object2.toString()); + } + }; + private ArrayList items = null; + private File currdir = new File(Environment.getExternalStorageDirectory().getAbsolutePath()); + private File file; + private TextView currentDir; + private Button select; + private FileType fileType; + private HashMap filter = new HashMap<>(); + private SelectionMode selectionMode = SelectionMode.FILE; + + public static void browse(Activity activity, FileType fileType, int requestCode) { + + String lastDir = getLastDir(activity, fileType); + + String state = Environment.getExternalStorageState(); + if (!Environment.MEDIA_MOUNTED.equals(state)) { + ToastUtils.toastShort(activity, activity.getResources().getString(R.string.sdcardNotMounted)); + return; + } + + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP // device is old + || LimboSettingsManager.getEnableLegacyFileManager(activity) // app configuration ASF is disallowed + || fileType == FileType.SHARED_DIR //TODO: allow sd card access for SHARED DIR (called from c-jni and create the readdir() dirent structs) + ) { + LimboFileManager.promptLegacyStorageAccess(activity, fileType, requestCode, lastDir); + } else { // we use Android ASF to open the file (sd card supported) + try { + LimboFileManager.promptOpenFileASF(activity, fileType, getASFFileManagerRequestCode(requestCode), lastDir); + + } catch (Exception ex) { + Log.e(TAG, "Using Legacy File Manager due to exception :" + ex.getMessage()); + //XXX; some device vendors don't have proper Android Storage Framework so we fallback to legacy file manager (sd card not supported) + LimboFileManager.promptLegacyStorageAccess(activity, fileType, requestCode, lastDir); + } + } + } + + private static String getLastDir(Context context, FileType fileType) { + if (fileType == FileType.SHARED_DIR) { + return LimboSettingsManager.getSharedDir(context); + } else if (fileType == FileType.EXPORT_DIR || fileType == FileType.IMPORT_FILE) { + return LimboSettingsManager.getExportDir(context); + } else if (fileType == FileType.IMAGE_DIR) { + return LimboSettingsManager.getImagesDir(context); + } + return LimboSettingsManager.getLastDir(context); + } + + private static int getASFFileManagerRequestCode(int requestCode) { + switch (requestCode) { + case Config.OPEN_IMAGE_FILE_REQUEST_CODE: + return Config.OPEN_IMAGE_FILE_ASF_REQUEST_CODE; + case Config.OPEN_IMAGE_DIR_REQUEST_CODE: + return Config.OPEN_IMAGE_DIR_ASF_REQUEST_CODE; + case Config.OPEN_SHARED_DIR_REQUEST_CODE: + return Config.OPEN_SHARED_DIR_ASF_REQUEST_CODE; + case Config.OPEN_EXPORT_DIR_REQUEST_CODE: + return Config.OPEN_EXPORT_DIR_ASF_REQUEST_CODE; + case Config.OPEN_IMPORT_FILE_REQUEST_CODE: + return Config.OPEN_IMPORT_FILE_ASF_REQUEST_CODE; + case Config.OPEN_IMPORT_BIOS_FILE_REQUEST_CODE: + return Config.OPEN_IMPORT_BIOS_FILE_ASF_REQUEST_CODE; + case Config.OPEN_LOG_FILE_DIR_REQUEST_CODE: + return Config.OPEN_LOG_FILE_DIR_ASF_REQUEST_CODE; + default: + return requestCode; + } + } + + public static void promptLegacyStorageAccess(Activity activity, FileType fileType, int requestCode, String lastDir) { + + String dir = null; + try { + + HashMap filterExt = getFileExt(fileType); + + Intent i = null; + i = getFileManIntent(activity); + Bundle b = new Bundle(); + if (lastDir != null && !lastDir.startsWith("content://")) + b.putString("lastDir", lastDir); + b.putSerializable("fileType", fileType); + b.putSerializable("filterExt", filterExt); + i.putExtras(b); + activity.startActivityForResult(i, requestCode); + } catch (Exception e) { + Log.e(TAG, "Error while starting Filemanager: " + e.getMessage()); + } + } + + public static Intent getFileManIntent(Activity activity) { + return new Intent(activity, com.max2idea.android.limbo.main.LimboFileManager.class); + } + + protected static void promptOpenFileASF(Activity context, FileType fileType, int requestCode, String lastDir) { + Intent intent = null; + if (isFileTypeDirectory(fileType)) + intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); + else + intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + + intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + + intent.putExtra("android.content.extra.SHOW_ADVANCED", true); + + intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); + + if (!isFileTypeDirectory(fileType)) { + String[] fileMimeTypes = getFileMimeTypes(fileType); + if (fileMimeTypes != null) { + for (String fileMimeType : fileMimeTypes) { + intent.setType(fileMimeType); + } + } + } + + + if (lastDir != null && lastDir.startsWith("content://")) { + Uri uri = Uri.parse(lastDir); + intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uri); + } + + context.startActivityForResult(intent, requestCode); + } + + private static boolean isFileTypeDirectory(FileType fileType) { + return (fileType == FileType.SHARED_DIR || fileType == FileType.EXPORT_DIR + || fileType == FileType.IMAGE_DIR || fileType == FileType.LOG_DIR); + + } + + private static String[] getFileMimeTypes(FileType fileType) { + if (fileType == FileType.IMPORT_FILE) + return new String[]{MimeTypeMap.getSingleton().getMimeTypeFromExtension("csv")}; + else + return new String[]{"*/*"}; + } + + private static HashMap getFileExt(FileType fileType) { + HashMap filterExt = new HashMap<>(); + + if (fileType == FileType.IMPORT_FILE) + filterExt.put("csv", "csv"); + + return filterExt; + } + + public static String getMimeType(String url) { + String type = null; + String extension = MimeTypeMap.getFileExtensionFromUrl(url); + if (extension != null) { + type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); + } + return type; + } + + //XXX: for now we dont use filters since file extensions on images is something not so standard + // and we don't want to hide files from the user + private boolean filter(File filePath) { + String ext = FileUtils.getExtensionFromFilename(filePath.getName()); + return (filter == null || filter.isEmpty() || filter.containsKey(ext.toLowerCase())); + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + setContentView(R.layout.directory_list); + select = findViewById(R.id.select_button); + select.setOnClickListener(new View.OnClickListener() { + public void onClick(View arg0) { + selectDir(); + } + }); + currentDir = findViewById(R.id.currDir); + Bundle b = this.getIntent().getExtras(); + String lastDirectory = b.getString("lastDir"); + fileType = (FileType) b.getSerializable("fileType"); + filter = (HashMap) b.getSerializable("filterExt"); + + if (isFileTypeDirectory(fileType)) + selectionMode = SelectionMode.DIRECTORY; + else { + selectionMode = SelectionMode.FILE; + select.setVisibility(View.GONE); + } + + if (selectionMode == SelectionMode.DIRECTORY) + setTitle(getString(R.string.SelectADirectory)); + else + setTitle(getString(R.string.SelectAFile)); + + //set starting directory + if (lastDirectory == null) { + lastDirectory = Environment.getExternalStorageDirectory().getPath(); + } + currdir = new File(lastDirectory); + if (!currdir.isDirectory() || !currdir.exists()) { + lastDirectory = Environment.getExternalStorageDirectory().getPath(); + currdir = new File(lastDirectory); + } + currentDir.setText(currdir.getPath()); + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + checkPermissionsAndBrowse(); + } + }, 500); + } + + private void checkPermissionsAndBrowse() { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) + != PackageManager.PERMISSION_GRANTED) { + if (ActivityCompat.shouldShowRequestPermissionRationale(this, + Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + DialogUtils.UIAlert(this, getString(R.string.WriteAccess), getString(R.string.FullAccessWarning), 16, false, + getString(R.string.OkIUnderstand), new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialogInterface, int i) { + ActivityCompat.requestPermissions(LimboFileManager.this, + new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, + REQUEST_WRITE_PERMISSION); + } + }, null, null, null, null); + } else { + ActivityCompat.requestPermissions(this, + new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, + REQUEST_WRITE_PERMISSION); + } + } else { + fill(currdir.listFiles()); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, + String permissions[], int[] grantResults) { + switch (requestCode) { + case REQUEST_WRITE_PERMISSION: { + if (grantResults.length > 0 + && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + fill(currdir.listFiles()); + } else { + ToastUtils.toastShort(this, getString(R.string.FeaturDisabled)); + finish(); + } + return; + } + } + } + + private void fill(File[] files) { + + + items = new ArrayList<>(); + items.add(new File(".. (Parent Directory)")); + + if (files != null) { + for (File file1 : files) { + if (file1 != null) { + if (file1.isFile() && filter(file1) + ) { + items.add(file1); + } else if (file1.isDirectory()) { + items.add(file1); + } + } + } + } + Collections.sort(items, comparator); + FileAdapter fileList = new FileAdapter(this, R.layout.dir_row, items); + setListAdapter(fileList); + + LimboSettingsManager.setLastDir(this, currdir.getAbsolutePath()); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + super.onListItemClick(l, v, position, id); + // int selectionRowID = (int) l.getSelectedItemId(); + int selectionRowID = (int) id; + if (selectionRowID == 0) { + fillWithParent(); + } else { + + file = items.get(selectionRowID); + if (file == null) { + ToastUtils.toastShort(this, getString(R.string.AccessDeniedCannotRetrieveDirectory)); + } else if (!file.isDirectory() && selectionMode == SelectionMode.DIRECTORY) { + ToastUtils.toastShort(this, getString(R.string.NotADirectory)); + } else if (file.isDirectory()) { + currdir = file; + File[] files = file.listFiles(); + if (files != null) { + currentDir.setText(file.getPath()); + fill(files); + } else { + new AlertDialog.Builder(this).setTitle(R.string.AccessDenied).setMessage(R.string.CannotListDirectory).show(); + } + } else { + this.selectFile(); + } + + } + } + + private void fillWithParent() { + if (currdir.getPath().equalsIgnoreCase("/")) { + currentDir.setText(currdir.getPath()); + fill(currdir.listFiles()); + } else { + currdir = currdir.getParentFile(); + currentDir.setText(currdir.getPath()); + fill(currdir.listFiles()); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(0, SELECT_DIR, 0, "Select Directory"); + menu.add(0, CREATE_DIR, 0, "Create Directory"); + menu.add(0, CANCEL, 0, "Cancel"); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case SELECT_DIR: + selectDir(); + return true; + case CREATE_DIR: + promptCreateDir(this); + return true; + case CANCEL: + cancel(); + return true; + } + return false; + } + + + public void promptCreateDir(final Activity activity) { + final AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(activity).create(); + alertDialog.setTitle(getString(R.string.NewDirectory)); + final EditText dirNameTextview = new EditText(activity); + dirNameTextview.setPadding(20, 20, 20, 20); + dirNameTextview.setEnabled(true); + dirNameTextview.setVisibility(View.VISIBLE); + dirNameTextview.setSingleLine(); + alertDialog.setView(dirNameTextview); + alertDialog.setCanceledOnTouchOutside(false); + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.Create), (DialogInterface.OnClickListener) null); + + alertDialog.show(); + + Button button = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); + button.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + if (dirNameTextview.getText().toString().trim().equals("")) + ToastUtils.toastShort(activity, getString(R.string.DirNameCannotBeEmpty)); + else { + createDirectory(dirNameTextview.getText().toString()); + fill(currdir.listFiles()); + alertDialog.dismiss(); + } + } + }); + } + + private void createDirectory(String dirName) { + File dir = new File(currdir, dirName); + if (!dir.exists()) { + dir.mkdirs(); + } else { + ToastUtils.toastShort(this, getString(R.string.DirectoryAlreadyExists)); + } + } + + public void selectDir() { + Intent data = new Intent(); + Bundle bundle = new Bundle(); + bundle.putString("currDir", this.currdir.getPath()); + bundle.putSerializable("fileType", fileType); + data.putExtras(bundle); + setResult(Config.FILEMAN_RETURN_CODE, data); + finish(); + } + + public void selectFile() { + Intent data = new Intent(); + Bundle bundle = new Bundle(); + bundle.putString("currDir", this.currdir.getPath()); + bundle.putString("file", this.file.getPath()); + bundle.putSerializable("fileType", fileType); + data.putExtras(bundle); + setResult(Config.FILEMAN_RETURN_CODE, data); + finish(); + } + + private void cancel() { + Intent data = new Intent(); + data.putExtra("currDir", ""); + setResult(Config.FILEMAN_RETURN_CODE, data); + finish(); + } + + + public enum SelectionMode { + DIRECTORY, + FILE + } + + public static class FileAdapter extends ArrayAdapter { + private final Context context; + private final ArrayList files; + + public FileAdapter(Context context, int layout, ArrayList files) { + super(context, layout, files); + this.context = context; + this.files = files; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + LayoutInflater inflater = (LayoutInflater) context + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + View rowView = inflater.inflate(R.layout.dir_row, parent, false); + TextView textView = (TextView) rowView.findViewById(R.id.FILE_NAME); + ImageView imageView = (ImageView) rowView.findViewById(R.id.FILE_ICON); + textView.setText(files.get(position).getName()); + + int iconRes = 0; + if (files.get(position).getName().startsWith("..") || files.get(position).isDirectory()) + imageView.setImageResource(R.drawable.folder); + else { + iconRes = FileUtils.getIconForFile(files.get(position).getName()); + imageView.setImageResource(iconRes); + } + return rowView; + } + } } diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboSDLActivity.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboSDLActivity.java index dd506a7f1..6ebb8d821 100644 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboSDLActivity.java +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboSDLActivity.java @@ -1,39 +1,48 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ package com.max2idea.android.limbo.main; -import androidx.appcompat.app.ActionBar; -import android.app.Activity; import android.app.AlertDialog; -import android.app.Dialog; -import android.app.ProgressDialog; +import android.content.ClipData; +import android.content.ClipboardManager; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.Configuration; -import android.graphics.Point; import android.media.AudioManager; -import android.os.AsyncTask; +import android.media.AudioRecord; +import android.media.AudioTrack; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.os.Vibrator; -import androidx.core.view.MenuItemCompat; - +import android.os.PowerManager; import android.util.Log; -import android.view.Display; -import android.view.GestureDetector; -import android.view.Gravity; -import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; -import android.view.SurfaceHolder; import android.view.View; -import android.view.ViewGroup; import android.view.WindowManager; -import android.widget.Button; +import android.widget.EditText; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.ScrollView; @@ -41,452 +50,265 @@ import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; -import com.limbo.emu.lib.R; -import com.max2idea.android.limbo.utils.DrivesDialogBox; -import com.max2idea.android.limbo.utils.FileUtils; -import com.max2idea.android.limbo.utils.Machine; -import com.max2idea.android.limbo.utils.QmpClient; -import com.max2idea.android.limbo.utils.UIUtils; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.widget.Toolbar; -import org.json.JSONObject; +import com.limbo.emu.lib.R; +import com.max2idea.android.limbo.files.FileUtils; +import com.max2idea.android.limbo.help.Help; +import com.max2idea.android.limbo.keyboard.KeyboardUtils; +import com.max2idea.android.limbo.keymapper.KeyMapManager; +import com.max2idea.android.limbo.log.Logger; +import com.max2idea.android.limbo.machine.Machine; +import com.max2idea.android.limbo.machine.MachineAction; +import com.max2idea.android.limbo.machine.MachineController; +import com.max2idea.android.limbo.machine.MachineProperty; +import com.max2idea.android.limbo.screen.ScreenUtils; +import com.max2idea.android.limbo.toast.ToastUtils; import org.libsdl.app.SDLActivity; -import org.libsdl.app.SDLControllerManager; +import org.libsdl.app.SDLAudioManager; -import java.io.File; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.egl.EGLContext; -import javax.microedition.khronos.egl.EGLDisplay; -import javax.microedition.khronos.egl.EGLSurface; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** - * SDL Activity + * Our overloaded SDLActivity with compatibility reroutes for the mouse and other extra functionality + * for better usability. In general SDL is slower than VNC but offers Audio support for QEMU. */ -public class LimboSDLActivity extends SDLActivity { - public static final String TAG = "LimboSDLActivity"; +public class LimboSDLActivity extends SDLActivity + implements KeyMapManager.OnSendKeyEventListener, KeyMapManager.OnSendMouseEventListener, + KeyMapManager.OnUnhandledTouchEventListener, MachineController.OnMachineStatusChangeListener, + MachineController.OnEventListener { + public static final int KEYBOARD = 10000; + private static final String TAG = "LimboSDLActivity"; + + public static boolean toggleKeyboardFlag = true; + public static boolean isResizing = false; + public static boolean pendingPause; + public static boolean pendingStop; + private static boolean machineRunning; + + public static MouseMode mouseMode = MouseMode.Trackpad; + private final ExecutorService mouseEventsExecutor = Executors.newFixedThreadPool(1); + private final ExecutorService keyEventsExecutor = Executors.newFixedThreadPool(1); + public DrivesDialogBox drives = null; + public AudioManager am; + protected int maxVolume; + // store state + private AudioTrack audioTrack; + private AudioRecord mAudioRecord; + private boolean monitorMode = false; + private KeyMapManager mKeyMapManager; + private ViewListener viewListener; + private boolean quit = false; + private View mGap; + private boolean resettingLayout; + + public void showHints() { + ToastUtils.toastShortTop(this, getString(R.string.PressVolumeDownForRightClick)); + } - public static LimboSDLActivity activity ; + public void setupToolBar() { + Toolbar tb = findViewById(R.id.toolbar); + setSupportActionBar(tb); + + // Get the ActionBar here to configure the way it behaves. + ActionBar ab = getSupportActionBar(); + if (ab != null) { + ab.setHomeAsUpIndicator(R.drawable.limbo); // set a custom icon + ab.setDisplayShowHomeEnabled(true); // show or hide the default home + ab.setDisplayHomeAsUpEnabled(true); + ab.setDisplayShowCustomEnabled(true); // enable overriding the + ab.setDisplayShowTitleEnabled(true); // disable the default title + ab.setTitle(R.string.app_name); + if (!LimboSettingsManager.getAlwaysShowMenuToolbar(this)) { + ab.hide(); + } + } + } - public static final int KEYBOARD = 10000; - public static final int QUIT = 10001; - public static final int HELP = 10002; - - private boolean monitorMode = false; - private boolean mouseOn = false; - private Object lockTime = new Object(); - private boolean timeQuit = false; - private boolean once = true; - private boolean zoomable = false; - private String status = null; - - public static int vm_width; - public static int vm_height; - - - private Thread timeListenerThread; - - private ProgressDialog progDialog; - protected static ViewGroup mMainLayout; - - public String cd_iso_path = null; - - // HDD - public String hda_img_path = null; - public String hdb_img_path = null; - public String hdc_img_path = null; - public String hdd_img_path = null; - - public String fda_img_path = null; - public String fdb_img_path = null; - public String cpu = null; - - // Default Settings - public int memory = 128; - public String bootdevice = null; - - // net - public String net_cfg = "None"; - public int nic_num = 1; - public String vga_type = "std"; - public String hd_cache = "default"; - public String nic_driver = null; - public String soundcard = null; - public String lib = "liblimbo.so"; - public String lib_path = null; - public String snapshot_name = "limbo"; - public int disableacpi = 0; - public int disablehpet = 0; - public int disabletsc = 0; - public int enableqmp = 0; - public int enablevnc = 0; - public String vnc_passwd = null; - public int vnc_allow_external = 0; - public String qemu_dev = null; - public String qemu_dev_value = null; - - public String dns_addr = null; - public int restart = 0; - - // This is what SDL runs in. It invokes SDL_main(), eventually - private static Thread mSDLThread; - - // EGL private objects - private static EGLContext mEGLContext; - private static EGLSurface mEGLSurface; - private static EGLDisplay mEGLDisplay; - private static EGLConfig mEGLConfig; - private static int mGLMajor, mGLMinor; - - - private static Activity activity1; - - // public static void showTextInput(int x, int y, int w, int h) { - // // Transfer the task to the main thread as a Runnable - // // mSingleton.commandHandler.post(new ShowTextInputHandler(x, y, w, h)); - // } - - public static void singleClick(final MotionEvent event, final int pointer_id) { - - Thread t = new Thread(new Runnable() { - public void run() { - // Log.d("SDL", "Mouse Single Click"); - try { - Thread.sleep(50); - } catch (InterruptedException ex) { - // Log.v("singletap", "Could not sleep"); - } - LimboActivity.vmexecutor.onLimboMouse(Config.SDL_MOUSE_LEFT, MotionEvent.ACTION_DOWN, 1,0, 0); - try { - Thread.sleep(50); - } catch (InterruptedException ex) { - // Log.v("singletap", "Could not sleep"); - } - LimboActivity.vmexecutor.onLimboMouse(Config.SDL_MOUSE_LEFT, MotionEvent.ACTION_UP, 1, 0, 0); - } - }); - t.start(); - } - - - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); if (requestCode == Config.OPEN_IMAGE_FILE_REQUEST_CODE || requestCode == Config.OPEN_IMAGE_FILE_ASF_REQUEST_CODE) { - String file = null; - if(requestCode == Config.OPEN_IMAGE_FILE_ASF_REQUEST_CODE) { + String file; + if (requestCode == Config.OPEN_IMAGE_FILE_ASF_REQUEST_CODE) { file = FileUtils.getFileUriFromIntent(this, data, true); } else { - DrivesDialogBox.filetype = FileUtils.getFileTypeFromIntent(this, data); - file = FileUtils.getFilePathFromIntent(activity, data); + drives.fileType = FileUtils.getFileTypeFromIntent(this, data); + file = FileUtils.getFilePathFromIntent(this, data); } - if(drives !=null && file!=null) - drives.setDriveAttr(DrivesDialogBox.filetype, file); - }else if (requestCode == Config.OPEN_LOG_FILE_DIR_REQUEST_CODE|| requestCode == Config.OPEN_LOG_FILE_DIR_ASF_REQUEST_CODE) { - String file = null; - if(requestCode == Config.OPEN_LOG_FILE_DIR_ASF_REQUEST_CODE) { + if (drives != null && file != null) + drives.setDriveAttr(drives.fileType, file); + } else if (requestCode == Config.OPEN_LOG_FILE_DIR_REQUEST_CODE || requestCode == Config.OPEN_LOG_FILE_DIR_ASF_REQUEST_CODE) { + String file; + if (requestCode == Config.OPEN_LOG_FILE_DIR_ASF_REQUEST_CODE) { file = FileUtils.getFileUriFromIntent(this, data, true); } else { file = FileUtils.getDirPathFromIntent(this, data); } - if(file!=null) { - FileUtils.saveLogToFile(activity, file); + if (file != null) { + FileUtils.saveLogToFile(this, file); } } + } + + public synchronized void sendKeys(int [] codes) { + sendKeys(codes, Config.keyDelay); + } + + public synchronized void sendKeys(int [] codes, int delay) { + for(int code : codes) + sendKeyEvent(null, code, true, delay); + for(int code : codes) + sendKeyEvent(null, code, false, delay); + } + + public synchronized void sendCtrlAlt(int code) { + sendKeys(new int[]{KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_ALT_LEFT, + code}, 100); + } + + public synchronized void sendCtrlAltShift(int code) { + sendKeys(new int[]{KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_ALT_LEFT, + KeyEvent.KEYCODE_SHIFT_LEFT, code}, 100); + } - } - - public void setParams(Machine machine) { - - if (machine == null) { - return; - } - memory = machine.memory; - vga_type = machine.vga_type; - hd_cache = machine.hd_cache; - snapshot_name = machine.snapshot_name; - disableacpi = machine.disableacpi; - disablehpet = machine.disablehpet; - disabletsc = machine.disabletsc; - enableqmp = machine.enableqmp; - enablevnc = machine.enablevnc; - - if (machine.cpu.endsWith("(64Bit)")) { - cpu = machine.cpu.split(" ")[0]; - } else { - cpu = machine.cpu; - } - - if (machine.cd_iso_path == null || machine.cd_iso_path.equals("None")) { - cd_iso_path = null; - } else { - cd_iso_path = machine.cd_iso_path; - } - if (machine.hda_img_path == null || machine.hda_img_path.equals("None")) { - hda_img_path = null; - } else { - hda_img_path = machine.hda_img_path; - } - - if (machine.hdb_img_path == null || machine.hdb_img_path.equals("None")) { - hdb_img_path = null; - } else { - hdb_img_path = machine.hdb_img_path; - } - - if (machine.hdc_img_path == null || machine.hdc_img_path.equals("None")) { - hdc_img_path = null; - } else { - hdc_img_path = machine.hdc_img_path; - } - - if (machine.hdd_img_path == null || machine.hdd_img_path.equals("None")) { - hdd_img_path = null; - } else { - hdd_img_path = machine.hdd_img_path; - } - - if (machine.fda_img_path == null || machine.fda_img_path.equals("None")) { - fda_img_path = null; - } else { - fda_img_path = machine.fda_img_path; - } - - if (machine.fdb_img_path == null || machine.fdb_img_path.equals("None")) { - fdb_img_path = null; - } else { - fdb_img_path = machine.fdb_img_path; - } - if (machine.bootdevice == null) { - bootdevice = null; - } else if (machine.bootdevice.equals("Default")) { - bootdevice = null; - } else if (machine.bootdevice.equals("CD Rom")) { - bootdevice = "d"; - } else if (machine.bootdevice.equals("Floppy")) { - bootdevice = "a"; - } else if (machine.bootdevice.equals("Hard Disk")) { - bootdevice = "c"; - } - - if (machine.net_cfg == null || machine.net_cfg.equals("None")) { - net_cfg = "none"; - nic_driver = null; - } else if (machine.net_cfg.equals("User")) { - net_cfg = "user"; - nic_driver = machine.nic_card; - } - - soundcard = machine.soundcard; - - } - - public static void delayKey(int ms) { - try { - Thread.sleep(ms); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - 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) { - SDLActivity.onNativeKeyDown(code); - delayKey(100); - SDLActivity.onNativeKeyUp(code); - delayKey(100); - } - SDLActivity.onNativeKeyUp(KeyEvent.KEYCODE_ALT_LEFT); - delayKey(100); - SDLActivity.onNativeKeyUp(KeyEvent.KEYCODE_CTRL_LEFT); - } - - public void stopTimeListener() { - Log.v("SaveVM", "Stopping Listener"); - synchronized (this.lockTime) { - this.timeQuit = true; - this.lockTime.notifyAll(); - } - } - - public void onDestroy() { - - // Now wait for the SDL thread to quit - Log.v("LimboSDL", "Waiting for SDL thread to quit"); - if (mSDLThread != null) { - try { - mSDLThread.join(); - } catch (Exception e) { - Log.v("SDL", "Problem stopping thread: " + e); - } - mSDLThread = null; - - Log.v("SDL", "Finished waiting for SDL thread"); - } - this.stopTimeListener(); - - LimboActivity.vmexecutor.doStopVM(0); - super.onDestroy(); - } - - - public void checkStatus() { - while (timeQuit != true) { - LimboActivity.VMStatus status = Machine.checkSaveVMStatus(activity); - Log.v(TAG, "Status: " + status); - if (status == LimboActivity.VMStatus.Unknown - || status == LimboActivity.VMStatus.Completed - || status == LimboActivity.VMStatus.Failed - ) { - Log.v("Inside", "Saving state is done: " + status); - stopTimeListener(); - return; - } - try { - Thread.sleep(1000); - } catch (InterruptedException ex) { - Log.w("SaveVM", "Interrupted"); - } - } - Log.v("SaveVM", "Save state complete"); - - } - - - public void startTimeListener() { - this.stopTimeListener(); - timeQuit = false; - try { - Log.v("Listener", "Time Listener Started..."); - checkStatus(); - synchronized (lockTime) { - while (timeQuit == false) { - lockTime.wait(); - } - lockTime.notifyAll(); - } - } catch (Exception ex) { - ex.printStackTrace(); - Log.v("SaveVM", "Time listener thread error: " + ex.getMessage()); - } - Log.v("Listener", "Time listener thread exited..."); - - } - - public DrivesDialogBox drives = null; - public static boolean toggleKeyboardFlag = true; - - @Override - public boolean onOptionsItemSelected(final MenuItem item) { - // Log.v("Limbo", "Inside Options Check"); - super.onOptionsItemSelected(item); - if (item.getItemId() == R.id.itemDrives) { - // Show up removable devices dialog - if (LimboActivity.currMachine.hasRemovableDevices()) { - drives = new DrivesDialogBox(activity, R.style.Transparent, this, LimboActivity.currMachine); - drives.show(); - } else { - UIUtils.toastShort(activity, "No removable devices attached"); - } - } else if (item.getItemId() == R.id.itemReset) { - Machine.resetVM(activity); - } else if (item.getItemId() == R.id.itemShutdown) { - UIUtils.hideKeyboard(this, mSurface); - Machine.stopVM(activity); - } else if (item.getItemId() == R.id.itemMouse) { - onMouseMode(); - } else if (item.getItemId() == this.KEYBOARD || item.getItemId() == R.id.itemKeyboard) { - //XXX: need to post after delay to work correctly + public void onDestroy() { + mNextNativeState = NativeState.PAUSED; + mIsResumedCalled = false; + + if (SDLActivity.mBrokenLibraries) { + return; + } + + SDLActivity.handleNativeState(); + SDLActivity.mSuspendOnly = true; + removeListeners(); + quit = true; + super.onDestroy(); + } + + private void removeListeners() { + MachineController.getInstance().removeOnStatusChangeListener(this); + mKeyMapManager.setOnSendKeyEventListener(this); + mKeyMapManager.setOnSendMouseEventListener(this); + mKeyMapManager.setOnUnhandledTouchEventListener(this); + setViewListener(null); + } + + @Override + public boolean onOptionsItemSelected(final MenuItem item) { + super.onOptionsItemSelected(item); + if (item.getItemId() == R.id.itemDrives) { + // Show up removable devices dialog + if (MachineController.getInstance().getMachine().hasRemovableDevices()) { + drives = new DrivesDialogBox(LimboSDLActivity.this, R.style.Transparent, MachineController.getInstance().getMachine()); + drives.show(); + } else { + ToastUtils.toastShort(this, getString(R.string.NoRemovableDevicesAttached)); + } + } else if (item.getItemId() == R.id.itemReset) { + LimboActivityCommon.promptResetVM(this, viewListener); + } else if (item.getItemId() == R.id.itemShutdown) { + KeyboardUtils.hideKeyboard(this, mSurface); + LimboActivityCommon.promptStopVM(this, viewListener); + } else if (item.getItemId() == R.id.itemDisconnet) { + finish(); + } else if (item.getItemId() == R.id.itemMouse) { + promptMouseMode(); + } else if (item.getItemId() == KEYBOARD || item.getItemId() == R.id.itemKeyboard) { + //XXX: need to delay to work properly new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { @Override public void run() { - toggleKeyboardFlag = UIUtils.onKeyboard(activity, toggleKeyboardFlag, mSurface); + toggleKeyboardFlag = KeyboardUtils.showKeyboard(LimboSDLActivity.this, toggleKeyboardFlag, mSurface); } - }, 200); - - } - else if (item.getItemId() == R.id.itemMonitor) { - if (this.monitorMode) { - this.onVMConsole(); - } else { - this.onMonitor(); - } - } else if (item.getItemId() == R.id.itemVolume) { - this.onSelectMenuVol(); - } else if (item.getItemId() == R.id.itemSaveState) { - this.promptPause(activity); - } else if (item.getItemId() == R.id.itemSaveSnapshot) { - //TODO: - //this.promptStateName(activity); - } else if (item.getItemId() == R.id.itemFitToScreen) { - onFitToScreen(); - } else if (item.getItemId() == R.id.itemStretchToScreen) { - onStretchToScreen(); - } else if (item.getItemId() == R.id.itemZoomIn) { - this.setZoomIn(); - } else if (item.getItemId() == R.id.itemZoomOut) { - this.setZoomOut(); - } else if (item.getItemId() == R.id.itemCtrlAltDel) { - this.onCtrlAltDel(); - } else if (item.getItemId() == R.id.itemCtrlC) { - this.onCtrlC(); - } else if (item.getItemId() == R.id.itemOneToOne) { - this.onNormalScreen(); - } else if (item.getItemId() == R.id.itemZoomable) { - this.setZoomable(); - } else if (item.getItemId() == this.QUIT) { - } else if (item.getItemId() == R.id.itemHelp) { - UIUtils.onHelp(this); - } else if (item.getItemId() == R.id.itemHideToolbar) { - this.onHideToolbar(); + }, 500); + } else if (item.getItemId() == R.id.itemMonitor) { + if (monitorMode) { + showVMDisplay(); + } else { + showMonitor(); + } + } else if (item.getItemId() == R.id.itemVolume) { + promptVolume(); + } else if (item.getItemId() == R.id.itemSaveState) { + LimboActivityCommon.promptPause(this, viewListener); + } else if (item.getItemId() == R.id.itemCtrlAltDel) { + sendCtrlAltDel(); + } else if (item.getItemId() == R.id.itemHelp) { + Help.showHelp(this); + } else if (item.getItemId() == R.id.itemHideToolbar) { + hideToolbar(); } else if (item.getItemId() == R.id.itemDisplay) { - this.onSelectMenuSDLDisplay(); - } else if (item.getItemId() == R.id.itemViewLog) { - this.onViewLog(); + promptSDLDisplay(); + } else if (item.getItemId() == R.id.itemViewLog) { + Logger.viewLimboLog(this); + } else if (item.getItemId() == R.id.itemKeyboardMapper) { + toggleKeyMapper(); + } else if (item.getItemId() == R.id.itemSendText) { + promptSendText(); } - // this.canvas.requestFocus(); + invalidateOptionsMenu(); + return true; + } - this.invalidateOptionsMenu(); - return true; - } + private void promptSendText() { + final AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(this).create(); + alertDialog.setTitle(getString(R.string.SendText)); + final EditText text = new EditText(this); + text.setText(""); + text.setEnabled(true); + text.setVisibility(View.VISIBLE); + text.setSingleLine(); + alertDialog.setView(text); + + ClipboardManager clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); + ClipData clipData = clipboardManager.getPrimaryClip(); + if (clipData != null) { + if (clipData.getItemCount() > 0) + text.setText(clipData.getItemAt(0).getText()); + } - public void onViewLog() { - FileUtils.viewLimboLog(this); + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.Send), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + String textStr = text.getText().toString(); + sendText(textStr); + } + }); + alertDialog.show(); } - public void onHideToolbar(){ - ActionBar bar = this.getSupportActionBar(); + public void hideToolbar() { + ActionBar bar = getSupportActionBar(); if (bar != null) { bar.hide(); } } + private void promptMouseMode() { - private void onMouseMode() { - - String [] items = {"Trackpad Mouse (Phone)", - "Bluetooth/USB Mouse (Desktop mode)", //Physical mouse for Chromebook, Android x86 PC, or Bluetooth Mouse + String[] items = { + getString(R.string.TrackpadDescr), + getString(R.string.TouchScreen), + getString(R.string.ExternalMouseDescr) }; final AlertDialog.Builder mBuilder = new AlertDialog.Builder(this); - mBuilder.setTitle("Mouse"); - mBuilder.setSingleChoiceItems(items, Config.mouseMode.ordinal(), new DialogInterface.OnClickListener() { + mBuilder.setTitle(R.string.Mouse); + mBuilder.setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int i) { - switch(i){ + switch (i) { case 0: - setUIModeMobile(true); + setTrackpadMode(); break; case 1: - promptSetUIModeDesktop(false); + case 2: + promptAbsoluteDevice(i == 2); break; default: break; @@ -496,95 +318,47 @@ public void onClick(DialogInterface dialog, int i) { }); final AlertDialog alertDialog = mBuilder.create(); alertDialog.show(); - } - public boolean checkVMResolutionFits() { - int width = mLayout.getWidth(); - int height = mLayout.getHeight(); - ActionBar bar = activity.getSupportActionBar(); - - if (!LimboSettingsManager.getAlwaysShowMenuToolbar(LimboSDLActivity.this) - && bar != null && bar.isShowing()) { - height += bar.getHeight(); - } - - if(vm_width < width && vm_height < height) - return true; - - return false; - } - - public void calibration() { - //XXX: No need to calibrate for SDL trackpad. - } - - private void setUIModeMobile(boolean fitToScreen){ - - try { - UIUtils.setOrientation(this); - MotionEvent a = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); - - //TODO: needed? - //LimboSDLActivity.singleClick(a, 0); - Config.mouseMode = Config.MouseMode.Trackpad; - LimboSettingsManager.setDesktopMode(this, false); - LimboActivity.vmexecutor.setRelativeMouseMode(1); - if(Config.showToast) - UIUtils.toastShort(this.getApplicationContext(), "Trackpad Enabled"); - if(fitToScreen) - onFitToScreen(); - else - onNormalScreen(); - calibration(); - invalidateOptionsMenu(); - }catch (Exception ex){ - if(Config.debug) + protected void setTrackpadMode() { + try { + ScreenUtils.updateOrientation(this, -1); + mouseMode = MouseMode.Trackpad; + invalidateOptionsMenu(); + ((LimboSDLSurface) mSurface).refreshSurfaceView(); + } catch (Exception ex) { + if (Config.debug) ex.printStackTrace(); } } - private void promptSetUIModeDesktop(final boolean mouseMethodAlt) { - - + private void promptAbsoluteDevice(final boolean externalMouse) { final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("Desktop Mode"); + alertDialog = new AlertDialog.Builder(this).create(); + alertDialog.setTitle(getString(R.string.desktopMode)); 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 textView = new TextView(this); textView.setVisibility(View.VISIBLE); - String desktopInstructions = this.getString(R.string.desktopInstructions); - if(!checkVMResolutionFits()){ - String resolutionWarning = "Warning: Machine resolution " - + 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"; - desktopInstructions = resolutionWarning + desktopInstructions; - } - textView.setText(desktopInstructions); - - ScrollView scrollView = new ScrollView(this); - scrollView.addView(textView); - - LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - mLayout.addView(scrollView, params); - + String instructions = getString(R.string.absolutePointerInstructions); + if (externalMouse) + instructions += "\n" + getString(R.string.externalMouseInstructions); + textView.setText(instructions); + mLayout.addView(textView); alertDialog.setView(mLayout); - - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() { + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.Ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { - setUIModeDesktop(); + // we handle external mouse at all times + if (!externalMouse) + setTouchScreenMode(); alertDialog.dismiss(); } }); - alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() { + alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.Cancel), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { alertDialog.dismiss(); } @@ -593,633 +367,380 @@ public void onClick(DialogInterface dialog, int which) { } - private void setUIModeDesktop() { - - try { - MotionEvent a = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); - - //TODO: needed? - //LimboSDLActivity.singleClick(a, 0); - - //TODO: not needed? - //SDLActivity.onNativeMouseReset(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); - //SDLActivity.onNativeMouseReset(0, 0, MotionEvent.ACTION_MOVE, vm_width, vm_height, 0); - - Config.mouseMode = Config.MouseMode.External; - LimboSettingsManager.setDesktopMode(this, true); - LimboActivity.vmexecutor.setRelativeMouseMode(0); - if(Config.showToast) - UIUtils.toastShort(LimboSDLActivity.this, "External Mouse Enabled"); - onNormalScreen(); - calibration(); + protected void setTouchScreenMode() { + try { + mouseMode = MouseMode.TOUCHSCREEN; invalidateOptionsMenu(); - }catch (Exception ex){ - if(Config.debug) - ex.printStackTrace(); - } - } - - private void onCtrlAltDel() { - - SDLActivity.onNativeKeyDown(KeyEvent.KEYCODE_CTRL_RIGHT); - SDLActivity.onNativeKeyDown(KeyEvent.KEYCODE_ALT_RIGHT); - SDLActivity.onNativeKeyDown(KeyEvent.KEYCODE_FORWARD_DEL); - SDLActivity.onNativeKeyUp(KeyEvent.KEYCODE_FORWARD_DEL); - SDLActivity.onNativeKeyUp(KeyEvent.KEYCODE_ALT_RIGHT); - SDLActivity.onNativeKeyUp(KeyEvent.KEYCODE_CTRL_RIGHT); - } - - private void onCtrlC() { - - SDLActivity.onNativeKeyDown(KeyEvent.KEYCODE_CTRL_RIGHT); - SDLActivity.onNativeKeyDown(KeyEvent.KEYCODE_C); - SDLActivity.onNativeKeyUp(KeyEvent.KEYCODE_C); - SDLActivity.onNativeKeyUp(KeyEvent.KEYCODE_CTRL_RIGHT); - } - - - //TODO: not working - private void onStretchToScreen() { - - - new Thread(new Runnable() { - public void run() { - Log.d(TAG, "onStretchToScreen"); - screenMode = SDLScreenMode.Fullscreen; - sendCtrlAltKey(KeyEvent.KEYCODE_F); // not working - if(Config.showToast) - UIUtils.toastShort(activity, "Resizing, Please Wait"); - resize(null); - - } - }).start(); - - } - - private void onFitToScreen() { - try { - UIUtils.setOrientation(this); - ActionBar bar = LimboSDLActivity.this.getSupportActionBar(); - if (bar != null && !LimboSettingsManager.getAlwaysShowMenuToolbar(this)) { - bar.hide(); - } - new Thread(new Runnable() { - public void run() { - Log.d(TAG, "onFitToScreen"); - screenMode = SDLScreenMode.FitToScreen; - if(Config.showToast) - UIUtils.toastShort(activity, "Resizing, Please Wait"); - resize(null); - - } - }).start(); - }catch (Exception ex){ - if(Config.debug) + ((LimboSDLSurface) mSurface).refreshSurfaceView(); + } catch (Exception ex) { + if (Config.debug) ex.printStackTrace(); } - } - private void onNormalScreen() { - try { - ActionBar bar = LimboSDLActivity.this.getSupportActionBar(); - if (bar != null && !LimboSettingsManager.getAlwaysShowMenuToolbar(this)) { - bar.hide(); - } - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); - new Thread(new Runnable() { - public void run() { - Log.d(TAG, "onNormalScreen"); - screenMode = SDLScreenMode.Normal; - if(Config.showToast) - UIUtils.toastShort(activity, "Resizing, Please Wait"); - resize(null); - - } - }).start(); - }catch (Exception ex){ - if(Config.debug) - ex.printStackTrace(); - } - + private void sendCtrlAltDel() { + sendCtrlAlt(KeyEvent.KEYCODE_FORWARD_DEL); } - public void resize(final Configuration newConfig) { - - //XXX: flag so no mouse events are processed - isResizing = true; - - //XXX: This is needed so Nougat+ devices will update their layout - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - ((LimboSDLSurface) mSurface).getHolder().setFixedSize(1, 1); - setLayout(newConfig); - - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - ((LimboSDLSurface) mSurface).doResize(false, newConfig); - } - }, 1000); - } - }); - - } - - private void setZoomIn() { - - new Thread(new Runnable() { - public void run() { - screenMode = SDLScreenMode.Normal; - sendCtrlAltKey(KeyEvent.KEYCODE_4); - } - }).start(); - - } - - private void setZoomOut() { - - - new Thread(new Runnable() { - public void run() { - screenMode = SDLScreenMode.Normal; - sendCtrlAltKey(KeyEvent.KEYCODE_3); - - } - }).start(); - - } - - private void setZoomable() { - - zoomable = true; - - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - menu.clear(); - return this.setupMenu(menu); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - menu.clear(); - return this.setupMenu(menu); - } - - public boolean setupMenu(Menu menu) { - // Log.v("Limbo", "Inside Options Created"); - getMenuInflater().inflate(R.menu.sdlactivitymenu, menu); + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + menu.clear(); + getMenuInflater().inflate(R.menu.sdlactivitymenu, menu); int maxMenuItemsShown = 4; - int actionShow = MenuItemCompat.SHOW_AS_ACTION_IF_ROOM; - if(UIUtils.isLandscapeOrientation(this)) { + int actionShow = MenuItem.SHOW_AS_ACTION_IF_ROOM; + if (ScreenUtils.isLandscapeOrientation(this)) { maxMenuItemsShown = 6; - actionShow = MenuItemCompat.SHOW_AS_ACTION_ALWAYS; + actionShow = MenuItem.SHOW_AS_ACTION_ALWAYS; } - // if (vncCanvas.scaling != null) { - // menu.findItem(vncCanvas.scaling.getId()).setChecked(true); - // } - - // Remove snapshots for now - menu.removeItem(menu.findItem(R.id.itemSaveSnapshot).getItemId()); - - // Remove Monitor console for SDL2 it creates 2 SDL windows and SDL for - // android supports only 1 - menu.removeItem(menu.findItem(R.id.itemMonitor).getItemId()); + // Remove Monitor console for SDL2 it creates 2 SDL windows and SDL for + // android supports only 1 + menu.removeItem(menu.findItem(R.id.itemMonitor).getItemId()); - // Remove scaling for now - menu.removeItem(menu.findItem(R.id.itemScaling).getItemId()); + // Remove scaling for now + menu.removeItem(menu.findItem(R.id.itemScaling).getItemId()); - // Remove external mouse for now - menu.removeItem(menu.findItem(R.id.itemExternalMouse).getItemId()); - //menu.removeItem(menu.findItem(R.id.itemUIMode).getItemId()); + // Remove external mouse for now + menu.removeItem(menu.findItem(R.id.itemExternalMouse).getItemId()); menu.removeItem(menu.findItem(R.id.itemCtrlAltDel).getItemId()); menu.removeItem(menu.findItem(R.id.itemCtrlC).getItemId()); - if (LimboSettingsManager.getAlwaysShowMenuToolbar(activity) || Config.mouseMode == Config.MouseMode.External) { - menu.removeItem(menu.findItem(R.id.itemHideToolbar).getItemId()); - maxMenuItemsShown--; - } - - if (soundcard==null || soundcard.equals("None")) { + if (MachineController.getInstance().getMachine().getSoundCard() == null) { 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); + menu.getItem(i).setShowAsAction(actionShow); } + return true; + } - return true; - - } - - private void onMonitor() { - new Thread(new Runnable() { - public void run() { - monitorMode = true; - // final KeyEvent altDown = new KeyEvent(downTime, eventTime, - // KeyEvent.ACTION_DOWN, - // KeyEvent.KEYCODE_2, 1, KeyEvent.META_ALT_LEFT_ON); - sendCtrlAltKey(KeyEvent.KEYCODE_2); - // sendCtrlAltKey(altDown); - Log.v("Limbo", "Monitor On"); - } - }).start(); - - } - - private void onVMConsole() { - monitorMode = false; - sendCtrlAltKey(KeyEvent.KEYCODE_1); - } - - - // FIXME: We need this to able to catch complex characters strings like - // grave and send it as text - @Override - 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) { - this.onBackPressed(); - return true; - } if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN) { - // We emulate right click with volume 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); - } - return true; - } else if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP) { - // We emulate middle click with volume up - 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); - } - return true; - } else { - return super.dispatchKeyEvent(event); - } - - } - - private static void sendText(String string) { - - // Log.v("sendText", string); - KeyCharacterMap keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); - KeyEvent[] keyEvents = keyCharacterMap.getEvents(string.toCharArray()); - if (keyEvents != null) - for (int i = 0; i < keyEvents.length; i++) { - - if (keyEvents[i].getAction() == KeyEvent.ACTION_DOWN) { - // Log.v("sendText", "Up: " + keyEvents[i].getKeyCode()); - SDLActivity.onNativeKeyDown(keyEvents[i].getKeyCode()); - } else if (keyEvents[i].getAction() == KeyEvent.ACTION_UP) { - // Log.v("sendText", "Down: " + keyEvents[i].getKeyCode()); - SDLActivity.onNativeKeyUp(keyEvents[i].getKeyCode()); - } - } - } - - - // Setup - protected void onCreate(Bundle savedInstanceState) { - // Log.v("SDL", "onCreate()"); - activity = this; - - if (LimboSettingsManager.getFullscreen(this)) - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - - super.onCreate(savedInstanceState); - setupVolume(); - - mSingleton = this; - - Log.v("SDL", "Max Mem = " + Runtime.getRuntime().maxMemory()); + private void showMonitor() { + new Thread(new Runnable() { + public void run() { + monitorMode = true; + //TODO: enable qemu monitor if and when libSDL for Android allows + // multiple windows + sendCtrlAlt(KeyEvent.KEYCODE_2); + } + }).start(); - this.activity1 = this; + } - if (LimboActivity.currMachine == null) { - Log.v("SDLAcivity", "No VM selected!"); - }else - setParams(LimboActivity.currMachine); + private void showVMDisplay() { + monitorMode = false; + sendCtrlAlt(KeyEvent.KEYCODE_1); + } - // So we can call stuff from static callbacks - mSingleton = this; + // FIXME: We need this to able to catch complex characters strings like + // grave and send it as text + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_MULTIPLE && event.getKeyCode() == KeyEvent.KEYCODE_UNKNOWN) { + sendText(event.getCharacters()); + return true; + } else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) { + onBackPressed(); + return true; + } + if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN) { + // We emulate right click with volume down + if (event.getAction() == KeyEvent.ACTION_DOWN) { + sendRightClick(); + } + return true; + } else if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP) { + // We emulate middle click with volume up + if (event.getAction() == KeyEvent.ACTION_DOWN) { + sendMiddleClick(); + } + return true; + } else { + return super.dispatchKeyEvent(event); + } + } - createUI(0, 0); + private void sendText(String string) { + KeyCharacterMap keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); + KeyEvent[] keyEvents = keyCharacterMap.getEvents(string.toCharArray()); + if (keyEvents == null) + return; + for (KeyEvent keyEvent : keyEvents) { + if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) { + sendKeyEvent(null, keyEvent.getKeyCode(), true); + } else if (keyEvent.getAction() == KeyEvent.ACTION_UP) { + sendKeyEvent(null, keyEvent.getKeyCode(), false, 10); + } + } + } - UIUtils.setupToolBar(this); + protected void onCreate(Bundle savedInstanceState) { + setupScreen(); + saveAudioState(); + super.onCreate(savedInstanceState); + mSingleton = this; + restoreAudioState(); + setupWidgets(); + setupListeners(); + setupToolBar(); + showHints(); + ScreenUtils.updateOrientation(this, -1); + checkPendingActions(); + setupUserInterface(); + setupAudio(); + } - UIUtils.showHints(this); + private void setupUserInterface() { + Config.keyDelay = LimboSettingsManager.getKeyPressDelay(this); + Config.mouseButtonDelay = LimboSettingsManager.getMouseButtonDelay(this); + } - this.resumeVM(); + private void setupScreen() { + if (LimboSettingsManager.getFullscreen(this)) { + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if(pm.isSustainedPerformanceModeSupported()) + getWindow().setSustainedPerformanceMode(true); + } + } - UIUtils.setOrientation(this); + private void setupListeners() { + MachineController.getInstance().addOnStatusChangeListener(this); + MachineController.getInstance().addOnEventListener(this); + setViewListener(LimboApplication.getViewListener()); + } + public void setViewListener(ViewListener viewListener) { + this.viewListener = viewListener; + } - } + private void saveAudioState() { + audioTrack = SDLAudioManager.mAudioTrack; + mAudioRecord = SDLAudioManager.mAudioRecord; + } - private void createUI(int w, int h) { - mSurface = new LimboSDLSurface(this); + private void restoreAudioState() { + SDLAudioManager.mAudioTrack = audioTrack; + SDLAudioManager.mAudioRecord = mAudioRecord; + } - int width = w; - int height = h; - if (width == 0) { - width = RelativeLayout.LayoutParams.WRAP_CONTENT; - } - if (height == 0) { - height = RelativeLayout.LayoutParams.WRAP_CONTENT; - } + private void setupWidgets() { + mSurface = new LimboSDLSurface(this, this); - setContentView(R.layout.limbo_sdl); + setContentView(R.layout.limbo_sdl); + mLayout = (RelativeLayout) findViewById(R.id.sdl_layout); - //TODO: - mLayout = (RelativeLayout) activity.findViewById(R.id.sdl_layout); - mMainLayout = (LinearLayout) activity.findViewById(R.id.main_layout); + setupKeyMapManager(); + RelativeLayout mLayout = (RelativeLayout) findViewById(R.id.sdl); + mLayout.addView(mSurface); - RelativeLayout mLayout = (RelativeLayout) findViewById(R.id.sdl); - RelativeLayout.LayoutParams surfaceParams = new RelativeLayout.LayoutParams(width, height); - surfaceParams.addRule(RelativeLayout.CENTER_IN_PARENT); + mGap = (View) findViewById(R.id.gap); + updateLayout(getResources().getConfiguration().orientation); - mLayout.addView(mSurface, surfaceParams); + } - } + private void setupKeyMapManager() { + try { + int size = LimboSettingsManager.getKeyMapperSize(this); + mKeyMapManager = new KeyMapManager(this, mSurface, size, 2 * size); + mKeyMapManager.setOnSendKeyEventListener(this); + mKeyMapManager.setOnSendMouseEventListener(this); + mKeyMapManager.setOnUnhandledTouchEventListener(this); + } catch (Exception e) { + e.printStackTrace(); + ToastUtils.toastShort(this, e.getMessage()); + } - protected void onPause() { - Log.v("SDL", "onPause()"); - LimboService.updateServiceNotification(LimboActivity.currMachine.machinename + ": VM Suspended"); - super.onPause(); + } - } + private void toggleKeyMapper() { + KeyboardUtils.hideKeyboard(LimboSDLActivity.this, mSurface); + boolean shown = mKeyMapManager.toggleKeyMapper(); + if (shown) { + //XXX: force portrait when key mapper is on edit mode + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + toggleKeyboardFlag = KeyboardUtils.showKeyboard(LimboSDLActivity.this, false, mSurface); + } + } + protected void onPause() { + if (MachineController.getInstance().isRunning()) + notifyAction(MachineAction.UPDATE_NOTIFICATION, + getString(R.string.VMSuspended)); + super.onPause(); + } - public void onSelectMenuVol() { + public void promptVolume() { - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("Volume"); + final AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(this).create(); + alertDialog.setTitle(getString(R.string.Volume)); LinearLayout.LayoutParams volParams = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - LinearLayout t = createVolumePanel(); - t.setLayoutParams(volParams); - - ScrollView s = new ScrollView(activity); - s.addView(t); - alertDialog.setView(s); - alertDialog.setButton(Dialog.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() { + LinearLayout t = createVolumePanel(); + t.setLayoutParams(volParams); - public void onClick(DialogInterface dialog, int which) { - alertDialog.cancel(); - } - }); - alertDialog.show(); + ScrollView s = new ScrollView(this); + s.addView(t); + alertDialog.setView(s); + alertDialog.setButton(android.app.Dialog.BUTTON_POSITIVE, getString(android.R.string.ok), new DialogInterface.OnClickListener() { - } + public void onClick(DialogInterface dialog, int which) { + alertDialog.cancel(); + } + }); + alertDialog.show(); - public LinearLayout createVolumePanel() { - LinearLayout layout = new LinearLayout (this); - layout.setPadding(20, 20, 20, 20); + } + public LinearLayout createVolumePanel() { + LinearLayout layout = new LinearLayout(this); + layout.setPadding(20, 20, 20, 20); LinearLayout.LayoutParams volparams = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); + SeekBar vol = new SeekBar(this); + int volume; + vol.setMax(maxVolume); + volume = getCurrentVolume(); + vol.setProgress(volume); + vol.setLayoutParams(volparams); + ((SeekBar) vol).setOnSeekBarChangeListener(new OnSeekBarChangeListener() { + public void onProgressChanged(SeekBar s, int progress, boolean touch) { + setVolume(progress); + } - SeekBar vol = new SeekBar(this); - - int volume = 0; - - //TODO: - vol.setMax(maxVolume); - volume = getCurrentVolume(); + public void onStartTrackingTouch(SeekBar arg0) { + } - vol.setProgress(volume); - vol.setLayoutParams(volparams); + public void onStopTrackingTouch(SeekBar arg0) { + } + }); + layout.addView(vol); + return layout; + } - ((SeekBar) vol).setOnSeekBarChangeListener(new OnSeekBarChangeListener() { + protected void onResume() { + if (MachineController.getInstance().isRunning()) + notifyAction(MachineAction.UPDATE_NOTIFICATION, + getString(R.string.VMRunning)); + super.onResume(); + } - public void onProgressChanged(SeekBar s, int progress, boolean touch) { - //TODO: - setVolume(progress); - } - - public void onStartTrackingTouch(SeekBar arg0) { - - } - - public void onStopTrackingTouch(SeekBar arg0) { - - } - }); - - layout.addView(vol); - - return layout; - - } - - protected void onResume() { - Log.v("SDL", "onResume()"); - LimboService.updateServiceNotification(LimboActivity.currMachine.machinename + ": VM Running"); - super.onResume(); - } - - // Messages from the SDLMain thread - static int COMMAND_CHANGE_TITLE = 1; - static int COMMAND_SAVEVM = 2; - - public void loadLibraries() { - //XXX: override for the specific arch - } - - - public void promptPause(final Activity activity) { - - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("Pause VM"); - TextView stateView = new TextView(activity); - stateView.setText("This make take a while depending on the RAM size used"); - stateView.setPadding(20, 20, 20, 20); - alertDialog.setView(stateView); - - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Pause", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - onPauseVM(); - return; - } - }); - alertDialog.show(); - } - - private void onPauseVM() { - Thread t = new Thread(new Runnable() { - public void run() { - // Delete any previous state file - if (LimboActivity.vmexecutor.save_state_name != null) { - File file = new File(LimboActivity.vmexecutor.save_state_name); - if (file.exists()) { - file.delete(); - } - } - if(Config.showToast) - UIUtils.toastShort(getApplicationContext(), "Please wait while saving VM State"); - LimboActivity.vmexecutor.current_fd = LimboActivity.vmexecutor.get_fd(LimboActivity.vmexecutor.save_state_name); - - String uri = "fd:" + LimboActivity.vmexecutor.current_fd; - String command = QmpClient.stop(); - String msg = QmpClient.sendCommand(command); - command = QmpClient.migrate(false, false, uri); - msg = QmpClient.sendCommand(command); - if (msg != null) { - processMigrationResponse(msg); - } + public void loadLibraries() { + //XXX: Do not remove we need this to prevent loading libraries from SDL so + // we handle libraries for specific architectures later + } - // XXX: Instead we poll to see if migration is complete - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - VMListener a = new VMListener(); - a.execute(); - } - }, 0); - } - }); - t.start(); - - } - - private void processMigrationResponse(String response) { - String errorStr = null; - try { - JSONObject object = new JSONObject(response); - errorStr = object.getString("error"); - }catch (Exception ex) { - if(Config.debug) - ex.printStackTrace(); - } - if (errorStr != null) { - String descStr = null; - - try { - JSONObject descObj = new JSONObject(errorStr); - descStr = descObj.getString("desc"); - }catch (Exception ex) { - if(Config.debug) - ex.printStackTrace(); - } - final String descStr1 = descStr; + @Override + public void onSendKeyEvent(int keyCode, boolean down) { + sendKeyEvent(null, keyCode, down); + } - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - Machine.pausedErrorVM(activity, descStr1); - } - }, 100); + @Override + public void onSendMouseEvent(int button, boolean down) { + // events from key mapper should be finger tool type with a delay + sendMouseEvent(button, down ? 0 : 1, MotionEvent.TOOL_TYPE_FINGER, 0, 0); + } - } + @Override + public void OnUnhandledTouchEvent(MotionEvent event) { + if (isRelativeMode(event.getToolType(0))) + processTrackPadEvents(event); + else + ((LimboSDLSurface) mSurface).onTouchProcess(mSurface, event); + } + @Override + public boolean onTouchEvent(MotionEvent event) { + processTrackPadEvents(event); + return true; } - private class VMListener extends AsyncTask { + /** + * For Virtual Trackpad we need relative coordinates so we capture the events from the + * activity since we want to use the whole area for touch gestures therefore this should be + * called from within the activity onTouchEvent callbacks + * + * @param event MotionEvent to be processed + */ + public void processTrackPadEvents(MotionEvent event) { + if (mouseMode == MouseMode.TOUCHSCREEN) + return; + ((LimboSDLSurface) mSurface).onTouchProcess(mSurface, event); + } - @Override - protected Void doInBackground(Void... arg0) { - startTimeListener(); - return null; - } + private void checkPendingActions() { + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + if (pendingStop) { + pendingStop = false; + LimboActivityCommon.promptStopVM(LimboSDLActivity.this, viewListener); + } else if (pendingPause) { + pendingPause = false; + LimboActivityCommon.promptPause(LimboSDLActivity.this, viewListener); + } + } + }, 1000); - @Override - protected void onPostExecute(Void test) { + if (!MachineController.getInstance().isPaused()) { + machineRunning = true; + } - } - } + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + if (MachineController.getInstance().isPaused()) + notifyAction(MachineAction.CONTINUE_VM, 0); + } + }, 5000); + } - @Override - public boolean onTouchEvent(MotionEvent event) { - boolean res = false; - if(Config.mouseMode == Config.MouseMode.External){ - return res; + public void onBackPressed() { + if (mKeyMapManager != null && mKeyMapManager.isEditMode()) { + toggleKeyMapper(); + } else if (!LimboSettingsManager.getAlwaysShowMenuToolbar(this)) { + ActionBar bar = getSupportActionBar(); + if (bar != null) { + if (bar.isShowing()) + bar.hide(); + else { + bar.show(); + } + } + } else { + KeyboardUtils.hideKeyboard(this, mSurface); + finish(); } - //TODO: - res = ((LimboSDLSurface) this.mSurface).onTouchProcess(this.mSurface, event); - res = ((LimboSDLSurface) this.mSurface).onTouchEventProcess(event); - return true; - } - - private void resumeVM() { - if(LimboActivity.vmexecutor == null){ - return; - } - Thread t = new Thread(new Runnable() { - public void run() { - if (LimboActivity.vmexecutor.paused == 1) { - - try { - Thread.sleep(4000); - } catch (InterruptedException ex) { - Logger.getLogger(LimboVNCActivity.class.getName()).log(Level.SEVERE, null, ex); - } - LimboActivity.vmexecutor.paused = 0; - - String command = QmpClient.cont(); - String msg = QmpClient.sendCommand(command); - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - if(Config.mouseMode == Config.MouseMode.External) - setUIModeDesktop(); - else - setUIModeMobile(screenMode == SDLScreenMode.FitToScreen); - } - }, 500); - } - } - }); - t.start(); - - } - - public void onBackPressed() { - if (!LimboSettingsManager.getAlwaysShowMenuToolbar(activity)) { - ActionBar bar = this.getSupportActionBar(); - if (bar != null) { - if (bar.isShowing()) - bar.hide(); - else - bar.show(); - } - } else { - UIUtils.hideKeyboard(this, mSurface); - Machine.stopVM(activity); - } - - } + } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - this.invalidateOptionsMenu(); + invalidateOptionsMenu(); + updateLayout(newConfig.orientation); + } + + public void updateLayout(int orientation) { + if (orientation == Configuration.ORIENTATION_PORTRAIT) + mGap.setVisibility(View.VISIBLE); + else + mGap.setVisibility(View.GONE); } - public void onSelectMenuSDLDisplay() { + public void promptSDLDisplay() { final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("Display"); + alertDialog = new AlertDialog.Builder(this).create(); + alertDialog.setTitle(getString(R.string.display)); LinearLayout.LayoutParams volParams = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); @@ -1227,20 +748,18 @@ public void onSelectMenuSDLDisplay() { LinearLayout t = createSDLDisplayPanel(); t.setLayoutParams(volParams); - ScrollView s = new ScrollView(activity); + ScrollView s = new ScrollView(this); s.addView(t); alertDialog.setView(s); - alertDialog.setButton(Dialog.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() { + alertDialog.setButton(android.app.Dialog.BUTTON_POSITIVE, getString(R.string.Ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { alertDialog.cancel(); } }); alertDialog.show(); - } - public LinearLayout createSDLDisplayPanel() { LinearLayout layout = new LinearLayout(this); layout.setOrientation(LinearLayout.VERTICAL); @@ -1248,775 +767,365 @@ public LinearLayout createSDLDisplayPanel() { LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - int currRate = getCurrentSDLRefreshRate(); - - LinearLayout buttonsLayout = new LinearLayout(this); - buttonsLayout.setOrientation(LinearLayout.HORIZONTAL); - buttonsLayout.setGravity(Gravity.CENTER_HORIZONTAL); - Button displayMode = new Button (this); - displayMode.setText("Display Mode"); - displayMode.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - onDisplayMode(); + int currRateDefault = getCurrentSDLRefreshRate(false); + final TextView valueDefault = new TextView(this); + String msg = getString(R.string.DefaultRefreshRate) + ": " + currRateDefault + " Hz"; + valueDefault.setText(msg); + valueDefault.setPadding(10, 10, 10, 10); + valueDefault.setLayoutParams(params); + SeekBar rateDefault = new SeekBar(this); + rateDefault.setMax(Config.MAX_DISPLAY_REFRESH_RATE); + rateDefault.setProgress(currRateDefault); + rateDefault.setLayoutParams(params); + rateDefault.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { + public void onProgressChanged(SeekBar s, int progress, boolean touch) { + String message = getString(R.string.DefaultRefreshRate) + ": " + (progress + 1) + " " + "Hz"; + valueDefault.setText(message); } - }); - buttonsLayout.addView(displayMode); - layout.addView(buttonsLayout); - - final TextView value = new TextView(this); - value.setText("Idle Refresh Rate: " + currRate+" Hz"); - layout.addView(value); - value.setLayoutParams(params); - SeekBar rate = new SeekBar(this); - rate.setMax(Config.MAX_DISPLAY_REFRESH_RATE); - - rate.setProgress(currRate); - rate.setLayoutParams(params); + public void onStartTrackingTouch(SeekBar arg0) { + } - ((SeekBar) rate).setOnSeekBarChangeListener(new OnSeekBarChangeListener() { + public void onStopTrackingTouch(SeekBar arg0) { + int progress = arg0.getProgress() + 1; + int refreshMs = 1000 / progress; + notifyAction(MachineAction.SET_SDL_REFRESH_RATE, new Object[] {refreshMs, false}); + } + }); + int currRateIdle = getCurrentSDLRefreshRate(true); + final TextView valueIdle = new TextView(this); + String msgIdle = getString(R.string.IdleRefreshRate) + ": " + currRateIdle + " Hz"; + valueIdle.setText(msgIdle); + valueIdle.setLayoutParams(params); + valueIdle.setPadding(10, 10, 10, 10); + SeekBar rateIdle = new SeekBar(this); + rateIdle.setMax(Config.MAX_DISPLAY_REFRESH_RATE); + rateIdle.setProgress(currRateIdle); + rateIdle.setLayoutParams(params); + rateIdle.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { public void onProgressChanged(SeekBar s, int progress, boolean touch) { - value.setText("Idle Refresh Rate: " + (progress+1)+" Hz"); + String message = getString(R.string.IdleRefreshRate) + ": " + (progress + 1) + " " + "Hz"; + valueIdle.setText(message); } 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); - LimboActivity.vmexecutor.setsdlrefreshrate(refreshMs); + notifyAction(MachineAction.SET_SDL_REFRESH_RATE, new Object[] {refreshMs, true}); } }); - - layout.addView(rate); + layout.addView(valueDefault); + layout.addView(rateDefault); + layout.addView(valueIdle); + layout.addView(rateIdle); return layout; - } - public int getCurrentSDLRefreshRate() { - return 1000 / LimboActivity.vmexecutor.getsdlrefreshrate(); + public int getCurrentSDLRefreshRate(boolean idle) { + return 1000 / MachineController.getInstance().getSdlRefreshRate(idle); } - - - private void onDisplayMode() { - - String [] items = { - "Normal (One-To-One)", - "Fit To Screen" -// ,"Stretch To Screen" //Stretched - }; - int currentScaleType = 0; - if(screenMode == SDLScreenMode.FitToScreen){ - currentScaleType = 1; - } else if(screenMode == SDLScreenMode.Fullscreen) - currentScaleType = 2; - - final AlertDialog.Builder mBuilder = new AlertDialog.Builder(this); - mBuilder.setTitle("Display Mode"); - mBuilder.setSingleChoiceItems(items, currentScaleType, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int i) { - switch(i){ - case 0: - onNormalScreen(); - break; - case 1: - if(Config.mouseMode == Config.MouseMode.External){ - UIUtils.toastShort(LimboSDLActivity.this, "Fit to Screen Disabled under Desktop Mode"); - dialog.dismiss(); - return; - } - onFitToScreen(); - break; - case 2: - if(Config.mouseMode == Config.MouseMode.External){ - UIUtils.toastShort(LimboSDLActivity.this, "Stretch Screen Disabled under Desktop Mode"); - dialog.dismiss(); - return; - } - onStretchToScreen(); - break; - default: - break; - } - dialog.dismiss(); + // private static Thread limboSDLThread = null; + @Override + protected synchronized void runSDLMain() { + notifyAction(MachineAction.START_VM, null); + //XXX: we hold the thread because SDLActivity will exit + while (!quit) { + try { + wait(); + } catch (Exception e) { + e.printStackTrace(); } - }); - final AlertDialog alertDialog = mBuilder.create(); - alertDialog.show(); - + } + Log.d(TAG, "SDLThread exited"); } + /** + * Notifies when the machine resolution changes. This is called from SDL compat extensions + * see folder jni/compat/sdl-extensions + * + * @param width Width + * @param height Height + */ + public void resolutionChanged(int width, int height) { + if (mSurface == null || LimboSDLActivity.isResizing) { + return; + } + Log.d(TAG, "VM resolution changed to " + width + "x" + height); + ((LimboSDLSurface) mSurface).refreshSurfaceView(); + } - @Override - protected synchronized void runSDLMain(){ - - //We go through the vm executor - LimboActivity.startvm(this, Config.UI_SDL); - - //XXX: we hold the thread because SDLActivity will exit - try { - wait(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - public static void onVMResolutionChanged(int w, int h) - { - boolean refreshDisplay = false; + protected void setupAudio() { + notifyAction(MachineAction.ENABLE_AAUDIO, LimboSettingsManager.getEnableAaudio(this)?1:0); + if (am == null) { + am = (AudioManager) mSingleton.getSystemService(Context.AUDIO_SERVICE); + maxVolume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + } + } - if(w!=vm_width || h!=vm_height) - refreshDisplay = true; - vm_width = w; - vm_height = h; + public void setVolume(int volume) { + if (am != null) + am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); + } - Log.v(TAG, "VM resolution changed to " + vm_width + "x" + vm_height); + protected int getCurrentVolume() { + int volumeTmp = 0; + if (am != null) + volumeTmp = am.getStreamVolume(AudioManager.STREAM_MUSIC); + return volumeTmp; + } + //XXX: We want to suspend only when app is calling onPause() + @Override + public void onWindowFocusChanged(boolean hasFocus) { - if(refreshDisplay) { - activity.resize(null); - } + } - } + public void sendRightClick() { + Thread t = new Thread(new Runnable() { + public void run() { + // we use a finger tool type to add the delay + sendMouseEvent(Config.SDL_MOUSE_RIGHT, MotionEvent.ACTION_DOWN, MotionEvent.TOOL_TYPE_FINGER, 0, 0); + sendMouseEvent(Config.SDL_MOUSE_RIGHT, MotionEvent.ACTION_UP, MotionEvent.TOOL_TYPE_FINGER, 0, 0); + } + }); + t.start(); + } - public static boolean isResizing = false; - public enum SDLScreenMode { - Normal, - FitToScreen, - Fullscreen //fullscreen not implemented yet + public void sendMiddleClick() { + Thread t = new Thread(new Runnable() { + public void run() { + // we use a finger tool type to add the delay + sendMouseEvent(Config.SDL_MOUSE_MIDDLE, MotionEvent.ACTION_DOWN, MotionEvent.TOOL_TYPE_FINGER, 0, 0); + sendMouseEvent(Config.SDL_MOUSE_MIDDLE, MotionEvent.ACTION_UP, MotionEvent.TOOL_TYPE_FINGER, 0, 0); + } + }); + t.start(); } - public SDLScreenMode screenMode = SDLScreenMode.FitToScreen; - - private void setLayout(Configuration newConfig) { + public boolean onKeyLongPress(int keyCode, KeyEvent event) { + return false; + } - boolean isLanscape = - (newConfig!=null && newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) - || UIUtils.isLandscapeOrientation(this); + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (!processKey(keyCode, event)) + return super.onKeyDown(keyCode, event); + return true; + } - 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 (isLanscape) { - vnc_canvas_layout_params = new RelativeLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ); -// vnc_canvas_layout_params.addRule(RelativeLayout.CENTER_IN_PARENT); - vnc_canvas_layout_params.addRule(RelativeLayout.ALIGN_PARENT_TOP); - vnc_canvas_layout_params.addRule(RelativeLayout.CENTER_HORIZONTAL); + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (!processKey(keyCode, event)) + return super.onKeyUp(keyCode, event); + return true; + } - } else { - vnc_canvas_layout_params = new RelativeLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ); - vnc_canvas_layout_params.addRule(RelativeLayout.ALIGN_PARENT_TOP); - vnc_canvas_layout_params.addRule(RelativeLayout.CENTER_HORIZONTAL); - } + public boolean processKey(int keyCode, KeyEvent event) { + if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) { + // dismiss android back and forward keys + return true; + } else if (event.getKeyCode() == KeyEvent.KEYCODE_MENU) { + return false; + } else if (event.getAction() == KeyEvent.ACTION_DOWN) { + sendKey(event, keyCode, event.getAction(), true); + return true; + } else if (event.getAction() == KeyEvent.ACTION_UP) { + sendKey(event, keyCode, event.getAction(), false); + return true; } else { - //fittoscreen - if (isLanscape) { - vnc_canvas_layout_params = new RelativeLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT - ); - vnc_canvas_layout_params.addRule(RelativeLayout.CENTER_IN_PARENT); - } else { + return false; + } + } - vnc_canvas_layout_params = new RelativeLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ); - vnc_canvas_layout_params.addRule(RelativeLayout.ALIGN_PARENT_TOP); - vnc_canvas_layout_params.addRule(RelativeLayout.CENTER_HORIZONTAL); - } + private synchronized void sendKey(final KeyEvent event, final int keyCode, final int action, + final boolean down) { + if (!handleMissingKeys(keyCode, action)) { + sendKeyEvent(event, keyCode, down); } - vnc_canvas_layout.setLayoutParams(vnc_canvas_layout_params); + } - this.invalidateOptionsMenu(); + // Handles key codes missing in sdl2-keymap.h + // This function will create them with a Shift Modifier + private boolean handleMissingKeys(int keyCode, int action) { + + int newKeyCode; + switch (keyCode) { + case 77: + newKeyCode = 9; + break; + case 81: + newKeyCode = 70; + break; + case 17: + newKeyCode = 15; + break; + case 18: + newKeyCode = 10; + break; + default: + return false; + } + if (action == KeyEvent.ACTION_DOWN) { + sendKeyEvent(null, 59, true); + sendKeyEvent(null, newKeyCode, true); + } else { + sendKeyEvent(null, 59, false); + sendKeyEvent(null, newKeyCode, false); + } + return true; } - public class LimboSDLSurface extends ExSDLSurface implements View.OnKeyListener, View.OnTouchListener { + private void delay(long ms) { + try { + Thread.sleep(ms); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } - public boolean initialized = false; + /** + * We treat as relative mode only events with TOOL_TYPE_FINGER as long as the user has not + * selected to emulate a touch screen. + * + * @param toolType Event Tool type + * @return True if the device will be expected as relative mode by the emulator + */ + public boolean isRelativeMode(int toolType) { + return toolType == MotionEvent.TOOL_TYPE_FINGER + && LimboSDLActivity.mouseMode != LimboSDLActivity.MouseMode.TOUCHSCREEN; + } - public LimboSDLSurface(Context context) { - super(context); - setOnKeyListener(this); - setOnTouchListener(this); - gestureDetector = new GestureDetector(activity, new GestureListener()); - setOnGenericMotionListener(new SDLGenericMotionListener_API12()); - } - public void surfaceChanged(SurfaceHolder holder, - int format, int width, int height) { - super.surfaceChanged(holder, format, width, height); - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { + protected void sendMouseEvent(int button, int action, int toolType, float x, float y) { + //HACK: we generate an artificial delay since the qemu main event loop + // is probably not able to process them if the timestamps are too close together? + sendMouseEvent(button, action, toolType, x, y, action == MotionEvent.ACTION_UP ? Config.mouseButtonDelay : 0); + } - SDLActivity.onNativeKeyDown(KeyEvent.KEYCODE_CTRL_LEFT); - SDLActivity.onNativeKeyUp(KeyEvent.KEYCODE_CTRL_LEFT); + private void sendMouseEvent(final int button, final int action, final int toolType, + final float x, final float y, final long delayMs) { + if(resettingLayout) + return; + mouseEventsExecutor.submit(new Runnable() { + @Override + public void run() { + boolean relative = isRelativeMode(toolType); + if (mKeyMapManager.processMouseMap(button, action)) { + return; } - }, 500); - } - - @Override - public void surfaceCreated(SurfaceHolder holder) { - super.surfaceCreated(holder); + if (delayMs > 0 && toolType != MotionEvent.TOOL_TYPE_MOUSE) + delay(delayMs); + //XXX: for mouse events we use our jni compatibility extensions instead of the sdl native functions + // SDLActivity.onSDLNativeMouse(button, action, x, y); + float nx = x; + float ny = y; + LimboSDLSurface.MouseState mouseState = ((LimboSDLSurface) mSurface).mouseState; + if (mouseState.isDoubleTap()) { + nx = mouseState.taps.get(0).x; + ny = mouseState.taps.get(0).y; + } +// Log.d(TAG, "sendMouseEvent button: " + button + ", action: " + action +// + ", relative: " + relative + ", nx = " + nx + ", ny = " + ny +// + ", delay = " + delayMs); + notifyAction(MachineAction.SEND_MOUSE_EVENT, new Object[]{button, action, relative ? 1 : 0, nx, ny}); + if (delayMs > 0 && toolType != MotionEvent.TOOL_TYPE_MOUSE) + delay(delayMs); + } + }); + } - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { + protected void sendKeyEvent(KeyEvent event, int keycode, boolean down) { + //HACK: we generate an artificial delay since the qemu main event loop + // is probably not able to process them if the timestamps are too close together? + sendKeyEvent(event, keycode, down, !down ? Config.keyDelay : 0); + } - if(Config.mouseMode == Config.MouseMode.External) - setUIModeDesktop(); - else - setUIModeMobile(screenMode == SDLScreenMode.FitToScreen); + private void sendKeyEvent(final KeyEvent event, final int keycode, final boolean down, final long delayMs) { + keyEventsExecutor.submit(new Runnable() { + @Override + public void run() { + if (mKeyMapManager != null && mKeyMapManager.processKeyMap(event)) { + return; } - },1000); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - 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; - Log.v(TAG, "Resizing Display"); - - Display display = SDLActivity.mSingleton.getWindowManager().getDefaultDisplay(); - int height = 0; - int width = 0; - - Point size = new Point(); - display.getSize(size); - int screen_width = size.x; - int screen_height = size.y; - - final ActionBar bar = ((SDLActivity) activity).getSupportActionBar(); - - if(LimboSDLActivity.mLayout != null) { - width = LimboSDLActivity.mLayout.getWidth(); - height = LimboSDLActivity.mLayout.getHeight(); - } - - //native resolution for use with external mouse - if(screenMode != SDLScreenMode.Fullscreen && screenMode != SDLScreenMode.FitToScreen) { - width = LimboSDLActivity.vm_width; - height = LimboSDLActivity.vm_height; - } - - if(reverse){ - int temp = width; - width = height; - height = temp; - } - - boolean portrait = SDLActivity.mSingleton.getResources() - .getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; - - if (portrait) { - if(Config.mouseMode != Config.MouseMode.External) { - int height_n = (int) (width / (LimboSDLActivity.vm_width / (float) LimboSDLActivity.vm_height)); - Log.d(TAG, "Resizing portrait: " + width + " x " + height_n); - getHolder().setFixedSize(width, height_n); - } - } else { - if ( (screenMode == SDLScreenMode.Fullscreen || screenMode == SDLScreenMode.FitToScreen) - && !LimboSettingsManager.getAlwaysShowMenuToolbar(LimboSDLActivity.this) - && bar != null && bar.isShowing()) { - height += bar.getHeight(); - } - Log.d(TAG, "Resizing landscape: " + width + " x " + height); - getHolder().setFixedSize(width, height); - } - initialized = true; - - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - isResizing = false; + if (delayMs > 0) + delay(delayMs); +// Log.d(TAG, "sendKeyEvent: " + ", keycode = " + keycode + ", down = " + down +// + ", delay = " + delayMs); + if (down) + SDLActivity.onNativeKeyDown(keycode); + else { + SDLActivity.onNativeKeyUp(keycode); } - }, 1000); - - - - } - - // XXX: SDL is missing some key codes in sdl2-keymap.h - // So we create them with a Shift Modifier - private boolean handleMissingKeys(int keyCode, int action) { - - int keyCodeTmp = keyCode; - switch (keyCode) { - case 77: - keyCodeTmp = 9; - break; - case 81: - keyCodeTmp = 70; - break; - case 17: - keyCodeTmp = 15; - break; - case 18: - keyCodeTmp = 10; - break; - default: - return false; - - } - if (action == KeyEvent.ACTION_DOWN) { - SDLActivity.onNativeKeyDown(59); - SDLActivity.onNativeKeyDown(keyCodeTmp); - } else { - SDLActivity.onNativeKeyUp(59); - SDLActivity.onNativeKeyUp(keyCodeTmp); - } - return true; - - } - - @Override - public boolean onKey(View v, int keyCode, KeyEvent event) { - - if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) { - // dismiss android back and forward keys - return true; - } else if (event.getKeyCode() == KeyEvent.KEYCODE_MENU) { - return false; - } else if (event.getAction() == KeyEvent.ACTION_DOWN) { - //Log.v("SDL", "key down: " + keyCode); - if (!handleMissingKeys(keyCode, event.getAction())) - SDLActivity.onNativeKeyDown(keyCode); - return true; - } else if (event.getAction() == KeyEvent.ACTION_UP) { - //Log.v("SDL", "key up: " + keyCode); - if (!handleMissingKeys(keyCode, event.getAction())) - SDLActivity.onNativeKeyUp(keyCode); - return true; - } else { - return super.onKey(v, keyCode, event); - } - - } - - // Touch events - public boolean onTouchProcess(View v, MotionEvent event) { - int action = event.getAction(); - float x = event.getX(0); - float y = event.getY(0); - float p = event.getPressure(0); - - int relative = Config.mouseMode == Config.MouseMode.External? 0: 1; - - int sdlMouseButton = 0; - if(event.getButtonState() == MotionEvent.BUTTON_PRIMARY) - sdlMouseButton = Config.SDL_MOUSE_LEFT; - else if(event.getButtonState() == MotionEvent.BUTTON_SECONDARY) - sdlMouseButton = Config.SDL_MOUSE_RIGHT; - else if(event.getButtonState() == MotionEvent.BUTTON_TERTIARY) - sdlMouseButton = Config.SDL_MOUSE_MIDDLE; - - - if (event.getAction() == MotionEvent.ACTION_MOVE) { - - if (mouseUp) { - old_x = x; - old_y = y; - mouseUp = false; - } - if (action == MotionEvent.ACTION_MOVE) { - if(Config.mouseMode == Config.MouseMode.External) { - //Log.d("SDL", "onTouch Absolute Move by=" + action + ", X,Y=" + (x) + "," + (y) + " P=" + p); - LimboActivity.vmexecutor.onLimboMouse(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); - LimboActivity.vmexecutor.onLimboMouse(0, MotionEvent.ACTION_MOVE,1, (x - old_x) * sensitivity_mult, (y - old_y) * sensitivity_mult); - } - - } - // save current - old_x = x; - old_y = y; - - } - 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 - ) { - LimboActivity.vmexecutor.onLimboMouse(sdlMouseButton, MotionEvent.ACTION_UP, relative, x, y); - } else if (sdlMouseButton != 0) { - LimboActivity.vmexecutor.onLimboMouse(sdlMouseButton, MotionEvent.ACTION_UP, relative, x, y); - } else { // if we don't have inforamtion about which button we can make some guesses - - //Or only the last one pressed - if (lastMouseButtonDown > 0) { - if(lastMouseButtonDown == Config.SDL_MOUSE_MIDDLE - ||lastMouseButtonDown == Config.SDL_MOUSE_RIGHT - ) { - LimboActivity.vmexecutor.onLimboMouse(lastMouseButtonDown, MotionEvent.ACTION_UP, relative,x, y); - }else - LimboActivity.vmexecutor.onLimboMouse(lastMouseButtonDown, MotionEvent.ACTION_UP, relative, x, y); - } else { - //ALl buttons - if (Config.mouseMode == Config.MouseMode.Trackpad) { - LimboActivity.vmexecutor.onLimboMouse(Config.SDL_MOUSE_LEFT, MotionEvent.ACTION_UP, 1, 0, 0); - } else if (Config.mouseMode == Config.MouseMode.External) { - LimboActivity.vmexecutor.onLimboMouse(Config.SDL_MOUSE_LEFT, MotionEvent.ACTION_UP, 0, x, y); - LimboActivity.vmexecutor.onLimboMouse(Config.SDL_MOUSE_RIGHT, MotionEvent.ACTION_UP, 0, x, y); - LimboActivity.vmexecutor.onLimboMouse(Config.SDL_MOUSE_MIDDLE, MotionEvent.ACTION_UP, 0, x, y); - } - } - } - lastMouseButtonDown = -1; - mouseUp = true; - } - 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)) { - sdlMouseButton = Config.SDL_MOUSE_LEFT; - } - - LimboActivity.vmexecutor.onLimboMouse(sdlMouseButton, MotionEvent.ACTION_DOWN, relative, x, y); - lastMouseButtonDown = sdlMouseButton; - } - return true; - } - - public boolean onTouch(View v, MotionEvent event) { - boolean res = false; - if(Config.mouseMode == Config.MouseMode.External){ - res = onTouchProcess(v,event); - res = onTouchEventProcess(event); - } - return res; - } - - public boolean onTouchEvent(MotionEvent event) { - return false; - } - - public boolean onTouchEventProcess(MotionEvent event) { - // Log.v("onTouchEvent", - // "Action=" + event.getAction() + ", X,Y=" + event.getX() + "," - // + event.getY() + " P=" + event.getPressure()); - // MK - if (event.getAction() == MotionEvent.ACTION_CANCEL) - return true; - - if (!firstTouch) { - firstTouch = true; - } - if (event.getPointerCount() > 1) { - - // XXX: Limbo Legacy enable Right Click with 2 finger touch - // Log.v("Right Click", - // "Action=" + event.getAction() + ", X,Y=" + event.getX() - // + "," + event.getY() + " P=" + event.getPressure()); - // rightClick(event); - return true; - } else - return gestureDetector.onTouchEvent(event); - } - } - - public AudioManager am; - protected int maxVolume; - - protected void setupVolume() { - if (am == null) { - am = (AudioManager) mSingleton.getSystemService(Context.AUDIO_SERVICE); - maxVolume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); - } - } - - public void setVolume(int volume) { - if(am!=null) - am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); - } - - protected int getCurrentVolume() { - int volumeTmp = 0; - if(am!=null) - volumeTmp = am.getStreamVolume(AudioManager.STREAM_MUSIC); - return volumeTmp; - } - - - //XXX: We want to suspend only when app is calling onPause() - @Override - public void onWindowFocusChanged(boolean hasFocus) { - - } - - public boolean rightClick(final MotionEvent e, final int i) { - Thread t = new Thread(new Runnable() { - public void run() { - Log.d("SDL", "Mouse Right Click"); - LimboActivity.vmexecutor.onLimboMouse(Config.SDL_MOUSE_RIGHT, MotionEvent.ACTION_DOWN, 1, -1, -1); - try { - Thread.sleep(100); - } catch (InterruptedException ex) { -// Log.v("SDLSurface", "Interrupted: " + ex); - } - LimboActivity.vmexecutor.onLimboMouse(Config.SDL_MOUSE_RIGHT, MotionEvent.ACTION_UP, 1, -1, -1); - } - }); - t.start(); - return true; - - } - - public boolean middleClick(final MotionEvent e, final int i) { - Thread t = new Thread(new Runnable() { - public void run() { - Log.d("SDL", "Mouse Middle Click"); - LimboActivity.vmexecutor.onLimboMouse(Config.SDL_MOUSE_MIDDLE, MotionEvent.ACTION_DOWN, 1,-1, -1); - try { - Thread.sleep(100); - } catch (InterruptedException ex) { -// Log.v("SDLSurface", "Interrupted: " + ex); - } - LimboActivity.vmexecutor.onLimboMouse(Config.SDL_MOUSE_MIDDLE, MotionEvent.ACTION_UP, 1,-1, -1); - } - }); - t.start(); - return true; - - } - - private void doubleClick(final MotionEvent event, final int pointer_id) { - - Thread t = new Thread(new Runnable() { - public void run() { - //Log.d("SDL", "Mouse Double Click"); - for (int i = 0; i < 2; i++) { - LimboActivity.vmexecutor.onLimboMouse(Config.SDL_MOUSE_LEFT, MotionEvent.ACTION_DOWN, 1, 0, 0); - try { - Thread.sleep(50); - } catch (InterruptedException ex) { - // Log.v("doubletap", "Could not sleep"); - } - LimboActivity.vmexecutor.onLimboMouse(Config.SDL_MOUSE_LEFT, MotionEvent.ACTION_UP, 1,0, 0); - try { - Thread.sleep(50); - } catch (InterruptedException ex) { - // Log.v("doubletap", "Could not sleep"); - } - } - } - }); - t.start(); - } - - - int lastMouseButtonDown = -1; - public float old_x = 0; - public float old_y = 0; - private boolean mouseUp = true; - private float sensitivity_mult = (float) 1.0; - private boolean firstTouch = false; - - - - private class GestureListener extends GestureDetector.SimpleOnGestureListener { - - @Override - public boolean onDown(MotionEvent event) { - // Log.v("onDown", "Action=" + event.getAction() + ", X,Y=" + event.getX() - // + "," + event.getY() + " P=" + event.getPressure()); - return true; - } - - @Override - 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) - return; - - if(Config.enableDragOnLongPress) - dragPointer(event); - } - - public boolean onSingleTapConfirmed(MotionEvent event) { - float x1 = event.getX(); - float y1 = event.getY(); - - if(Config.mouseMode == Config.MouseMode.External) - return true; - -// Log.d("onSingleTapConfirmed", "Tapped at: (" + x1 + "," + y1 + -// ")"); - - for (int i = 0; i < event.getPointerCount(); i++) { - int action = event.getAction(); - float x = event.getX(i); - float y = event.getY(i); - float p = event.getPressure(i); - - //Log.v("onSingleTapConfirmed", "Action=" + action + ", X,Y=" + x + "," + y + " P=" + p); - if (event.getAction() == event.ACTION_DOWN - && MotionEvent.TOOL_TYPE_FINGER == event.getToolType(0)) { - //Log.d("SDL", "onTouch Down: " + event.getButtonState()); - LimboSDLActivity.singleClick(event, i); - } - } - return true; - - } - - // event when double tap occurs - @Override - public boolean onDoubleTap(MotionEvent event) { -// Log.d("onDoubleTap", "Tapped at: (" + event.getX() + "," + event.getY() + ")"); - - if(Config.mouseMode == Config.MouseMode.External - //&& MotionEvent.TOOL_TYPE_MOUSE == event.getToolType(0) - ) - return true; - - if(!Config.enableDragOnLongPress) - processDoubleTap(event); - else - doubleClick(event, 0); - - return true; - } - } - - private void dragPointer(MotionEvent event) { - - LimboActivity.vmexecutor.onLimboMouse(Config.SDL_MOUSE_LEFT, MotionEvent.ACTION_DOWN, 1, 0, 0); - Vibrator v = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE); - if (v.hasVibrator()) { - v.vibrate(100); + } + }); + } + + @Override + public void onMachineStatusChanged(Machine machine, MachineController.MachineStatus status, Object o) { + switch (status) { + case SaveFailed: + LimboActivityCommon.promptPausedErrorVM(this, (String) o, viewListener); + break; + case SaveCompleted: + LimboActivityCommon.promptPausedVM(this, viewListener); + break; } - } + } - private void processDoubleTap(final MotionEvent event) { + public void notifyFieldChange(MachineProperty property, Object value) { + if (viewListener != null) + viewListener.onFieldChange(property, value); + } - Thread t = new Thread(new Runnable() { - public void run() { - try { - Thread.sleep(400); - } catch (InterruptedException e1) { - e1.printStackTrace(); - } + public void notifyAction(MachineAction action, Object value) { + if (viewListener != null) + viewListener.onAction(action, value); + } - if (!mouseUp) { - dragPointer(event); - } else { - // Log.v("onDoubleTap", "Action=" + action + ", X,Y=" + x + "," + y + " P=" + p); - doubleClick(event, 0); - } + @Override + public void onEvent(Machine machine, MachineController.Event event, Object o) { + switch (event) { + case MachineResolutionChanged: + Object[] params = (Object[]) o; + resolutionChanged((int) params[0], (int) params[1]); + break; + case MachineContinued: + machineRunning = true; + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + invalidateOptionsMenu(); + ((LimboSDLSurface) mSurface).refreshSurfaceView(); + } + }, 1000); + break; + } + } - } - }); - t.start(); + public synchronized void setFullscreen() { + if(!machineRunning) { + Log.w(TAG, "Machine not running not reset layout"); + return; + } + resettingLayout = true; + try { + Log.d(TAG, "Requesting fullscreen"); + viewListener.onAction(MachineAction.FULLSCREEN, null); + } catch (Exception ex) { + ex.printStackTrace(); + } + resettingLayout = false; + } - } - - class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener { - private LimboSDLSurface mSurface; - - @Override - public boolean onGenericMotion(View v, MotionEvent event) { - float x, y; - int action; - - switch (event.getSource()) { - case InputDevice.SOURCE_JOYSTICK: - case InputDevice.SOURCE_GAMEPAD: - case InputDevice.SOURCE_DPAD: - SDLControllerManager.handleJoystickMotionEvent(event); - return true; - - case InputDevice.SOURCE_MOUSE: - if(Config.mouseMode == Config.MouseMode.Trackpad) - break; - - action = event.getActionMasked(); -// Log.d("SDL", "onGenericMotion, action = " + action + "," + event.getX() + ", " + event.getY()); - switch (action) { - case MotionEvent.ACTION_SCROLL: - x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0); - y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0); -// Log.d("SDL", "Mouse Scroll: " + x + "," + y); - LimboActivity.vmexecutor.onLimboMouse(0, action, 0, x, y); - return true; - - case MotionEvent.ACTION_HOVER_MOVE: - if(Config.processMouseHistoricalEvents) { - final int historySize = event.getHistorySize(); - for (int h = 0; h < historySize; h++) { - float ex = event.getHistoricalX(h); - float ey = event.getHistoricalY(h); - float ep = event.getHistoricalPressure(h); - processHoverMouse(ex, ey, ep, action); - } - } - - float ex = event.getX(); - float ey = event.getY(); - float ep = event.getPressure(); - processHoverMouse(ex, ey, ep, action); - return true; - - case MotionEvent.ACTION_UP: - - default: - break; - } - break; - - default: - break; - } - - // Event was not managed - return false; - } - - private void processHoverMouse(float x,float y,float p, int action) { - - - - if(Config.mouseMode == Config.MouseMode.External) { - //Log.d("SDL", "Mouse Hover: " + x + "," + y); - LimboActivity.vmexecutor.onLimboMouse(0, action, 0, x, y); - } - } - - } - - GestureDetector gestureDetector; + public enum MouseMode { + Trackpad, TOUCHSCREEN + } } diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboSDLSurface.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboSDLSurface.java new file mode 100644 index 000000000..a8dd58510 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboSDLSurface.java @@ -0,0 +1,351 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.main; + +import android.content.Context; +import android.util.Log; +import android.view.InputDevice; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.View; + +import com.max2idea.android.limbo.machine.MachineAction; + +import org.libsdl.app.SDLActivity; +import org.libsdl.app.SDLControllerManager; + +import java.util.ArrayList; + +/** + * This class is an extension of the SurfaceView used by the SDL java portion of the code. This + * custom class enables trackpad and external mouse support (Desktop Mode) as well as orientation + * changes, and a usefull keymapper. The trackpad and mouse support is part our custom SDL extensions + * (see jni/compat/sdl-extensions). + */ +public class LimboSDLSurface extends SDLActivity.ExSDLSurface + implements View.OnKeyListener, View.OnTouchListener { + private static final String TAG = "LimboSDLSurface"; + + MouseState mouseState = new MouseState(); + private boolean firstTouch = false; + + private final LimboSDLActivity sdlActivity; + + public LimboSDLSurface(LimboSDLActivity sdlActivity, Context context) { + super(context); + this.sdlActivity = sdlActivity; + //XXX: we don't process keys in this view but in LimboSDLActivity + // here we just suppress + setOnKeyListener(this); + setOnTouchListener(this); + setOnGenericMotionListener(new SDLGenericMotionListener_API12()); + } + + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + Log.d(TAG, "surfaceChanged: " + width + "x" + height); + super.surfaceChanged(holder, format, width, height); + refreshSurfaceView(); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + Log.d(TAG, "surfaceCreated"); + super.surfaceCreated(holder); + setWillNotDraw(false); + refreshSurfaceView(); + } + + public void refreshSurfaceView() { + new Thread(new Runnable() { + @Override + public void run() { + sdlActivity.setFullscreen(); + // notify the controller that our display has changed + sdlActivity.notifyAction(MachineAction.DISPLAY_CHANGED, + new Object[]{getWidth(), getHeight(), getResources().getConfiguration().orientation}); + } + }).start(); + } + + public boolean onTouchProcess(View v, MotionEvent event) { + int action = event.getActionMasked(); + mouseState.x = event.getX(); + mouseState.y = event.getY(); + + processMouseMovement(action, event.getToolType(0), mouseState.x, mouseState.y); + processMouseButton(event, action, mouseState.x, mouseState.y); + return false; + } + + private void processMouseMovement(int action, int toolType, float x, float y) { + if (action == MotionEvent.ACTION_MOVE) { + if (mouseState.mouseUp) { + mouseState.old_x = x; + mouseState.old_y = y; + mouseState.mouseUp = false; + } + + float nx = x; + float ny = y; + if (sdlActivity.isRelativeMode(toolType)) { + nx = (x - mouseState.old_x); + ny = (y - mouseState.old_y); + } + sdlActivity.sendMouseEvent(0, MotionEvent.ACTION_MOVE, toolType, nx, ny); + + mouseState.old_x = x; + mouseState.old_y = y; + } + } + + private void processMouseButton(MotionEvent event, int action, float x, float y) { + processPendingMouseButtonDown(action, event.getToolType(0), x, y); + int sdlMouseButton = getMouseButton(event); + + if (action == MotionEvent.ACTION_UP) { + mouseState.addAction(event.getToolType(0), System.currentTimeMillis(), event.getActionMasked(), x, y); + //XXX: The Button state might not be available when the action is UP + // 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 + || sdlMouseButton != 0) { + if (sdlActivity.isRelativeMode(event.getToolType(0))) + sdlActivity.sendMouseEvent(sdlMouseButton, MotionEvent.ACTION_UP, event.getToolType(0), 0, 0); + else + sdlActivity.sendMouseEvent(sdlMouseButton, MotionEvent.ACTION_UP, event.getToolType(0), x, y); + + } else { // if we don't have information about which button we can make some guesses + guessMouseButtonUp(event.getToolType(0), x, y); + } + mouseState.lastMouseButtonDown = -1; + mouseState.mouseUp = true; + + } else if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + mouseState.addAction(event.getToolType(0), System.currentTimeMillis(), event.getActionMasked(), x, y); + //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)) { + sdlMouseButton = Config.SDL_MOUSE_LEFT; + } + if (sdlActivity.isRelativeMode(event.getToolType(0))) { + if (!firstTouch) { + sdlActivity.sendMouseEvent(sdlMouseButton, MotionEvent.ACTION_DOWN, event.getToolType(0), 0, 0); + firstTouch = true; + } else { + setPendingMouseDown(x, y, sdlMouseButton); + } + } else { + sdlActivity.sendMouseEvent(sdlMouseButton, MotionEvent.ACTION_DOWN, event.getToolType(0), x, y); + } + mouseState.lastMouseButtonDown = sdlMouseButton; + + } + } + + private void processPendingMouseButtonDown(int action, int toolType, float x, float y) { + long delta = System.currentTimeMillis() - mouseState.down_event_time; + if (mouseState.down_pending && sdlActivity.isRelativeMode(toolType) + && (Math.abs(x - mouseState.down_x) < 20 && Math.abs(y - mouseState.down_y) < 20) + && ((action == MotionEvent.ACTION_MOVE && delta > 400) + || action == MotionEvent.ACTION_UP)) { + sdlActivity.sendMouseEvent(mouseState.down_mouse_button, MotionEvent.ACTION_DOWN, toolType, 0, 0); + mouseState.down_pending = false; + } else if (System.currentTimeMillis() - mouseState.down_event_time > 400) { + mouseState.down_pending = false; + } + } + + private int getMouseButton(MotionEvent event) { + int sdlMouseButton = 0; + if (event.getButtonState() == MotionEvent.BUTTON_PRIMARY) + sdlMouseButton = Config.SDL_MOUSE_LEFT; + else if (event.getButtonState() == MotionEvent.BUTTON_SECONDARY) + sdlMouseButton = Config.SDL_MOUSE_RIGHT; + else if (event.getButtonState() == MotionEvent.BUTTON_TERTIARY) + sdlMouseButton = Config.SDL_MOUSE_MIDDLE; + return sdlMouseButton; + } + + private void guessMouseButtonUp(int toolType, float x, float y) { + //Or only the last one pressed + if (mouseState.lastMouseButtonDown > 0) { + if (sdlActivity.isRelativeMode(toolType)) { + sdlActivity.sendMouseEvent(mouseState.lastMouseButtonDown, MotionEvent.ACTION_UP, toolType, 0, 0); + } else { + sdlActivity.sendMouseEvent(mouseState.lastMouseButtonDown, MotionEvent.ACTION_UP, toolType, x, y); + } + } else { + //ALl buttons + if (sdlActivity.isRelativeMode(toolType)) { + sdlActivity.sendMouseEvent(Config.SDL_MOUSE_LEFT, MotionEvent.ACTION_UP, toolType, 0, 0); + } else { + sdlActivity.sendMouseEvent(Config.SDL_MOUSE_LEFT, MotionEvent.ACTION_UP, toolType, x, y); + sdlActivity.sendMouseEvent(Config.SDL_MOUSE_RIGHT, MotionEvent.ACTION_UP, toolType, x, y); + sdlActivity.sendMouseEvent(Config.SDL_MOUSE_MIDDLE, MotionEvent.ACTION_UP, toolType, x, y); + } + } + } + + private void setPendingMouseDown(float x, float y, int sdlMouseButton) { + mouseState.down_pending = true; + mouseState.down_x = x; + mouseState.down_y = y; + mouseState.down_mouse_button = sdlMouseButton; + mouseState.down_event_time = System.currentTimeMillis(); + } + + public boolean onTouch(View v, MotionEvent event) { + return processExternalMouseEvents(v, event); + } + + /** + * For External Mouse we need absolute coordinates so we capture the events from the + * surface view so this should be called from within the surfaceview's onTouch() callback + * + * @param v View originating + * @param event MotionEvent to be processed + * @return true if event is consumed + */ + private boolean processExternalMouseEvents(View v, MotionEvent event) { + if (event.getToolType(0) != MotionEvent.TOOL_TYPE_FINGER + || sdlActivity.mouseMode == LimboSDLActivity.MouseMode.TOUCHSCREEN) { + onTouchProcess(v, event); + return true; + } + return false; + } + + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + // if keys are other than keyboard (joystick, etc) we send to the parent SDLSurface + if (event.getSource() != InputDevice.SOURCE_KEYBOARD) + return super.onKey(v, keyCode, event); + return false; + } + + class MouseState { + public float x = 0; + public float y = 0; + public float old_x = 0; + public float old_y = 0; + public float down_x = 0; + public float down_y = 0; + public int down_mouse_button = 0; + public long down_event_time = 0; + public ArrayList taps = new ArrayList<>(); + private boolean mouseUp = true; + private int lastMouseButtonDown = -1; + private boolean down_pending = false; + + public void addAction(int toolType, long time, int actionMasked, float x, float y) { + + if (taps.size() > 1 && time - taps.get(taps.size() - 2).time > 200) { + taps.clear(); + } else if (taps.size() == 4) { + taps.clear(); + } + taps.add(new MouseAction(toolType, time, actionMasked, (int) x, (int) y)); + + } + + public boolean isDoubleTap() { + if (LimboSDLActivity.mouseMode == LimboSDLActivity.MouseMode.TOUCHSCREEN + && taps.size() >= 3 + && taps.get(0).toolType == MotionEvent.TOOL_TYPE_FINGER + && taps.get(0).action == MotionEvent.ACTION_DOWN + && taps.get(1).toolType == MotionEvent.TOOL_TYPE_FINGER + && taps.get(1).action == MotionEvent.ACTION_UP + && taps.get(0).x == taps.get(1).x + && taps.get(0).y == taps.get(1).y + && taps.get(2).toolType == MotionEvent.TOOL_TYPE_FINGER + && taps.get(2).action == MotionEvent.ACTION_DOWN + ) + return true; + return false; + } + + public class MouseAction { + private final long time; + int action; + int toolType; + int x, y; + + public MouseAction(int toolType, long time, int actionMasked, int x, int y) { + this.action = actionMasked; + this.time = time; + this.toolType = toolType; + this.x = x; + this.y = y; + } + } + } + + /** + * A motion listener for the external mouse and other peripheral which are NOT TESTED! + */ + class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener { + + @Override + public boolean onGenericMotion(View v, MotionEvent event) { + float x, y; + int action; + + switch (event.getSource()) { + case InputDevice.SOURCE_JOYSTICK: + case InputDevice.SOURCE_GAMEPAD: + case InputDevice.SOURCE_DPAD: + SDLControllerManager.handleJoystickMotionEvent(event); + return true; + + case InputDevice.SOURCE_MOUSE: + action = event.getActionMasked(); + switch (action) { + case MotionEvent.ACTION_SCROLL: + x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0); + y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0); + sdlActivity.sendMouseEvent(0, action, event.getToolType(0), x, y); + return true; + + case MotionEvent.ACTION_HOVER_MOVE: + if (Config.processMouseHistoricalEvents) { + final int historySize = event.getHistorySize(); + for (int h = 0; h < historySize; h++) { + float ex = event.getHistoricalX(h); + float ey = event.getHistoricalY(h); + float ep = event.getHistoricalPressure(h); + sdlActivity.sendMouseEvent(0, action, event.getToolType(0), ex, ey); + } + } + float ex = event.getX(); + float ey = event.getY(); + float ep = event.getPressure(); + sdlActivity.sendMouseEvent(0, action, event.getToolType(0), ex, ey); + return true; + case MotionEvent.ACTION_UP: + default: + break; + } + break; + default: + break; + } + return false; + } + } + +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboService.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboService.java deleted file mode 100644 index 0ead8c7e2..000000000 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboService.java +++ /dev/null @@ -1,249 +0,0 @@ -package com.max2idea.android.limbo.main; - -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.graphics.BitmapFactory; -import android.net.wifi.WifiManager; -import android.net.wifi.WifiManager.WifiLock; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.PowerManager; -import android.os.PowerManager.WakeLock; -import android.util.Log; - -import com.limbo.emu.lib.R; -import com.max2idea.android.limbo.jni.VMExecutor; -import com.max2idea.android.limbo.utils.FileUtils; -import com.max2idea.android.limbo.utils.UIUtils; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; - -import androidx.core.app.NotificationCompat; - -public class LimboService extends Service { - - private static final String TAG = "LimboService"; - private static Notification mNotification; - private static WifiLock mWifiLock; - public static LimboService service; - private static WakeLock mWakeLock; - private NotificationManager mNotificationManager; - - @Override - public IBinder onBind(Intent arg0) { - - return null; - } - - public static VMExecutor executor; - private static NotificationCompat.Builder builder; - - public static final int notifID = 1000; - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - final String action = intent.getAction(); - final Bundle b = intent.getExtras(); - final int ui = b.getInt("ui", 0); - - if (action.equals(Config.ACTION_START)) { - if (LimboActivity.currMachine == null) - return START_NOT_STICKY; - - setUpAsForeground(LimboActivity.currMachine.machinename + ": VM Running"); - - FileUtils.startLogging(); - - scheduleTimer(); - - Thread t = new Thread(new Runnable() { - public void run() { - - //XXX: wait till logging starts capturing - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - Log.v(TAG, "Starting VM: " + LimboActivity.currMachine.machinename); - - setupLocks(); - - if(ui == Config.UI_VNC) { - - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - - @Override - public void run() { - if(LimboActivity.getInstance()!=null) { - LimboActivity.getInstance().startvnc(); - } else - Log.e(TAG, "Could not start VM"); - - } - - }, 2000); - } - - //Start vm - String res = executor.startvm(); - - //VM has exited - LimboActivity.cleanup(); - - } - }); - t.setName("VMExecutor"); - t.start(); - - - } - - - // Don't restart if killed - return START_NOT_STICKY; - } - - private void scheduleTimer() { - Thread t = new Thread(new Runnable() { - public void run() { - try { - Thread.sleep(2000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - if (LimboActivity.getInstance() != null) { - LimboActivity.getInstance().startTimer(); - } else - Log.e(TAG, "Could not start timer"); - } - }); - t.start(); - } - - - private void setUpAsForeground(String text) { - Class clientClass = null; - if (LimboActivity.currMachine != null) { - if (LimboActivity.currMachine.ui != null) { - if (LimboActivity.currMachine.ui.equals("VNC")) { - clientClass = LimboVNCActivity.class; - } else if (LimboActivity.currMachine.ui.equals("SDL")) { - clientClass = LimboSDLActivity.class; - } else { - Log.e(TAG, "Unknown User Interface"); - return; - } - } else { - // UIUtils.toastLong(service, "Machine UI is not set"); - //using VNC by default - clientClass = LimboVNCActivity.class; - } - } else { - Log.e(TAG, "No Machine selected"); - return; - } - Intent intent = new Intent(service.getApplicationContext(), clientClass); - - PendingIntent pi = PendingIntent.getActivity(service.getApplicationContext(), 0, intent, - PendingIntent.FLAG_UPDATE_CURRENT); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - NotificationChannel chan = new NotificationChannel(Config.notificationChannelID, Config.notificationChannelName, NotificationManager.IMPORTANCE_NONE); - NotificationManager notifService = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - notifService.createNotificationChannel(chan); - builder = new NotificationCompat.Builder(service, Config.notificationChannelID); - - } else - builder = new NotificationCompat.Builder(service, ""); - mNotification = builder.setContentIntent(pi).setContentTitle(getString(R.string.app_name)).setContentText(text) - .setSmallIcon(R.drawable.limbo) - .setLargeIcon(BitmapFactory.decodeResource(service.getResources(), R.drawable.limbo)).build(); - mNotification.tickerText = text; - mNotification.flags |= Notification.FLAG_ONGOING_EVENT; - - service.startForeground(notifID, mNotification); - - - - } - - public static void updateServiceNotification(String text) { - if (builder != null) { - builder.setContentText(text); - mNotification = builder.build(); - // mNotification.tickerText = text ; - - NotificationManager mNotificationManager = (NotificationManager) - service.getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE); - // mId allows you to update the notification later on. - mNotificationManager.notify(notifID, mNotification); - } - } - - @Override - public void onCreate() { - Log.d(TAG, "Creating Service"); - service = this; - } - - private void setupLocks() { - - mWifiLock = ((WifiManager) service.getApplicationContext().getSystemService(Context.WIFI_SERVICE)) - .createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, Config.wifiLockTag); - mWifiLock.setReferenceCounted(false); - - PowerManager pm = (PowerManager) service.getApplicationContext().getSystemService(Context.POWER_SERVICE); - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Config.wakeLockTag); - mWakeLock.setReferenceCounted(false); - - mNotificationManager = (NotificationManager) service.getApplicationContext().getSystemService(NOTIFICATION_SERVICE); - - } - - private static void releaseLocks() { - - if (mWifiLock != null && mWifiLock.isHeld()) { - Log.d(TAG, "Release Wifi lock..."); - mWifiLock.release(); - } - - if (mWakeLock != null && mWakeLock.isHeld()) { - Log.d(TAG, "Release Wake lock..."); - mWakeLock.release(); - } - - } - - public static void stopService() { - - Thread t = new Thread(new Runnable() { - public void run() { - releaseLocks(); - if(service!=null) { - service.stopForeground(true); - service.stopSelf(); - } - - } - }); - t.setName("VMExecutor"); - t.start(); - - - } - -} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboSettingsManager.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboSettingsManager.java index 0512db4ec..068c25d64 100644 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboSettingsManager.java +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboSettingsManager.java @@ -18,185 +18,131 @@ */ package com.max2idea.android.limbo.main; +import android.annotation.SuppressLint; import android.app.Activity; +import android.app.AlertDialog; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; +import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.preference.PreferenceActivity; import android.preference.PreferenceManager; +import android.text.InputType; +import android.text.method.HideReturnsTransformationMethod; +import android.text.method.PasswordTransformationMethod; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; import com.limbo.emu.lib.R; +import com.max2idea.android.limbo.network.NetworkUtils; +import com.max2idea.android.limbo.toast.ToastUtils; -public class LimboSettingsManager extends PreferenceActivity { +import java.util.HashSet; +import java.util.Set; - static String getDNSServer(Activity activity) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - return prefs.getString("dnsServer", Config.defaultDNSServer); - } - - public static void setDNSServer(Activity activity, String dnsServer) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - SharedPreferences.Editor edit = prefs.edit(); - edit.putString("dnsServer", dnsServer); - edit.apply(); - } - - public static int getOrientationSetting(Activity activity) { - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - int orientation = prefs.getInt("orientation", 0); - // UIUtils.log("Getting First time: " + firstTime); - return orientation; - } - - public static void setOrientationSetting(Activity activity, int orientation) { - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - SharedPreferences.Editor edit = prefs.edit(); - edit.putInt("orientation", orientation); - edit.apply(); - } - - - public static boolean getPromptUpdateVersion(Activity activity) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - return prefs.getBoolean("updateVersionPrompt", Config.defaultCheckNewVersion); - } - - - public static void setPromptUpdateVersion(Activity activity, boolean flag) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - SharedPreferences.Editor edit = prefs.edit(); - edit.putBoolean("updateVersionPrompt", flag); - edit.apply(); - // UIUtils.log("Setting First time: "); - } - - static boolean getPrio(Activity activity) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - return prefs.getBoolean("HighPrio", false); - } - - public static void setPrio(Activity activity, boolean flag) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - SharedPreferences.Editor edit = prefs.edit(); - edit.putBoolean("HighPrio", flag); - edit.apply(); - // UIUtils.log("Setting First time: "); - } - - public static boolean getAlwaysShowMenuToolbar(Activity activity) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - return prefs.getBoolean("AlwaysShowMenuToolbar", false); - } - - public static void setAlwaysShowMenuToolbar(Activity activity, boolean flag) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - SharedPreferences.Editor edit = prefs.edit(); - edit.putBoolean("AlwaysShowMenuToolbar", flag); - edit.apply(); - // UIUtils.log("Setting First time: "); - } - - public static boolean getFullscreen(Activity activity) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - return prefs.getBoolean("ShowFullscreen", true); - } - - public static void setFullscreen(Activity activity, boolean flag) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - SharedPreferences.Editor edit = prefs.edit(); - edit.putBoolean("ShowFullscreen", flag); - edit.apply(); - // UIUtils.log("Setting First time: "); - } - - public static boolean getDesktopMode(Activity activity) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - return prefs.getBoolean("DesktopMode", false); - } - - public static void setDesktopMode(Activity activity, boolean flag) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - SharedPreferences.Editor edit = prefs.edit(); - edit.putBoolean("DesktopMode", flag); - edit.apply(); - // UIUtils.log("Setting First time: "); - } - - public static boolean getEnableLegacyFileManager(Activity activity) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - return prefs.getBoolean("EnableLegacyFileManager", false); - } +public class LimboSettingsManager extends PreferenceActivity { + private static final String TAG = "LimboSettingsManager"; + // DNS server + static String getDNSServer(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getString("dnsServer", Config.defaultDNSServer); + } - public static void setEnableLegacyFileManager(Activity activity, boolean flag) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); + public static void setDNSServer(Context context, String dnsServer) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor edit = prefs.edit(); - edit.putBoolean("EnableLegacyFileManager", flag); + edit.putString("dnsServer", dnsServer); edit.apply(); - // UIUtils.log("Setting First time: "); } - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - Intent data = new Intent(); - setResult(Config.SETTINGS_RETURN_CODE, data); - addPrefs(); - } + // Screen + public static int getOrientationSetting(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + int orientation = Integer.parseInt(prefs.getString("orientationPref", "0")); + return orientation; + } - public void addPrefs() { - addPreferencesFromResource(R.xml.settings); - } + public static boolean getAlwaysShowMenuToolbar(Context activity) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); + return prefs.getBoolean("AlwaysShowMenuToolbar", false); + } + public static boolean getFullscreen(Context activity) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); + return prefs.getBoolean("ShowFullscreen", true); + } + // updates + public static boolean getPromptUpdateVersion(Context context) { + if(!Config.enableSoftwareUpdates) + return false; + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean("updateVersionPrompt", Config.defaultCheckNewVersion); + } + + public static void setPromptUpdateVersion(Context context, boolean value) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor edit = prefs.edit(); + edit.putBoolean("updateVersionPrompt", value); + edit.apply(); + } + + // advanced + public static boolean getPrio(Context activity) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); + return prefs.getBoolean("HighPrio", false); + } + + // files + public static boolean getEnableLegacyFileManager(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean("EnableLegacyFileManager", false); + } public static String getLastDir(Context context) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - String imagesDir = prefs.getString("lastDir", null); - return imagesDir; + return prefs.getString("lastDir", null); } public static void setLastDir(Context context, String imagesPath) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor edit = prefs.edit(); edit.putString("lastDir", imagesPath); - edit.commit(); + edit.apply(); } public static String getImagesDir(Context context) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - String imagesDir = prefs.getString("imagesDir", null); - return imagesDir; + return prefs.getString("imagesDir", null); } public static void setImagesDir(Context context, String imagesPath) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor edit = prefs.edit(); edit.putString("imagesDir", imagesPath); - edit.commit(); + edit.apply(); } - public static String getExportDir(Context context) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - String imagesDir = prefs.getString("exportDir", null); - return imagesDir; + return prefs.getString("exportDir", null); } public static void setExportDir(Context context, String imagesPath) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor edit = prefs.edit(); edit.putString("exportDir", imagesPath); - edit.commit(); + edit.apply(); } - public static String getSharedDir(Context context) { String lastDir = Environment.getExternalStorageDirectory().getPath(); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); @@ -208,17 +154,17 @@ public static void setSharedDir(Context context, String lastDir) { SharedPreferences.Editor edit = prefs.edit(); edit.putString("sharedDir", lastDir); edit.apply(); - // UIUtils.log("Setting First time: "); } - - + // exit code public static int getExitCode(Context context) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - int exitCode = prefs.getInt("exitCode", 1); - return exitCode; + return prefs.getInt("exitCode", Config.EXIT_SUCCESS); } + //XXX: we need to make sure this gets written to preferences right before we call System.exit() + // so we need to use commit instead of apply + @SuppressLint("ApplySharedPref") public static void setExitCode(Context context, int exitCode) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor edit = prefs.edit(); @@ -226,34 +172,225 @@ public static void setExitCode(Context context, int exitCode) { edit.commit(); } + public static boolean isFirstLaunch(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean("firstTime" + LimboApplication.getLimboVersionString(), true); + } + + public static void setFirstLaunch(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor edit = prefs.edit(); + edit.putBoolean("firstTime" + LimboApplication.getLimboVersionString(), false); + edit.apply(); + } - public static boolean isFirstLaunch(Activity activity) { - PackageInfo pInfo = null; + // Key Mapper + public static Set getKeyMappers(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + Set set = new HashSet<>(); + return prefs.getStringSet("keyMappers", set); + } - try { - pInfo = activity.getPackageManager().getPackageInfo(activity.getClass().getPackage().getName(), - PackageManager.GET_META_DATA); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } + public static void setKeyMappers(Context context, Set keyMappers) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor edit = prefs.edit(); + edit.putStringSet("keyMappers", keyMappers); + edit.apply(); + } + + public static int getKeyMapperSize(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + String sizeStr = prefs.getString("keyMapperSize", "3"); + return Integer.parseInt(sizeStr); + } + + // VNC + public static boolean getVNCEnablePassword(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean("enableVNCPassword", false); + } + + public static String getVNCPass(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getString("vncPass", ""); + } + + public static void setVNCPass(Context context, String value) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor edit = prefs.edit(); + edit.putString("vncPass", value); + edit.apply(); + } + + public static boolean getEnableExternalVNC(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean("enableExternalVNC", false); + } + + // QMP + public static boolean getEnableQmp(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean("enableQMP", true); + } + + public static boolean getEnableExternalQMP(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean("enableExternalQMP", false); + } + + public static boolean getImmersiveMode(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean("immersiveMode", false); + } + + public static int getKeyPressDelay(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + String sizeStr = prefs.getString("keyPressDelay", "100"); + return Integer.parseInt(sizeStr); + } + + public static int getMouseButtonDelay(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + String sizeStr = prefs.getString("mouseButtonDelay", "100"); + return Integer.parseInt(sizeStr); + } + + public static boolean getEnableAaudio(Context activity) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - boolean firstTime = prefs.getBoolean("firstTime" + pInfo.versionName, true); - return firstTime; + return prefs.getBoolean("enableAaudio", false); + } + + public static String getDiskCache(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getString("diskCachePref", context.getString(R.string.Default)); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Intent data = new Intent(); + setResult(Config.SETTINGS_RETURN_CODE, data); + addPrefs(); + getPreferenceManager().findPreference("enableVNCPassword").setOnPreferenceChangeListener( + new android.preference.Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(android.preference.Preference preference, Object newValue) { + if ((boolean) newValue) + promptVNCPass(LimboSettingsManager.this); + return true; + } + }); + getPreferenceManager().findPreference("vncPass").setOnPreferenceClickListener( + new android.preference.Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(android.preference.Preference preference) { + promptVNCPass(LimboSettingsManager.this); + return true; + } + }); + + } + + public void addPrefs() { + addPreferencesFromResource(R.xml.settings); + if(Config.enableSoftwareUpdates) + addPreferencesFromResource(R.xml.software_updates); + if(Config.enableImmersiveMode) + addPreferencesFromResource(R.xml.immersive); + if (Build.VERSION.SDK_INT >= 26) + addPreferencesFromResource(R.xml.aaudio); } - public static void setFirstLaunch(Activity activity) { - PackageInfo pInfo = null; + public void promptVNCPass(final Activity activity) { + final AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(activity).create(); + alertDialog.setTitle(getString(R.string.VNCPassword)); + + TextView textView = new TextView(activity); + textView.setVisibility(View.VISIBLE); + textView.setPadding(20, 20, 20, 20); + textView.setText(getString(R.string.vncServer) + ": " + NetworkUtils.getVNCAddress(activity) + + ":" + Config.defaultVNCPort + "\n" + getString(R.string.externalVNCWarning)); + + final EditText passwdView = new EditText(activity); + passwdView.setInputType(InputType.TYPE_CLASS_TEXT | + InputType.TYPE_TEXT_VARIATION_PASSWORD | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + passwdView.setSelection(passwdView.getText().length()); + passwdView.setHint(R.string.Password); + passwdView.setEnabled(true); + passwdView.setVisibility(View.VISIBLE); + passwdView.setSingleLine(); + + LinearLayout mLayout = new LinearLayout(this); + mLayout.setPadding(20, 20, 20, 20); + mLayout.setOrientation(LinearLayout.VERTICAL); + + LinearLayout.LayoutParams textViewParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + mLayout.addView(textView, textViewParams); + + LinearLayout.LayoutParams passwordViewParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + mLayout.addView(passwdView, passwordViewParams); + + alertDialog.setView(mLayout); + passwdView.setTag(false); + passwdView.setTransformationMethod(new PasswordTransformationMethod()); + passwdView.setSelection(passwdView.getText().length()); + + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, getString(android.R.string.ok), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + if (passwdView.getText().toString().trim().equals("")) { + ToastUtils.toastShort(getApplicationContext(), getString(R.string.passwordCannotBeEmpty)); + } else { + LimboSettingsManager.setVNCPass(activity, passwdView.getText().toString()); + } + } + }); + alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.Cancel), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + LimboSettingsManager.setVNCPass(activity, null); + } + }); + + + alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + LimboSettingsManager.setVNCPass(activity, null); + } + }); + alertDialog.show(); try { - pInfo = activity.getPackageManager().getPackageInfo(activity.getClass().getPackage().getName(), - PackageManager.GET_META_DATA); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); + Button passwordButton = alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL); + passwordButton.setVisibility(View.VISIBLE); + passwordButton.setBackgroundResource(android.R.drawable.ic_menu_view); + passwordButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if ((boolean) passwdView.getTag()) { + passwdView.setTransformationMethod(new PasswordTransformationMethod()); + } else + passwdView.setTransformationMethod(new HideReturnsTransformationMethod()); + passwdView.setTag(!(boolean) passwdView.getTag()); + passwdView.setSelection(passwdView.getText().length()); + } + }); + } catch (Exception ex) { + ex.printStackTrace(); } - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - SharedPreferences.Editor edit = prefs.edit(); - edit.putBoolean("firstTime" + pInfo.versionName, false); - edit.commit(); + } + + + public static boolean getPreventMouseOutOfBounds(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean("preventMouseOutOfBounds", false); + } + + public static boolean getIgnoreBreakpointInvalidation(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean("ignoreBreakpointInvalidation", false); } } diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboVNCActivity.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboVNCActivity.java deleted file mode 100644 index 360053c8c..000000000 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/LimboVNCActivity.java +++ /dev/null @@ -1,1219 +0,0 @@ -/* - Copyright (C) Max Kastanas 2012 - - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -package com.max2idea.android.limbo.main; - -import android.androidVNC.AbstractScaling; -import android.androidVNC.VncCanvasActivity; -import androidx.appcompat.app.ActionBar; -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.ProgressDialog; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.res.Configuration; -import android.graphics.Point; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.SystemClock; -import android.util.Log; -import android.view.Display; -import android.view.Gravity; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; -import android.widget.ScrollView; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; -import android.widget.TextView; - -import com.limbo.emu.lib.R; -import com.max2idea.android.limbo.utils.DrivesDialogBox; -import com.max2idea.android.limbo.utils.FileUtils; -import com.max2idea.android.limbo.utils.Machine; -import com.max2idea.android.limbo.utils.MachineOpenHelper; -import com.max2idea.android.limbo.utils.QmpClient; -import com.max2idea.android.limbo.utils.UIUtils; - -import java.io.File; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.json.JSONObject; - -import androidx.core.view.MenuItemCompat; - - -/** - * - * @author Dev - */ -public class LimboVNCActivity extends VncCanvasActivity { - - public static final int KEYBOARD = 10000; - public static final int QUIT = 10001; - public static final int HELP = 10002; - private static boolean monitorMode = false; - private boolean mouseOn = false; - private Object lockTime = new Object(); - private boolean timeQuit = false; - private Thread timeListenerThread; - private ProgressDialog progDialog; - private static boolean firstConnection; - - @Override - public void onCreate(Bundle b) { - - if (LimboSettingsManager.getFullscreen(this)) - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - - super.onCreate(b); - - this.vncCanvas.setFocusableInTouchMode(true); - - UIUtils.setupToolBar(this); - - setDefaulViewMode(); - -// setUIModeMobile(); - - - } - - private void setDefaulViewMode() { - - - // Fit to Screen - AbstractScaling.getById(R.id.itemFitToScreen).setScaleTypeForActivity(this); - showPanningState(); - -// screenMode = VNCScreenMode.FitToScreen; - setLayout(getResources().getConfiguration()); - - UIUtils.setOrientation(this); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - setLayout(newConfig); - } - - public enum VNCScreenMode { - Normal, - FitToScreen, - Fullscreen //fullscreen not implemented yet - } - - public static VNCScreenMode screenMode = VNCScreenMode.FitToScreen; - - private void setLayout(Configuration newConfig) { - - boolean isLanscape = - (newConfig!=null && newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) - || UIUtils.isLandscapeOrientation(this); - - View vnc_canvas_layout = (View) this.findViewById(R.id.vnc_canvas_layout); - RelativeLayout.LayoutParams vnc_canvas_layout_params = null; - RelativeLayout.LayoutParams vnc_params = null; - //normal 1-1 - if(screenMode == VNCScreenMode.Normal) { - if (isLanscape) { - vnc_params = new RelativeLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ); -// vnc_params.addRule(RelativeLayout.CENTER_IN_PARENT); - vnc_params.addRule(RelativeLayout.CENTER_HORIZONTAL); - vnc_params.addRule(RelativeLayout.ALIGN_PARENT_TOP); - - vnc_canvas_layout_params = new RelativeLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ); -// vnc_canvas_layout_params.addRule(RelativeLayout.CENTER_IN_PARENT); - vnc_canvas_layout_params.addRule(RelativeLayout.CENTER_HORIZONTAL); - vnc_canvas_layout_params.addRule(RelativeLayout.ALIGN_PARENT_TOP); - - } else { - vnc_params = new RelativeLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ); - vnc_params.addRule(RelativeLayout.CENTER_HORIZONTAL); - vnc_params.addRule(RelativeLayout.ALIGN_PARENT_TOP); - - vnc_canvas_layout_params = new RelativeLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ); - vnc_canvas_layout_params.addRule(RelativeLayout.ALIGN_PARENT_TOP); - vnc_canvas_layout_params.addRule(RelativeLayout.CENTER_HORIZONTAL); - } - } else { - //fittoscreen - if (isLanscape) { - vnc_params = new RelativeLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT - ); - vnc_params.addRule(RelativeLayout.CENTER_IN_PARENT); - vnc_canvas_layout_params = new RelativeLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT - ); - vnc_canvas_layout_params.addRule(RelativeLayout.CENTER_IN_PARENT); - } else { - final Display display = getWindow().getWindowManager().getDefaultDisplay(); - Point size = new Point(); - display.getSize(size); - - int h = ViewGroup.LayoutParams.WRAP_CONTENT; - if(vncCanvas!=null && vncCanvas.rfb!=null - && vncCanvas.rfb.framebufferWidth != 0 - && vncCanvas.rfb.framebufferHeight != 0) { - h = size.x * vncCanvas.rfb.framebufferHeight / vncCanvas.rfb.framebufferWidth; - } - vnc_params = new RelativeLayout.LayoutParams( - size.x, - h - ); - vnc_params.addRule(RelativeLayout.CENTER_HORIZONTAL); - vnc_params.addRule(RelativeLayout.ALIGN_PARENT_TOP); - - vnc_canvas_layout_params = new RelativeLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ); - vnc_canvas_layout_params.addRule(RelativeLayout.ALIGN_PARENT_TOP); - vnc_canvas_layout_params.addRule(RelativeLayout.CENTER_HORIZONTAL); - } - } - this.vncCanvas.setLayoutParams(vnc_params); - vnc_canvas_layout.setLayoutParams(vnc_canvas_layout_params); - - this.invalidateOptionsMenu(); - } - - public void stopTimeListener() { - Log.v(TAG, "Stopping Listener"); - synchronized (this.lockTime) { - this.timeQuit = true; - this.lockTime.notifyAll(); - } - } - - public void onDestroy() { - super.onDestroy(); - this.stopTimeListener(); - - } - - public void onPause() { - if(LimboActivity.currMachine!=null) - LimboService.updateServiceNotification(LimboActivity.currMachine.machinename + ": VM Running in Background"); - super.onPause(); - } - - public void onResume() { - if(LimboActivity.currMachine!=null) - LimboService.updateServiceNotification(LimboActivity.currMachine.machinename + ": VM Running"); - super.onResume(); - } - - public void checkStatus() { - while (timeQuit != true) { - LimboActivity.VMStatus status = Machine.checkSaveVMStatus(activity); - Log.v(TAG, "Status: " + status); - if (status == LimboActivity.VMStatus.Unknown - || status == LimboActivity.VMStatus.Completed - || status == LimboActivity.VMStatus.Failed - ) { - //Log.v(TAG, "Saving state is done: " + status); - stopTimeListener(); - return; - } - try { - Thread.sleep(1000); - } catch (InterruptedException ex) { - Log.w("SaveVM", "Interrupted"); - } - } - Log.v("SaveVM", "Save state complete"); - - } - - public void startSaveVMListener() { - this.stopTimeListener(); - timeQuit = false; - try { - Log.v("Listener", "Time Listener Started..."); - checkStatus(); - synchronized (lockTime) { - while (timeQuit == false) { - lockTime.wait(); - } - lockTime.notifyAll(); - } - } catch (Exception ex) { - ex.printStackTrace(); - Log.v("SaveVM", "Time listener thread error: " + ex.getMessage()); - } - Log.v("Listener", "Time listener thread exited..."); - - } - - String TAG = "LimboVNCActivity"; - - - DrivesDialogBox drives = null; - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - // Log.v(TAG, "RET CODE: " + resultCode); - if (requestCode == Config.OPEN_IMAGE_FILE_REQUEST_CODE || requestCode == Config.OPEN_IMAGE_FILE_ASF_REQUEST_CODE) { - String file = null; - if(requestCode == Config.OPEN_IMAGE_FILE_ASF_REQUEST_CODE) { - file = FileUtils.getFileUriFromIntent(this, data, true); - } else { - DrivesDialogBox.filetype = FileUtils.getFileTypeFromIntent(this, data); - file = FileUtils.getFilePathFromIntent(activity, data); - } - if(drives !=null && file!=null) - drives.setDriveAttr(DrivesDialogBox.filetype, file); - }else if (requestCode == Config.OPEN_LOG_FILE_DIR_REQUEST_CODE|| requestCode == Config.OPEN_LOG_FILE_DIR_ASF_REQUEST_CODE) { - String file = null; - if(requestCode == Config.OPEN_LOG_FILE_DIR_ASF_REQUEST_CODE) { - file = FileUtils.getFileUriFromIntent(this, data, true); - } else { - file = FileUtils.getDirPathFromIntent(this, data); - } - if(file!=null) { - FileUtils.saveLogToFile(activity, file); - } - } - - } - - @Override - public boolean onOptionsItemSelected(final MenuItem item) { - super.onOptionsItemSelected(item); - if (item.getItemId() == this.KEYBOARD || item.getItemId() == R.id.itemKeyboard) { - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - toggleKeyboardFlag = UIUtils.onKeyboard(activity, toggleKeyboardFlag, vncCanvas); - } - }, 200); - } else if (item.getItemId() == R.id.itemReset) { - Machine.resetVM(activity); - } else if (item.getItemId() == R.id.itemShutdown) { - UIUtils.hideKeyboard(this, vncCanvas); - Machine.stopVM(activity); - } else if (item.getItemId() == R.id.itemDrives) { - // Show up removable devices dialog - if (LimboActivity.currMachine.hasRemovableDevices()) { - drives = new DrivesDialogBox(activity, R.style.Transparent, this, LimboActivity.currMachine); - drives.show(); - } else { - UIUtils.toastShort(activity, "No removable devices attached"); - } - - } else if (item.getItemId() == R.id.itemMonitor) { - if (this.monitorMode) { - this.onVNC(); - } else { - this.onMonitor(); - } - } else if (item.getItemId() == R.id.itemSaveState) { - this.promptPause(activity); - } else if (item.getItemId() == R.id.itemSaveSnapshot) { - this.promptStateName(activity); - } else if (item.getItemId() == R.id.itemFitToScreen) { - return onFitToScreen(); - } else if (item.getItemId() == R.id.itemFullScreen) { - return toggleFullScreen(); - } else if (item.getItemId() == this.QUIT) { - } else if (item.getItemId() == R.id.itemCenterMouse) { - onMouseMode(); - } else if (item.getItemId() == R.id.itemCalibrateMouse) { - calibration(); - } - else if (item.getItemId() == R.id.itemHelp) { - UIUtils.onHelp(this); - } else if (item.getItemId() == R.id.itemHideToolbar) { - this.onHideToolbar(); - } else if (item.getItemId() == R.id.itemDisplay) { - this.onSelectMenuVNCDisplay(); - } else if (item.getItemId() == R.id.itemViewLog) { - this.onViewLog(); - } - - this.invalidateOptionsMenu(); - - return true; - } - - private void onMouseMode() { - - 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); - mBuilder.setTitle("Mouse"); - mBuilder.setSingleChoiceItems(items, Config.mouseMode.ordinal(), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int i) { - switch(i){ - case 0: - setUIModeMobile(true); - break; - case 1: - promptSetUIModeDesktop(LimboVNCActivity.this, false); - break; - default: - break; - } - dialog.dismiss(); - } - }); - final AlertDialog alertDialog = mBuilder.create(); - alertDialog.show(); - - } - - public boolean checkVMResolutionFits() { - if(vncCanvas.rfb.framebufferWidth < vncCanvas.getWidth() - && vncCanvas.rfb.framebufferHeight < vncCanvas.getHeight()) - return true; - - return false; - } - private void onDisplayMode() { - - String [] items = { - "Normal (One-To-One)", - "Fit To Screen" - //"Full Screen" //Stretched - }; - int currentScaleType = vncCanvas.getScaleType() == ImageView.ScaleType.FIT_CENTER? 1 : 0; - - final AlertDialog.Builder mBuilder = new AlertDialog.Builder(this); - mBuilder.setTitle("Display Mode"); - mBuilder.setSingleChoiceItems(items, currentScaleType, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int i) { - switch(i){ - case 0: - onNormalScreen(); - onMouse(); - break; - case 1: - if(Config.mouseMode == Config.MouseMode.External){ - UIUtils.toastShort(LimboVNCActivity.this, "Fit to Screen disabled under Desktop mode"); - dialog.dismiss(); - return; - } - onFitToScreen(); - onMouse(); - break; - default: - break; - } - dialog.dismiss(); - } - }); - final AlertDialog alertDialog = mBuilder.create(); - alertDialog.show(); - - } - - private void setUIModeMobile(boolean fitToScreen){ - - try { - MotionEvent a = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); - - Config.mouseMode = Config.MouseMode.Trackpad; - LimboSettingsManager.setDesktopMode(this, false); - if(fitToScreen) - onFitToScreen(); - else - onNormalScreen(); - onMouse(); - - //UIUtils.toastShort(LimboVNCActivity.this, "Trackpad Calibrating"); - invalidateOptionsMenu(); - } catch (Exception ex) { - if(Config.debug) - ex.printStackTrace(); - } - } - - private void promptSetUIModeDesktop(final Activity activity, final boolean mouseMethodAlt) { - - - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("Desktop mode"); - - LinearLayout mLayout = new LinearLayout(this); - 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()){ - String resolutionWarning = "Warning: Machine resolution " - + vncCanvas.rfb.framebufferWidth + "x" + vncCanvas.rfb.framebufferHeight + - " is too high for Desktop Mode. " + - "Scaling will be used and Mouse Alignment will not be accurate. " + - "Reduce display resolution for better experience\n\n"; - desktopInstructions = resolutionWarning + desktopInstructions; - } - textView.setText(desktopInstructions); - - LinearLayout.LayoutParams textViewParams = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - ScrollView scrollView = new ScrollView(this); - scrollView.addView(textView); - mLayout.addView(scrollView, textViewParams); - alertDialog.setView(mLayout); - - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - - setUIModeDesktop(); - alertDialog.dismiss(); - } - }); - alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - alertDialog.dismiss(); - } - }); - alertDialog.show(); - - } - - private void setUIModeDesktop() { - - try { - MotionEvent a = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); - Config.mouseMode = Config.MouseMode.External; - LimboSettingsManager.setDesktopMode(this, true); - if(Config.showToast) - UIUtils.toastShort(LimboVNCActivity.this, "External Mouse Enabled"); - onNormalScreen(); - AbstractScaling.getById(R.id.itemOneToOne).setScaleTypeForActivity(LimboVNCActivity.this); - showPanningState(); - - onMouse(); - } catch (Exception e) { - if(Config.debug) - e.printStackTrace(); - } - //vncCanvas.reSize(false); - invalidateOptionsMenu(); - } - - - public void onViewLog() { - FileUtils.viewLimboLog(this); - } - - public void setContentView() { - - setContentView(R.layout.limbo_vnc); - - } - - private boolean toggleFullScreen() { - - UIUtils.toastShort(this, "VNC Fullscreen not supported"); - - return false; - } - - private boolean onFitToScreen() { - - try { - UIUtils.setOrientation(this); - ActionBar bar = this.getSupportActionBar(); - if (bar != null && !LimboSettingsManager.getAlwaysShowMenuToolbar(this)) { - bar.hide(); - } - - inputHandler = getInputHandlerById(R.id.itemInputTouchpad); - connection.setInputMode(inputHandler.getName()); - connection.setFollowMouse(true); - mouseOn = true; - AbstractScaling.getById(R.id.itemFitToScreen).setScaleTypeForActivity(this); - showPanningState(); - screenMode = VNCScreenMode.FitToScreen; - setLayout(null); - - return true; - } catch (Exception ex) { - if(Config.debug) - ex.printStackTrace(); - } - return false; - } - - private boolean onNormalScreen() { - - try { - //Force only landscape - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); - ActionBar bar = LimboVNCActivity.this.getSupportActionBar(); - if (bar != null) { - bar.show(); - } - - inputHandler = getInputHandlerById(R.id.itemInputTouchpad); - connection.setInputMode(inputHandler.getName()); - connection.setFollowMouse(true); - mouseOn = true; - AbstractScaling.getById(R.id.itemOneToOne).setScaleTypeForActivity(this); - showPanningState(); - screenMode = VNCScreenMode.Normal; - setLayout(null); - - return true; - } catch (Exception ex) { - if(Config.debug) - ex.printStackTrace(); - } finally { - - } - return false; - } - - private boolean onMouse() { - - // Limbo: For now we disable other modes - if(Config.disableMouseModes) - mouseOn = false; - - - if (mouseOn == false) { - inputHandler = getInputHandlerById(R.id.itemInputTouchpad); - connection.setInputMode(inputHandler.getName()); - connection.setFollowMouse(true); - mouseOn = true; - } else { - // XXX: Limbo - // we disable panning for now - // input1 = getInputHandlerById(R.id.itemFitToScreen); - // input1 = getInputHandlerById(R.id.itemInputTouchPanZoomMouse); - // connection.setFollowMouse(false); - // mouseOn = false; - } - - //Start calibration - calibration(); - - return true; - } - - //XXX: We need to adjust the mouse inside the Guest - // This is a known issue with QEMU under VNC mode - // this only fixes things temporarily. - // There is a workaround to choose USB Tablet for mouse emulation - // though it might not work for all Guest OSes - public void calibration() { - Thread t = new Thread(new Runnable() { - public void run() { - try { - - int origX = vncCanvas.mouseX; - int origY = vncCanvas.mouseY; - MotionEvent event = null; - - for(int i = 0; i< 4*20; i++) { - int x = 0+i*50; - int y = 0+i*50; - if(i%4==1){ - x=vncCanvas.rfb.framebufferWidth; - }else if (i%4==2) { - y=vncCanvas.rfb.framebufferHeight; - }else if (i%4==3) { - x=0; - } - - event = MotionEvent.obtain(SystemClock.uptimeMillis(), - SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE, - x,y, 0); - Thread.sleep(10); - vncCanvas.processPointerEvent(event, false, false); - - - } - - Thread.sleep(50); - event = MotionEvent.obtain(SystemClock.uptimeMillis(), - SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE, - origX,origY, 0); - vncCanvas.processPointerEvent(event, false, false); - - }catch(Exception ex) { - - } - } - }); - t.start(); - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - menu.clear(); - return this.setupMenu(menu); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - menu.clear(); - return this.setupMenu(menu); - - } - - public boolean setupMenu(Menu menu) { - getMenuInflater().inflate(R.menu.vnccanvasactivitymenu, menu); - - int maxMenuItemsShown = 4; - int actionShow = MenuItemCompat.SHOW_AS_ACTION_IF_ROOM; - if(UIUtils.isLandscapeOrientation(this)) { - maxMenuItemsShown = 6; - actionShow = MenuItemCompat.SHOW_AS_ACTION_ALWAYS; - } - - if (vncCanvas.scaling != null) { - menu.findItem(vncCanvas.scaling.getId()).setChecked(true); - } - - if (this.monitorMode) { - menu.findItem(R.id.itemMonitor).setTitle("VM Display"); - menu.findItem(R.id.itemMonitor).setIcon(R.drawable.ui); - - } else { - menu.findItem(R.id.itemMonitor).setTitle("QEMU Monitor"); - menu.findItem(R.id.itemMonitor).setIcon(R.drawable.terminal); - - } - - //XXX: We don't need these for now - menu.removeItem(menu.findItem(R.id.itemEnterText).getItemId()); - menu.removeItem(menu.findItem(R.id.itemSendKeyAgain).getItemId()); - menu.removeItem(menu.findItem(R.id.itemSpecialKeys).getItemId()); - menu.removeItem(menu.findItem(R.id.itemInputMode).getItemId()); - menu.removeItem(menu.findItem(R.id.itemScaling).getItemId()); - menu.removeItem(menu.findItem(R.id.itemCtrlAltDel).getItemId()); - menu.removeItem(menu.findItem(R.id.itemCtrlC).getItemId()); - menu.removeItem(menu.findItem(R.id.itemColorMode).getItemId()); - menu.removeItem(menu.findItem(R.id.itemFullScreen).getItemId()); - - if (LimboSettingsManager.getAlwaysShowMenuToolbar(activity) || Config.mouseMode == Config.MouseMode.External) { - menu.removeItem(menu.findItem(R.id.itemHideToolbar).getItemId()); - maxMenuItemsShown--; - } - - // Menu inputMenu = menu.findItem(R.id.itemInputMode).getSubMenu(); - // - // inputModeMenuItems = new MenuItem[inputModeIds.length]; - // for (int i = 0; i < inputModeIds.length; i++) { - // inputModeMenuItems[i] = inputMenu.findItem(inputModeIds[i]); - // } - // updateInputMenu(); - // menu.removeItem(menu.findItem(R.id.itemCenterMouse).getItemId()); - - // Limbo: Disable Panning for now - // if (this.mouseOn) { - // menu.findItem(R.id.itemCenterMouse).setTitle("Pan (Mouse Off)"); - // menu.findItem(R.id.itemCenterMouse).setIcon(R.drawable.pan); - // } else { - menu.findItem(R.id.itemCenterMouse).setTitle("Mouse"); - menu.findItem(R.id.itemCenterMouse).setIcon(R.drawable.mouse); - // - // } - - - for (int i = 0; i < menu.size() && i < maxMenuItemsShown; i++) { - MenuItemCompat.setShowAsAction(menu.getItem(i), actionShow); - } - - return true; - - } - - - - - - public static boolean toggleKeyboardFlag = true; - - private void onMonitor() { - if(Config.showToast) - UIUtils.toastShort(this, "Connecting to QEMU Monitor"); - - Thread t = new Thread(new Runnable() { - public void run() { - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } - monitorMode = true; - vncCanvas.sendMetaKey1(50, 6); - - } - }); - t.start(); - } - - private void onVNC() { - UIUtils.toastShort(this, "Connecting to VM"); - - Thread t = new Thread(new Runnable() { - public void run() { - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } - monitorMode = false; - vncCanvas.sendMetaKey1(49, 6); - } - }); - t.start(); - - - } - - // FIXME: We need this to able to catch complex characters strings like - // grave and send it as text - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - if (event.getAction() == KeyEvent.ACTION_MULTIPLE && event.getKeyCode() == KeyEvent.KEYCODE_UNKNOWN) { - vncCanvas.sendText(event.getCharacters().toString()); - return true; - } else - return super.dispatchKeyEvent(event); - - } - - //FIXME: need to use QMP instead - private void onSaveSnapshot(final String stateName) { - Thread t = new Thread(new Runnable() { - public void run() { - if(LimboActivity.getInstance()!=null) - LimboActivity.getInstance().saveSnapshotDB(stateName); - - onMonitor(); - try { - Thread.sleep(500); - } catch (InterruptedException ex) { - Logger.getLogger(LimboVNCActivity.class.getName()).log(Level.SEVERE, null, ex); - } - - String command = "savevm " + stateName + "\n"; - for (int i = 0; i < command.length(); i++) - vncCanvas.sendText(command.charAt(i) + ""); - - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - Logger.getLogger(LimboVNCActivity.class.getName()).log(Level.SEVERE, null, ex); - } - - onVNC(); - - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - UIUtils.toastShort(LimboVNCActivity.this, "Please wait while saving HD Snapshot"); - // progDialog = ProgressDialog.show(activity, "Please - // Wait", "Saving VM - // State...", true); - VMListener a = new VMListener(); - a.execute(); - } - }, 3000); - } - }); - t.start(); - - } - - private void resumeVM() { - if(LimboActivity.vmexecutor == null){ - return; - } - Thread t = new Thread(new Runnable() { - public void run() { - if (LimboActivity.vmexecutor.paused == 1) { - try { - Thread.sleep(4000); - } catch (InterruptedException ex) { - Logger.getLogger(LimboVNCActivity.class.getName()).log(Level.SEVERE, null, ex); - } - if(vncCanvas == null) - return; - - LimboActivity.vmexecutor.paused = 0; - String command = QmpClient.cont(); - String msg = QmpClient.sendCommand(command); - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - setUIModeMobile(screenMode == VNCScreenMode.FitToScreen); - } - }, 500); - - } - } - }); - t.start(); - - } - - private void onPauseVM() { - Thread t = new Thread(new Runnable() { - public void run() { - // Delete any previous state file - if (LimboActivity.vmexecutor.save_state_name != null) { - File file = new File(LimboActivity.vmexecutor.save_state_name); - if (file.exists()) { - file.delete(); - } - } - - UIUtils.toastShort(getApplicationContext(), "Please wait while saving VM State"); - - String uri = "fd:" + LimboActivity.vmexecutor.get_fd(LimboActivity.vmexecutor.save_state_name); - String command = QmpClient.stop(); - String msg = QmpClient.sendCommand(command); -// if (msg != null) -// Log.i(TAG, msg); - command = QmpClient.migrate(false, false, uri); - msg = QmpClient.sendCommand(command); - if (msg != null) { -// Log.i(TAG, msg); - processMigrationResponse(msg); - } - - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - VMListener a = new VMListener(); - a.execute(); - } - }, 0); - } - }); - t.start(); - - } - - private void processMigrationResponse(String response) { - String errorStr = null; - - if(response.contains("error")) { - try { - JSONObject object = new JSONObject(response); - errorStr = object.getString("error"); - } catch (Exception ex) { - if (Config.debug) - ex.printStackTrace(); - } - } - if (errorStr != null && errorStr.contains("desc")) { - String descStr = null; - - try { - JSONObject descObj = new JSONObject(errorStr); - descStr = descObj.getString("desc"); - }catch (Exception ex) { - if(Config.debug) - ex.printStackTrace(); - } - final String descStr1 = descStr; - - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - Machine.pausedErrorVM(activity, descStr1); - } - }, 100); - - } - - } - - - private class VMListener extends AsyncTask { - - @Override - protected Void doInBackground(Void... arg0) { - startSaveVMListener(); - return null; - } - - @Override - protected void onPostExecute(Void test) { - // if (progDialog.isShowing()) { - // progDialog.dismiss(); - // } - - } - } - - private void fullScreen() { - AbstractScaling.getById(R.id.itemFitToScreen).setScaleTypeForActivity(this); - showPanningState(); - } - - public void promptPause(final Activity activity) { - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("Pause VM"); - TextView stateView = new TextView(activity); - stateView.setText("This make take a while depending on the RAM size used"); - stateView.setPadding(20, 20, 20, 20); - alertDialog.setView(stateView); - - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Pause", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - onPauseVM(); - return; - } - }); - alertDialog.show(); - - } - - //TODO: Snapshot is not supported right now - public void promptStateName(final Activity activity) { - if (// - (LimboActivity.currMachine.hda_img_path == null || !LimboActivity.currMachine.hda_img_path.contains(".qcow2")) - && (LimboActivity.currMachine.hdb_img_path == null - || !LimboActivity.currMachine.hdb_img_path.contains(".qcow2")) - && (LimboActivity.currMachine.hdc_img_path == null - || !LimboActivity.currMachine.hdc_img_path.contains(".qcow2")) - && (LimboActivity.currMachine.hdd_img_path == null - || !LimboActivity.currMachine.hdd_img_path.contains(".qcow2"))) - - { - UIUtils.toastLong(LimboVNCActivity.this, "No HDD image found, please create a qcow2 image from Limbo console"); - return; - } - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("Snapshot/State Name"); - final EditText stateView = new EditText(activity); - if (LimboActivity.currMachine.snapshot_name != null) { - stateView.setText(LimboActivity.currMachine.snapshot_name); - } - stateView.setEnabled(true); - stateView.setVisibility(View.VISIBLE); - stateView.setSingleLine(); - alertDialog.setView(stateView); - - // alertDialog.setMessage(body); - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Create", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - - if (Config.enableSaveVMmonitor) { - // XXX: Safer for now via the Monitor console - onSaveSnapshot(stateView.getText().toString()); - } else { - QmpClient.sendCommand(QmpClient.save_snapshot(LimboActivity.vmexecutor.snapshot_name)); - } - - return; - } - }); - alertDialog.show(); - - } - - - public void onBackPressed() { - - // super.onBackPressed(); - if (!LimboSettingsManager.getAlwaysShowMenuToolbar(activity)) { - ActionBar bar = this.getSupportActionBar(); - if (bar != null) { - if (bar.isShowing() && Config.mouseMode == Config.MouseMode.Trackpad) { - bar.hide(); - } else - bar.show(); - } - } else - super.onBackPressed(); - - } - - public void onHideToolbar(){ - ActionBar bar = this.getSupportActionBar(); - if (bar != null) { - bar.hide(); - } - } - - @Override - public void onConnected() { - this.resumeVM(); - LimboActivity.currMachine.paused = 0; - MachineOpenHelper.getInstance(activity).update(LimboActivity.currMachine, - MachineOpenHelper.getInstance(activity).PAUSED, 0 + ""); - if(!firstConnection) - UIUtils.showHints(this); - firstConnection = true; - - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - - if(Config.mouseMode == Config.MouseMode.External) - setUIModeDesktop(); - else - setUIModeMobile(screenMode == VNCScreenMode.FitToScreen); - } - },1000); - - } - - public void onSelectMenuVNCDisplay() { - - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("Display"); - - LinearLayout.LayoutParams volParams = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - - LinearLayout t = createVNCDisplayPanel(); - t.setLayoutParams(volParams); - - ScrollView s = new ScrollView(activity); - s.addView(t); - alertDialog.setView(s); - alertDialog.setButton(Dialog.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() { - - public void onClick(DialogInterface dialog, int which) { - alertDialog.cancel(); - } - }); - alertDialog.show(); - - } - - - public LinearLayout createVNCDisplayPanel() { - LinearLayout layout = new LinearLayout(this); - layout.setOrientation(LinearLayout.VERTICAL); - layout.setPadding(20, 20, 20, 20); - - LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - int currRate = getCurrentVNCRefreshRate(); - - LinearLayout buttonsLayout = new LinearLayout(this); - buttonsLayout.setOrientation(LinearLayout.HORIZONTAL); - buttonsLayout.setGravity(Gravity.CENTER_HORIZONTAL); - Button displayMode = new Button (this); - - displayMode.setText("Display Mode"); - displayMode.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - onDisplayMode(); - } - }); - buttonsLayout.addView(displayMode); - - - Button colors = new Button(this); - colors.setText("Color Mode"); - colors.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - selectColorModel(); - - } - }); - buttonsLayout.addView(colors); - - layout.addView(buttonsLayout); - - final TextView value = new TextView(this); - value.setText("Display Refresh Rate: " + currRate+" Hz"); - layout.addView(value); - value.setLayoutParams(params); - - SeekBar rate = new SeekBar(this); - rate.setMax(Config.MAX_DISPLAY_REFRESH_RATE); - - rate.setProgress(currRate); - rate.setLayoutParams(params); - - ((SeekBar) rate).setOnSeekBarChangeListener(new OnSeekBarChangeListener() { - - public void onProgressChanged(SeekBar s, int progress, boolean touch) { - value.setText("Refresh Rate: " + (progress+1)+" Hz"); - } - - public void onStartTrackingTouch(SeekBar arg0) { - - } - - public void onStopTrackingTouch(SeekBar arg0) { - int progress = arg0.getProgress()+1; - int refreshMs = 1000 / progress; - Log.v(TAG, "Changing display refresh rate (ms): " + refreshMs); - LimboActivity.vmexecutor.setvncrefreshrate(refreshMs); - - } - }); - - - layout.addView(rate); - - return layout; - - } - public int getCurrentVNCRefreshRate() { - return 1000 / LimboActivity.vmexecutor.getvncrefreshrate(); - } - -} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/ViewListener.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/ViewListener.java new file mode 100644 index 000000000..45566bee5 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/main/ViewListener.java @@ -0,0 +1,27 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.main; + +import com.max2idea.android.limbo.machine.MachineAction; +import com.max2idea.android.limbo.machine.MachineProperty; + +public interface ViewListener { + void onFieldChange(MachineProperty property, Object value); + void onAction(MachineAction stopVm, Object value); +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/network/NetworkUtils.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/network/NetworkUtils.java new file mode 100644 index 000000000..d7a3499f1 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/network/NetworkUtils.java @@ -0,0 +1,117 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.network; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; + +import com.max2idea.android.limbo.main.Config; +import com.max2idea.android.limbo.main.LimboSettingsManager; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.URL; +import java.util.Enumeration; + +/** Utility Class offers simple network support. + */ +public class NetworkUtils { + private static final String TAG = "NetworkUtils"; + + public static void openURL(Activity activity, String url) { + try { + Intent fileIntent = new Intent(Intent.ACTION_VIEW); + fileIntent.setData(Uri.parse(url)); + activity.startActivity(fileIntent); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + public static String getVNCAddress(Context context) { + String addr = null; + if (LimboSettingsManager.getEnableExternalVNC(context)) + addr = getExternalIpAddress(); + if(addr == null) + return getLocalIpAddress(); + return addr; + } + + public static String getLocalIpAddress() { + return Config.defaultVNCHost; + } + + public static String getExternalIpAddress() { + try { + for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) { + NetworkInterface intf = en.nextElement(); + for (Enumeration enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) { + InetAddress inetAddress = enumIpAddr.nextElement(); + if (!inetAddress.isLoopbackAddress() && inetAddress.getHostAddress().contains(".")) { + return inetAddress.getHostAddress(); + } + } + } + } catch (SocketException ex) { + ex.printStackTrace(); + } + return null; + } + + public static byte[] getContentFromUrl(String urlPath) throws IOException { + HttpURLConnection conn = null; + InputStream is = null; + + byte[] streamData = null; + try { + URL url = new URL(urlPath); + conn = (HttpURLConnection) url.openConnection(); + conn.connect(); + + is = conn.getInputStream(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + int read = 0; + byte[] buff = new byte[1024]; + while ((read = is.read(buff)) != -1) { + bos.write(buff, 0, read); + } + streamData = bos.toByteArray(); + } catch (IOException e) { + throw e; + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (conn != null) + conn.disconnect(); + } + return streamData; + } +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/QmpClient.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/qmp/QmpClient.java similarity index 68% rename from limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/QmpClient.java rename to limbo-android-lib/src/main/java/com/max2idea/android/limbo/qmp/QmpClient.java index cd7ed6610..13a394a1a 100644 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/QmpClient.java +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/qmp/QmpClient.java @@ -1,26 +1,51 @@ -package com.max2idea.android.limbo.utils; +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.qmp; import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.util.Log; import com.max2idea.android.limbo.main.Config; +import com.max2idea.android.limbo.main.LimboApplication; +import com.max2idea.android.limbo.toast.ToastUtils; import org.json.JSONObject; import java.io.BufferedReader; -import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; +/** A simple QMP CLient that is needed for communicating with QEMU. You can use it for + * changing VNC password, checking the status when saving the vm, and change removable drives. + */ public class QmpClient { private static final String TAG = "QmpClient"; - private static String requestCommandMode = "{ \"execute\": \"qmp_capabilities\" }"; - public static boolean allow_external = false; + private static final String requestCommandMode = "{ \"execute\": \"qmp_capabilities\" }"; + private static boolean external = false; + public static void setExternal(boolean value) { + external = value; + } public synchronized static String sendCommand(String command) { String response = null; int trial=0; @@ -30,47 +55,26 @@ public synchronized static String sendCommand(String command) { BufferedReader in = null; try { - if(allow_external) { + if(external) { pingSocket = new Socket(Config.QMPServer, Config.QMPPort); pingSocket.setSoTimeout(5000); out = new PrintWriter(pingSocket.getOutputStream(), true); in = new BufferedReader(new InputStreamReader(pingSocket.getInputStream())); } else { localSocket = new LocalSocket(); - String localQMPSocketPath = Config.getLocalQMPSocketPath(); + String localQMPSocketPath = LimboApplication.getLocalQMPSocketPath(); LocalSocketAddress localSocketAddr = new LocalSocketAddress(localQMPSocketPath, LocalSocketAddress.Namespace.FILESYSTEM); localSocket.connect(localSocketAddr); localSocket.setSoTimeout(5000); out = new PrintWriter(localSocket.getOutputStream(), true); in = new BufferedReader(new InputStreamReader(localSocket.getInputStream())); } - - sendRequest(out, QmpClient.requestCommandMode); - while(true){ - response = getResponse(in); - if(response == null || response.equals("") || trial <10) - break; - - Thread.sleep(1000); - trial++; - } - + response = tryGetResponse(in); sendRequest(out, command); - trial=0; - while((response = getResponse(in)).equals("") && trial < 10){ - Thread.sleep(1000); - trial++; - } - } catch (java.net.ConnectException e) { - Log.w(TAG, "Could not connect to QMP: " + e); - if(Config.debugQmp) - e.printStackTrace(); - } catch(Exception e) { - // TODO Auto-generated catch block - Log.e(TAG, "Error while connecting to QMP: " + e); - if(Config.debugQmp) - e.printStackTrace(); + response = tryGetResponse(in); + } catch (Exception e) { + e.printStackTrace(); } finally { if (out != null) out.close(); @@ -80,33 +84,40 @@ public synchronized static String sendCommand(String command) { if (pingSocket != null) pingSocket.close(); } catch (IOException e) { - // TODO Auto-generated catch block e.printStackTrace(); } - } + if(Config.debugQmp) + Log.d(TAG, "Response: " + response); + return response; + } + private static String tryGetResponse(BufferedReader in) throws Exception { + String response = null; + int trial = 0; + while((response = getResponse(in)).equals("") && trial < 3){ + Thread.sleep(1000); + trial++; + } return response; } private static void sendRequest(PrintWriter out, String request) { if(Config.debugQmp) - Log.i(TAG, "QMP request" + request); + Log.d(TAG, "QMP request" + request); out.println(request); } private static String getResponse(BufferedReader in) throws Exception { - String line; StringBuilder stringBuilder = new StringBuilder(""); - try { do { line = in.readLine(); if (line != null) { if(Config.debugQmp) - Log.i(TAG, "QMP response: " + line); + Log.d(TAG, "QMP response: " + line); JSONObject object = new JSONObject(line); String returnStr = null; String errStr = null; @@ -132,15 +143,11 @@ private static String getResponse(BufferedReader in) throws Exception { if(Config.debugQmp) ex.printStackTrace(); } - stringBuilder.append(line); stringBuilder.append("\n"); - if (errStr != null) { break; } - - } else break; } while (true); @@ -162,7 +169,7 @@ private static String getQueryMigrateResponse(BufferedReader in) throws Exceptio line = in.readLine(); if (line != null) { if(Config.debugQmp) - Log.i(TAG, "QMP query-migrate response: " + line); + Log.d(TAG, "QMP query-migrate response: " + line); JSONObject object = new JSONObject(line); String returnStr = null; String errStr = null; @@ -182,15 +189,12 @@ private static String getQueryMigrateResponse(BufferedReader in) throws Exceptio } catch (Exception ex) { } - stringBuilder.append(line); stringBuilder.append("\n"); if (errStr != null) { break; } - - } else break; } while (true); @@ -200,7 +204,7 @@ private static String getQueryMigrateResponse(BufferedReader in) throws Exceptio return stringBuilder.toString(); } - public static String migrate(boolean block, boolean inc, String uri) { + public static String getMigrateCommand(boolean block, boolean inc, String uri) { // XXX: Detach should not be used via QMP according to docs // return "{\"execute\":\"migrate\",\"arguments\":{\"detach\":" + detach @@ -212,66 +216,41 @@ public static String migrate(boolean block, boolean inc, String uri) { // see qmp-commands.hx for more info return "{\"execute\":\"migrate\",\"arguments\":{\"blk\":" + block + ",\"inc\":" + inc + ",\"uri\":\"" + uri + "\"},\"id\":\"limbo\"}"; - } - public static String changevncpasswd(String passwd) { - + public static String getChangeVncPasswdCommand(String passwd) { return "{\"execute\": \"change\", \"arguments\": { \"device\": \"vnc\", \"target\": \"password\", \"arg\": \"" + passwd +"\" } }"; - } - public static String ejectdev(String dev) { - + public static String getEjectDeviceCommand(String dev) { return "{ \"execute\": \"eject\", \"arguments\": { \"device\": \""+ dev +"\" } }"; - } - public static String changedev(String dev, String value) { - + public static String getChangeDeviceCommand(String dev, String value) { return "{ \"execute\": \"change\", \"arguments\": { \"device\": \""+dev+"\", \"target\": \"" + value + "\" } }"; - } - - - public static String query_migrate() { + public static String getQueryMigrationCommand() { return "{ \"execute\": \"query-migrate\" }"; - } - public static String save_snapshot(String snapshot_name) { - return "{\"execute\": \"snapshot-create\", \"arguments\": {\"name\": \""+ snapshot_name+"\"} }"; - - } - - public static String query_snapshot() { - return "{ \"execute\": \"query-snapshot-status\" }"; - - } - - public static String stop() { + public static String getStopVMCommand() { return "{ \"execute\": \"stop\" }"; - } - public static String cont() { + public static String getContinueVMCommand() { return "{ \"execute\": \"cont\" }"; - } - public static String powerDown() { + public static String getPowerDownCommand() { return "{ \"execute\": \"system_powerdown\" }"; - } - public static String reset() { + public static String getResetCommand() { return "{ \"execute\": \"system_reset\" }"; - } - public static String getState() { + public static String getStateCommand() { return "{ \"execute\": \"query-status\" }"; - } } diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/screen/ScreenUtils.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/screen/ScreenUtils.java new file mode 100644 index 000000000..fd0045af4 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/screen/ScreenUtils.java @@ -0,0 +1,62 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.screen; + +import android.app.Activity; +import android.content.pm.ActivityInfo; +import android.graphics.Point; +import android.view.Display; + +import com.max2idea.android.limbo.main.LimboSettingsManager; + +/** Utility Class for Screen Orientation changes + */ +public class ScreenUtils { + private static final String TAG = "ScreenUtils"; + public static void updateOrientation(Activity activity, int lastOrientation) { + int orientation = LimboSettingsManager.getOrientationSetting(activity); + switch (orientation) { + case 0: + if(lastOrientation >= 0) + activity.setRequestedOrientation(lastOrientation); + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); + break; + case 1: + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + break; + case 2: + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); + break; + case 3: + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + break; + case 4: + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); + break; + } + } + + public static boolean isLandscapeOrientation(Activity activity) { + Display display = activity.getWindowManager().getDefaultDisplay(); + Point screenSize = new Point(); + display.getSize(screenSize); + return screenSize.x >= screenSize.y; + } + +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/toast/ToastUtils.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/toast/ToastUtils.java new file mode 100644 index 000000000..804b69062 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/toast/ToastUtils.java @@ -0,0 +1,69 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.toast; + +import android.app.Activity; +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.view.Gravity; + +/** ToastUtils offers wrappers for showing toast notifications + * + */ +public class ToastUtils { + private static final String TAG = "ToastUtils"; + + public static void toastLong(final Context activity, final String msg) { + toastLong(activity, Gravity.CENTER, msg); + } + + public static void toastLong(final Context activity, final int gravity, final String msg) { + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + android.widget.Toast toast = android.widget.Toast.makeText(activity, msg, android.widget.Toast.LENGTH_LONG); + toast.setGravity(gravity, 0, 0); + toast.show(); + } + }); + } + + public static void toastShortTop(final Activity activity, final String msg) { + toast(activity, msg, Gravity.TOP | Gravity.CENTER, android.widget.Toast.LENGTH_SHORT); + } + + public static void toast(final Context context, final String msg, final int gravity, final int length) { + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + if (context instanceof Activity && ((Activity) context).isFinishing()) { + return; + } + android.widget.Toast toast = android.widget.Toast.makeText(context, msg, length); + toast.setGravity(gravity, 0, 0); + toast.show(); + } + }); + } + + public static void toastShort(final Context context, final String msg) { + toast(context, msg, Gravity.CENTER, android.widget.Toast.LENGTH_SHORT); + } +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/ui/SpinnerAdapter.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/ui/SpinnerAdapter.java new file mode 100644 index 000000000..ec9ab6ec9 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/ui/SpinnerAdapter.java @@ -0,0 +1,102 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.ui; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.Spinner; +import android.widget.TextView; + +import com.limbo.emu.lib.R; +import com.max2idea.android.limbo.files.FileUtils; + +import java.util.ArrayList; + +/** Custom SpinnerAdapter that shows alternative display values than the ones it was originally + * initialized with. The alternative values that are displayed are a compact version of the file + * uris that are retrieved by the Android Storage Framework. + */ +public class SpinnerAdapter extends ArrayAdapter { + private static final String TAG = "SpinnerAdapter"; + + int index; + public SpinnerAdapter(Context context, int layout, ArrayList items, int index) { + super(context, layout, items); + this.index = index; + } + + public static int getItemPosition(Spinner spinner, String value) { + for(int i =0; i) spinner.getAdapter()).add(value); + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + View view = super.getDropDownView(position, convertView, parent); + if (position >= index) { + TextView textView = (TextView) view.findViewById(R.id.customSpinnerDropDownItem); + String textStr = FileUtils.convertFilePath(textView.getText() + "", position); + textView.setText(textStr); + } + return view; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view = super.getView(position, convertView, parent); + if (position >= index) { + TextView textView = (TextView) view.findViewById(R.id.customSpinnerItem); + String textStr = FileUtils.convertFilePath(textView.getText() + "", position); + textView.setText(textStr); + } + return view; + } + + public static int getPositionFromSpinner(Spinner spinner, String value) { + for (int i = 0; i < spinner.getCount(); i++) { + if (spinner.getItemAtPosition(i).equals(value)) + return i; + } + return -1; + } + + public static void setDiskAdapterValue(final Spinner spinner, final String value) { + spinner.post(new Runnable() { + public void run() { + if (value != null) { + int pos = SpinnerAdapter.getPositionFromSpinner(spinner, value); + spinner.setSelection(Math.max(pos, 0)); + } else { + spinner.setSelection(0); + } + } + }); + } + +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/updates/UpdateChecker.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/updates/UpdateChecker.java new file mode 100644 index 000000000..b950707a4 --- /dev/null +++ b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/updates/UpdateChecker.java @@ -0,0 +1,107 @@ +/* +Copyright (C) Max Kastanas 2012 + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.max2idea.android.limbo.updates; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; +import android.widget.TextView; + +import com.limbo.emu.lib.R; +import com.max2idea.android.limbo.main.Config; +import com.max2idea.android.limbo.main.LimboApplication; +import com.max2idea.android.limbo.main.LimboSettingsManager; +import com.max2idea.android.limbo.network.NetworkUtils; + +/** Software Update notifier for checking if a new version is published. + */ +public class UpdateChecker { + private static final String TAG = "UpdateChecker"; + + public static void checkNewVersion(final Activity activity) { + if (!LimboSettingsManager.getPromptUpdateVersion(activity)) { + return; + } + + try { + byte[] streamData = NetworkUtils.getContentFromUrl(Config.newVersionLink); + final String versionStr = new String(streamData).trim(); + float version = Float.parseFloat(versionStr); + String versionName = getVersionName(versionStr); + + int versionCheck = (int) (version * 100); + if (versionCheck > LimboApplication.getLimboVersion()) { + final String finalVersionName = versionName; + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + promptNewVersion(activity, finalVersionName); + } + }); + } + } catch (Exception ex) { + Log.w(TAG, "Could not get new version: " + ex.getMessage()); + if (Config.debug) + ex.printStackTrace(); + } + } + + private static String getVersionName(String versionStr) { + String[] versionSegments = versionStr.split("\\."); + int maj = Integer.parseInt(versionSegments[0]) / 100; + int min = Integer.parseInt(versionSegments[0]) % 100; + int mic = 0; + if (versionSegments.length > 1) { + mic = Integer.parseInt(versionSegments[1]); + } + String versionName = maj + "." + min + "." + mic; + return versionName; + } + + public static void promptNewVersion(final Activity activity, String version) { + + final AlertDialog alertDialog; + alertDialog = new AlertDialog.Builder(activity).create(); + alertDialog.setTitle(activity.getString(R.string.NewVersion) + " " + version); + TextView stateView = new TextView(activity); + stateView.setText(R.string.NewVersionWarning); + stateView.setPadding(20, 20, 20, 20); + alertDialog.setView(stateView); + alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.GenNewVersion), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + NetworkUtils.openURL(activity, Config.downloadLink); + } + }); + alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, activity.getString(R.string.DoNotShowAgain), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + LimboSettingsManager.setPromptUpdateVersion(activity, false); + + } + }); + alertDialog.show(); + + } +} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/DrivesDialogBox.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/DrivesDialogBox.java deleted file mode 100644 index 5a5d30e02..000000000 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/DrivesDialogBox.java +++ /dev/null @@ -1,412 +0,0 @@ -package com.max2idea.android.limbo.utils; - -import android.app.Activity; -import android.app.Dialog; -import android.content.Context; -import android.os.Handler; -import android.os.Looper; -import android.view.View; -import android.view.WindowManager; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.ArrayAdapter; -import android.widget.LinearLayout; -import android.widget.Spinner; - -import com.limbo.emu.lib.R; -import com.max2idea.android.limbo.main.Config; -import com.max2idea.android.limbo.main.LimboActivity; - -import java.util.ArrayList; -import java.util.Iterator; - -public class DrivesDialogBox extends Dialog { - public Spinner mCD; - public LinearLayout mCDLayout; - public LinearLayout mFDALayout; - public LinearLayout mFDBLayout; - public LinearLayout mSDLayout; - public Spinner mFDA; - public Spinner mSD; - public Spinner mFDB; - public static LimboActivity.FileType filetype; - private Activity activity; - private Machine currMachine; - public DrivesDialogBox(Context context, int theme, Activity activity1, Machine currMachine) { - super(context, theme); - this.currMachine = currMachine; - activity = activity1; - getWindow().setFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND, WindowManager.LayoutParams.FLAG_DIM_BEHIND); - setContentView(R.layout.dev_dialog); - this.setTitle("Device Manager"); - getWidgets(); - initUI(); - - - } - - @Override - public void onBackPressed() { - this.dismiss(); - } - - private void getWidgets() { - mCD = (Spinner) findViewById(R.id.cdromimgval); - mCDLayout = (LinearLayout) findViewById(R.id.cdromimgl); - - mFDA = (Spinner) findViewById(R.id.floppyimgval); - mFDALayout = (LinearLayout) findViewById(R.id.floppyimgl); - - mFDB = (Spinner) findViewById(R.id.floppybimgval); - mFDBLayout = (LinearLayout) findViewById(R.id.floppybimgl); - - mSD = (Spinner) findViewById(R.id.sdimgval); - mSDLayout = (LinearLayout) findViewById(R.id.sdimgl); - - } - - private void setupListeners() { - - mCD.setOnItemSelectedListener(new OnItemSelectedListener() { - - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - String cd = (String) ((ArrayAdapter) mCD.getAdapter()).getItem(position); - - if (position == 0) { - update(LimboActivity.currMachine, MachineOpenHelper.CDROM, ""); - LimboActivity.currMachine.cd_iso_path = ""; - } else if (position == 1) { - filetype = LimboActivity.FileType.CD; - FileManager.browse(activity, filetype, Config.OPEN_IMAGE_FILE_REQUEST_CODE); - mCD.setSelection(0); - } else if (position > 1) { - update(LimboActivity.currMachine, MachineOpenHelper.CDROM, cd); - LimboActivity.currMachine.cd_iso_path = cd; - // TODO: If Machine is running eject and set - // floppy img - } - if (LimboActivity.vmexecutor != null && position > 1) { - mCD.setEnabled(false); - LimboActivity.vmexecutor.change_dev("ide1-cd0", LimboActivity.currMachine.cd_iso_path); - mCD.setEnabled(true); - } else if (LimboActivity.vmexecutor != null && position == 0) { - mCD.setEnabled(false); - LimboActivity.vmexecutor.change_dev("ide1-cd0", null); // Eject - mCD.setEnabled(true); - } - } - - public void onNothingSelected(AdapterView parentView) { - // your code here - // Log.v(TAG, "Nothing selected"); - } - }); - - mFDA.setOnItemSelectedListener(new OnItemSelectedListener() { - - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - String fda = (String) ((ArrayAdapter) mFDA.getAdapter()).getItem(position); - if (position == 0) { - update(LimboActivity.currMachine, MachineOpenHelper.FDA, ""); - LimboActivity.currMachine.fda_img_path = ""; - } else if (position == 1) { - filetype = LimboActivity.FileType.FDA; - FileManager.browse(activity, filetype, Config.OPEN_IMAGE_FILE_REQUEST_CODE); - mFDA.setSelection(0); - } else if (position > 1) { - update(LimboActivity.currMachine, MachineOpenHelper.FDA, fda); - LimboActivity.currMachine.fda_img_path = fda; - // TODO: If Machine is running eject and set - // floppy img - } - if (LimboActivity.vmexecutor != null && position > 1) { - mFDA.setEnabled(false); - LimboActivity.vmexecutor.change_dev("floppy0", LimboActivity.currMachine.fda_img_path); - mFDA.setEnabled(true); - } else if (LimboActivity.vmexecutor != null && position == 0) { - mFDA.setEnabled(false); - LimboActivity.vmexecutor.change_dev("floppy0", null); // Eject - mFDA.setEnabled(true); - } - } - - public void onNothingSelected(AdapterView parentView) { - // your code here - // Log.v(TAG, "Nothing selected"); - } - }); - - mFDB.setOnItemSelectedListener(new OnItemSelectedListener() { - - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - String fdb = (String) ((ArrayAdapter) mFDB.getAdapter()).getItem(position); - if (position == 0) { - update(LimboActivity.currMachine, MachineOpenHelper.FDB, ""); - LimboActivity.currMachine.fdb_img_path = ""; - } else if (position == 1) { - filetype = LimboActivity.FileType.FDB; - FileManager.browse(activity, filetype, Config.OPEN_IMAGE_FILE_REQUEST_CODE); - mFDB.setSelection(0); - } else if (position > 1) { - update(LimboActivity.currMachine, MachineOpenHelper.FDB, fdb); - LimboActivity.currMachine.fdb_img_path = fdb; - // TODO: If Machine is running eject and set - // floppy img - } - if (LimboActivity.vmexecutor != null && position > 1) { - mFDB.setEnabled(false); - LimboActivity.vmexecutor.change_dev("floppy1", LimboActivity.currMachine.fdb_img_path); - mFDB.setEnabled(true); - } else if (LimboActivity.vmexecutor != null && position == 0) { - mFDB.setEnabled(false); - LimboActivity.vmexecutor.change_dev("floppy1", null); // Eject - mFDB.setEnabled(true); - } - } - - public void onNothingSelected(AdapterView parentView) { - // your code here - // Log.v(TAG, "Nothing selected"); - } - }); - - mSD.setOnItemSelectedListener(new OnItemSelectedListener() { - - public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - String sd = (String) ((ArrayAdapter) mSD.getAdapter()).getItem(position); - if (position == 0) { - update(LimboActivity.currMachine, MachineOpenHelper.SD, ""); - LimboActivity.currMachine.sd_img_path = ""; - } else if (position == 1) { - filetype = LimboActivity.FileType.SD; - FileManager.browse(activity, filetype, Config.OPEN_IMAGE_FILE_REQUEST_CODE); - mSD.setSelection(0); - } else if (position > 1) { - update(LimboActivity.currMachine, MachineOpenHelper.SD, sd); - LimboActivity.currMachine.sd_img_path = sd; - // TODO: If Machine is running eject and set - // floppy img - } - if (LimboActivity.vmexecutor != null && position > 1) { - mSD.setEnabled(false); - LimboActivity.vmexecutor.change_dev("sd0", LimboActivity.currMachine.sd_img_path); - mSD.setEnabled(true); - } else if (LimboActivity.vmexecutor != null && position == 0) { - mSD.setEnabled(false); - LimboActivity.vmexecutor.change_dev("sd0", null); // Eject - mSD.setEnabled(true); - } - } - - public void onNothingSelected(AdapterView parentView) { - // your code here - // Log.v(TAG, "Nothing selected"); - } - }); - - } - - - // Set File Adapters - public void populateDiskAdapter(final Spinner spinner, final String fileType, final boolean createOption, - final String value) { - - Thread t = new Thread(new Runnable() { - public void run() { - - ArrayList oldHDs = FavOpenHelper.getInstance(activity).getFav(fileType); - int length = 0; - if (oldHDs == null || oldHDs.size() == 0) { - length = 0; - } else { - length = oldHDs.size(); - } - - final ArrayList arraySpinner = new ArrayList(); - arraySpinner.add("None"); - if (createOption) - arraySpinner.add("New"); - arraySpinner.add("Open"); - final int index = arraySpinner.size(); - Iterator i = oldHDs.iterator(); - while (i.hasNext()) { - String file = (String) i.next(); - if (file != null) { - arraySpinner.add(file); - } - } - - new Handler(Looper.getMainLooper()).post(new Runnable() { - public void run() { - UIUtils.LimboFileSpinnerAdapter adapter = new UIUtils.LimboFileSpinnerAdapter(activity, R.layout.custom_spinner_item, arraySpinner, index); - adapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item); - spinner.setAdapter(adapter); - spinner.invalidate(); - setDiskValue(spinner, value); - } - }); - } - }); - t.start(); - } - - private void initUI() { - // Set spinners to values from currmachine - - Thread thread = new Thread(new Runnable() { - public void run() { - - if (currMachine.enableCDROM) { - populateDiskAdapter(mCD, "cd", false, currMachine.cd_iso_path); - } else { - mCDLayout.setVisibility(View.GONE); - } - if (currMachine.enableFDA) { - populateDiskAdapter(mFDA, "fda", false, currMachine.fda_img_path); - } else { - mFDALayout.setVisibility(View.GONE); - } - if (currMachine.enableFDB) { - - populateDiskAdapter(mFDB, "fdb", false, currMachine.fdb_img_path); - } else { - mFDBLayout.setVisibility(View.GONE); - } - if (currMachine.enableSD) { - populateDiskAdapter(mSD, "sd", false, currMachine.cd_iso_path); - } else { - mSDLayout.setVisibility(View.GONE); - } - - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - public void run() { - setupListeners(); - } - }, 500); - } - }); - thread.setPriority(Thread.MIN_PRIORITY); - thread.start(); - } - - - @SuppressWarnings("unchecked") - public void setDriveAttr(LimboActivity.FileType fileType, final String file) { - - addDriveToList(file, fileType); - - if (fileType != null && fileType == LimboActivity.FileType.CD && file != null && !file.trim().equals("")) { - update(LimboActivity.currMachine, MachineOpenHelper.CDROM, file); - - new Handler(Looper.getMainLooper()).post(new Runnable() { - public void run() { - if (((ArrayAdapter) mCD.getAdapter()).getPosition(file) < 0) { - ((ArrayAdapter) mCD.getAdapter()).add(file); - } - setDiskValue(mCD, file); - int res = mCD.getSelectedItemPosition(); - if (res == 1) { - mCD.setSelection(0); - } - } - }); - - } else if (fileType != null && fileType == LimboActivity.FileType.SD && file != null && !file.trim().equals("")) { - update(LimboActivity.currMachine, MachineOpenHelper.SD, file); - - new Handler(Looper.getMainLooper()).post(new Runnable() { - public void run() { - if (((ArrayAdapter) mSD.getAdapter()).getPosition(file) < 0) { - ((ArrayAdapter) mSD.getAdapter()).add(file); - } - setDiskValue(mSD, file); - int res = mSD.getSelectedItemPosition(); - if (res == 1) { - mSD.setSelection(0); - - } - } - }); - - } else if (file != null && !file.trim().equals("") && fileType == LimboActivity.FileType.FDA) { - update(LimboActivity.currMachine, MachineOpenHelper.FDA, file); - - new Handler(Looper.getMainLooper()).post(new Runnable() { - public void run() { - if (((ArrayAdapter) mFDA.getAdapter()).getPosition(file) < 0) { - ((ArrayAdapter) mFDA.getAdapter()).add(file); - } - setDiskValue(mFDA, file); - int res = mFDA.getSelectedItemPosition(); - if (res == 1) { - mFDA.setSelection(0); - } - } - }); - - } else if (file != null && !file.trim().equals("") && fileType == LimboActivity.FileType.FDB) { - update(LimboActivity.currMachine, MachineOpenHelper.FDB, file); - - new Handler(Looper.getMainLooper()).post(new Runnable() { - public void run() { - if (((ArrayAdapter) mFDB.getAdapter()).getPosition(file) < 0) { - ((ArrayAdapter) mFDB.getAdapter()).add(file); - } - setDiskValue(mFDB, file); - int res = mFDB.getSelectedItemPosition(); - if (res == 1) { - mFDB.setSelection(0); - - } - } - }); - - } - } - - - private void setDiskValue(Spinner spinner, String value) { - if (spinner != null) { - int pos = ((ArrayAdapter) spinner.getAdapter()).getPosition(value); - // Log.v("DB", "Got pos: " + pos + " for CDROM=" + cdrom); - if (pos > 1) { - spinner.setSelection(pos); - } else { - spinner.setSelection(0); - } - } else { - spinner.setSelection(0); - } - } - - private void addDriveToList(String file, LimboActivity.FileType type) { - // Check if exists - // Log.v(TAG, "Adding To list: " + type + ":" + file); - int res = FavOpenHelper.getInstance(activity).getFavSeq(file, type.toString().toLowerCase()); - if (res == -1) { - if (type == LimboActivity.FileType.CD) { - mCD.getAdapter().getCount(); - } else if (type == LimboActivity.FileType.FDA) { - mFDA.getAdapter().getCount(); - } else if (type == LimboActivity.FileType.FDB) { - mFDB.getAdapter().getCount(); - } - FavOpenHelper.getInstance(activity).insertFav(file, type.toString().toLowerCase()); - } - - } - - - public void update(final Machine myMachine, final String colname, final String value) { - - Thread thread = new Thread(new Runnable() { - public void run() { - MachineOpenHelper.getInstance(activity).update(myMachine, colname, value); - } - }); - thread.setPriority(Thread.MIN_PRIORITY); - thread.start(); - - } - -} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/FileManager.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/FileManager.java deleted file mode 100644 index 5192655a8..000000000 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/FileManager.java +++ /dev/null @@ -1,559 +0,0 @@ -/* -Copyright (C) Max Kastanas 2012 - - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -package com.max2idea.android.limbo.utils; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; - -import com.limbo.emu.lib.R; -import com.max2idea.android.limbo.main.Config; -import com.max2idea.android.limbo.main.LimboActivity; -import com.max2idea.android.limbo.main.LimboFileManager; -import com.max2idea.android.limbo.main.LimboSettingsManager; - -import android.Manifest; -import android.app.Activity; -import android.app.AlertDialog; -import android.app.ListActivity; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.net.Uri; -import android.os.Bundle; -import android.os.Environment; -import android.os.Handler; -import android.os.Looper; - -import android.provider.DocumentsContract; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.webkit.MimeTypeMap; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.TextView; - -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; - -/** - * @author thedoc - */ -public class FileManager extends ListActivity { - - private static final int REQUEST_WRITE_PERMISSION = 1001; - private final int SELECT_DIR = 1; - private final int CREATE_DIR = 2; - private final int CANCEL = 3; - - public Comparator comperator = new Comparator() { - - public int compare(String object1, String object2) { - if (object1.startsWith("..")) - return -1; - else if (object2.startsWith("..")) - return 1; - else if (object1.endsWith("/") && !object2.endsWith("/")) - return -1; - else if (!object1.endsWith("/") && object2.endsWith("/")) - return 1; - return object1.toString().compareToIgnoreCase(object2.toString()); - } - }; - private ArrayList items = null; - private File currdir = new File(Environment.getExternalStorageDirectory().getAbsolutePath()); - private File file; - private TextView currentDir; - private Button select; - private LimboActivity.FileType fileType; - private static String TAG = "FileManager"; - private HashMap filter = new HashMap<>(); - - //XXX: for now we dont use filters since file extensions on images is something not so standard - // and we don't want to hide files from the user - private boolean filter(File filePath) { - String ext = FileUtils.getExtensionFromFilename(filePath.getName()); - return (filter == null || filter.isEmpty() || filter.containsKey(ext.toLowerCase())); - } - private SelectionMode selectionMode = SelectionMode.FILE; - - public static void browse(Activity activity, LimboActivity.FileType fileType, int requestCode) { - - String lastDir = getLastDir(activity, fileType); - - String state = Environment.getExternalStorageState(); - if (!Environment.MEDIA_MOUNTED.equals(state)) { - UIUtils.toastShort(activity.getApplicationContext(), "Error: SD card is not mounted"); - UIUtils.toastShort(activity, "Error: SD card is not mounted"); - return; - } - - if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP // device is old - || LimboSettingsManager.getEnableLegacyFileManager(activity) // app configuration ASF is disallowed - || fileType == LimboActivity.FileType.SHARED_DIR //TODO: allow sd card access for SHARED DIR (called from c-jni and create the readdir() dirent structs) - ) { - LimboFileManager.promptLegacyStorageAccess(activity, fileType, requestCode, lastDir); - } else { // we use Android ASF to open the file (sd card supported) - try { -// if(true) -// throw new Exception("Test ASF missing exception"); - LimboFileManager.promptOpenFileASF(activity, fileType, getASFFileManagerRequestCode(requestCode), lastDir); - - } catch (Exception ex) { - Log.e(TAG, "Using Legacy File Manager due to exception :" + ex.getMessage()); - //XXX; some device vendors don't have proper Android Storage Framework so we fallback to legacy file manager (sd card not supported) - LimboFileManager.promptLegacyStorageAccess(activity, fileType, requestCode, lastDir); - } - } - } - - private static String getLastDir(Context context, LimboActivity.FileType fileType) { - if(fileType == LimboActivity.FileType.SHARED_DIR) { - return LimboSettingsManager.getSharedDir(context); - } else if(fileType == LimboActivity.FileType.EXPORT_DIR || fileType == LimboActivity.FileType.IMPORT_FILE) { - return LimboSettingsManager.getExportDir(context); - } else if(fileType == LimboActivity.FileType.IMAGE_DIR) { - return LimboSettingsManager.getImagesDir(context); - } - return LimboSettingsManager.getLastDir(context); - } - - private static int getASFFileManagerRequestCode(int requestCode) { - switch (requestCode) { - case Config.OPEN_IMAGE_FILE_REQUEST_CODE: - return Config.OPEN_IMAGE_FILE_ASF_REQUEST_CODE; - case Config.OPEN_IMAGE_DIR_REQUEST_CODE: - return Config.OPEN_IMAGE_DIR_ASF_REQUEST_CODE; - case Config.OPEN_SHARED_DIR_REQUEST_CODE: - return Config.OPEN_SHARED_DIR_ASF_REQUEST_CODE; - case Config.OPEN_EXPORT_DIR_REQUEST_CODE: - return Config.OPEN_EXPORT_DIR_ASF_REQUEST_CODE; - case Config.OPEN_IMPORT_FILE_REQUEST_CODE: - return Config.OPEN_IMPORT_FILE_ASF_REQUEST_CODE; - case Config.OPEN_LOG_FILE_DIR_REQUEST_CODE: - return Config.OPEN_LOG_FILE_DIR_ASF_REQUEST_CODE; - default: - return requestCode; - } - } - - public static void promptLegacyStorageAccess(Activity activity, LimboActivity.FileType fileType, int requestCode, String lastDir) { - - String dir = null; - try { - - HashMap filterExt = getFileExt(fileType); - - Intent i = null; - i = getFileManIntent(activity); - Bundle b = new Bundle(); - if(lastDir!=null && !lastDir.startsWith("content://")) - b.putString("lastDir", lastDir); - b.putSerializable("fileType", fileType); - b.putSerializable("filterExt", filterExt); - i.putExtras(b); - activity.startActivityForResult(i, requestCode); - - } catch (Exception e) { - Log.e(TAG, "Error while starting Filemanager: " + - e.getMessage()); - } - } - - - public static Intent getFileManIntent(Activity activity) { - return new Intent(activity, com.max2idea.android.limbo.main.LimboFileManager.class); - } - - protected static void promptOpenFileASF(Activity context, LimboActivity.FileType fileType, int requestCode, String lastDir) { - Intent intent = null; - if (isFileTypeDirectory(fileType)) - intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); - else - intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); - - intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - - intent.putExtra("android.content.extra.SHOW_ADVANCED", true); - - intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); - - //TODO: is there another way to get this object in the result? -// intent.getExtras().putSerializable("fileType", fileType); - - if (!isFileTypeDirectory(fileType)) { - String[] fileMimeTypes = getFileMimeTypes(fileType); - if (fileMimeTypes != null) { - for (String fileMimeType : fileMimeTypes) { - intent.setType(fileMimeType); - } - } - } - - - if(lastDir!=null && lastDir.startsWith("content://")) { - Uri uri = Uri.parse(lastDir); - intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uri); - } - - context.startActivityForResult(intent, requestCode); - } - - private static boolean isFileTypeDirectory(LimboActivity.FileType fileType) { - return (fileType == LimboActivity.FileType.SHARED_DIR || fileType == LimboActivity.FileType.EXPORT_DIR - || fileType == LimboActivity.FileType.IMAGE_DIR || fileType == LimboActivity.FileType.LOG_DIR); - - } - - private static String[] getFileMimeTypes(LimboActivity.FileType fileType) { - if(fileType == LimboActivity.FileType.IMPORT_FILE) - return new String[]{MimeTypeMap.getSingleton().getMimeTypeFromExtension("csv")}; - else - return new String[] {"*/*"}; - } - - - private static HashMap getFileExt(LimboActivity.FileType fileType) { - HashMap filterExt = new HashMap<>(); - - if(fileType == LimboActivity.FileType.IMPORT_FILE) - filterExt.put("csv","csv"); - - return filterExt; - } - - public static String getMimeType(String url) { - String type = null; - String extension = MimeTypeMap.getFileExtensionFromUrl(url); - if (extension != null) { - type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); - } - return type; - } - - /** - * Called when the activity is first created. - */ - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - - setContentView(R.layout.directory_list); - select = (Button) findViewById(R.id.select_button); - select.setOnClickListener(new View.OnClickListener() { - public void onClick(View arg0) { - selectDir(); - } - }); - currentDir = (TextView) findViewById(R.id.currDir); - Bundle b = this.getIntent().getExtras(); - String lastDirectory = b.getString("lastDir"); - fileType = (LimboActivity.FileType) b.getSerializable("fileType"); - filter = (HashMap) b.getSerializable("filterExt"); - - if (isFileTypeDirectory(fileType)) - selectionMode = SelectionMode.DIRECTORY; - else { - selectionMode = SelectionMode.FILE; - select.setVisibility(View.GONE); - } - - if (selectionMode == SelectionMode.DIRECTORY) - setTitle("Select a Directory"); - else - setTitle("Select a File"); - - //set starting directory - if (lastDirectory == null) { - lastDirectory = Environment.getExternalStorageDirectory().getPath(); - } - currdir = new File(lastDirectory); - if (!currdir.isDirectory() || !currdir.exists()) { - lastDirectory = Environment.getExternalStorageDirectory().getPath(); - currdir = new File(lastDirectory); - } - currentDir.setText(currdir.getPath()); - - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - - checkPermissionsAndBrowse(); - } - },500); - - } - - private void checkPermissionsAndBrowse() { - if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) - != PackageManager.PERMISSION_GRANTED) { - if (ActivityCompat.shouldShowRequestPermissionRationale(this, - Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - UIUtils.UIAlert(this, "WRITE ACCESS", "Warning! Providing FULL ACCESS to the write disk is discouraged!\n\n" + - "You either request for Shared Access to the local drive or your device doesn't have Android Storage Framework implemented. " + - "If you understand the risks press OK to continue", 16, false, - "OK, I understand", new DialogInterface.OnClickListener(){ - - @Override - public void onClick(DialogInterface dialogInterface, int i) { - ActivityCompat.requestPermissions(FileManager.this, - new String [] { Manifest.permission.WRITE_EXTERNAL_STORAGE}, - REQUEST_WRITE_PERMISSION); - } - }, null, null, null, null); - - } else { - ActivityCompat.requestPermissions(this, - new String [] { Manifest.permission.WRITE_EXTERNAL_STORAGE}, - REQUEST_WRITE_PERMISSION); - } - } else { - fill(currdir.listFiles()); - } - } - - - @Override - public void onRequestPermissionsResult(int requestCode, - String permissions[], int[] grantResults) { - switch (requestCode) { - case REQUEST_WRITE_PERMISSION: { - if (grantResults.length > 0 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - fill(currdir.listFiles()); - } else { - UIUtils.toastShort(this, "Feature disabled"); - finish(); - } - return; - } - - } - } - - private void fill(File[] files) { - - - items = new ArrayList(); - items.add(".. (Parent Directory)"); - - if (files != null) { - for (File file1 : files) { - if (file1 != null) { - String filename = file1.getName(); - if (filename != null && file1.isFile() - && filter(file1) - ) { - items.add(filename); - } else if (filename != null && file1.isDirectory()) { - items.add(filename + "/"); - } - } - } - } - Collections.sort(items, comperator); - FileAdapter fileList = new FileAdapter(this, R.layout.dir_row, items); - setListAdapter(fileList); - - LimboSettingsManager.setLastDir(this, currdir.getAbsolutePath()); - } - - @Override - protected void onListItemClick(ListView l, View v, int position, long id) { - super.onListItemClick(l, v, position, id); - // int selectionRowID = (int) l.getSelectedItemId(); - int selectionRowID = (int) id; - if (selectionRowID == 0) { - fillWithParent(); - } else { - - file = new File(currdir.getPath() + "/" + items.get(selectionRowID)); - if (file == null) { - UIUtils.toastShort(this, "Access Denied: cannot retrieve directory"); - } else if (!file.isDirectory() && selectionMode == SelectionMode.DIRECTORY) { - UIUtils.toastShort(this, "Not a Directory"); - } else if (file.isDirectory()) { -currdir = file; - File[] files = file.listFiles(); - if (files != null) { - currentDir.setText(file.getPath()); - fill(files); - } else { - new AlertDialog.Builder(this).setTitle("Access Denied").setMessage("Cannot list directory").show(); - } - } else { - this.selectFile(); - } - - } - } - - private void fillWithParent() { - if (currdir.getPath().equalsIgnoreCase("/")) { - currentDir.setText(currdir.getPath()); - fill(currdir.listFiles()); - } else { - currdir = currdir.getParentFile(); - currentDir.setText(currdir.getPath()); - fill(currdir.listFiles()); - } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - menu.add(0, SELECT_DIR, 0, "Select Directory"); - menu.add(0, CREATE_DIR, 0, "Create Directory"); - menu.add(0, CANCEL, 0, "Cancel"); - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case SELECT_DIR: - selectDir(); - return true; - case CREATE_DIR: - promptCreateDir(this); - return true; - case CANCEL: - cancel(); - return true; - } - return false; - } - - - public void promptCreateDir(final Activity activity) { - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("New Directory"); - final EditText dirNameTextview = new EditText(activity); - dirNameTextview.setPadding(20, 20, 20, 20); - dirNameTextview.setEnabled(true); - dirNameTextview.setVisibility(View.VISIBLE); - dirNameTextview.setSingleLine(); - alertDialog.setView(dirNameTextview); - alertDialog.setCanceledOnTouchOutside(false); - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Create", (DialogInterface.OnClickListener) null); - - alertDialog.show(); - - Button button = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); - button.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - if (dirNameTextview.getText().toString().trim().equals("")) - UIUtils.toastShort(activity, "Directory name cannot be empty"); - else { - createDirectory(dirNameTextview.getText().toString()); - fill(currdir.listFiles()); - alertDialog.dismiss(); - } - } - }); - } - - private void createDirectory(String dirName) { - File dir = new File(currdir, dirName); - if(!dir.exists()) { - dir.mkdirs(); - } else { - UIUtils.toastShort(this, "Directory already exists!"); - } - } - - public void selectDir() { - Intent data = new Intent(); - Bundle bundle = new Bundle(); - bundle.putString("currDir", this.currdir.getPath()); - bundle.putSerializable("fileType", fileType); - data.putExtras(bundle); - setResult(Config.FILEMAN_RETURN_CODE, data); - finish(); - } - - public void selectFile() { - Intent data = new Intent(); - Bundle bundle = new Bundle(); - bundle.putString("currDir", this.currdir.getPath()); - bundle.putString("file", this.file.getPath()); - bundle.putSerializable("fileType", fileType); - data.putExtras(bundle); - setResult(Config.FILEMAN_RETURN_CODE, data); - finish(); - } - - private void cancel() { - Intent data = new Intent(); - data.putExtra("currDir", ""); - setResult(Config.FILEMAN_RETURN_CODE, data); - finish(); - } - - - public enum SelectionMode { - DIRECTORY, - FILE - } - - public class FileAdapter extends ArrayAdapter { - private final Context context; - private final ArrayList files; - - public FileAdapter(Context context, int layout, ArrayList files) { - super(context, layout, files); - this.context = context; - this.files = files; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - LayoutInflater inflater = (LayoutInflater) context - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - View rowView = inflater.inflate(R.layout.dir_row, parent, false); - TextView textView = (TextView) rowView.findViewById(R.id.FILE_NAME); - ImageView imageView = (ImageView) rowView.findViewById(R.id.FILE_ICON); - textView.setText(files.get(position)); - - int iconRes = 0; - if (files.get(position).startsWith("..") || files.get(position).endsWith("/")) - imageView.setImageResource(R.drawable.folder); - else { - iconRes = FileUtils.getIconForFile(files.get(position)); - imageView.setImageResource(iconRes); - } - return rowView; - } - } - -} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/FileUtils.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/FileUtils.java deleted file mode 100644 index e97861c10..000000000 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/FileUtils.java +++ /dev/null @@ -1,908 +0,0 @@ -/* -Copyright (C) Max Kastanas 2012 - - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -package com.max2idea.android.limbo.utils; - -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.os.ParcelFileDescriptor; -import androidx.documentfile.provider.DocumentFile; -import android.text.Spannable; -import android.util.Log; -import android.webkit.MimeTypeMap; - -import com.limbo.emu.lib.R; -import com.max2idea.android.limbo.main.Config; -import com.max2idea.android.limbo.main.LimboActivity; - -/** - * @author dev - */ -public class FileUtils { - private final static String TAG = "FileUtils"; - public static HashMap fds = new HashMap(); - - public static String getNativeLibDir(Context context) { - return context.getApplicationInfo().nativeLibraryDir; - } - - public static String getFullPathFromDocumentFilePath(String filePath) { - - filePath = filePath.replaceAll("%3A", "^3A"); - int index = filePath.lastIndexOf("^3A"); - if (index > 0) - filePath = filePath.substring(index + 3); - if (!filePath.startsWith("/")) - filePath = "/" + filePath; - -// filePath = filePath.replaceAll("%2F", "/"); -// filePath = filePath.replaceAll("\\^2F", "/"); - - //remove any spaces encoded by the ASF - try { - filePath = URLDecoder.decode(filePath, "UTF-8"); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - - return filePath; - } - - public static String getFilenameFromPath(String filePath) { - filePath = filePath.replaceAll("%2F", "/"); - filePath = filePath.replaceAll("%3A", "/"); - filePath = filePath.replaceAll("\\^2F", "/"); - filePath = filePath.replaceAll("\\^3A", "/"); - - - int index = filePath.lastIndexOf("/"); - if (index > 0) - return filePath.substring(index + 1); - return filePath; - } - - public static String unconvertDocumentFilePath(String filePath) { - if (filePath != null && filePath.startsWith("/content//")) { - filePath = filePath.replace("/content//", "content://"); - filePath = filePath.replaceAll("\\^\\^\\^", "%"); - } - return filePath; - } - - public static String convertDocumentFilePath(String filePath) { - if (filePath != null && filePath.startsWith("content://")) { - filePath = filePath.replace("content://", "/content//"); - filePath = filePath.replaceAll("%", "\\^\\^\\^");; - - } - return filePath; - } - - public static void saveFileContents(String filePath, String contents) { - // TODO: we assume that the contents are of small size so we keep in an array - byteArrayToFile(contents.getBytes(), new File(filePath)); - } - - public static void byteArrayToFile(byte[] byteData, File filePath) { - - try { - FileOutputStream fos = new FileOutputStream(filePath); - fos.write(byteData); - fos.close(); - - } catch (FileNotFoundException ex) { - System.out.println("FileNotFoundException : " + ex); - } catch (IOException ioe) { - System.out.println("IOException : " + ioe); - } - - } - - public static ArrayList getVMsFromFile(Context context, String importFilePath) { - // TODO Auto-generated method stub - ArrayList machines = new ArrayList(); - BufferedReader buffreader = null; - InputStream instream = null; - // Read machines from csv file - try { - // open the file for reading - Log.v("CSV Parser", "Import file: " + importFilePath); - instream = getStreamFromFilePath(context, importFilePath); - - // if file the available for reading - if (instream != null) { - // prepare the file for reading - InputStreamReader inputreader = new InputStreamReader(instream); - buffreader = new BufferedReader(inputreader); - - String line; - - String[] machineAttrNames = {"MACHINE_NAME", "UI", "PAUSED", "ARCH", "MACHINETYPE", "CPU", "CPUNUM", - "MEMORY", // Arch - "HDA", "HDB", "HDC", "HDD", "SHARED_FOLDER", "SHARED_FOLDER_MODE", // Storage - "CDROM", "FDA", "FDB", "SD", // Removable media - "VGA", "SOUNDCARD", "NETCONFIG", "NICCONFIG", "HOSTFWD", "GUESTFWD", // Misc - "SNAPSHOT_NAME", "HDCONFIG", "ENABLE_USBMOUSE", // Deprecated - "BOOT_CONFIG", "KERNEL", "INITRD", "APPEND", // Boot - // Settings - "DISABLE_ACPI", "DISABLE_HPET", "DISABLE_FD_BOOT_CHK", // Other - "EXTRA_PARAMS" // Extra Params - }; - - Hashtable attrs = new Hashtable(); - - // read every line of the file into the line-variable, on line - // at the time - // Parse headers - line = buffreader.readLine(); - String headers[] = line.split(","); - for (int i = 0; i < headers.length; i++) { - attrs.put(i, headers[i].replace("\"", "")); - } - - while (line != null) { - line = buffreader.readLine(); - if (line == null) - break; - // do something with the line - // Log.v("CSV Parser", "Line: " + line); - - // Parse - String machineAttr[] = line.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1); - Machine mach = new Machine(machineAttr[0]); - for (int i = 0; i < machineAttr.length; i++) { - String attr = null; - if (machineAttr[i].equals("\"null\"")) { - continue; - } - - if (attrs.get(i).equals("MACHINE_NAME")) - mach.machinename = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals("UI")) - mach.ui = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals("PAUSED")) - mach.paused = Integer.parseInt(machineAttr[i].replace("\"", "")); - - // Arch - else if (attrs.get(i).equals("ARCH")) - mach.arch = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals("MACHINETYPE")) - mach.machine_type = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals("CPU")) - mach.cpu = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals("CPUNUM")) - mach.cpuNum = Integer.parseInt(machineAttr[i].replace("\"", "")); - else if (attrs.get(i).equals("MEMORY")) - mach.memory = Integer.parseInt(machineAttr[i].replace("\"", "")); - - // Storage - else if (attrs.get(i).equals(MachineOpenHelper.HDA)) - mach.hda_img_path = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals(MachineOpenHelper.HDB)) - mach.hdb_img_path = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals(MachineOpenHelper.HDC)) - mach.hdc_img_path = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals(MachineOpenHelper.HDD)) - mach.hdd_img_path = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals(MachineOpenHelper.SHARED_FOLDER)) - mach.shared_folder = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals(MachineOpenHelper.SHARED_FOLDER_MODE)) - mach.shared_folder_mode = Integer.parseInt(machineAttr[i].replace("\"", "")); - - // Removable Media - else if (attrs.get(i).equals(MachineOpenHelper.CDROM)) - mach.cd_iso_path = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals(MachineOpenHelper.FDA)) - mach.fda_img_path = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals(MachineOpenHelper.FDB)) - mach.fdb_img_path = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals(MachineOpenHelper.SD)) - mach.sd_img_path = machineAttr[i].replace("\"", ""); - - // Misc - else if (attrs.get(i).equals(MachineOpenHelper.VGA)) - mach.vga_type = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals(MachineOpenHelper.SOUNDCARD_CONFIG)) - mach.soundcard = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals(MachineOpenHelper.NET_CONFIG)) - mach.net_cfg = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals(MachineOpenHelper.NIC_CONFIG)) - mach.nic_card = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals(MachineOpenHelper.HOSTFWD)) - mach.hostfwd = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals(MachineOpenHelper.GUESTFWD)) - mach.guestfwd = machineAttr[i].replace("\"", ""); - - // Depreacated - else if (attrs.get(i).equals(MachineOpenHelper.HDCACHE_CONFIG)) - mach.hd_cache = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals(MachineOpenHelper.SNAPSHOT_NAME)) - mach.snapshot_name = machineAttr[i].replace("\"", ""); - // Other - else if (attrs.get(i).equals(MachineOpenHelper.DISABLE_ACPI)) - mach.disableacpi = Integer.parseInt(machineAttr[i].replace("\"", "")); - else if (attrs.get(i).equals(MachineOpenHelper.DISABLE_HPET)) - mach.disablehpet = Integer.parseInt(machineAttr[i].replace("\"", "")); - else if (attrs.get(i).equals(MachineOpenHelper.DISABLE_TSC)) - mach.disabletsc = Integer.parseInt(machineAttr[i].replace("\"", "")); - else if (attrs.get(i).equals(MachineOpenHelper.DISABLE_FD_BOOT_CHK)) - mach.disablefdbootchk = Integer.parseInt(machineAttr[i].replace("\"", "")); - // else if(attrs.get(i).equals("DISABLE_TSC")) - // mach.disablefdbootchk=Integer.parseInt(machineAttr[i].replace("\"", - // "")); - // else - // if(attrs.get(i).equals("ENABLE_USBMOUSE")) - // //Disable for now - - // Boot Settings - else if (attrs.get(i).equals(MachineOpenHelper.BOOT_CONFIG)) - mach.bootdevice = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals(MachineOpenHelper.KERNEL)) - mach.kernel = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals(MachineOpenHelper.INITRD)) - mach.initrd = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals(MachineOpenHelper.APPEND)) - mach.append = machineAttr[i].replace("\"", ""); - - // Extra Params - else if (attrs.get(i).equals(MachineOpenHelper.EXTRA_PARAMS)) - mach.extra_params = machineAttr[i].replace("\"", ""); - - else if (attrs.get(i).equals(MachineOpenHelper.MOUSE)) - mach.mouse = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals(MachineOpenHelper.KEYBOARD)) - mach.keyboard = machineAttr[i].replace("\"", ""); - else if (attrs.get(i).equals(MachineOpenHelper.ENABLE_MTTCG)) - mach.enableMTTCG = Integer.parseInt(machineAttr[i].replace("\"", "")); - else if (attrs.get(i).equals(MachineOpenHelper.ENABLE_KVM)) - mach.enableKVM = Integer.parseInt(machineAttr[i].replace("\"", "")); - - } - Log.v("CSV Parser", "Adding Machine: " + mach.machinename); - machines.add(mach); - } - - } - } catch (Exception ex) { - ex.printStackTrace(); - } finally { - - try { - if (buffreader != null) - buffreader.close(); - - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - try { - if (instream != null) - instream.close(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - return machines; - } - - public static InputStream getStreamFromFilePath(Context context, String importFilePath) throws FileNotFoundException { - InputStream stream = null; - if (importFilePath.startsWith("content://")) { - Uri uri = Uri.parse(importFilePath); - String mode = "rw"; - ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, mode); - return new FileInputStream(pfd.getFileDescriptor()); - } else { - return new FileInputStream(importFilePath); - } - } - - public static String getFileContents(String filePath) { - - File file = new File(filePath); - if(!file.exists()) - return ""; - StringBuilder builder = new StringBuilder(""); - try { - FileInputStream stream = new FileInputStream(file); - byte[] buff = new byte[32768]; - int bytesRead = 0; - while ((bytesRead = stream.read(buff, 0, buff.length)) > 0) { - builder.append(new String(buff, "UTF-8")); - } - } catch (Exception e) { - e.printStackTrace(); - } - - String contents = builder.toString(); - return contents; - } - - public static void viewLimboLog(final Activity activity) { - - String contents = FileUtils.getFileContents(Config.logFilePath); - - if (contents.length() > 50 * 1024) - contents = contents.substring(0, 25 * 1024) - + "\n.....\n" + - contents.substring(contents.length() - 25 * 1024); - - final String finalContents = contents; - final Spannable contentsFormatted = UIUtils.formatAndroidLog(contents); - - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - - if (Config.viewLogInternally) { - UIUtils.UIAlertLog(activity, "Limbo Log", contentsFormatted); - } else { - try { - Intent intent = new Intent(Intent.ACTION_EDIT); - File file = new File(Config.logFilePath); - Uri uri = Uri.fromFile(file); - intent.setDataAndType(uri, "text/plain"); - activity.startActivity(intent); - } catch (Exception ex) { -// UIUtils.toastShort(activity, "Could not find a Text Viewer on your device"); - UIUtils.UIAlertLog(activity, "Limbo Log", contentsFormatted); - } - } - - - } - }); - - } - - public static boolean fileValid(Context context, String path) { - - if (path == null || path.equals("")) - return true; - if (path.startsWith("content://") || path.startsWith("/content/")) { - int fd = get_fd(context, path); - if (fd <= 0) - return false; - } else { - File file = new File(path); - return file.exists(); - } - return true; - } - - //TODO: we should pass the modes from the backend and translate them - // instead of blindly using "rw". ie ISOs should be read only. - public static int get_fd(final Context context, String path) { - synchronized (fds) { - int fd = 0; - if (path == null) - return 0; - -// Log.d(TAG, "Opening Filepath: " + path); - if (path.startsWith("/content//") || path.startsWith("content://")) { - String npath = unconvertDocumentFilePath(path); - -//Is this needed? -// FileInfo info = getExistingFd(npath); -// if (info!=null) { -// ParcelFileDescriptor pfd = info.pfd; -// fd = pfd.getFd(); -// Log.d(TAG, "Retrieved hashed documentfile: " + npath + ", FD: " + fd); -// return fd; -// } - - -// Log.d(TAG, "Opening unconverted: " + npath); - try { - Uri uri = Uri.parse(npath); - String mode = "rw"; - if (path.toLowerCase().endsWith(".iso")) - mode = "r"; - ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, mode); - fd = pfd.getFd(); -// Log.d(TAG, "Opening DocumentFile: " + npath + ", FD: " + fd); - fds.put(fd, new FileInfo(path, npath, pfd)); - - } catch (Exception e) { - Log.e(TAG, "Could not open DocumentFile: " + npath + ", FD: " + fd); - if(Config.debug) - e.printStackTrace(); - } - } else { - //Is this needed? -// FileInfo info = getExistingFd(path); -// if (info!=null) { -// ParcelFileDescriptor pfd = info.pfd; -// fd = pfd.getFd(); -// Log.d(TAG, "Retrieved hashed file: " + path + ", FD: " + fd); -// return fd; -// } - - try { - int mode = ParcelFileDescriptor.MODE_READ_WRITE; - if (path.toLowerCase().endsWith(".iso")) - mode = ParcelFileDescriptor.MODE_READ_ONLY; - - File file = new File(path); - if (!file.exists()) - file.createNewFile(); - ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, mode); - fd = pfd.getFd(); - fds.put(fd, new FileInfo(path, path, pfd)); - Log.d(TAG, "Opening File: " + path + ", FD: " + fd); - } catch (Exception e) { - Log.e(TAG, "Could not open File: " + path + ", FD: " + fd); - if(Config.debug) - e.printStackTrace(); - } - - } - return fd; - } - } - - private static FileInfo getExistingFd(String npath) { - Set> fileInfoSet = fds.entrySet(); - Iterator> iter = fileInfoSet.iterator(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - FileInfo fileInfo = entry.getValue(); - if (fileInfo.npath.equals(npath)) { - return fileInfo; - } - } - return null; - } - - public static void close_fds() { - synchronized (fds) { - Integer[] fds = FileUtils.fds.keySet().toArray(new Integer[FileUtils.fds.keySet().size()]); - for (int i = 0; i < fds.length; i++) { - FileUtils.close_fd(fds[i]); - } - } - } - - public static int close_fd(int fd) { - if(!Config.closeFileDescriptors) { - return 0; - } - synchronized (fds) { -// Log.d(TAG, "Closing FD: " + fd); - if (FileUtils.fds.containsKey(fd)) { - - FileInfo info = FileUtils.fds.get(fd); - - - try { - - ParcelFileDescriptor pfd = info.pfd; - try { - pfd.getFileDescriptor().sync(); - } catch (IOException e) { - if(Config.debug) { - Log.w(TAG, "Syncing DocumentFile: " + info.path + ": " + fd + " : " + e); - e.printStackTrace(); - } - } - -// Log.d(TAG, "Closing DocumentFile: " + info.npath + ", FD: " + fd); - pfd.close(); - FileUtils.fds.remove(fd); - return 0; // success for Native side - } catch (IOException e) { - Log.e(TAG, "Error Closing DocumentFile: " + info.path + ": " + fd + " : " + e); - if(Config.debug) - e.printStackTrace(); - } - - - } else { - - ParcelFileDescriptor pfd = null; - String path = "unknown"; - try { - - //xxx: check the hash - FileInfo info = FileUtils.fds.get(fd); - if(info!=null) { - pfd = info.pfd; - path = info.path; -// Log.d(TAG, "Closing hashe File FD: " + fd + ": " + info.path); - } - - //xxx: else get a new parcel - if(pfd == null) - pfd = ParcelFileDescriptor.fromFd(fd); - -// Log.d(TAG, "Closing File FD: " + fd); - try { - pfd.getFileDescriptor().sync(); - } catch (IOException e) { - if(Config.debug) { - Log.e(TAG, "Error Syncing File: " + path + ": " + fd + " : " + e); - e.printStackTrace(); - } - } - - pfd.close(); - return 0; - } catch (Exception e) { - Log.e(TAG, "Error Closing File FD: " + path + ": " + fd + " : " + e); - if(Config.debug) - e.printStackTrace(); - } - } - return -1; - } - } - - public static void startLogging() { - - if (Config.logFilePath == null) { - Log.e(TAG, "Log file is not setup"); - return; - } - - Thread t = new Thread(new Runnable() { - public void run() { - - FileOutputStream os = null; - File logFile = null; - try { - logFile = new File(Config.logFilePath); - if (logFile.exists()) { - logFile.delete(); - } - logFile.createNewFile(); - Runtime.getRuntime().exec("logcat -c"); - Process process = Runtime.getRuntime().exec("logcat v main"); - os = new FileOutputStream(logFile); - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream())); - - StringBuilder log = new StringBuilder(""); - String line = ""; - while ((line = bufferedReader.readLine()) != null) { - log.setLength(0); - log.append(line).append("\n"); - os.write(log.toString().getBytes("UTF-8")); - os.flush(); - } - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - if (os != null) { - os.flush(); - os.close(); - } - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - } - - } - }); - t.setName("LimboLogger"); - t.start(); - } - - public static String LoadFile(Activity activity, String fileName, boolean loadFromRawFolder) throws IOException { - // Create a InputStream to read the file into - InputStream iS; - if (loadFromRawFolder) { - // get the resource id from the file name - int rID = activity.getResources().getIdentifier(activity.getClass().getPackage().getName() + ":raw/" + fileName, - null, null); - // get the file as a stream - iS = activity.getResources().openRawResource(rID); - } else { - // get the file as a stream - iS = activity.getResources().getAssets().open(fileName); - } - - ByteArrayOutputStream oS = new ByteArrayOutputStream(); - byte[] buffer = new byte[iS.available()]; - int bytesRead = 0; - while ((bytesRead = iS.read(buffer)) > 0) { - oS.write(buffer); - } - oS.close(); - iS.close(); - - // return the output stream as a String - return oS.toString(); - } - - public static String getExtensionFromFilename(String fileName) { - if (fileName == null) - return ""; - - int index = fileName.lastIndexOf("."); - if (index >= 0) { - return fileName.substring(index + 1); - } else - return ""; - } - - public static int getIconForFile(String file) { - file = file.toLowerCase(); - String ext = FileUtils.getExtensionFromFilename(file).toLowerCase(); - - if(ext.equals("img") || ext.equals("qcow") - || ext.equals("qcow2") || ext.equals("vmdk") || ext.equals("vdi") || ext.equals("cow") - || ext.equals("dmg") || ext.equals("bochs") || ext.equals("vpc") - || ext.equals("vhd") || ext.equals("fs")) - return R.drawable.harddisk; - else if(ext.equals("iso")) - return R.drawable.cd; - else if(ext.equals("ima")) - return R.drawable.floppy; - else if(ext.equals("csv")) - return R.drawable.importvms; - else if(file.contains("kernel") || file.contains("vmlinuz") || file.contains("initrd")) - return R.drawable.sysfile; - else - return R.drawable.close; - - } - - - - - public static Uri saveLogFileSDCard(Activity activity, - Uri destDir) { - - DocumentFile destFileF = null; - OutputStream os = null; - Uri uri = null; - - try { - String logFileContents = getFileContents(Config.logFilePath); - DocumentFile dir = DocumentFile.fromTreeUri(activity, destDir); - - //Create the file if doesn't exist - destFileF = dir.findFile(Config.destLogFilename); - if(destFileF == null) { - destFileF = dir.createFile(MimeTypeMap.getSingleton().getMimeTypeFromExtension("txt"), Config.destLogFilename); - } - - //Write to the dest - os = activity.getContentResolver().openOutputStream(destFileF.getUri()); - os.write(logFileContents.getBytes()); - - //success - uri = destFileF.getUri(); - - } catch (Exception ex) { - UIUtils.toastShort(activity, "Failed to save log file: " + ex.getMessage()); - } finally { - if(os!=null) { - try { - os.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - } - return uri; - } - - public static Uri exportFileSDCard(Activity activity, - Uri destDir, String destFile) { - - DocumentFile destFileF = null; - OutputStream os = null; - Uri uri = null; - - try { - String machinesToExport = MachineOpenHelper.getInstance(activity).exportMachines(); - DocumentFile dir = DocumentFile.fromTreeUri(activity, destDir); - - //Create the file if doesn't exist - destFileF = dir.findFile(destFile); - if(destFileF == null) { - destFileF = dir.createFile(MimeTypeMap.getSingleton().getMimeTypeFromExtension("csv"), destFile); - } - else { - UIUtils.toastShort(activity, "File exists, choose another filename"); - return null; - } - - //Write to the dest - os = activity.getContentResolver().openOutputStream(destFileF.getUri()); - os.write(machinesToExport.getBytes()); - - //success - uri = destFileF.getUri(); - - } catch (Exception ex) { - UIUtils.toastShort(activity, "Failed to export file: " + destFile + ", Error:" + ex.getMessage()); - } finally { - if(os!=null) { - try { - os.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - } - return uri; - } - - public static String exportFileLegacy(Activity activity, - String destDir, String destFile) { - - String filePath = null; - File destFileF = new File(destDir, destFile); - - try { - String machinesToExport = MachineOpenHelper.getInstance(activity).exportMachines(); - FileUtils.saveFileContents(destFileF.getAbsolutePath(), machinesToExport); - - //success - filePath = destFileF.getAbsolutePath(); - - } catch (Exception ex) { - UIUtils.toastShort(activity, "Failed to export file: " + destFileF.getAbsolutePath() + ", Error:" + ex.getMessage()); - } finally { - } - return filePath; - } - - - public static String saveLogFileLegacy(Activity activity, - String destLogFilePath) { - - String filePath = null; - File destFileF = new File(destLogFilePath, Config.destLogFilename); - - try { - String logFileContents = getFileContents(Config.logFilePath); - FileUtils.saveFileContents(destFileF.getAbsolutePath(), logFileContents); - - //success - filePath = destFileF.getAbsolutePath(); - - } catch (Exception ex) { - UIUtils.toastShort(activity, "Failed to save log file: " + destFileF.getAbsolutePath() + ", Error:" + ex.getMessage()); - } finally { - } - return filePath; - } - - - public static String getFileUriFromIntent(Activity activity, Intent data, boolean write) { - if(data == null) - return null; - - Uri uri = data.getData(); - DocumentFile pickedFile = DocumentFile.fromSingleUri(activity, uri); - String file = uri.toString(); - if (!file.contains("com.android.externalstorage.documents")) { - UIUtils.showFileNotSupported(activity); - return null; - } - activity.grantUriPermission(activity.getPackageName(), uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); - if(write) - activity.grantUriPermission(activity.getPackageName(), uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - activity.grantUriPermission(activity.getPackageName(), uri, Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); - - int takeFlags = data.getFlags() & Intent.FLAG_GRANT_READ_URI_PERMISSION; - if(write) - takeFlags = takeFlags | Intent.FLAG_GRANT_WRITE_URI_PERMISSION; - - activity.getContentResolver().takePersistableUriPermission(uri, takeFlags); - return file; - } - - public static String getDirPathFromIntent(Activity activity, Intent data) { - if(data == null) - return null; - Bundle b = data.getExtras(); - String file = b.getString("currDir"); - return file; - } - - - public static String getFilePathFromIntent(Activity activity, Intent data) { - if(data == null) - return null; - Bundle b = data.getExtras(); - String file = b.getString("file"); - return file; - } - - public static LimboActivity.FileType getFileTypeFromIntent(Activity activity, Intent data) { - if(data == null) - return null; - Bundle b = data.getExtras(); - LimboActivity.FileType fileType = (LimboActivity.FileType) b.getSerializable("fileType"); - return fileType; - } - - public static class FileInfo { - public String path; - public String npath; - public ParcelFileDescriptor pfd; - - public FileInfo(String path, String npath, ParcelFileDescriptor pfd) { - this.npath = npath; - this.path = path; - this.pfd = pfd; - } - } - - - - public static void saveLogToFile(final Activity activity, final String logFileDestDir) { - - - Thread t = new Thread(new Runnable() { - public void run() { - - String displayName = null; - if (logFileDestDir.startsWith("content://")) { - Uri exportDirUri = Uri.parse(logFileDestDir); - Uri fileCreatedUri = FileUtils.saveLogFileSDCard(activity, - exportDirUri); - displayName = FileUtils.getFullPathFromDocumentFilePath(fileCreatedUri.toString()); - } else { - String filePath = FileUtils.saveLogFileLegacy(activity, logFileDestDir); - displayName = filePath; - } - - if(displayName!=null){ - - UIUtils.toastShort(activity, "Logfile saved"); - } - } - }); - t.start(); - } - - -} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/Machine.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/Machine.java deleted file mode 100644 index 6d1a09f53..000000000 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/Machine.java +++ /dev/null @@ -1,326 +0,0 @@ -/* - Copyright (C) Max Kastanas 2012 - - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -package com.max2idea.android.limbo.utils; - -import com.max2idea.android.limbo.main.Config; -import com.max2idea.android.limbo.main.LimboActivity; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.os.Build; -import android.os.Handler; -import android.os.Looper; -import android.preference.PreferenceManager; -import android.util.Log; - -import org.json.JSONException; -import org.json.JSONObject; - -public class Machine { - - public static final String EMPTY = "empty"; - public static String TAG = "Machine"; - - public String machinename; - - public String snapshot_name = ""; - - public String ui = "VNC"; - public String keyboard="en-us"; - public String mouse = null; - public int enablespice; - public int enablevnc = 1; - - public String arch; - public String machine_type; - public String cpu = "Default"; - public int cpuNum = 1; - public int memory = 128; - public int enableMTTCG; - public int enableKVM; - public int disableacpi = 0; - public int disablehpet = 0; - public int disablefdbootchk = 0; - public int disabletsc = 1; //disabling TSC by default - - // Storage - public String hda_img_path; - public String hdb_img_path; - public String hdc_img_path; - public String hdd_img_path; - public String hd_cache = "default"; - public String shared_folder; - public int shared_folder_mode; - - //Removable devices - public boolean enableCDROM; - public boolean enableFDA; - public boolean enableFDB; - public boolean enableSD; - public String cd_iso_path; - public String fda_img_path; - public String fdb_img_path; - public String sd_img_path; - - // Default Settings - public String bootdevice = "Default"; - public String kernel; - public String initrd; - public String append; - - // net - public String net_cfg = "None"; - public String nic_card = "ne2k_pci"; - public String guestfwd; - public String hostfwd; - - //display - public String vga_type = "std"; - - //sound - public String soundcard = "None"; - - //extra qemu params - public String extra_params; - - public int status = Config.STATUS_NULL; - public String lib = "liblimbo.so"; - public String lib_path = "libqemu-system-i386.so"; - public int enableqmp = 1; - public int restart = 0; - public int paused; - - public Machine(String machinename) { - this.machinename = machinename; - } - - - public static void promptPausedVM(final Activity activity) { - - new AlertDialog.Builder(activity).setCancelable(false).setTitle("Paused").setMessage("VM is now Paused tap OK to exit") - .setPositiveButton("OK", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - - Log.i(TAG, "VM Paused, Shutting Down"); - if (activity.getParent() != null) { - activity.getParent().finish(); - } else { - activity.finish(); - } - - if (LimboActivity.vmexecutor != null) { - LimboActivity.vmexecutor.stopvm(0); - } - } - }).show(); - } - - - public static void onRestartVM(final Context context) { - Thread t = new Thread(new Runnable() { - public void run() { - if (LimboActivity.vmexecutor != null) { - Log.v(TAG, "Restarting the VM..."); - LimboActivity.vmexecutor.stopvm(1); - - LimboActivity.vmStarted = true; - if(Config.showToast) - UIUtils.toastShort(context, "VM Reset"); - - } else { - if(Config.showToast) - UIUtils.toastShort(context, "VM Not Running"); - } - } - }); - t.start(); - } - - public static void pausedErrorVM(Activity activity, String errStr) { - - errStr = errStr != null ? errStr : "Could not pause VM. View log for details"; - - new AlertDialog.Builder(activity).setTitle("Error").setMessage(errStr) - .setPositiveButton("OK", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - - Thread t = new Thread(new Runnable() { - public void run() { - String command = QmpClient.cont(); - String msg = QmpClient.sendCommand(command); - } - }); - t.start(); - } - }).show(); - } - - public static void stopVM(final Activity activity) { - - new AlertDialog.Builder(activity).setTitle("Shutdown VM") - .setMessage("To avoid any corrupt data make sure you " - + "have already shutdown the Operating system from within the VM. Continue?") - .setPositiveButton("Yes", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - if (activity.getParent() != null) { - activity.getParent().finish(); - } else { - activity.finish(); - } - - if (LimboActivity.vmexecutor != null) { - LimboActivity.vmexecutor.stopvm(0); - } - } - }).setNegativeButton("No", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - } - }).show(); - } - - public static LimboActivity.VMStatus checkSaveVMStatus(final Activity activity) { - String pause_state = ""; - if (LimboActivity.vmexecutor != null) { - - String command = QmpClient.query_migrate(); - String res = QmpClient.sendCommand(command); - - if (res != null && !res.equals("")) { - //Log.d(TAG, "Migrate status: " + res); - try { - JSONObject resObj = new JSONObject(res); - String resInfo = resObj.getString("return"); - JSONObject resInfoObj = new JSONObject(resInfo); - pause_state = resInfoObj.getString("status"); - } catch (JSONException e) { - if (Config.debug) - Log.e(TAG,e.getMessage()); - //e.printStackTrace(); - } - if (pause_state != null && pause_state.toUpperCase().equals("FAILED")) { - Log.e(TAG, "Error: " + res); - } - } - } - - if (pause_state.toUpperCase().equals("ACTIVE")) { - return LimboActivity.VMStatus.Saving; - } else if (pause_state.toUpperCase().equals("COMPLETED")) { - LimboActivity.vmexecutor.paused = 1; - LimboActivity.saveStateVMDB(); - - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - promptPausedVM(activity); - } - }, 1000); - return LimboActivity.VMStatus.Completed; - - } else if (pause_state.toUpperCase().equals("FAILED")) { - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { - @Override - public void run() { - pausedErrorVM(activity, null); - } - }, 100); - return LimboActivity.VMStatus.Failed; - } - return LimboActivity.VMStatus.Unknown; - } - - public static boolean isHostX86_64() { - if(Build.SUPPORTED_64_BIT_ABIS != null) - { - for(int i=0; i< Build.SUPPORTED_64_BIT_ABIS.length ; i++) - if(Build.SUPPORTED_64_BIT_ABIS[i].equals("x86_64")) - return true; - } - return false; - } - - public static boolean isHostX86() { - if(Build.SUPPORTED_32_BIT_ABIS != null) - { - for(int i=0; i< Build.SUPPORTED_32_BIT_ABIS.length ; i++) - if(Build.SUPPORTED_32_BIT_ABIS[i].equals("x86")) - return true; - } - return false; - } - - public static boolean isHostArm() { - if(Build.SUPPORTED_32_BIT_ABIS != null) - { - for(int i=0; i< Build.SUPPORTED_32_BIT_ABIS.length ; i++) - if(Build.SUPPORTED_32_BIT_ABIS[i].equals("armeabi-v7a")) - return true; - } - return false; - } - - public static boolean isHostArmv8() { - if(Build.SUPPORTED_64_BIT_ABIS != null) - { - for(int i=0; i< Build.SUPPORTED_64_BIT_ABIS.length ; i++) - if(Build.SUPPORTED_64_BIT_ABIS[i].equals("arm64-v8a")) - return true; - } - return false; - } - - public static boolean isHost64Bit() { - return Build.SUPPORTED_64_BIT_ABIS!=null && Build.SUPPORTED_64_BIT_ABIS.length > 0 ; - } - - - public static void resetVM(final Activity activity) { - - new AlertDialog.Builder(activity).setTitle("Reset VM") - .setMessage("To avoid any corrupt data make sure you " - + "have already shutdown the Operating system from within the VM. Continue?") - .setPositiveButton("Yes", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - new Thread(new Runnable() { - public void run() { - Log.v(TAG, "VM is reset"); - Machine.onRestartVM(activity); - } - }).start(); - - } - }).setNegativeButton("No", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - } - }).show(); - } - - public void insertMachineDB(Context context) { - int rows = MachineOpenHelper.getInstance(context).insertMachine(this); - Log.v(TAG, "Attempting insert to DB after rows = " + rows); - } - - public boolean hasRemovableDevices() { - - return enableCDROM || enableFDA || enableFDB || enableSD; - } -} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/MachineOpenHelper.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/MachineOpenHelper.java deleted file mode 100644 index 3ea8b837e..000000000 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/MachineOpenHelper.java +++ /dev/null @@ -1,514 +0,0 @@ -/* - Copyright (C) Max Kastanas 2012 - - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -package com.max2idea.android.limbo.utils; - -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; - -import com.max2idea.android.limbo.main.Config; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.util.Log; - -/** - * - * @author Dev - */ -public class MachineOpenHelper extends SQLiteOpenHelper { - - private static final int DATABASE_VERSION = 15; - private static final String DATABASE_NAME = "LIMBO"; - private static final String MACHINE_TABLE_NAME = "machines"; - - // Columns - public static final String MACHINE_NAME = "MACHINE_NAME"; - public static final String SNAPSHOT_NAME = "SNAPSHOT_NAME"; - public static final String ARCH = "ARCH"; - public static final String CPU = "CPU"; - public static final String CPUNUM = "CPUNUM"; - public static final String MEMORY = "MEMORY"; - public static final String KERNEL = "KERNEL"; - public static final String INITRD = "INITRD"; - public static final String APPEND = "APPEND"; - public static final String MACHINE_TYPE = "MACHINETYPE"; - public static final String CDROM = "CDROM"; - public static final String FDA = "FDA"; - public static final String FDB = "FDB"; - public static final String HDA = "HDA"; - public static final String HDB = "HDB"; - public static final String HDC = "HDC"; - public static final String HDD = "HDD"; - public static final String SD = "SD"; - public static final String SHARED_FOLDER = "SHARED_FOLDER"; - public static final String SHARED_FOLDER_MODE = "SHARED_FOLDER_MODE"; - public static final String BOOT_CONFIG = "BOOT_CONFIG"; - public static final String NET_CONFIG = "NETCONFIG"; - public static final String HOSTFWD = "HOSTFWD"; - public static final String GUESTFWD = "GUESTFWD"; - public static final String NIC_CONFIG = "NICCONFIG"; - public static final String VGA = "VGA"; - public static final String SOUNDCARD_CONFIG = "SOUNDCARD"; - public static final String HDCACHE_CONFIG = "HDCONFIG"; - public static final String DISABLE_ACPI = "DISABLE_ACPI"; - public static final String DISABLE_HPET = "DISABLE_HPET"; - public static final String DISABLE_TSC = "DISABLE_TSC"; - public static final String DISABLE_FD_BOOT_CHK = "DISABLE_FD_BOOT_CHK"; - public static final String ENABLE_USBMOUSE = "ENABLE_USBMOUSE"; - public static final String STATUS = "STATUS"; - public static final String LASTUPDATED = "LAST_UPDATED"; - public static final String PAUSED = "PAUSED"; - public static final String EXTRA_PARAMS = "EXTRA_PARAMS"; - public static final String UI = "UI"; - public static final String MOUSE = "MOUSE"; - public static final String KEYBOARD = "KEYBOARD"; - public static final String ENABLE_MTTCG = "ENABLE_MTTCG"; - public static final String ENABLE_KVM = "ENABLE_KVM"; - - // Create DDL - private static final String MACHINE_TABLE_CREATE = "CREATE TABLE IF NOT EXISTS " + MACHINE_TABLE_NAME + " (" - + MACHINE_NAME + " TEXT , " + SNAPSHOT_NAME + " TEXT , " + CPU + " TEXT, " + ARCH + " TEXT, " + MEMORY - + " TEXT, " + FDA + " TEXT, " + FDB + " TEXT, " + CDROM + " TEXT, " + HDA + " TEXT, " + HDB + " TEXT, " - + HDC + " TEXT, " + HDD + " TEXT, " + BOOT_CONFIG + " TEXT, " + NET_CONFIG + " TEXT, " + NIC_CONFIG - + " TEXT, " + VGA + " TEXT, " + SOUNDCARD_CONFIG + " TEXT, " + HDCACHE_CONFIG + " TEXT, " + DISABLE_ACPI - + " INTEGER, " + DISABLE_HPET + " INTEGER, " + ENABLE_USBMOUSE + " INTEGER, " + STATUS + " TEXT, " - + LASTUPDATED + " DATE, " + KERNEL + " INTEGER, " + INITRD + " TEXT, " + APPEND + " TEXT, " + CPUNUM - + " INTEGER, " + MACHINE_TYPE + " TEXT, " + DISABLE_FD_BOOT_CHK + " INTEGER, " + SD + " TEXT, " + PAUSED - + " INTEGER, " + SHARED_FOLDER + " TEXT, " + SHARED_FOLDER_MODE + " INTEGER, " + EXTRA_PARAMS + " TEXT, " - + HOSTFWD + " TEXT, " + GUESTFWD + " TEXT, " + UI + " TEXT, " + DISABLE_TSC + " INTEGER, " - + MOUSE + " TEXT, " + KEYBOARD + " TEXT, " + ENABLE_MTTCG + " INTEGER, " + ENABLE_KVM + " INTEGER " - + ");"; - - private String TAG = "MachineOpenHelper"; - - private static MachineOpenHelper sInstance; - private SQLiteDatabase db; - - - public MachineOpenHelper(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - getDB(); - } - - private synchronized void getDB() { - - if (db == null) - db = getWritableDatabase(); - } - - - public static synchronized MachineOpenHelper getInstance(Context context) { - - if (sInstance == null) { - sInstance = new MachineOpenHelper(context.getApplicationContext()); - sInstance.setWriteAheadLoggingEnabled(true); - } - return sInstance; - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL(MACHINE_TABLE_CREATE); - - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - - Log.w("machineOpenHelper", "Upgrading database from version " + oldVersion + " to " + newVersion); - - if (newVersion >= 3 && oldVersion <= 2) { - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + KERNEL + " TEXT;"); - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + INITRD + " TEXT;"); - - } - - if (newVersion >= 4 && oldVersion <= 3) { - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + CPUNUM + " TEXT;"); - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MACHINE_TYPE + " TEXT;"); - } - - if (newVersion >= 5 && oldVersion <= 4) { - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + HDC + " TEXT;"); - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + HDD + " TEXT;"); - } - - if (newVersion >= 6 && oldVersion <= 5) { - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + APPEND + " TEXT;"); - } - - if (newVersion >= 7 && oldVersion <= 6) { - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + DISABLE_FD_BOOT_CHK + " INTEGER;"); - } - - if (newVersion >= 8 && oldVersion <= 7) { - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + ARCH + " TEXT;"); - } - - if (newVersion >= 9 && oldVersion <= 8) { - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + SD + " TEXT;"); - } - - if (newVersion >= 10 && oldVersion <= 9) { - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + PAUSED + " INTEGER;"); - } - - if (newVersion >= 11 && oldVersion <= 10) { - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + SHARED_FOLDER + " TEXT;"); - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + SHARED_FOLDER_MODE + " INTEGER;"); - } - - if (newVersion >= 12 && oldVersion <= 11) { - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + EXTRA_PARAMS + " TEXT;"); - } - - if (newVersion >= 13 && oldVersion <= 12) { - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + HOSTFWD + " TEXT;"); - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + GUESTFWD + " TEXT;"); - } - - if (newVersion >= 14 && oldVersion <= 13) { - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + UI + " TEXT;"); - } - - if (newVersion >= 15 && oldVersion <= 14) { - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + DISABLE_TSC + " INTEGER;"); - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + MOUSE + " TEXT;"); - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + KEYBOARD + " TEXT;"); - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + ENABLE_MTTCG + " INTEGER;"); - db.execSQL("ALTER TABLE " + MACHINE_TABLE_NAME + " ADD COLUMN " + ENABLE_KVM + " INTEGER;"); - } - } - - public synchronized int insertMachine(Machine machine) { - int seqnum = -1; - SQLiteDatabase db = getWritableDatabase(); - - Log.v("DB", "insert machine: " + machine.machinename); - ContentValues stateValues = new ContentValues(); - stateValues.put(MACHINE_NAME, machine.machinename); - stateValues.put(SNAPSHOT_NAME, machine.snapshot_name); - stateValues.put(CPU, machine.cpu); - stateValues.put(CPUNUM, machine.cpuNum); - stateValues.put(MEMORY, machine.memory); - stateValues.put(HDA, machine.hda_img_path); - stateValues.put(HDB, machine.hdb_img_path); - stateValues.put(HDC, machine.hdc_img_path); - stateValues.put(HDD, machine.hdd_img_path); - stateValues.put(CDROM, machine.cd_iso_path); - stateValues.put(FDA, machine.fda_img_path); - stateValues.put(FDB, machine.fdb_img_path); - stateValues.put(SHARED_FOLDER, machine.shared_folder); - stateValues.put(SHARED_FOLDER_MODE, machine.shared_folder_mode); - stateValues.put(BOOT_CONFIG, machine.bootdevice); - stateValues.put(NET_CONFIG, machine.net_cfg); - stateValues.put(NIC_CONFIG, machine.nic_card); - stateValues.put(VGA, machine.vga_type); - stateValues.put(HDCACHE_CONFIG, machine.hd_cache); - stateValues.put(DISABLE_ACPI, machine.disableacpi); - stateValues.put(DISABLE_HPET, machine.disablehpet); - stateValues.put(DISABLE_TSC, machine.disabletsc); - stateValues.put(DISABLE_FD_BOOT_CHK, machine.disablefdbootchk); - stateValues.put(SOUNDCARD_CONFIG, machine.soundcard); - stateValues.put(KERNEL, machine.kernel); - stateValues.put(INITRD, machine.initrd); - stateValues.put(APPEND, machine.append); - stateValues.put(MACHINE_TYPE, machine.machine_type); - stateValues.put(ARCH, machine.arch); - stateValues.put(EXTRA_PARAMS, machine.extra_params); - stateValues.put(HOSTFWD, machine.hostfwd); - stateValues.put(GUESTFWD, machine.guestfwd); - stateValues.put(UI, machine.ui); - stateValues.put(MOUSE, machine.mouse); - stateValues.put(KEYBOARD, machine.keyboard); - stateValues.put(ENABLE_MTTCG, machine.enableMTTCG); - stateValues.put(ENABLE_KVM, machine.enableKVM); - - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - Date date = new Date(); - stateValues.put(LASTUPDATED, dateFormat.format(date)); - stateValues.put(STATUS, Config.STATUS_CREATED); - - try { - seqnum = (int) db.insertOrThrow(MACHINE_TABLE_NAME, null, stateValues); - Log.v(TAG, "Inserted Machine: " + machine.machinename + " : " + seqnum); - } catch (Exception e) { - // catch code - Log.e(TAG, "Error while Insert machine: " + e.getMessage()); - if(Config.debug) - e.printStackTrace(); - } - return seqnum; - } - - public int deleteMachines() { - int rowsAffected = 0; - SQLiteDatabase db = getReadableDatabase(); - // Insert arrFiles in - try { - db.delete(MACHINE_TABLE_NAME, STATUS + "=\"" + Config.STATUS_CREATED + "\"" + "or " + STATUS - + "=\"" + Config.STATUS_PAUSED + "\"", null); - } catch (Exception e) { - // catch code - Log.e(TAG, "Error while deleting Machines: " + e.getMessage()); - if(Config.debug) - e.printStackTrace(); - } - - - return rowsAffected; - } - - public void update(final Machine machine, final String colname, final String value) { - Thread t = new Thread(new Runnable() { - public void run() { - updateDB(machine, colname, value); - } - }); - t.start(); - } - - public int updateDB(Machine machine, String colname, String value) { - if (machine == null) - return -1; - int rows = -1; - - // Log.v("DB", "Updating Machine: " + myMachine.machinename - // + " column: " + colname + " = " + value); - ContentValues stateValues = new ContentValues(); - stateValues.put(colname, value); - - try { - db.beginTransaction(); - rows = (int) db.update(MACHINE_TABLE_NAME, stateValues, - MACHINE_NAME + "=\"" + machine.machinename + "\"" + " and " + SNAPSHOT_NAME + "=\"\"", - null); - db.setTransactionSuccessful(); - } catch (Exception e) { - Log.e(TAG, "Error while Updating value: " + e.getMessage()); - if(Config.debug) - e.printStackTrace(); - } finally { - db.endTransaction(); - } - - return rows; - } - - public Machine getMachine(String machine, String snapshot) { - String qry = "select " - + MACHINE_NAME + " , " + CPU + " , " + MEMORY + " , " + CDROM + " , " + FDA - + " , " + FDB + " , " + HDA + " , " + HDB + " , " + HDC + " , " + HDD + " , " - + NET_CONFIG + " , " + NIC_CONFIG + " , " + VGA + " , " + SOUNDCARD_CONFIG + " , " - + HDCACHE_CONFIG + " , " + DISABLE_ACPI + " , " + DISABLE_HPET + " , " - + ENABLE_USBMOUSE + " , " + SNAPSHOT_NAME + " , " + BOOT_CONFIG + " , " + KERNEL - + " , " + INITRD + " , " + APPEND + " , " + CPUNUM + " , " + MACHINE_TYPE + " , " - + DISABLE_FD_BOOT_CHK + " , " + ARCH + " , " + PAUSED + " , " + SD + " , " - + SHARED_FOLDER + " , " + SHARED_FOLDER_MODE + " , " + EXTRA_PARAMS + " , " - + HOSTFWD + " , " + GUESTFWD + " , " + UI + ", " + DISABLE_TSC + ", " - + MOUSE + ", " + KEYBOARD + ", " + ENABLE_MTTCG + ", " + ENABLE_KVM - + " from " + MACHINE_TABLE_NAME - + " where " + STATUS + " in ( " + Config.STATUS_CREATED + " , " + Config.STATUS_PAUSED + " " - + " ) " + " and " + MACHINE_NAME + "=\"" + machine + "\"" + " and " + SNAPSHOT_NAME + "=\"" - + snapshot + "\"" + ";"; - - Machine myMachine = null; - - Cursor cur = db.rawQuery(qry, null); - - cur.moveToFirst(); - while (!cur.isAfterLast()) { - - String machinename = cur.getString(0); - myMachine = new Machine(machinename); - - myMachine.cpu = cur.getString(1); - myMachine.memory = cur.getInt(2); - myMachine.cd_iso_path = cur.getString(3); - if (myMachine.cd_iso_path != null) - myMachine.enableCDROM = true; - - myMachine.fda_img_path = cur.getString(4); - if (myMachine.fda_img_path != null) - myMachine.enableFDA = true; - myMachine.fdb_img_path = cur.getString(5); - if (myMachine.fdb_img_path != null) - myMachine.enableFDB = true; - - myMachine.hda_img_path = cur.getString(6); - myMachine.hdb_img_path = cur.getString(7); - myMachine.hdc_img_path = cur.getString(8); - myMachine.hdd_img_path = cur.getString(9); - - myMachine.net_cfg = cur.getString(10); - myMachine.nic_card = cur.getString(11); - myMachine.vga_type= cur.getString(12); - myMachine.soundcard = cur.getString(13); - myMachine.hd_cache = cur.getString(14); - myMachine.disableacpi = cur.getInt(15); - myMachine.disablehpet = cur.getInt(16); - int usbmouse = cur.getInt(17); - myMachine.snapshot_name = cur.getString(18); - myMachine.bootdevice = cur.getString(19); - myMachine.kernel = cur.getString(20); - myMachine.initrd = cur.getString(21); - myMachine.append = cur.getString(22); - myMachine.cpuNum = cur.getInt(23); - myMachine.machine_type = cur.getString(24); - myMachine.disablefdbootchk = cur.getInt(25); - myMachine.arch = cur.getString(26); - myMachine.paused = cur.getInt(27); - - myMachine.sd_img_path = cur.getString(28); - if (myMachine.sd_img_path != null) - myMachine.enableSD = true; - - myMachine.shared_folder= cur.getString(29); - myMachine.shared_folder_mode = 1; //hard drives are always Read/Write - myMachine.extra_params = cur.getString(31); - myMachine.hostfwd = cur.getString(32); - myMachine.guestfwd = cur.getString(33); - myMachine.ui = cur.getString(34); - - myMachine.disabletsc = cur.getInt(35); - - myMachine.mouse = cur.getString(36); - myMachine.keyboard = cur.getString(37); - myMachine.enableMTTCG = cur.getInt(38); - myMachine.enableKVM = cur.getInt(39); - - - break; - } - cur.close(); - - return myMachine; - } - - public ArrayList getMachines() { - String qry = "select " + MACHINE_NAME + " " + " from " + MACHINE_TABLE_NAME + " where " + STATUS - + " in ( " + Config.STATUS_CREATED + " , " + Config.STATUS_PAUSED + " " + " ) " + " and " - + SNAPSHOT_NAME + " = " + "\"\" " + ";"; - - ArrayList arrStr = new ArrayList(); - - Cursor cur = db.rawQuery(qry, null); - - cur.moveToFirst(); - while (!cur.isAfterLast()) { - String machinename = cur.getString(0); - cur.moveToNext(); - arrStr.add(machinename); - } - cur.close(); - - return arrStr; - } - - - - public int deleteMachineDB(Machine machine) { - int rowsAffected = 0; - try { - db.delete(MACHINE_TABLE_NAME, MACHINE_NAME + "=\"" + machine.machinename + "\"" + " and " - + SNAPSHOT_NAME + "=\"" + machine.snapshot_name + "\"", null); - } catch (Exception e) { - Log.e(TAG, "Error while deleting VM: " + e.getMessage()); - if(Config.debug) - e.printStackTrace(); - } - return rowsAffected; - } - - public ArrayList getSnapshots(Machine machine) { - String qry = "select " + SNAPSHOT_NAME + " " + " from " + MACHINE_TABLE_NAME + " where " + STATUS - + " in ( " + Config.STATUS_CREATED + " , " + Config.STATUS_PAUSED + " ) " + " and " + MACHINE_NAME - + " = " + "\"" + machine.machinename + "\" " + " and " + SNAPSHOT_NAME + " != " + "\"\" " - + ";"; - - ArrayList arrStr = new ArrayList(); - Cursor cur = db.rawQuery(qry, null); - - cur.moveToFirst(); - while (!cur.isAfterLast()) { - String snapshotname = cur.getString(0); - - cur.moveToNext(); - arrStr.add(snapshotname); - } - cur.close(); - - return arrStr; - } - - public void insertMachines(String machines) { - - - } - - public String exportMachines() { - String qry = "select " - //Columns - + MACHINE_NAME + " , " + CPU + " , " + MEMORY + " , " + CDROM + " , " + FDA - + " , " + FDB + " , " + HDA + " , " + HDB + " , " + HDC + " , " + HDD + " , " - + NET_CONFIG + " , " + NIC_CONFIG + " , " + VGA + " , " + SOUNDCARD_CONFIG + " , " - + HDCACHE_CONFIG + " , " + DISABLE_ACPI + " , " + DISABLE_HPET + " , " - + ENABLE_USBMOUSE + " , " + SNAPSHOT_NAME + " , " + BOOT_CONFIG + " , " + KERNEL - + " , " + INITRD + " , " + APPEND + " , " + CPUNUM + " , " + MACHINE_TYPE + " , " - + DISABLE_FD_BOOT_CHK + " , " + ARCH + " , " + PAUSED + " , " + SD + " , " - + SHARED_FOLDER + " , " + SHARED_FOLDER_MODE + " , " + EXTRA_PARAMS + " , " - + HOSTFWD + " , " + GUESTFWD + " , " + UI + ", " + DISABLE_TSC + ", " - + MOUSE + ", " + KEYBOARD + ", " + ENABLE_MTTCG + ", " + ENABLE_KVM - // Table - + " from " + MACHINE_TABLE_NAME + "; "; - - String arrStr = ""; - Cursor cur = db.rawQuery(qry, null); - - cur.moveToFirst(); - String headerline = ""; - for (int i = 0; i < cur.getColumnCount(); i++) { - headerline += ("\"" + cur.getColumnName(i) + "\""); - if (i < cur.getColumnCount() - 1) { - headerline += ","; - } - } - arrStr += (headerline + "\n"); - while (!cur.isAfterLast()) { - String line = ""; - for (int i = 0; i < cur.getColumnCount(); i++) { - line += ("\"" + cur.getString(i) + "\""); - if (i < cur.getColumnCount() - 1) { - line += ","; - } - - } - arrStr += (line + "\n"); - cur.moveToNext(); - } - cur.close(); - - return arrStr; - } - -} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/OSDialogBox.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/OSDialogBox.java deleted file mode 100644 index 1d87b2b06..000000000 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/OSDialogBox.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.max2idea.android.limbo.utils; - -import java.util.ArrayList; -import java.util.Iterator; - -import com.limbo.emu.lib.R; -import com.max2idea.android.limbo.main.Config; - -import android.app.Dialog; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.view.View; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.LinearLayout; -import android.widget.Spinner; - -public class OSDialogBox extends Dialog { - public LinearLayout mLayout; - private Button mDownload; - private Button mCancel; - - public OSDialogBox(Context context) { - super(context); - setContentView(R.layout.oses_dialog); - getWidgets(); - this.setTitle("Downloads OSes"); - setupListeners(); - getWidgets(); - } - - @Override - public void onBackPressed() { - this.dismiss(); - } - - private void getWidgets() { - mLayout = (LinearLayout) findViewById(R.id.osimgl); - mDownload = (Button) findViewById(R.id.download); - mDownload.setEnabled(true); - mCancel = (Button) findViewById(R.id.cancel); - } - - private void setupListeners() { - mDownload.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - Intent intent = new Intent(getContext(), LinksManager.class); - getContext().startActivity(intent); - } - }); - mCancel.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - dismiss(); - } - }); - - } - -} diff --git a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/UIUtils.java b/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/UIUtils.java deleted file mode 100644 index ef13bb5bd..000000000 --- a/limbo-android-lib/src/main/java/com/max2idea/android/limbo/utils/UIUtils.java +++ /dev/null @@ -1,629 +0,0 @@ -package com.max2idea.android.limbo.utils; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.graphics.Color; -import android.graphics.Point; -import android.net.Uri; -import android.os.Handler; -import android.os.Looper; -import android.text.InputType; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.style.ForegroundColorSpan; -import android.util.Log; -import android.view.Display; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.view.inputmethod.InputMethodManager; -import android.webkit.WebView; -import android.widget.ArrayAdapter; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.LinearLayout; -import android.widget.ScrollView; -import android.widget.TextView; -import android.widget.Toast; - -import com.limbo.emu.lib.R; -import com.max2idea.android.limbo.main.Config; -import com.max2idea.android.limbo.main.LimboActivity; -import com.max2idea.android.limbo.main.LimboSettingsManager; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.ArrayList; -import java.util.Scanner; - -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; - -public class UIUtils { - - - private static final String TAG = "UIUtils"; - - public static Spannable formatAndroidLog(String contents) { - - Scanner scanner = null; - Spannable formattedString = new SpannableString(contents); - if(contents.length()==0) - return formattedString; - - try { - scanner = new Scanner(contents); - int counter = 0; - ForegroundColorSpan colorSpan = null; - while (scanner.hasNextLine()) { - String line = scanner.nextLine(); - //FIXME: some devices don't have standard format for the log - if (line.startsWith("E/") || line.contains(" E ")) { - colorSpan = new ForegroundColorSpan(Color.rgb(255, 22, 22)); - } else if (line.startsWith("W/") || line.contains(" W ")) { - colorSpan = new ForegroundColorSpan(Color.rgb(22, 44, 255)); - } else { - colorSpan = null; - } - if (colorSpan!= null) { - formattedString.setSpan(colorSpan, counter, counter + line.length(), - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - counter += line.length()+1; - } - - }catch (Exception ex) { - Log.e(TAG, "Could not format limbo log: " + ex.getMessage()); - } finally { - if(scanner!=null) { - try { - scanner.close(); - } catch (Exception ex) { - if(Config.debug) - ex.printStackTrace(); - } - } - - } - return formattedString; - } - - - public static void checkNewVersion(final Activity activity) { - - if (!LimboSettingsManager.getPromptUpdateVersion(activity)) { - return; - } - - HttpURLConnection conn; - InputStream is = null; - try { - URL url = new URL(Config.downloadUpdateLink); - conn = (HttpURLConnection) url.openConnection(); - - conn.connect(); - is = conn.getInputStream(); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - - int read = 0; - byte[] buff = new byte[1024]; - while ((read = is.read(buff)) != -1) { - bos.write(buff, 0, read); - } - byte[] streamData = bos.toByteArray(); - - final String versionStr = new String(streamData).trim(); - float version = Float.parseFloat(versionStr); - String versionName = getVersionName(versionStr); - - PackageInfo pInfo = activity.getPackageManager().getPackageInfo(activity.getPackageName(), - PackageManager.GET_META_DATA); - int versionCheck = (int) (version * 100); - if (versionCheck > pInfo.versionCode) { - final String finalVersionName = versionName; - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - UIUtils.promptNewVersion(activity, finalVersionName); - } - }); - } - } catch (Exception ex) { - Log.e(TAG, "Could not get new version: " + ex.getMessage()); - if(Config.debug) - ex.printStackTrace(); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - - e.printStackTrace(); - } - } - if (is != null) { - try { - is.close(); - } catch (IOException e) { - - e.printStackTrace(); - } - } - } - } - - private static String getVersionName(String versionStr) { - String [] versionSegments = versionStr.split("\\."); - int maj = Integer.parseInt(versionSegments[0]) / 100; - int min = Integer.parseInt(versionSegments[0]) % 100; - int mic = 0; - if(versionSegments.length > 1){ - mic = Integer.parseInt(versionSegments[1]); - } - String versionName = maj +"." + min + "." + mic; - return versionName; - } - - - public static class LimboFileSpinnerAdapter extends ArrayAdapter - { - int index = 0; - public LimboFileSpinnerAdapter(Context context, int layout, ArrayList items, int index) - { - - super(context, layout, items); - this.index = index; - } - - @Override - public View getDropDownView(int position, View convertView, ViewGroup parent) - { - View view = super.getDropDownView(position, convertView, parent); - if(position >= index) { - TextView textView = (TextView) view.findViewById(R.id.customSpinnerDropDownItem); - String textStr = convertFilePath(textView.getText() + "", position); - textView.setText(textStr); - } - return view; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) - { - View view = super.getView(position, convertView, parent); - if(position >= index) { - TextView textView = (TextView)view.findViewById(R.id.customSpinnerItem); - String textStr = convertFilePath(textView.getText() + "", position); - textView.setText(textStr); - } - - return view; - } - - - private String convertFilePath(String text, int position) - { - try { - text = FileUtils.getFullPathFromDocumentFilePath(text); - } catch (Exception ex) { - if(Config.debug) - ex.printStackTrace(); - } - - return text; - } - } - - public static void toastLong(final Context activity, final String errStr) { - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - - Toast toast = Toast.makeText(activity, errStr, Toast.LENGTH_LONG); - toast.setGravity(Gravity.CENTER | Gravity.CENTER, 0, 0); - toast.show(); - - } - }); - - } - - public static void showFileNotSupported(Activity context){ - UIAlert(context, "Error", "File path is not supported. Make sure you choose a file/directory from your internal storage or external sd card. Root and Download Directories are not supported."); - } - - - public static boolean onKeyboard(Activity activity, boolean toggle, View view) { - // Prevent crashes from activating mouse when machine is paused - if (LimboActivity.vmexecutor.paused == 1) - return !toggle; - - InputMethodManager inputMgr = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); - - //XXX: we need to get the focused view to make this always work - //inputMgr.toggleSoftInput(0, 0); - - -// View view = activity.getCurrentFocus(); - if (toggle || !Config.enableToggleKeyboard){ - if(view!=null) { - view.requestFocus(); - inputMgr.showSoftInput(view, InputMethodManager.SHOW_FORCED); - } - } else { - if (view != null) { - inputMgr.hideSoftInputFromWindow(view.getWindowToken(), 0); - } - } - - return !toggle; - } - - public static void hideKeyboard(Activity activity, View view) { - InputMethodManager inputMgr = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); - if (view != null) { - inputMgr.hideSoftInputFromWindow(view.getWindowToken(), 0); - } - } - - public static void toastShortTop(final Activity activity, final String errStr) { - UIUtils.toast(activity, errStr, Gravity.TOP | Gravity.CENTER, Toast.LENGTH_SHORT); - } - - public static void toast(final Context context, final String errStr, final int gravity, final int length) { - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - if(context instanceof Activity && ((Activity) context).isFinishing()) { - return ; - } - Toast toast = Toast.makeText(context, errStr, length); - toast.setGravity(gravity, 0, 0); - toast.show(); - - } - }); - - } - - public static void toastShort(final Context context, final String errStr) { - toast(context, errStr, Gravity.CENTER | Gravity.CENTER, Toast.LENGTH_SHORT); - - } - - public static void setOrientation(Activity activity) { - int orientation = LimboSettingsManager.getOrientationSetting(activity); - switch (orientation) { - case 0: - activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); - break; - case 1: - activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - break; - case 2: - activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); - break; - case 3: - activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - break; - case 4: - activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); - break; - } - } - - - public static void onChangeLog(Activity activity) { - PackageInfo pInfo = null; - - try { - pInfo = activity.getPackageManager().getPackageInfo(activity.getClass().getPackage().getName(), - PackageManager.GET_META_DATA); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - FileUtils fileutils = new FileUtils(); - try { - UIUtils.UIAlert(activity,"CHANGELOG", fileutils.LoadFile(activity, "CHANGELOG", false), - 0, false, "OK", null, null, null, null, null); - } catch (IOException e) { - - e.printStackTrace(); - } - } - - - - public static void showHints(Activity activity) { - - - UIUtils.toastShortTop(activity, "Press Volume Down for Right Click"); - - - } - - public static void setupToolBar(AppCompatActivity activity) { - - Toolbar tb = (Toolbar) activity.findViewById(R.id.toolbar); - activity.setSupportActionBar(tb); - - // Get the ActionBar here to configure the way it behaves. - ActionBar ab = activity.getSupportActionBar(); - ab.setHomeAsUpIndicator(R.drawable.limbo); // set a custom icon for - // the - // default home button - ab.setDisplayShowHomeEnabled(true); // show or hide the default home - // button - ab.setDisplayHomeAsUpEnabled(true); - ab.setDisplayShowCustomEnabled(true); // enable overriding the - // default - // toolbar layout - ab.setDisplayShowTitleEnabled(true); // disable the default title - // element here (for - // centered - // title) - ab.setTitle(R.string.app_name); - - if(!LimboSettingsManager.getAlwaysShowMenuToolbar(activity)){ - ab.hide(); - } - } - - - public static boolean isLandscapeOrientation(Activity activity) - { - Display display = activity.getWindowManager().getDefaultDisplay(); - Point screenSize = new Point(); - display.getSize(screenSize); - if(screenSize.x < screenSize.y) - return false; - return true; - } - - - public static void onHelp(final Activity activity) { - PackageInfo pInfo = null; - - try { - pInfo = activity.getPackageManager().getPackageInfo(activity.getApplicationContext().getPackageName(), - PackageManager.GET_META_DATA); - } catch (Exception e) { - e.printStackTrace(); - } - - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle(Config.APP_NAME + " v" + pInfo.versionName); - - LinearLayout mLayout = new LinearLayout(activity); - mLayout.setOrientation(LinearLayout.VERTICAL); - TextView textView = new TextView(activity); - textView.setBackgroundColor(Color.WHITE); - textView.setTextColor(Color.BLACK); - textView.setTextSize(15); - textView.setText("Welcome to Limbo Emulator, a port of QEMU for Android devices. " + - "Limbo can emulate light weight operating systems by loading and running virtual disks images. " + - "\n\nFor Downloads, Guides, Help, ISO, and Virtual Disk images visit the Limbo Wiki. " + - "Make sure you always download isos and images only from websites you trust, generally use at your own risk."); - textView.setPadding(20, 20, 20, 20); - ScrollView scrollView = new ScrollView(activity); - scrollView.setLayoutParams(new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, 300)); - scrollView.addView(textView); - mLayout.addView(scrollView); - - CheckBox checkUpdates= new CheckBox(activity); - checkUpdates.setText("Check for Updates on Startup"); - checkUpdates.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton compoundButton, boolean b) { - LimboSettingsManager.setPromptUpdateVersion(activity, b); - } - }); - checkUpdates.setChecked(true); - mLayout.addView(checkUpdates); - alertDialog.setView(mLayout); - - // alertDialog.setMessage(body); - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Go To Wiki", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - UIUtils.openURL(activity, Config.guidesLink); - } - }); - - alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "OK", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - - } - }); - alertDialog.show(); - - - } - - private static void openURL(Activity activity, String url) { - try { - Intent fileIntent = new Intent(Intent.ACTION_VIEW); - fileIntent.setData(Uri.parse(url)); - activity.startActivity(fileIntent); - }catch (Exception ex) { - UIUtils.toastShort(activity, "Could not open url"); - } - } - - public static void UIAlert(Activity activity, String title, String body) { - - AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle(title); - TextView textView = new TextView(activity); - textView.setPadding(20,20,20,20); - textView.setText(body); - ScrollView view = new ScrollView(activity); - view.addView(textView); - alertDialog.setView(view); - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - return; - } - }); - alertDialog.show(); - } - - public static void UIAlert(Activity activity, String title, String body, int textSize, boolean cancelable, - String button1title, DialogInterface.OnClickListener button1Listener, - String button2title, DialogInterface.OnClickListener button2Listener, - String button3title, DialogInterface.OnClickListener button3Listener - ) { - - AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle(title); - alertDialog.setCanceledOnTouchOutside(cancelable); - TextView textView = new TextView(activity); - textView.setPadding(20,20,20,20); - textView.setText(body); - if(textSize>0) - textView.setTextSize(textSize); - textView.setBackgroundColor(Color.WHITE); - textView.setTextColor(Color.BLACK); - ScrollView view = new ScrollView(activity); - view.addView(textView); - alertDialog.setView(view); - if(button1title!=null) - alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, button1title, button1Listener); - if(button2title!=null) - alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, button2title, button2Listener); - if(button3title!=null) - alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, button3title, button3Listener); - alertDialog.show(); - } - - public static void UIAlertLog(final Activity activity, String title, Spannable body) { - - AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle(title); - TextView textView = new TextView(activity); - textView.setPadding(20,20,20,20); - textView.setText(body); - textView.setBackgroundColor(Color.BLACK); - textView.setTextSize(12); - textView.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); - textView.setSingleLine(false); - ScrollView view = new ScrollView(activity); - view.addView(textView); - alertDialog.setView(view); - alertDialog.setCanceledOnTouchOutside(false); - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - return; - } - }); - alertDialog.setButton(DialogInterface.BUTTON_NEUTRAL, "Copy To", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - UIUtils.toastShort(activity, "Choose a Directory to save the logfile"); - FileManager.browse(activity, LimboActivity.FileType.LOG_DIR, Config.OPEN_LOG_FILE_DIR_REQUEST_CODE); - return; - } - }); - alertDialog.show(); - } - - public static void UIAlertHtml(String title, String body, Activity activity) { - - AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle(title); - - try { - WebView webview = new WebView(activity); - webview.loadData(body, "text/html", "UTF-8"); - alertDialog.setView(webview); - } catch (Exception ex) { - TextView textView = new TextView(activity); - textView.setText(body); - alertDialog.setView(textView); - } - - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - return; - } - }); - alertDialog.show(); - } - - public static void promptNewVersion(final Activity activity, String version) { - - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("New Version " + version); - TextView stateView = new TextView(activity); - stateView.setText("There is a new version available with fixes and new features. Do you want to update?"); - stateView.setPadding(20, 20, 20, 20); - alertDialog.setView(stateView); - - // alertDialog.setMessage(body); - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Get New Version", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - openURL(activity, Config.downloadLink); - } - }); - alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Don't Show Again", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - - // UIUtils.log("Searching..."); - LimboSettingsManager.setPromptUpdateVersion(activity, false); - - } - }); - alertDialog.show(); - - } - - public static void promptShowLog(final Activity activity) { - - final AlertDialog alertDialog; - alertDialog = new AlertDialog.Builder(activity).create(); - alertDialog.setTitle("Show log"); - TextView stateView = new TextView(activity); - stateView.setText("Something happened during last run, do you want to see the log?"); - stateView.setPadding(20, 20, 20, 20); - alertDialog.setView(stateView); - - // alertDialog.setMessage(body); - alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Yes", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - - FileUtils.viewLimboLog(activity); - } - }); - alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - - - } - }); - alertDialog.show(); - - } - - -} diff --git a/limbo-android-lib/src/main/java/org/libsdl/app/SDLActivity.java b/limbo-android-lib/src/main/java/org/libsdl/app/SDLActivity.java index 6421b0e6c..747c65c3f 100644 --- a/limbo-android-lib/src/main/java/org/libsdl/app/SDLActivity.java +++ b/limbo-android-lib/src/main/java/org/libsdl/app/SDLActivity.java @@ -29,21 +29,24 @@ import android.content.pm.PackageManager; import android.content.pm.ApplicationInfo; -import com.max2idea.android.limbo.main.LimboActivity; - import androidx.appcompat.app.AppCompatActivity; /** - SDL Activity -*/ -public class SDLActivity extends AppCompatActivity { + SDL Activity + */ +public class SDLActivity + //LIMBO: + extends AppCompatActivity + //LIMBO +{ + private static final String TAG = "SDL"; public static boolean mIsResumedCalled, mIsSurfaceReady, mHasFocus; // Handle the state of the native layer public enum NativeState { - INIT, RESUMED, PAUSED + INIT, RESUMED, PAUSED } public static NativeState mNextNativeState; @@ -53,6 +56,9 @@ public enum NativeState { /** If shared libraries (e.g. SDL or the native application) could not be loaded. */ public static boolean mBrokenLibraries; + //LIMBO: + public static boolean mSuspendOnly; + //LIMBO // If we want to separate mouse and touch events. // This is only toggled in native code when a hint is set! @@ -103,20 +109,20 @@ protected String getMainFunction() { */ protected String[] getLibraries() { return new String[] { - "SDL2", - // "SDL2_image", - // "SDL2_mixer", - // "SDL2_net", - // "SDL2_ttf", - "main" + "SDL2", + // "SDL2_image", + // "SDL2_mixer", + // "SDL2_net", + // "SDL2_ttf", + "main" }; } // Load the .so public void loadLibraries() { - for (String lib : getLibraries()) { - System.loadLibrary(lib); - } + for (String lib : getLibraries()) { + System.loadLibrary(lib); + } } /** @@ -174,22 +180,22 @@ protected void onCreate(Bundle savedInstanceState) { mSingleton = this; AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this); dlgAlert.setMessage("An error occurred while trying to start the application. Please try again and/or reinstall." - + System.getProperty("line.separator") - + System.getProperty("line.separator") - + "Error: " + errorMsgBrokenLib); + + System.getProperty("line.separator") + + System.getProperty("line.separator") + + "Error: " + errorMsgBrokenLib); dlgAlert.setTitle("SDL Error"); dlgAlert.setPositiveButton("Exit", - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog,int id) { - // if this button is clicked, close current activity - SDLActivity.mSingleton.finish(); - } - }); - dlgAlert.setCancelable(false); - dlgAlert.create().show(); + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog,int id) { + // if this button is clicked, close current activity + SDLActivity.mSingleton.finish(); + } + }); + dlgAlert.setCancelable(false); + dlgAlert.create().show(); - return; + return; } // Set up JNI @@ -209,16 +215,15 @@ public void onClick(DialogInterface dialog,int id) { mClipboardHandler = new SDLClipboardHandler_Old(); } - //Limbo: override // Set up the surface - //mSurface = new SDLSurface(getApplication()); - - //Limbo: override - //mLayout = new RelativeLayout(this); - //mLayout.addView(mSurface); - - //setContentView(mLayout); - + // LIMBO: +// mSurface = new SDLSurface(getApplication()); +// +// mLayout = new RelativeLayout(this); +// mLayout.addView(mSurface); +// +// setContentView(mLayout); + //LIMBO setWindowStyle(false); // Get filename from "Open with" of another application @@ -241,7 +246,7 @@ protected void onPause() { mIsResumedCalled = false; if (SDLActivity.mBrokenLibraries) { - return; + return; } SDLActivity.handleNativeState(); @@ -255,7 +260,7 @@ protected void onResume() { mIsResumedCalled = true; if (SDLActivity.mBrokenLibraries) { - return; + return; } SDLActivity.handleNativeState(); @@ -268,14 +273,14 @@ public void onWindowFocusChanged(boolean hasFocus) { Log.v(TAG, "onWindowFocusChanged(): " + hasFocus); if (SDLActivity.mBrokenLibraries) { - return; + return; } SDLActivity.mHasFocus = hasFocus; if (hasFocus) { - mNextNativeState = NativeState.RESUMED; + mNextNativeState = NativeState.RESUMED; } else { - mNextNativeState = NativeState.PAUSED; + mNextNativeState = NativeState.PAUSED; } SDLActivity.handleNativeState(); @@ -287,7 +292,7 @@ public void onLowMemory() { super.onLowMemory(); if (SDLActivity.mBrokenLibraries) { - return; + return; } SDLActivity.nativeLowMemory(); @@ -297,12 +302,25 @@ public void onLowMemory() { protected void onDestroy() { Log.v(TAG, "onDestroy()"); + //LIMBO: + //XXX: don't stop the sdl running + if(SDLActivity.mSuspendOnly) { + Thread currSDLThread = mSDLThread; + SDLActivity.initialize(); + SDLActivity.mExitCalledFromJava = true; + if(currSDLThread!=null) + currSDLThread.interrupt(); + SDLActivity.mSuspendOnly = false; + super.onDestroy(); + return; + } if (SDLActivity.mBrokenLibraries) { super.onDestroy(); // Reset everything in case the user re opens the app SDLActivity.initialize(); return; } + //LIMBO mNextNativeState = NativeState.PAUSED; SDLActivity.handleNativeState(); @@ -333,17 +351,17 @@ protected void onDestroy() { public boolean dispatchKeyEvent(KeyEvent event) { if (SDLActivity.mBrokenLibraries) { - return false; + return false; } int keyCode = event.getKeyCode(); // Ignore certain special keys so they're handled by Android if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || - keyCode == KeyEvent.KEYCODE_VOLUME_UP || - keyCode == KeyEvent.KEYCODE_CAMERA || - keyCode == KeyEvent.KEYCODE_ZOOM_IN || /* API 11 */ - keyCode == KeyEvent.KEYCODE_ZOOM_OUT /* API 11 */ - ) { + keyCode == KeyEvent.KEYCODE_VOLUME_UP || + keyCode == KeyEvent.KEYCODE_CAMERA || + keyCode == KeyEvent.KEYCODE_ZOOM_IN || /* API 11 */ + keyCode == KeyEvent.KEYCODE_ZOOM_OUT /* API 11 */ + ) { return false; } return super.dispatchKeyEvent(event); @@ -434,18 +452,18 @@ public void handleMessage(Message msg) { return; } switch (msg.arg1) { - case COMMAND_CHANGE_TITLE: - if (context instanceof Activity) { - ((Activity) context).setTitle((String)msg.obj); - } else { - Log.e(TAG, "error handling message, getContext() returned no Activity"); - } - break; - case COMMAND_CHANGE_WINDOW_STYLE: - if (Build.VERSION.SDK_INT < 19) { - // This version of Android doesn't support the immersive fullscreen mode + case COMMAND_CHANGE_TITLE: + if (context instanceof Activity) { + ((Activity) context).setTitle((String)msg.obj); + } else { + Log.e(TAG, "error handling message, getContext() returned no Activity"); + } break; - } + case COMMAND_CHANGE_WINDOW_STYLE: + if (Build.VERSION.SDK_INT < 19) { + // This version of Android doesn't support the immersive fullscreen mode + break; + } /* This needs more testing, per bug 4096 - Enabling fullscreen on Android causes the app to toggle fullscreen mode continuously in a loop *** if (context instanceof Activity) { @@ -458,11 +476,11 @@ public void handleMessage(Message msg) { View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; - window.getDecorView().setSystemUiVisibility(flags); + window.getDecorView().setSystemUiVisibility(flags); window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); } else { int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE; - window.getDecorView().setSystemUiVisibility(flags); + window.getDecorView().setSystemUiVisibility(flags); window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); } } @@ -470,38 +488,38 @@ public void handleMessage(Message msg) { Log.e(TAG, "error handling message, getContext() returned no Activity"); } ***/ - break; - case COMMAND_TEXTEDIT_HIDE: - if (mTextEdit != null) { - // Note: On some devices setting view to GONE creates a flicker in landscape. - // Setting the View's sizes to 0 is similar to GONE but without the flicker. - // The sizes will be set to useful values when the keyboard is shown again. - mTextEdit.setLayoutParams(new RelativeLayout.LayoutParams(0, 0)); - - InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0); - - mScreenKeyboardShown = false; - } - break; - case COMMAND_SET_KEEP_SCREEN_ON: - { - if (context instanceof Activity) { - Window window = ((Activity) context).getWindow(); - if (window != null) { - if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) { - window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } else { - window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + break; + case COMMAND_TEXTEDIT_HIDE: + if (mTextEdit != null) { + // Note: On some devices setting view to GONE creates a flicker in landscape. + // Setting the View's sizes to 0 is similar to GONE but without the flicker. + // The sizes will be set to useful values when the keyboard is shown again. + mTextEdit.setLayoutParams(new RelativeLayout.LayoutParams(0, 0)); + + InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0); + + mScreenKeyboardShown = false; + } + break; + case COMMAND_SET_KEEP_SCREEN_ON: + { + if (context instanceof Activity) { + Window window = ((Activity) context).getWindow(); + if (window != null) { + if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) { + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } else { + window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } } } + break; } - break; - } - default: - if ((context instanceof SDLActivity) && !((SDLActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) { - Log.e(TAG, "error handling message, command is " + msg.arg1); - } + default: + if ((context instanceof SDLActivity) && !((SDLActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) { + Log.e(TAG, "error handling message, command is " + msg.arg1); + } } } } @@ -529,10 +547,10 @@ boolean sendCommand(int command, Object data) { public static native void onNativeKeyDown(int keycode); public static native void onNativeKeyUp(int keycode); public static native void onNativeKeyboardFocusLost(); - private static native void onNativeMouse(int button, int action, float x, float y); - private static native void onNativeTouch(int touchDevId, int pointerFingerId, - int action, float x, - float y, float p); + public static native void onNativeMouse(int button, int action, float x, float y); + public static native void onNativeTouch(int touchDevId, int pointerFingerId, + int action, float x, + float y, float p); public static native void onNativeAccel(float x, float y, float z); public static native void onNativeClipboardChanged(); public static native void onNativeSurfaceChanged(); @@ -540,15 +558,6 @@ private static native void onNativeTouch(int touchDevId, int pointerFingerId, public static native String nativeGetHint(String name); public static native void nativeSetenv(String name, String value); - public static void onSDLNativeMouse(int button, int action, float x, float y){ - Log.d(TAG, "onSDLNativeMouse: B: " + button + ", A: " + action + ", X: " + x + ", Y: " + y); - onNativeMouse(button, action, x, y); - } - public static void onSDLNativeTouch(int touchDevId, int pointerFingerId, int action, float x, float y, float p){ - Log.d(TAG, "onSDLNativeTouch: F: " + pointerFingerId + ", A: " + action + ", X: " + x + ", Y: " + y); - onNativeTouch(touchDevId, pointerFingerId, action, x, y, p); - - } /** * This method is called by SDL using JNI. */ @@ -568,7 +577,7 @@ public static void setWindowStyle(boolean fullscreen) { /** * This method is called by SDL using JNI. * This is a static method for JNI convenience, it calls a non-static method - * so that is can be overridden + * so that is can be overridden */ public static void setOrientation(int w, int h, boolean resizable, String hint) { @@ -576,11 +585,11 @@ public static void setOrientation(int w, int h, boolean resizable, String hint) mSingleton.setOrientationBis(w, h, resizable, hint); } } - + /** * This can be overridden */ - public void setOrientationBis(int w, int h, boolean resizable, String hint) + public void setOrientationBis(int w, int h, boolean resizable, String hint) { int orientation = -1; @@ -621,7 +630,7 @@ public void setOrientationBis(int w, int h, boolean resizable, String hint) /** * This method is called by SDL using JNI. */ - public static boolean isScreenKeyboardShown() + public static boolean isScreenKeyboardShown() { if (mTextEdit == null) { return false; @@ -688,9 +697,9 @@ public static boolean getManifestEnvironmentVariables() { } } /* environment variables set! */ - return true; + return true; } catch (Exception e) { - Log.v("SDL", "exception " + e.toString()); + Log.v("SDL", "exception " + e.toString()); } return false; } @@ -745,12 +754,12 @@ public static boolean showTextInput(int x, int y, int w, int h) { } public static boolean isTextInputEvent(KeyEvent event) { - + // Key pressed with Ctrl should be sent as SDL_KEYDOWN/SDL_KEYUP and not SDL_TEXTINPUT if (Build.VERSION.SDK_INT >= 11) { if (event.isCtrlPressed()) { return false; - } + } } return event.isPrintingKey() || event.getKeyCode() == KeyEvent.KEYCODE_SPACE; @@ -824,11 +833,11 @@ public static InputStream openAPKExpansionInputStream(String fileName) throws IO // To avoid direct dependency on Google APK expansion library that is // not a part of Android SDK we access it using reflection expansionFile = Class.forName("com.android.vending.expansion.zipfile.APKExpansionSupport") - .getMethod("getAPKExpansionZipFile", Context.class, int.class, int.class) - .invoke(null, SDL.getContext(), mainVersion, patchVersion); + .getMethod("getAPKExpansionZipFile", Context.class, int.class, int.class) + .invoke(null, SDL.getContext(), mainVersion, patchVersion); expansionFileMethod = expansionFile.getClass() - .getMethod("getInputStream", String.class); + .getMethod("getInputStream", String.class); } catch (Exception ex) { ex.printStackTrace(); expansionFile = null; @@ -1066,7 +1075,7 @@ public boolean onKey(DialogInterface d, int keyCode, KeyEvent event) { public static boolean clipboardHasText() { return mClipboardHandler.clipboardHasText(); } - + /** * This method is called by SDL using JNI. */ @@ -1080,23 +1089,19 @@ public static String clipboardGetText() { public static void clipboardSetText(String string) { mClipboardHandler.clipboardSetText(string); } - - //Limbo: - protected void runSDLMain(){ - - } - - public class ExSDLSurface extends SDLSurface { - + //LIMBO: + protected void runSDLMain(){ } + public static class ExSDLSurface extends SDLSurface { public ExSDLSurface(Context context) { super(context); } } + //LIMBO } /** - Simple runnable to start the SDL application -*/ + Simple runnable to start the SDL application + */ class SDLMain implements Runnable { @Override public void run() { @@ -1106,12 +1111,10 @@ public void run() { String[] arguments = SDLActivity.mSingleton.getArguments(); Log.v("SDL", "Running main function " + function + " from library " + library); - - //Limbo: we override + //LIMBO: we override //SDLActivity.nativeRunMain(library, function, arguments); SDLActivity.mSingleton.runSDLMain(); - - + //LIMBO Log.v("SDL", "Finished main function"); // Native thread has finished, let's finish the Activity @@ -1119,21 +1122,17 @@ public void run() { SDLActivity.handleNativeExit(); } } - - } - /** - SDLSurface. This is what we draw on, so we need to know when it's created - in order to do anything useful. + SDLSurface. This is what we draw on, so we need to know when it's created + in order to do anything useful. - Because of this, that's where we set up the SDL thread -*/ -//Limbo: should be public + Because of this, that's where we set up the SDL thread + */ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, - View.OnKeyListener, View.OnTouchListener, SensorEventListener { + View.OnKeyListener, View.OnTouchListener, SensorEventListener { // Sensors protected static SensorManager mSensorManager; @@ -1210,47 +1209,47 @@ public void surfaceChanged(SurfaceHolder holder, int sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 by default switch (format) { - case PixelFormat.A_8: - Log.v("SDL", "pixel format A_8"); - break; - case PixelFormat.LA_88: - Log.v("SDL", "pixel format LA_88"); - break; - case PixelFormat.L_8: - Log.v("SDL", "pixel format L_8"); - break; - case PixelFormat.RGBA_4444: - Log.v("SDL", "pixel format RGBA_4444"); - sdlFormat = 0x15421002; // SDL_PIXELFORMAT_RGBA4444 - break; - case PixelFormat.RGBA_5551: - Log.v("SDL", "pixel format RGBA_5551"); - sdlFormat = 0x15441002; // SDL_PIXELFORMAT_RGBA5551 - break; - case PixelFormat.RGBA_8888: - Log.v("SDL", "pixel format RGBA_8888"); - sdlFormat = 0x16462004; // SDL_PIXELFORMAT_RGBA8888 - break; - case PixelFormat.RGBX_8888: - Log.v("SDL", "pixel format RGBX_8888"); - sdlFormat = 0x16261804; // SDL_PIXELFORMAT_RGBX8888 - break; - case PixelFormat.RGB_332: - Log.v("SDL", "pixel format RGB_332"); - sdlFormat = 0x14110801; // SDL_PIXELFORMAT_RGB332 - break; - case PixelFormat.RGB_565: - Log.v("SDL", "pixel format RGB_565"); - sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 - break; - case PixelFormat.RGB_888: - Log.v("SDL", "pixel format RGB_888"); - // Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead? - sdlFormat = 0x16161804; // SDL_PIXELFORMAT_RGB888 - break; - default: - Log.v("SDL", "pixel format unknown " + format); - break; + case PixelFormat.A_8: + Log.v("SDL", "pixel format A_8"); + break; + case PixelFormat.LA_88: + Log.v("SDL", "pixel format LA_88"); + break; + case PixelFormat.L_8: + Log.v("SDL", "pixel format L_8"); + break; + case PixelFormat.RGBA_4444: + Log.v("SDL", "pixel format RGBA_4444"); + sdlFormat = 0x15421002; // SDL_PIXELFORMAT_RGBA4444 + break; + case PixelFormat.RGBA_5551: + Log.v("SDL", "pixel format RGBA_5551"); + sdlFormat = 0x15441002; // SDL_PIXELFORMAT_RGBA5551 + break; + case PixelFormat.RGBA_8888: + Log.v("SDL", "pixel format RGBA_8888"); + sdlFormat = 0x16462004; // SDL_PIXELFORMAT_RGBA8888 + break; + case PixelFormat.RGBX_8888: + Log.v("SDL", "pixel format RGBX_8888"); + sdlFormat = 0x16261804; // SDL_PIXELFORMAT_RGBX8888 + break; + case PixelFormat.RGB_332: + Log.v("SDL", "pixel format RGB_332"); + sdlFormat = 0x14110801; // SDL_PIXELFORMAT_RGB332 + break; + case PixelFormat.RGB_565: + Log.v("SDL", "pixel format RGB_565"); + sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 + break; + case PixelFormat.RGB_888: + Log.v("SDL", "pixel format RGB_888"); + // Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead? + sdlFormat = 0x16161804; // SDL_PIXELFORMAT_RGB888 + break; + default: + Log.v("SDL", "pixel format unknown " + format); + break; } mWidth = width; @@ -1269,29 +1268,29 @@ public void surfaceChanged(SurfaceHolder holder, else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) { if (mWidth > mHeight) { - skip = true; + skip = true; } } else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) { if (mWidth < mHeight) { - skip = true; + skip = true; } } // Special Patch for Square Resolution: Black Berry Passport if (skip) { - double min = Math.min(mWidth, mHeight); - double max = Math.max(mWidth, mHeight); + double min = Math.min(mWidth, mHeight); + double max = Math.max(mWidth, mHeight); - if (max / min < 1.20) { - Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution."); - skip = false; - } + if (max / min < 1.20) { + Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution."); + skip = false; + } } if (skip) { - Log.v("SDL", "Skip .. Surface is not ready."); - SDLActivity.mIsSurfaceReady = false; - return; + Log.v("SDL", "Skip .. Surface is not ready."); + SDLActivity.mIsSurfaceReady = false; + return; } /* Surface is ready */ @@ -1347,11 +1346,11 @@ else if (event.getAction() == KeyEvent.ACTION_UP) { // they are ignored here because sending them as mouse input to SDL is messy if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) { switch (event.getAction()) { - case KeyEvent.ACTION_DOWN: - case KeyEvent.ACTION_UP: - // mark the event as handled or it will be handled by system - // handling KEYCODE_BACK by system will call onBackPressed() - return true; + case KeyEvent.ACTION_DOWN: + case KeyEvent.ACTION_UP: + // mark the event as handled or it will be handled by system + // handling KEYCODE_BACK by system will call onBackPressed() + return true; } } } @@ -1382,7 +1381,7 @@ public boolean onTouch(View v, MotionEvent event) { mouseButton = 1; // oh well. } } - SDLActivity.onSDLNativeMouse(mouseButton, action, event.getX(0), event.getY(0)); + SDLActivity.onNativeMouse(mouseButton, action, event.getX(0), event.getY(0)); } else { switch(action) { case MotionEvent.ACTION_MOVE: @@ -1396,7 +1395,7 @@ public boolean onTouch(View v, MotionEvent event) { // see the documentation of getPressure(i) p = 1.0f; } - SDLActivity.onSDLNativeTouch(touchDevId, pointerFingerId, action, x, y, p); + SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p); } break; @@ -1420,7 +1419,7 @@ public boolean onTouch(View v, MotionEvent event) { // see the documentation of getPressure(i) p = 1.0f; } - SDLActivity.onSDLNativeTouch(touchDevId, pointerFingerId, action, x, y, p); + SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p); break; case MotionEvent.ACTION_CANCEL: @@ -1434,7 +1433,7 @@ public boolean onTouch(View v, MotionEvent event) { // see the documentation of getPressure(i) p = 1.0f; } - SDLActivity.onSDLNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p); + SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p); } break; @@ -1444,18 +1443,18 @@ public boolean onTouch(View v, MotionEvent event) { } return true; - } + } // Sensor events public void enableSensor(int sensortype, boolean enabled) { // TODO: This uses getDefaultSensor - what if we have >1 accels? if (enabled) { mSensorManager.registerListener(this, - mSensorManager.getDefaultSensor(sensortype), - SensorManager.SENSOR_DELAY_GAME, null); + mSensorManager.getDefaultSensor(sensortype), + SensorManager.SENSOR_DELAY_GAME, null); } else { mSensorManager.unregisterListener(this, - mSensorManager.getDefaultSensor(sensortype)); + mSensorManager.getDefaultSensor(sensortype)); } } @@ -1487,8 +1486,8 @@ public void onSensorChanged(SensorEvent event) { break; } SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH, - y / SensorManager.GRAVITY_EARTH, - event.values[2] / SensorManager.GRAVITY_EARTH); + y / SensorManager.GRAVITY_EARTH, + event.values[2] / SensorManager.GRAVITY_EARTH); } } } @@ -1513,7 +1512,7 @@ public boolean onCheckIsTextEditor() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { - /* + /* * This handles the hardware keyboard input */ if (event.getAction() == KeyEvent.ACTION_DOWN) { @@ -1631,9 +1630,9 @@ public boolean deleteSurroundingText(int beforeLength, int afterLength) { boolean ret = true; // backspace(s) while (beforeLength-- > 0) { - boolean ret_key = sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)) - && sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)); - ret = ret && ret_key; + boolean ret_key = sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)) + && sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)); + ret = ret && ret_key; } return ret; } @@ -1652,19 +1651,19 @@ interface SDLClipboardHandler { class SDLClipboardHandler_API11 implements - SDLClipboardHandler, - android.content.ClipboardManager.OnPrimaryClipChangedListener { + SDLClipboardHandler, + android.content.ClipboardManager.OnPrimaryClipChangedListener { protected android.content.ClipboardManager mClipMgr; SDLClipboardHandler_API11() { - mClipMgr = (android.content.ClipboardManager) SDL.getContext().getSystemService(Context.CLIPBOARD_SERVICE); - mClipMgr.addPrimaryClipChangedListener(this); + mClipMgr = (android.content.ClipboardManager) SDL.getContext().getSystemService(Context.CLIPBOARD_SERVICE); + mClipMgr.addPrimaryClipChangedListener(this); } @Override public boolean clipboardHasText() { - return mClipMgr.hasText(); + return mClipMgr.hasText(); } @Override @@ -1672,18 +1671,18 @@ public String clipboardGetText() { CharSequence text; text = mClipMgr.getText(); if (text != null) { - return text.toString(); + return text.toString(); } return null; } @Override public void clipboardSetText(String string) { - mClipMgr.removePrimaryClipChangedListener(this); - mClipMgr.setText(string); - mClipMgr.addPrimaryClipChangedListener(this); + mClipMgr.removePrimaryClipChangedListener(this); + mClipMgr.setText(string); + mClipMgr.addPrimaryClipChangedListener(this); } - + @Override public void onPrimaryClipChanged() { SDLActivity.onNativeClipboardChanged(); @@ -1692,32 +1691,32 @@ public void onPrimaryClipChanged() { } class SDLClipboardHandler_Old implements - SDLClipboardHandler { - + SDLClipboardHandler { + protected android.text.ClipboardManager mClipMgrOld; - + SDLClipboardHandler_Old() { - mClipMgrOld = (android.text.ClipboardManager) SDL.getContext().getSystemService(Context.CLIPBOARD_SERVICE); + mClipMgrOld = (android.text.ClipboardManager) SDL.getContext().getSystemService(Context.CLIPBOARD_SERVICE); } @Override public boolean clipboardHasText() { - return mClipMgrOld.hasText(); + return mClipMgrOld.hasText(); } @Override public String clipboardGetText() { - CharSequence text; - text = mClipMgrOld.getText(); - if (text != null) { - return text.toString(); - } - return null; + CharSequence text; + text = mClipMgrOld.getText(); + if (text != null) { + return text.toString(); + } + return null; } @Override public void clipboardSetText(String string) { - mClipMgrOld.setText(string); + mClipMgrOld.setText(string); } } diff --git a/limbo-android-lib/src/main/java/org/libsdl/app/SDLAudioManager.java b/limbo-android-lib/src/main/java/org/libsdl/app/SDLAudioManager.java index 26baf8220..d0ea3ebe2 100644 --- a/limbo-android-lib/src/main/java/org/libsdl/app/SDLAudioManager.java +++ b/limbo-android-lib/src/main/java/org/libsdl/app/SDLAudioManager.java @@ -7,8 +7,10 @@ public class SDLAudioManager { protected static final String TAG = "SDLAudio"; - protected static AudioTrack mAudioTrack; - protected static AudioRecord mAudioRecord; + //LIMBO: + public static AudioTrack mAudioTrack; + public static AudioRecord mAudioRecord; + //LIMBO public static void initialize() { mAudioTrack = null; @@ -21,6 +23,13 @@ public static void initialize() { * This method is called by SDL using JNI. */ public static int audioOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) { + // LIMBO: a small delay seems to fix a weird issue with StackOverflowError + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + // LIMBO int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO; int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT; int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1); diff --git a/limbo-android-lib/src/main/java/org/libsdl/app/SDLControllerManager.java b/limbo-android-lib/src/main/java/org/libsdl/app/SDLControllerManager.java index a1513b069..9a95ba4d6 100644 --- a/limbo-android-lib/src/main/java/org/libsdl/app/SDLControllerManager.java +++ b/limbo-android-lib/src/main/java/org/libsdl/app/SDLControllerManager.java @@ -8,9 +8,10 @@ import android.content.Context; import android.os.*; import android.view.*; +import android.util.Log; -public class SDLControllerManager +public class SDLControllerManager { public static native int nativeSetupJNI(); @@ -89,16 +90,16 @@ public static boolean isDeviceSDLJoystick(int deviceId) { /* This is called for every button press, so let's not spam the logs */ /** - if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) { - Log.v(TAG, "Input device " + device.getName() + " is a joystick."); - } - if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) { - Log.v(TAG, "Input device " + device.getName() + " is a dpad."); - } - if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) { - Log.v(TAG, "Input device " + device.getName() + " is a gamepad."); - } - **/ + if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) { + Log.v(TAG, "Input device " + device.getName() + " is a joystick."); + } + if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) { + Log.v(TAG, "Input device " + device.getName() + " is a dpad."); + } + if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) { + Log.v(TAG, "Input device " + device.getName() + " is a gamepad."); + } + **/ return (((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) || ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) || @@ -176,7 +177,7 @@ public void pollInputDevices() { for (InputDevice.MotionRange range : ranges ) { if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { if (range.getAxis() == MotionEvent.AXIS_HAT_X || - range.getAxis() == MotionEvent.AXIS_HAT_Y) { + range.getAxis() == MotionEvent.AXIS_HAT_Y) { joystick.hats.add(range); } else { @@ -187,7 +188,7 @@ public void pollInputDevices() { mJoysticks.add(joystick); SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, 0, -1, - joystick.axes.size(), joystick.hats.size()/2, 0); + joystick.axes.size(), joystick.hats.size()/2, 0); } } } @@ -284,7 +285,7 @@ class SDLHaptic { } private ArrayList mHaptics; - + public SDLHapticHandler() { mHaptics = new ArrayList(); } @@ -297,7 +298,7 @@ public void run(int device_id, int length) { } public void pollHapticDevices() { - + final int deviceId_VIBRATOR_SERVICE = 999999; boolean hasVibratorService = false; @@ -341,7 +342,7 @@ public void pollHapticDevices() { haptic = new SDLHaptic(); haptic.device_id = deviceId_VIBRATOR_SERVICE; haptic.name = "VIBRATOR_SERVICE"; - haptic.vib = vib; + haptic.vib = vib; mHaptics.add(haptic); SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name); } @@ -383,7 +384,7 @@ protected SDLHaptic getHaptic(int device_id) { } } return null; - } + } } class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener { @@ -408,14 +409,14 @@ public boolean onGenericMotion(View v, MotionEvent event) { case MotionEvent.ACTION_SCROLL: x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0); y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0); - SDLActivity.onSDLNativeMouse(0, action, x, y); + SDLActivity.onNativeMouse(0, action, x, y); return true; case MotionEvent.ACTION_HOVER_MOVE: x = event.getX(0); y = event.getY(0); - SDLActivity.onSDLNativeMouse(0, action, x, y); + SDLActivity.onNativeMouse(0, action, x, y); return true; default: diff --git a/limbo-android-lib/src/main/jni/Android.mk b/limbo-android-lib/src/main/jni/Android.mk index 2179b6730..be6596b51 100644 --- a/limbo-android-lib/src/main/jni/Android.mk +++ b/limbo-android-lib/src/main/jni/Android.mk @@ -3,7 +3,10 @@ #dep libs include $(NDK_PROJECT_PATH)/jni/compat/musl/Android.mk include $(NDK_PROJECT_PATH)/jni/compat/Android.mk -include $(NDK_PROJECT_PATH)/jni/compat/sdl-addons/Android.mk +ifeq ($(USE_AAUDIO),true) + include $(NDK_PROJECT_PATH)/jni/compat/sdl-addons/Android.mk +endif + ifeq ($(USE_SDL),true) include $(NDK_PROJECT_PATH)/jni/SDL2/Android.mk endif diff --git a/limbo-android-lib/src/main/jni/Application.mk b/limbo-android-lib/src/main/jni/Application.mk index 3d9910bcd..96ebf7b44 100644 --- a/limbo-android-lib/src/main/jni/Application.mk +++ b/limbo-android-lib/src/main/jni/Application.mk @@ -13,22 +13,21 @@ else endif #Don't remove this -APP_CFLAGS += $(ARCH_CFLAGS) -APP_CFLAGS += $(ARCH_EXTRA_CFLAGS) -APP_LDFLAGS += $(ARCH_LD_FLAGS) - -#FIXME: we should use memmove for the utils also -#APP_CFLAGS += -include $(FIXUTILS) +APP_CFLAGS += -include $(LOGUTILS) +APP_LDFLAGS += -llog +ifeq ($(USE_GCC),true) + APP_CFLAGS +=-std=gnu99 +endif APP_ARM_MODE=$(ARM_MODE) -#$(warning NDK_TOOLCHAIN_VERSION = $(NDK_TOOLCHAIN_VERSION)) -#$(warning NDK_DEBUG = $(NDK_DEBUG)) -#$(warning APP_ARM_MODE = $(APP_ARM_MODE)) -#$(warning APP_ARM_NEON = $(APP_ARM_NEON)) -#$(warning APP_OPTIM = $(APP_OPTIM)) -#$(warning APP_ABI = $(APP_ABI)) -#$(warning APP_PLATFORM = $(APP_PLATFORM)) -#$(warning NDK_PROJECT_PATH = $(NDK_PROJECT_PATH)) -#$(warning ARCH_CFLAGS = $(ARCH_CFLAGS)) -#$(warning APP_CFLAGS = $(APP_CFLAGS)) +$(info NDK_TOOLCHAIN_VERSION = $(NDK_TOOLCHAIN_VERSION)) +$(info NDK_DEBUG = $(NDK_DEBUG)) +$(info APP_ARM_MODE = $(APP_ARM_MODE)) +$(info APP_ARM_NEON = $(APP_ARM_NEON)) +$(info APP_OPTIM = $(APP_OPTIM)) +$(info APP_ABI = $(APP_ABI)) +$(info APP_PLATFORM = $(APP_PLATFORM)) +$(info NDK_PROJECT_PATH = $(NDK_PROJECT_PATH)) +$(info ARCH_CFLAGS = $(ARCH_CFLAGS)) +$(info APP_CFLAGS = $(APP_CFLAGS)) diff --git a/limbo-android-lib/src/main/jni/Makefile b/limbo-android-lib/src/main/jni/Makefile index 307eb03f5..a236117a9 100644 --- a/limbo-android-lib/src/main/jni/Makefile +++ b/limbo-android-lib/src/main/jni/Makefile @@ -86,6 +86,7 @@ COPY_ROMS=-mkdir -p ../assets/roms && cp -rf ./qemu/pc-bios/*.rom \ ../assets/roms/ COPY_KEYMAPS=cp -rf ./qemu/pc-bios/keymaps ../assets/roms/ +COPY_QEMU_VERSION=cp -f ./qemu/VERSION ../assets/QEMU_VERSION COPY_RESOLV_CONF=mkdir -p ../assets/roms/etc/ && printf "nameserver 8.8.8.8\n\n" > ../assets/roms/etc/resolv.conf COPY_ASSETS_X86=mkdir -p ../../../../limbo-android-x86/src/main/assets/ && \ @@ -257,6 +258,7 @@ _build-qemu: ndk $(RENAME_PARPROUTED) $(COPY_ROMS) $(COPY_KEYMAPS) + $(COPY_QEMU_VERSION) $(COPY_RESOLV_CONF) qemu: ndk diff --git a/limbo-android-lib/src/main/jni/android-config/android-device-config/android-armv7a-softfp.mak b/limbo-android-lib/src/main/jni/android-config/android-device-config/android-armv7a-softfp.mak index 5ed4f0e40..b7b33250d 100644 --- a/limbo-android-lib/src/main/jni/android-config/android-device-config/android-armv7a-softfp.mak +++ b/limbo-android-lib/src/main/jni/android-config/android-device-config/android-armv7a-softfp.mak @@ -5,8 +5,9 @@ ARCH_CFLAGS += -D__ANDROID_API__=$(NDK_PLATFORM_API) #CLANG ONLY ifeq ($(NDK_TOOLCHAIN_VERSION),clang) + TARGET_PREFIX = armv7-none-linux-androideabi # ARCH_CLANG_FLAGS += -gcc-toolchain $(TOOLCHAIN_DIR) - ARCH_CLANG_FLAGS += -target armv7-none-linux-androideabi$(NDK_PLATFORM_API) + ARCH_CLANG_FLAGS += -target $(TARGET_PREFIX)$(NDK_PLATFORM_API) ARCH_CFLAGS += $(ARCH_CLANG_FLAGS) -D__ANDROID_API__=$(NDK_PLATFORM_API) # ARCH_CFLAGS += -fno-integrated-as ARCH_LD_FLAGS += -Wc,-target -Wc,armv7-none-linux-androideabi$(NDK_PLATFORM_API) diff --git a/limbo-android-lib/src/main/jni/android-config/android-device-config/android-armv8.mak b/limbo-android-lib/src/main/jni/android-config/android-device-config/android-armv8.mak index 0bc2713f0..e26093cdc 100644 --- a/limbo-android-lib/src/main/jni/android-config/android-device-config/android-armv8.mak +++ b/limbo-android-lib/src/main/jni/android-config/android-device-config/android-armv8.mak @@ -5,8 +5,9 @@ ARCH_CFLAGS += -D__ANDROID_API__=$(NDK_PLATFORM_API) #CLANG ONLY ifeq ($(NDK_TOOLCHAIN_VERSION),clang) + TARGET_PREFIX = aarch64-none-linux-android # ARCH_CLANG_FLAGS += -gcc-toolchain $(TOOLCHAIN_DIR) - ARCH_CLANG_FLAGS += -target aarch64-none-linux-android$(NDK_PLATFORM_API) + ARCH_CLANG_FLAGS += -target $(TARGET_PREFIX)$(NDK_PLATFORM_API) ARCH_CFLAGS += $(ARCH_CLANG_FLAGS) -D__ANDROID_API__=$(NDK_PLATFORM_API) # ARCH_CFLAGS += -fno-integrated-as ARCH_LD_FLAGS += -Wc,-target -Wc,aarch64-none-linux-android$(NDK_PLATFORM_API) diff --git a/limbo-android-lib/src/main/jni/android-config/android-device-config/android-generic.mak b/limbo-android-lib/src/main/jni/android-config/android-device-config/android-generic.mak index 20a4fb658..d65697f99 100644 --- a/limbo-android-lib/src/main/jni/android-config/android-device-config/android-generic.mak +++ b/limbo-android-lib/src/main/jni/android-config/android-device-config/android-generic.mak @@ -27,7 +27,6 @@ ARCH_LD_FLAGS += -lc -lm -llog # add aaudio ifeq ($(USE_AAUDIO),true) ARCH_CFLAGS += -D__ENABLE_AAUDIO__ - ARCH_LD_FLAGS += -laaudio endif # Suppress some warnings diff --git a/limbo-android-lib/src/main/jni/android-config/android-device-config/android-x86.mak b/limbo-android-lib/src/main/jni/android-config/android-device-config/android-x86.mak index a23a98c4a..6af7bb6af 100644 --- a/limbo-android-lib/src/main/jni/android-config/android-device-config/android-x86.mak +++ b/limbo-android-lib/src/main/jni/android-config/android-device-config/android-x86.mak @@ -6,8 +6,9 @@ ARCH_LD_FLAGS += -latomic #CLANG ONLY ifeq ($(NDK_TOOLCHAIN_VERSION),clang) + TARGET_PREFIX = i686-none-linux-android # ARCH_CLANG_FLAGS += -gcc-toolchain $(TOOLCHAIN_DIR) - ARCH_CLANG_FLAGS += -target i686-none-linux-android$(NDK_PLATFORM_API) + ARCH_CLANG_FLAGS += -target $(TARGET_PREFIX)$(NDK_PLATFORM_API) ARCH_CFLAGS += $(ARCH_CLANG_FLAGS) -D__ANDROID_API__=$(NDK_PLATFORM_API) #ARCH_CFLAGS += -fno-integrated-as ARCH_LD_FLAGS += -Wc,-target -Wc,i686-none-linux-android$(NDK_PLATFORM_API) diff --git a/limbo-android-lib/src/main/jni/android-config/android-device-config/android-x86_64.mak b/limbo-android-lib/src/main/jni/android-config/android-device-config/android-x86_64.mak index da3dfa677..8b12dc9a3 100644 --- a/limbo-android-lib/src/main/jni/android-config/android-device-config/android-x86_64.mak +++ b/limbo-android-lib/src/main/jni/android-config/android-device-config/android-x86_64.mak @@ -6,8 +6,9 @@ ARCH_LD_FLAGS += -latomic #CLANG ONLY ifeq ($(NDK_TOOLCHAIN_VERSION),clang) + TARGET_PREFIX = x86_64-none-linux-android # ARCH_CLANG_FLAGS += -gcc-toolchain $(TOOLCHAIN_DIR) - ARCH_CLANG_FLAGS += -target x86_64-none-linux-android$(NDK_PLATFORM_API) + ARCH_CLANG_FLAGS += -target $(TARGET_PREFIX)$(NDK_PLATFORM_API) ARCH_CFLAGS += $(ARCH_CLANG_FLAGS) -D__ANDROID_API__=$(NDK_PLATFORM_API) # ARCH_CFLAGS += -fno-integrated-as ARCH_LD_FLAGS += -Wc,-target -Wc,x86_64-none-linux-android$(NDK_PLATFORM_API) diff --git a/limbo-android-lib/src/main/jni/android-config/android-limbo-config.mak b/limbo-android-lib/src/main/jni/android-config/android-limbo-config.mak index af9c646d7..511f07596 100644 --- a/limbo-android-lib/src/main/jni/android-config/android-limbo-config.mak +++ b/limbo-android-lib/src/main/jni/android-config/android-limbo-config.mak @@ -6,17 +6,17 @@ #NDK_ROOT = /home/dev/tools/ndk/android-ndk-r14b #USE_GCC?=true # Or use r23 with clang -NDK_ROOT = /home/dev/tools/ndk/android-ndk-r23 +NDK_ROOT ?= /home/dev/tools/ndk/android-ndk-r23b USE_GCC?=false ### the ndk api should be the same as the minSdkVersion in your AndroidManifest.xml -NDK_PLATFORM_API=26 +NDK_PLATFORM_API=21 # Set to true if you use platform-21 or above USE_NDK_PLATFORM21 ?= true # Set to true if you use platform-26 or above -USE_NDK_PLATFORM26 ?= true +USE_NDK_PLATFORM26 ?= false # Optimization, generally it is better set to false when debugging USE_OPTIMIZATION ?= true @@ -24,18 +24,32 @@ USE_OPTIMIZATION ?= true # Hardening: it produces slower runtimes but helps preventing buffer overflow attacks USE_SECURITY ?= true +# Uncomment to enable debugging +# If you enable debugging you should turn off optimization as well +#NDK_DEBUG=1 + # Uncomment if you use Linux x86, Linux 64bit, or macosx PC to compile # Compiling on Windows is no longer supported -#NDK_ENV = linux-x86 -NDK_ENV = linux-x86_64 -#NDK_ENV = darwin-x86 +#NDK_ENV ?= linux-x86 +NDK_ENV ?= linux-x86_64 +#NDK_ENV ?= darwin-x86 # Build threads (make -j ?) makes building faster -BUILD_THREADS = 3 +BUILD_THREADS ?= 3 ############## QEMU Host and Guest -BUILD_HOST=arm64-v8a -BUILD_GUEST=x86_64-softmmu + +# Android device type (host arch) +# values: armeabi-v7a, arm64-v8a, x86, x86_64 +BUILD_HOST?=arm64-v8a + +# GUEST_ARCH is the Emulator type +# values: x86_64-softmmu,aarch64-softmmu,sparc64-softmmu,ppc64-softmmu +BUILD_GUEST?=x86_64-softmmu + +# QEMU Version +# values: 2.9.1, 5.1.0 +USE_QEMU_VERSION ?= 5.1.0 # If you want to use SDL interface USE_SDL ?= true diff --git a/limbo-android-lib/src/main/jni/android-config/android-qemu-config-4.0.0.mak b/limbo-android-lib/src/main/jni/android-config/android-qemu-config-4.0.0.mak deleted file mode 100644 index 1822cd95d..000000000 --- a/limbo-android-lib/src/main/jni/android-config/android-qemu-config-4.0.0.mak +++ /dev/null @@ -1,21 +0,0 @@ - -#### QEMU version-specific options - -# QEMU version 4.x is not using a stab lib -USE_QEMUSTAB ?= false - -# For QEMU 4.0.0 uses slirp as a static lib so set to true -USE_SLIRP_LIB ?= true - -# For QEMU 4.0.0 set the explicit sdlabi to false -USE_SDL_ABI ?= false - -# For QEMU 2.11.0 and above (3.x, 4.x) disable these features -MISC += --disable-capstone -MISC += --disable-malloc-trim - -#BLUETOOTH -BLUETOOTH = --disable-bluez - - -SSH2 += --disable-libssh2 \ No newline at end of file diff --git a/limbo-android-lib/src/main/jni/android-config/android-qemu-config.mak b/limbo-android-lib/src/main/jni/android-config/android-qemu-config.mak index 1366ba972..e139e2ad3 100644 --- a/limbo-android-lib/src/main/jni/android-config/android-qemu-config.mak +++ b/limbo-android-lib/src/main/jni/android-config/android-qemu-config.mak @@ -1,16 +1,17 @@ -# Development specific settings - +#### DO NOT CHANGE QEMU_TARGET_LIST = $(BUILD_GUEST) - -#### QEMU version-spcific options - QEMU_CONFIG_DIR=$(LIMBO_JNI_ROOT)/android-config + +ifeq ($(USE_QEMU_VERSION),2.9.1) include $(QEMU_CONFIG_DIR)/android-qemu-config-2.9.1.mak -#include $(QEMU_CONFIG_DIR)/android-qemu-config-4.0.0.mak -#include $(QEMU_CONFIG_DIR)/android-qemu-config-5.1.0.mak +else ifeq ($(USE_QEMU_VERSION),5.1.0) +include $(QEMU_CONFIG_DIR)/android-qemu-config-5.1.0.mak +else +$(error Unsupported QEMU version = $(USE_QEMU_VERSION)) +endif -##### QEMU advance options +##### QEMU generic configuration #use coroutine #ucontext is deprecated and also not avail in Bionic diff --git a/limbo-android-lib/src/main/jni/android-limbo-build.mak b/limbo-android-lib/src/main/jni/android-limbo-build.mak index 79136c462..ec87083d3 100644 --- a/limbo-android-lib/src/main/jni/android-limbo-build.mak +++ b/limbo-android-lib/src/main/jni/android-limbo-build.mak @@ -12,9 +12,23 @@ SHELL := env PATH=$(PATH) /bin/bash APP_PLATFORM = android-$(NDK_PLATFORM_API) NDK_PLATFORM = platforms/$(APP_PLATFORM) +ifeq ($(USE_NDK_PLATFORM21),true) +USE_PLATFORM21_FLAGS = -D__ANDROID_HAS_SIGNAL__ \ + -D__ANDROID_HAS_FS_IOC__ \ + -D__ANDROID_HAS_SYS_GETTID__ \ + -D__ANDROID_HAS_PARPORT__ \ + -D__ANDROID_HAS_IEEE__ \ + -D__ANDROID_HAS_STATVFS__ \ + -D__ANDROID__HAS_PTHREAD_ATFORK_ +endif + +ifeq ($(USE_NDK_PLATFORM26),true) +USE_PLATFORM26_FLAGS = -D__ANDROID_HAVE_STRCHRNUL__ +endif + #SET/RESET vars ARCH_CFLAGS := -D__LIMBO__ -D__ANDROID__ -DANDROID -D__linux__ -DCONFIG_LINUX $(USE_NDK11) \ - $(USE_PLATFORM21_FLAGS) $(USE_PLATFORM26_FLAGS) + $(USE_PLATFORM21_FLAGS) $(USE_PLATFORM26_FLAGS) ARCH_LD_FLAGS= ifeq ($(BUILD_HOST), arm64-v8a) @@ -123,21 +137,6 @@ COMPATUTILS_FD = $(LIMBO_JNI_ROOT)/compat/limbo_compat_filesystem.h COMPATUTILS_QEMU = $(LIMBO_JNI_ROOT)/compat/limbo_compat_qemu.h COMPATMACROS = $(LIMBO_JNI_ROOT)/compat/limbo_compat_macros.h COMPATANDROID = $(LIMBO_JNI_ROOT)/compat/limbo_compat.h - -ifeq ($(USE_NDK_PLATFORM21),true) -USE_PLATFORM21_FLAGS = -D__ANDROID_HAS_SIGNAL__ \ - -D__ANDROID_HAS_FS_IOC__ \ - -D__ANDROID_HAS_SYS_GETTID__ \ - -D__ANDROID_HAS_PARPORT__ \ - -D__ANDROID_HAS_IEEE__ \ - -D__ANDROID_HAS_STATVFS__ \ - -D__ANDROID__HAS_PTHREAD_ATFORK_ -endif - - -ifeq ($(USE_NDK_PLATFORM26),true) -USE_PLATFORM26_FLAGS = -D__ANDROID_HAVE_STRCHRNUL__ -endif # Needed for some c++ source code to compile some ARM 64 disas # We don't need them right now @@ -169,21 +168,22 @@ SYSTEM_INCLUDE = \ -include $(COMPATANDROID) #info -$(warning VARIABLES) -$(warning PATH = $(PATH)) -$(warning NDK_ROOT = $(NDK_ROOT)) -$(warning NDK_TOOLCHAIN_VERSION = $(NDK_TOOLCHAIN_VERSION)) -$(warning APP_PLATFORM = $(APP_PLATFORM)) -$(warning USE_NDK_PLATFORM21 = $(USE_NDK_PLATFORM21)) -$(warning USE_NDK_PLATFORM26 = $(USE_NDK_PLATFORM26)) -$(warning APP_ABI = $(APP_ABI)) -$(warning USE_OPTIMIZATION = $(USE_OPTIMIZATION)) -$(warning USE_SECURITY = $(USE_SECURITY)) -$(warning BUILD_THREADS = $(BUILD_THREADS)) -$(warning NDK_ENV = $(NDK_ENV)) -$(warning BUILD_HOST = $(BUILD_HOST)) -$(warning BUILD_GUEST= $(BUILD_GUEST)) -$(warning USE_SDL = $(USE_SDL)) -$(warning USE_SDL_AUDIO = $(USE_SDL_AUDIO)) -$(warning USE_AAUDIO = $(USE_AAUDIO)) -$(warning USE_KVM = $(USE_KVM)) +$(info VARIABLES) +$(info PATH = $(PATH)) +$(info NDK_ROOT = $(NDK_ROOT)) +$(info NDK_TOOLCHAIN_VERSION = $(NDK_TOOLCHAIN_VERSION)) +$(info APP_PLATFORM = $(APP_PLATFORM)) +$(info USE_NDK_PLATFORM21 = $(USE_NDK_PLATFORM21)) +$(info USE_NDK_PLATFORM26 = $(USE_NDK_PLATFORM26)) +$(info APP_ABI = $(APP_ABI)) +$(info USE_OPTIMIZATION = $(USE_OPTIMIZATION)) +$(info USE_SECURITY = $(USE_SECURITY)) +$(info BUILD_THREADS = $(BUILD_THREADS)) +$(info NDK_ENV = $(NDK_ENV)) +$(info BUILD_HOST = $(BUILD_HOST)) +$(info BUILD_GUEST= $(BUILD_GUEST)) +$(info USE_QEMU_VERSION = $(USE_QEMU_VERSION)) +$(info USE_SDL = $(USE_SDL)) +$(info USE_SDL_AUDIO = $(USE_SDL_AUDIO)) +$(info USE_AAUDIO = $(USE_AAUDIO)) +$(info USE_KVM = $(USE_KVM)) diff --git a/limbo-android-lib/src/main/jni/compat/limbo_compat.h b/limbo-android-lib/src/main/jni/compat/limbo_compat.h index 802a6c01f..c43c75563 100644 --- a/limbo-android-lib/src/main/jni/compat/limbo_compat.h +++ b/limbo-android-lib/src/main/jni/compat/limbo_compat.h @@ -11,9 +11,6 @@ extern pthread_mutex_t fd_lock; extern const char * storage_base_dir; extern const char * limbo_base_dir; -#ifdef __cplusplus -extern "C" { -#endif void set_jni(JNIEnv* env, jobject obj1, jclass jclass1, const char * storage_dir, const char * limbo_dir); void * valloc (size_t size); @@ -21,10 +18,5 @@ void * valloc (size_t size); const char* strchrnul(const char* s, int c); #endif -#ifdef __cplusplus -} -#endif - - #endif diff --git a/limbo-android-lib/src/main/jni/compat/limbo_compat_filesystem.h b/limbo-android-lib/src/main/jni/compat/limbo_compat_filesystem.h index 21dc150c4..560ef161f 100644 --- a/limbo-android-lib/src/main/jni/compat/limbo_compat_filesystem.h +++ b/limbo-android-lib/src/main/jni/compat/limbo_compat_filesystem.h @@ -27,17 +27,11 @@ void *get_fd_thread(void *t); int create_thread_get_fd(const char * filepath); #endif -#ifdef __cplusplus -extern "C" { -#endif FILE* android_fopen(const char *path, const char * mode); int android_open(const char *path, int flags, ...); //int android_close(int fd); int android_stat(const char*, struct stat*); int android_mkstemp(char * path); int lockf(int fd, int cmd, off_t len); -#ifdef __cplusplus -} -#endif #endif diff --git a/limbo-android-lib/src/main/jni/compat/limbo_compat_qemu.h b/limbo-android-lib/src/main/jni/compat/limbo_compat_qemu.h index 4ab426b39..2b5efc6ff 100644 --- a/limbo-android-lib/src/main/jni/compat/limbo_compat_qemu.h +++ b/limbo-android-lib/src/main/jni/compat/limbo_compat_qemu.h @@ -8,12 +8,6 @@ typedef struct sdl_res_t { int height; } sdl_res_t; -#ifdef __cplusplus -extern "C" { -#endif void Android_JNI_SetVMResolution(int width, int height); -#ifdef __cplusplus -} -#endif #endif diff --git a/limbo-android-lib/src/main/jni/compat/limbo_logutils.h b/limbo-android-lib/src/main/jni/compat/limbo_logutils.h index 342d5ca7b..0f98e192e 100644 --- a/limbo-android-lib/src/main/jni/compat/limbo_logutils.h +++ b/limbo-android-lib/src/main/jni/compat/limbo_logutils.h @@ -19,10 +19,6 @@ #ifndef _LIMBO_LOGUTILS_H #define _LIMBO_LOGUTILS_H -#ifdef __cplusplus -extern "C" { -#endif - #include #include #include @@ -122,9 +118,5 @@ static inline int limbo_vfprintf(FILE *stream, const char *format, va_list ap){ #endif //end DEBUG_OUTPUT -#ifdef __cplusplus - } -#endif - #endif /* _LIMBO_LOGUTILS_H */ diff --git a/limbo-android-lib/src/main/jni/compat/sdl-addons/Android.mk b/limbo-android-lib/src/main/jni/compat/sdl-addons/Android.mk index b31b5a32c..9428664b9 100644 --- a/limbo-android-lib/src/main/jni/compat/sdl-addons/Android.mk +++ b/limbo-android-lib/src/main/jni/compat/sdl-addons/Android.mk @@ -2,10 +2,13 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE := compat-SDL2-addons +# We override the target platform +LOCAL_CFLAGS += -target $(TARGET_PREFIX)26 +LOCAL_LDFLAGS += -target $(TARGET_PREFIX)26 + +LOCAL_LDLIBS += -laaudio +LOCAL_MODULE := compat-SDL2-addons LOCAL_SRC_FILES := SDL_limboaudio.c -LOCAL_C_INCLUDES := include $(BUILD_SHARED_LIBRARY) - diff --git a/limbo-android-lib/src/main/jni/compat/sdl-addons/SDL_limboaudio.c b/limbo-android-lib/src/main/jni/compat/sdl-addons/SDL_limboaudio.c index 8c235e744..e6430d545 100644 --- a/limbo-android-lib/src/main/jni/compat/sdl-addons/SDL_limboaudio.c +++ b/limbo-android-lib/src/main/jni/compat/sdl-addons/SDL_limboaudio.c @@ -17,8 +17,6 @@ Copyright (C) Max Kastanas 2012 * */ -// Requires minSdkVersion 26+ -#if defined(__ENABLE_AAUDIO__) #include #include #include @@ -36,8 +34,6 @@ float aaudioResampleRate = 22050.0; // we drop the frames you can use 0 to do a median filter instead int aaudioDropFrames = 1; -int enableAaudio = 0; - // FIXME: buggy int enableAaudioHighPriority = 0; @@ -68,10 +64,6 @@ int aaudioResampleFrames = 0; sem_t mutex; -int isAaudioEnabled() { - return enableAaudio; -} - // FIXME: this is buggy, though since the aaudio write function is // fast enough we don't bother for now aaudio_data_callback_result_t aaudio_callback( @@ -309,12 +301,3 @@ void writeAaudioQueue() { void* getAaudioBuffer() { return aaudioMidBuffer; } - -JNIEXPORT void JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_nativeEnableAaudio( - JNIEnv* env, jobject thiz, - int value) { - printf("set enable aaudio: %d\n", value); - enableAaudio = value; -} - -#endif diff --git a/limbo-android-lib/src/main/jni/compat/sdl-extensions/SDL_limbomouse.c b/limbo-android-lib/src/main/jni/compat/sdl-extensions/SDL_limbomouse.c index d7d982391..6aef584a5 100644 --- a/limbo-android-lib/src/main/jni/compat/sdl-extensions/SDL_limbomouse.c +++ b/limbo-android-lib/src/main/jni/compat/sdl-extensions/SDL_limbomouse.c @@ -110,9 +110,9 @@ JNIEXPORT void JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_nativeMous JNIEXPORT void JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_nativeMouseBounds( JNIEnv* env, jobject thiz, int xmin, int xmax, int ymin, int ymax) { checkBounds = true; - x_min = xmin; - x_max = xmax; - y_min = ymin; - y_max = ymax; + x_min = xmin+1; + x_max = xmax-1; + y_min = ymin+1; + y_max = ymax-1; } diff --git a/limbo-android-lib/src/main/jni/limbo/Android.mk b/limbo-android-lib/src/main/jni/limbo/Android.mk index 23ddfce17..62f1256be 100644 --- a/limbo-android-lib/src/main/jni/limbo/Android.mk +++ b/limbo-android-lib/src/main/jni/limbo/Android.mk @@ -5,7 +5,7 @@ include $(CLEAR_VARS) LOCAL_ARM_MODE:= $(APP_ARM_MODE) LOCAL_SRC_FILES := \ - vm-executor-jni.cpp + vm-executor-jni.c LOCAL_MODULE := limbo diff --git a/limbo-android-lib/src/main/jni/limbo/vm-executor-jni.cpp b/limbo-android-lib/src/main/jni/limbo/vm-executor-jni.c similarity index 78% rename from limbo-android-lib/src/main/jni/limbo/vm-executor-jni.cpp rename to limbo-android-lib/src/main/jni/limbo/vm-executor-jni.c index 0f073a82f..f50227f8e 100644 --- a/limbo-android-lib/src/main/jni/limbo/vm-executor-jni.cpp +++ b/limbo-android-lib/src/main/jni/limbo/vm-executor-jni.c @@ -37,12 +37,17 @@ static int started = 0; void * handle = 0; -void * loadLib(const char * lib_path_str) { +void * loadLib(const char* lib_filename, const char * lib_path_str) { char res_msg[MAX_STRING_LEN]; sprintf(res_msg, "Loading lib: %s", lib_path_str); LOGV("%s", res_msg); - void *ldhandle = dlopen(lib_path_str, RTLD_LAZY); + void *ldhandle = dlopen(lib_filename, RTLD_LAZY); + if(ldhandle == NULL) { + // try with the lib path + printf("trying loading with full path: %s\n", lib_path_str); + ldhandle = dlopen(lib_path_str, RTLD_LAZY); + } return ldhandle; } @@ -53,46 +58,44 @@ void setup_jni(JNIEnv* env, jobject thiz, jstring storage_dir, jstring base_dir) const char *storage_dir_str = NULL; if (base_dir != NULL) - base_dir_str = env->GetStringUTFChars(base_dir, 0); + base_dir_str = (*env)->GetStringUTFChars(env, base_dir, 0); if (storage_dir != NULL) - storage_dir_str = env->GetStringUTFChars(storage_dir, 0); + storage_dir_str = (*env)->GetStringUTFChars(env, storage_dir, 0); - jclass c = env->GetObjectClass(thiz); + jclass c = (*env)->GetObjectClass(env, thiz); set_jni(env, thiz, c, storage_dir_str, base_dir_str); } int get_qemu_var(JNIEnv* env, jobject thiz, const char * var) { char res_msg[MSG_BUFSIZE + 1] = { 0 }; - + dlerror(); - void * obj = dlsym (handle, var); const char *dlsym_error = dlerror(); if (dlsym_error) { LOGE("Cannot load symbol %s: %s\n", var, dlsym_error); return -1; } - int * var_ptr = (int *) obj; - //LOGD("Set Var %s: %s, %d\n", var, dlsym_error, *var_ptr); - return *var_ptr; } void set_qemu_var(JNIEnv* env, jobject thiz, const char * var, jint jvalue){ + int value_int = (jint) jvalue; dlerror(); - - int value_int = (jint) jvalue; - //LOGI("change var: %s = %d\n", var, value_int); void * obj = dlsym (handle, var); + const char *dlsym_error = dlerror(); + if (dlsym_error) { + LOGE("Cannot load symbol %s: %s\n", var, dlsym_error); + return; + } int * var_ptr = (int *) obj; *var_ptr = value_int; } JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *pvt) { - printf("* JNI_OnLoad called\n"); return JNI_VERSION_1_2; } @@ -147,10 +150,18 @@ JNIEXPORT jint JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_getvncrefr return res; } +JNIEXPORT void JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_nativeIgnoreBreakpointInvalidate( + JNIEnv* env, jobject thiz, jint jvalue) { + if(handle == NULL) { + return; + } + set_qemu_var(env, thiz, "limbo_ignore_breakpoint_invalidate", jvalue); +} + JNIEXPORT jstring JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_start( JNIEnv* env, jobject thiz, jstring storage_dir, jstring base_dir, - jstring lib_path, + jstring lib_filename, jstring lib_path, jint sdl_scale_hint, jobjectArray params) { int res; @@ -159,7 +170,7 @@ JNIEXPORT jstring JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_start( if (started) { sprintf(res_msg, "VM Already started"); LOGV("%s", res_msg); - return env->NewStringUTF(res_msg); + return (*env)->NewStringUTF(env, res_msg); } LOGV("Processing params"); @@ -168,51 +179,46 @@ JNIEXPORT jstring JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_start( int argc = 0; char ** argv; - //params - argc = env->GetArrayLength(params); - //LOGV("Params = %d", argc); + argc = (*env)->GetArrayLength(env, params); argv = (char **) malloc((argc + 1) * sizeof(*argv)); for (int i = 0; i < argc; i++) { - jstring string = (jstring)(env->GetObjectArrayElement(params, i)); - const char *param_str = env->GetStringUTFChars(string, 0); + jstring string = (jstring)((*env)->GetObjectArrayElement(env, params, i)); + const char *param_str = (*env)->GetStringUTFChars(env, string, 0); int length = strlen(param_str)+1; argv[i] = (char *) malloc(length * sizeof(char)); strcpy(argv[i], param_str); - env->ReleaseStringUTFChars(string, param_str); + (*env)->ReleaseStringUTFChars(env, string, param_str); } //XXX: Do not remove argv[argc] = NULL; - for (int k = 0; k < argc; k++) { - //LOGV("param[%d]=%s", k, argv[k]); - } - - printf("Starting VM..."); + printf("Starting VM"); started = 1; //LOAD LIB - const char *lib_path_str = NULL; - if (lib_path != NULL) - lib_path_str = env->GetStringUTFChars(lib_path, 0); + const char *lib_filename_str = NULL; + if (lib_filename!= NULL) + lib_filename_str = (*env)->GetStringUTFChars(env, lib_filename, 0); + const char *lib_path_str = NULL; + if (lib_path != NULL) + lib_path_str = (*env)->GetStringUTFChars(env, lib_path, 0); if (handle == NULL) { - handle = loadLib(lib_path_str); + handle = loadLib(lib_filename_str, lib_path_str); } if (!handle) { sprintf(res_msg, "Error opening lib: %s :%s", lib_path_str, dlerror()); LOGV("%s", res_msg); - return env->NewStringUTF(res_msg); + return (*env)->NewStringUTF(env, res_msg); } setup_jni(env, thiz, storage_dir, base_dir); - set_qemu_var(env, thiz, "limbo_sdl_scale_hint", sdl_scale_hint); - LOGV("Loading symbol main...\n"); typedef void (*main_t)(int argc, char **argv, char **envp); typedef void (*qemu_main_loop_t)(); typedef void (*qemu_cleanup_t)(); @@ -220,7 +226,6 @@ JNIEXPORT jstring JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_start( qemu_main_loop_t qemu_main_loop = NULL; qemu_cleanup_t qemu_cleanup = NULL; - // reset errors dlerror(); main_t qemu_main = (main_t) dlsym(handle, "qemu_init"); const char *dlsym_error = dlerror(); @@ -232,7 +237,7 @@ JNIEXPORT jstring JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_start( LOGE("Cannot find qemu symbol 'qemu_init' or 'main': %s\n", dlsym_error); dlclose(handle); handle = NULL; - return env->NewStringUTF(dlsym_error); + return (*env)->NewStringUTF(env, dlsym_error); } qemu_main(argc, argv, NULL); } else { // new versions of qemu @@ -242,7 +247,7 @@ JNIEXPORT jstring JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_start( LOGE("Cannot find qemu symbol 'qemu_main_loop': %s\n", dlsym_error); dlclose(handle); handle = NULL; - return env->NewStringUTF(dlsym_error); + return (*env)->NewStringUTF(env, dlsym_error); } qemu_cleanup = (qemu_cleanup_t) dlsym(handle, "qemu_cleanup"); @@ -251,7 +256,7 @@ JNIEXPORT jstring JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_start( LOGE("Cannot find qemu symbol 'qemu_cleanup': %s\n", dlsym_error); dlclose(handle); handle = NULL; - return env->NewStringUTF(dlsym_error); + return (*env)->NewStringUTF(env, dlsym_error); } qemu_main(argc, argv, NULL); @@ -259,18 +264,17 @@ JNIEXPORT jstring JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_start( qemu_cleanup(); } - //UNLOAD LIB sprintf(res_msg, "Closing lib: %s", lib_path_str); LOGV("%s", res_msg); dlclose(handle); handle = NULL; started = 0; - env->ReleaseStringUTFChars(lib_path, lib_path_str); + (*env)->ReleaseStringUTFChars(env, lib_path, lib_path_str); sprintf(res_msg, "VM shutdown"); LOGV("%s", res_msg); - return env->NewStringUTF(res_msg); + return (*env)->NewStringUTF(env, res_msg); } @@ -287,7 +291,7 @@ JNIEXPORT jstring JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_stop( const char *dlsym_error = dlerror(); if (dlsym_error) { LOGE("Cannot load symbol 'qemu_system_reset_request': %s\n", dlsym_error); - return env->NewStringUTF(res_msg); + return (*env)->NewStringUTF(env, res_msg); } qemu_system_reset_request(6); //SHUTDOWN_CAUSE_GUEST_RESET sprintf(res_msg, "VM Restart Request"); @@ -298,7 +302,7 @@ JNIEXPORT jstring JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_stop( const char *dlsym_error = dlerror(); if (dlsym_error) { LOGE("Cannot load symbol 'qemu_system_shutdown_request': %s\n", dlsym_error); - return env->NewStringUTF(res_msg); + return (*env)->NewStringUTF(env, res_msg); } qemu_system_shutdown_request(3); //SHUTDOWN_CAUSE_HOST_SIGNAL sprintf(res_msg, "VM Stop Request"); @@ -308,7 +312,7 @@ JNIEXPORT jstring JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_stop( started = restart_int; - return env->NewStringUTF(res_msg); + return (*env)->NewStringUTF(env, res_msg); } // JNI End diff --git a/limbo-android-lib/src/main/jni/limbo/vm-executor-jni.h b/limbo-android-lib/src/main/jni/limbo/vm-executor-jni.h index fe44c968a..5ffdfce3e 100644 --- a/limbo-android-lib/src/main/jni/limbo/vm-executor-jni.h +++ b/limbo-android-lib/src/main/jni/limbo/vm-executor-jni.h @@ -28,7 +28,7 @@ typedef struct Error Error; -void * loadLib(const char * lib_path_str); +void * loadLib(const char* lib_filename, const char * lib_path_str); void setup_jni(JNIEnv* env, jobject thiz, jstring storage_dir, jstring base_dir); @@ -36,8 +36,6 @@ int get_qemu_var(JNIEnv* env, jobject thiz, const char * var); void set_qemu_var(JNIEnv* env, jobject thiz, const char * var, jint jvalue); -extern "C" { - JNIEXPORT void JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_nativeRefreshScreen( JNIEnv* env, jobject thiz, jint jvalue); @@ -59,15 +57,18 @@ JNIEXPORT jint JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_getSDLRefr JNIEXPORT jint JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_getvncrefreshrate( JNIEnv* env, jobject thiz); +JNIEXPORT void JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_nativeIgnoreBreakpointInvalidate( + JNIEnv* env, jobject thiz, jint jvalue); + JNIEXPORT jstring JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_start( JNIEnv* env, jobject thiz, - jstring storage_dir, jstring base_dir,jstring lib_path, - jint sdl_scale_hint, jobjectArray params); + jstring storage_dir, jstring base_dir, + jstring lib_filename, jstring lib_path, + jint sdl_scale_hint, + jobjectArray params); JNIEXPORT jstring JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_stop( JNIEnv* env, jobject thiz, jint jint_restart); -} - #endif diff --git a/limbo-android-lib/src/main/jni/patches/qemu-2.9.1.patch b/limbo-android-lib/src/main/jni/patches/qemu-2.9.1.patch index 633432a46..5689853fd 100644 --- a/limbo-android-lib/src/main/jni/patches/qemu-2.9.1.patch +++ b/limbo-android-lib/src/main/jni/patches/qemu-2.9.1.patch @@ -1,6 +1,6 @@ diff -ru --no-dereference /tmp/qemu-2.9.1/audio/audio.c ./audio/audio.c ---- /tmp/qemu-2.9.1/audio/audio.c 2017-09-07 19:28:14.000000000 +0300 -+++ ./audio/audio.c 2021-09-19 22:13:54.255212688 +0300 +--- /tmp/qemu-2.9.1/audio/audio.c 2017-09-07 12:28:14.000000000 -0400 ++++ ./audio/audio.c 2022-03-03 07:53:17.683428743 -0500 @@ -76,7 +76,11 @@ .nb_voices = 1, .greedy = 1, @@ -26,8 +26,8 @@ diff -ru --no-dereference /tmp/qemu-2.9.1/audio/audio.c ./audio/audio.c .fmt = AUD_FMT_S16, .endianness = AUDIO_HOST_ENDIANNESS, diff -ru --no-dereference /tmp/qemu-2.9.1/configure ./configure ---- /tmp/qemu-2.9.1/configure 2017-09-07 19:28:14.000000000 +0300 -+++ ./configure 2021-09-12 11:46:06.150323416 +0300 +--- /tmp/qemu-2.9.1/configure 2017-09-07 12:28:14.000000000 -0400 ++++ ./configure 2022-03-03 07:53:17.683428743 -0500 @@ -1831,7 +1831,8 @@ # pkg-config probe @@ -105,9 +105,33 @@ diff -ru --no-dereference /tmp/qemu-2.9.1/configure ./configure fi ########################################## +diff -ru --no-dereference /tmp/qemu-2.9.1/exec.c ./exec.c +--- /tmp/qemu-2.9.1/exec.c 2017-09-07 12:28:14.000000000 -0400 ++++ ./exec.c 2022-03-03 07:56:02.835432622 -0500 +@@ -725,6 +725,10 @@ + #endif + } + ++#ifdef __LIMBO__ ++int limbo_ignore_breakpoint_invalidate = 0; ++#endif ++ + static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) + { + /* Flush the whole TB as this will not have race conditions +@@ -732,6 +736,9 @@ + * Ideally we would just invalidate the TBs for the + * specified PC. + */ ++#ifdef __LIMBO__ ++ if(!limbo_ignore_breakpoint_invalidate) ++#endif + tb_flush(cpu); + } + diff -ru --no-dereference /tmp/qemu-2.9.1/hw/display/vga.c ./hw/display/vga.c ---- /tmp/qemu-2.9.1/hw/display/vga.c 2017-09-07 19:28:14.000000000 +0300 -+++ ./hw/display/vga.c 2021-09-15 19:04:07.231208893 +0300 +--- /tmp/qemu-2.9.1/hw/display/vga.c 2017-09-07 12:28:14.000000000 -0400 ++++ ./hw/display/vga.c 2022-03-03 07:53:17.735428744 -0500 @@ -1449,6 +1449,12 @@ memory_region_set_log(&s->vram, false, DIRTY_MEMORY_VGA); } @@ -144,8 +168,8 @@ diff -ru --no-dereference /tmp/qemu-2.9.1/hw/display/vga.c ./hw/display/vga.c /* reset modified pages */ if (page_max >= page_min) { diff -ru --no-dereference /tmp/qemu-2.9.1/include/ui/console.h ./include/ui/console.h ---- /tmp/qemu-2.9.1/include/ui/console.h 2017-09-07 19:28:14.000000000 +0300 -+++ ./include/ui/console.h 2021-09-19 15:39:26.512726435 +0300 +--- /tmp/qemu-2.9.1/include/ui/console.h 2017-09-07 12:28:14.000000000 -0400 ++++ ./include/ui/console.h 2022-03-03 07:53:17.755428745 -0500 @@ -27,8 +27,16 @@ #define QEMU_CAPS_LOCK_LED (1 << 2) @@ -164,8 +188,8 @@ diff -ru --no-dereference /tmp/qemu-2.9.1/include/ui/console.h ./include/ui/cons /* Color number is match to standard vga palette */ enum qemu_color_names { diff -ru --no-dereference /tmp/qemu-2.9.1/kvm-all.c ./kvm-all.c ---- /tmp/qemu-2.9.1/kvm-all.c 2017-09-07 19:28:14.000000000 +0300 -+++ ./kvm-all.c 2021-09-12 11:46:06.158323341 +0300 +--- /tmp/qemu-2.9.1/kvm-all.c 2017-09-07 12:28:14.000000000 -0400 ++++ ./kvm-all.c 2022-03-03 07:53:17.759428745 -0500 @@ -1601,7 +1601,12 @@ s->vmfd = -1; s->fd = qemu_open("/dev/kvm", O_RDWR); @@ -180,8 +204,8 @@ diff -ru --no-dereference /tmp/qemu-2.9.1/kvm-all.c ./kvm-all.c goto err; } diff -ru --no-dereference /tmp/qemu-2.9.1/Makefile ./Makefile ---- /tmp/qemu-2.9.1/Makefile 2017-09-07 19:28:14.000000000 +0300 -+++ ./Makefile 2021-09-12 11:46:06.158323341 +0300 +--- /tmp/qemu-2.9.1/Makefile 2017-09-07 12:28:14.000000000 -0400 ++++ ./Makefile 2022-03-03 07:53:17.759428745 -0500 @@ -203,7 +203,7 @@ LIBS+=-lz $(LIBS_TOOLS) @@ -210,8 +234,8 @@ diff -ru --no-dereference /tmp/qemu-2.9.1/Makefile ./Makefile fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS) fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap diff -ru --no-dereference /tmp/qemu-2.9.1/Makefile.target ./Makefile.target ---- /tmp/qemu-2.9.1/Makefile.target 2017-09-07 19:28:14.000000000 +0300 -+++ ./Makefile.target 2021-09-12 11:46:06.162323303 +0300 +--- /tmp/qemu-2.9.1/Makefile.target 2017-09-07 12:28:14.000000000 -0400 ++++ ./Makefile.target 2022-03-03 07:53:17.759428745 -0500 @@ -204,8 +204,10 @@ COMMON_LDADDS = $(trace-obj-y) ../libqemuutil.a ../libqemustub.a @@ -226,8 +250,8 @@ diff -ru --no-dereference /tmp/qemu-2.9.1/Makefile.target ./Makefile.target $(call quiet-command,Rez -append $(SRC_PATH)/pc-bios/qemu.rsrc -o $@,"REZ","$(TARGET_DIR)$@") $(call quiet-command,SetFile -a C $@,"SETFILE","$(TARGET_DIR)$@") diff -ru --no-dereference /tmp/qemu-2.9.1/monitor.c ./monitor.c ---- /tmp/qemu-2.9.1/monitor.c 2017-09-07 19:28:14.000000000 +0300 -+++ ./monitor.c 2021-09-12 11:46:06.174323190 +0300 +--- /tmp/qemu-2.9.1/monitor.c 2017-09-07 12:28:14.000000000 -0400 ++++ ./monitor.c 2022-03-03 07:53:17.759428745 -0500 @@ -1869,6 +1869,14 @@ { mon_fd_t *monfd; @@ -244,8 +268,8 @@ diff -ru --no-dereference /tmp/qemu-2.9.1/monitor.c ./monitor.c int fd; diff -ru --no-dereference /tmp/qemu-2.9.1/slirp/misc.c ./slirp/misc.c ---- /tmp/qemu-2.9.1/slirp/misc.c 2017-09-07 19:28:14.000000000 +0300 -+++ ./slirp/misc.c 2021-09-12 11:46:06.178323152 +0300 +--- /tmp/qemu-2.9.1/slirp/misc.c 2017-09-07 12:28:14.000000000 -0400 ++++ ./slirp/misc.c 2022-03-03 07:53:17.775428745 -0500 @@ -60,7 +60,7 @@ } @@ -256,8 +280,8 @@ diff -ru --no-dereference /tmp/qemu-2.9.1/slirp/misc.c ./slirp/misc.c int fork_exec(struct socket *so, const char *ex, int do_pty) diff -ru --no-dereference /tmp/qemu-2.9.1/ui/console.c ./ui/console.c ---- /tmp/qemu-2.9.1/ui/console.c 2017-09-07 19:28:14.000000000 +0300 -+++ ./ui/console.c 2021-09-19 22:12:34.299175702 +0300 +--- /tmp/qemu-2.9.1/ui/console.c 2017-09-07 12:28:14.000000000 -0400 ++++ ./ui/console.c 2022-03-03 07:53:17.863428747 -0500 @@ -34,6 +34,11 @@ #define DEFAULT_BACKSCROLL 512 #define CONSOLE_CURSOR_PERIOD 500 @@ -271,8 +295,8 @@ diff -ru --no-dereference /tmp/qemu-2.9.1/ui/console.c ./ui/console.c uint8_t fgcol:4; uint8_t bgcol:4; diff -ru --no-dereference /tmp/qemu-2.9.1/ui/sdl2-2d.c ./ui/sdl2-2d.c ---- /tmp/qemu-2.9.1/ui/sdl2-2d.c 2017-09-07 19:28:14.000000000 +0300 -+++ ./ui/sdl2-2d.c 2021-09-15 19:47:25.910989528 +0300 +--- /tmp/qemu-2.9.1/ui/sdl2-2d.c 2017-09-07 12:28:14.000000000 -0400 ++++ ./ui/sdl2-2d.c 2022-03-03 07:53:17.867428748 -0500 @@ -66,7 +66,15 @@ SDL_UpdateTexture(scon->texture, NULL, surface_data(surf), @@ -304,8 +328,8 @@ diff -ru --no-dereference /tmp/qemu-2.9.1/ui/sdl2-2d.c ./ui/sdl2-2d.c void sdl2_2d_refresh(DisplayChangeListener *dcl) diff -ru --no-dereference /tmp/qemu-2.9.1/ui/sdl2.c ./ui/sdl2.c ---- /tmp/qemu-2.9.1/ui/sdl2.c 2017-09-07 19:28:14.000000000 +0300 -+++ ./ui/sdl2.c 2021-09-15 18:55:37.466911654 +0300 +--- /tmp/qemu-2.9.1/ui/sdl2.c 2017-09-07 12:28:14.000000000 -0400 ++++ ./ui/sdl2.c 2022-03-03 07:53:17.891428748 -0500 @@ -90,7 +90,15 @@ surface_width(scon->surface), surface_height(scon->surface), @@ -322,40 +346,42 @@ diff -ru --no-dereference /tmp/qemu-2.9.1/ui/sdl2.c ./ui/sdl2.c if (scon->opengl) { scon->winctx = SDL_GL_GetCurrentContext(); } -@@ -284,6 +292,12 @@ +@@ -268,6 +276,13 @@ + }; + static uint32_t prev_state; + ++#ifdef __LIMBO__ ++// LIMBO: the console can be NULL when the mouse moves outside of the window ++// and it crashes the app ++ if(scon==NULL) ++ return; ++#endif ++ + if (prev_state != state) { + qemu_input_update_buttons(scon->dcl.con, bmap, prev_state, state); + prev_state = state; +@@ -284,6 +299,12 @@ struct sdl2_console *thiscon = &sdl2_console[i]; if (thiscon->real_window && thiscon->surface) { SDL_GetWindowSize(thiscon->real_window, &scr_w, &scr_h); +#ifdef __LIMBO__ +// New versions of limbo we don't use the whole screen so +// we cap one the actual surface -+ max_w = surface_width(scon->surface); -+ max_h = surface_height(scon->surface); ++ max_w = surface_width(scon->surface) - 2; ++ max_h = surface_height(scon->surface) - 2; +#else cur_off_x = thiscon->x; cur_off_y = thiscon->y; if (scr_w + cur_off_x > max_w) { -@@ -296,9 +310,10 @@ +@@ -296,6 +317,7 @@ off_x = cur_off_x; off_y = cur_off_y; } +#endif //LIMBO } } -- qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, max_w); -+ qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, max_w); - qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, max_h); - } else { - if (guest_cursor) { -@@ -309,6 +324,7 @@ - dx = x; - dy = y; - } -+ - qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_X, dx); - qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_Y, dy); - } -@@ -595,6 +611,10 @@ + qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, max_w); +@@ -595,6 +617,10 @@ } while (SDL_PollEvent(ev)) { @@ -366,7 +392,7 @@ diff -ru --no-dereference /tmp/qemu-2.9.1/ui/sdl2.c ./ui/sdl2.c switch (ev->type) { case SDL_KEYDOWN: idle = 0; -@@ -756,6 +776,10 @@ +@@ -756,6 +782,10 @@ } } @@ -377,7 +403,7 @@ diff -ru --no-dereference /tmp/qemu-2.9.1/ui/sdl2.c ./ui/sdl2.c void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) { int flags; -@@ -768,6 +792,7 @@ +@@ -768,6 +798,7 @@ gui_noframe = 1; } @@ -385,7 +411,7 @@ diff -ru --no-dereference /tmp/qemu-2.9.1/ui/sdl2.c ./ui/sdl2.c #ifdef __linux__ /* on Linux, SDL may use fbcon|directfb|svgalib when run without * accessible $DISPLAY to open X11 window. This is often the case -@@ -780,6 +805,12 @@ +@@ -780,6 +811,12 @@ */ setenv("SDL_VIDEODRIVER", "x11", 0); #endif @@ -399,8 +425,8 @@ diff -ru --no-dereference /tmp/qemu-2.9.1/ui/sdl2.c ./ui/sdl2.c flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE; if (SDL_Init(flags)) { diff -ru --no-dereference /tmp/qemu-2.9.1/ui/vnc.c ./ui/vnc.c ---- /tmp/qemu-2.9.1/ui/vnc.c 2017-09-07 19:28:14.000000000 +0300 -+++ ./ui/vnc.c 2021-09-12 14:31:14.102083548 +0300 +--- /tmp/qemu-2.9.1/ui/vnc.c 2017-09-07 12:28:14.000000000 -0400 ++++ ./ui/vnc.c 2022-03-03 07:53:17.891428748 -0500 @@ -47,8 +47,19 @@ #include "qemu/cutils.h" #include "io/dns-resolver.h" @@ -434,7 +460,7 @@ diff -ru --no-dereference /tmp/qemu-2.9.1/ui/vnc.c ./ui/vnc.c + + if ((sym >= 123 && sym <= 126) + || (sym >= 33 && sym <= 38) -+ || (sym >= 40 && sym <= 44) ++ || (sym >= 40 && sym < 44) + || (sym >= 62 && sym <= 64) + || (sym >= 94 && sym <= 95) + || sym == 58 || sym == 60) { @@ -474,8 +500,8 @@ diff -ru --no-dereference /tmp/qemu-2.9.1/ui/vnc.c ./ui/vnc.c } diff -ru --no-dereference /tmp/qemu-2.9.1/util/qemu-openpty.c ./util/qemu-openpty.c ---- /tmp/qemu-2.9.1/util/qemu-openpty.c 2017-09-07 19:28:14.000000000 +0300 -+++ ./util/qemu-openpty.c 2021-09-12 11:46:06.194323001 +0300 +--- /tmp/qemu-2.9.1/util/qemu-openpty.c 2017-09-07 12:28:14.000000000 -0400 ++++ ./util/qemu-openpty.c 2022-03-03 07:53:17.899428748 -0500 @@ -107,7 +107,9 @@ termios_p->c_cc[VTIME] = 0; } diff --git a/limbo-android-lib/src/main/jni/patches/qemu-4.0.0.patch b/limbo-android-lib/src/main/jni/patches/qemu-4.0.0.patch deleted file mode 100644 index e4f119f31..000000000 --- a/limbo-android-lib/src/main/jni/patches/qemu-4.0.0.patch +++ /dev/null @@ -1,361 +0,0 @@ -diff -ru --no-dereference /tmp/qemu-4.0.0/accel/kvm/kvm-all.c ./accel/kvm/kvm-all.c ---- /tmp/qemu-4.0.0/accel/kvm/kvm-all.c 2019-04-23 21:14:45.000000000 +0300 -+++ ./accel/kvm/kvm-all.c 2019-05-16 12:47:37.347790012 +0300 -@@ -1563,7 +1563,12 @@ - s->vmfd = -1; - s->fd = qemu_open("/dev/kvm", O_RDWR); - if (s->fd == -1) { -+#ifdef __LIMBO__ -+ //no m specifier in android -+ fprintf(stderr, "Could not access KVM kernel module: /dev/kvm\n"); -+#else - fprintf(stderr, "Could not access KVM kernel module: %m\n"); -+#endif - ret = -errno; - goto err; - } -diff -ru --no-dereference /tmp/qemu-4.0.0/block/vvfat.c ./block/vvfat.c ---- /tmp/qemu-4.0.0/block/vvfat.c 2019-04-23 21:14:45.000000000 +0300 -+++ ./block/vvfat.c 2019-05-16 12:45:07.763626279 +0300 -@@ -77,6 +77,10 @@ - #define DIR_KANJI_FAKE 0x05 - #define DIR_FREE 0x00 - -+#ifdef __LIMBO__ -+int size_clusters = 0; -+#endif //__LIMBO__ -+ - /* dynamic array functions */ - typedef struct array_t { - char* pointer; -@@ -3058,8 +3062,15 @@ - - for (i = sector2cluster(s, sector_num); - i <= sector2cluster(s, sector_num + nb_sectors - 1); i++) -+#ifdef __LIMBO__ -+ //FIXME: Limbo: For some reason the sector_num is lesser than the fake_sectors -+ // for now we add this check so qemu doesn't hang during a write operation -+ if (i >= 0 && i < size_clusters) -+ s->used_clusters[i] |= USED_ALLOCATED; -+#else - if (i >= 0) - s->used_clusters[i] |= USED_ALLOCATED; -+#endif - - DLOG(checkpoint()); - /* TODO: add timeout */ -@@ -3157,6 +3168,11 @@ - int size = sector2cluster(s, s->sector_count); - QDict *options; - -+//Limbo: -+#ifdef __LIMBO__ -+ size_clusters = size; -+#endif //__LIMBO__ -+ - s->used_clusters = calloc(size, 1); - - array_init(&(s->commits), sizeof(commit_t)); -diff -ru --no-dereference /tmp/qemu-4.0.0/configure ./configure ---- /tmp/qemu-4.0.0/configure 2019-04-23 21:14:45.000000000 +0300 -+++ ./configure 2019-05-16 15:14:56.669573910 +0300 -@@ -2278,7 +2278,8 @@ - # pkg-config probe - - if ! has "$pkg_config_exe"; then -- error_exit "pkg-config binary '$pkg_config_exe' not found" -+ #error_exit "pkg-config binary '$pkg_config_exe' not found" -+ echo Limbo: ignoring pkg-config - fi - - ########################################## -@@ -3009,7 +3010,8 @@ - sdlversion=$($sdlconfig --version) - else - if test "$sdl" = "yes" ; then -- feature_not_found "sdl" "Install SDL2-devel" -+ # feature_not_found "sdl" "Install SDL2-devel" -+ echo Limbo: ignoring SDL2-devel - fi - sdl=no - # no need to do the rest -@@ -3112,6 +3114,9 @@ - fi - fi - -+#Limbo: force sdl after bypassing probe -+sdl=yes -+ - ########################################## - # RDMA needs OpenFabrics libraries - if test "$rdma" != "no" ; then -@@ -3611,7 +3616,8 @@ - # glib support probe - - glib_req_ver=2.40 --glib_modules=gthread-2.0 -+# Limbo: no need for gthread -+# glib_modules=gthread-2.0 - if test "$modules" = yes; then - glib_modules="$glib_modules gmodule-export-2.0" - fi -@@ -3727,8 +3733,9 @@ - pixman_cflags=$($pkg_config --cflags pixman-1) - pixman_libs=$($pkg_config --libs pixman-1) - else -- error_exit "pixman >= 0.21.8 not present." \ -+ # error_exit "pixman >= 0.21.8 not present." \ - "Please install the pixman devel package." -+ echo Limbo: ignoring pixman devel - fi - - ########################################## -@@ -4029,6 +4036,9 @@ - preadv=yes - fi - -+#Limbo: preadv fails during linking -+preadv=no -+ - ########################################## - # fdt probe - # fdt support is mandatory for at least some target architectures, -@@ -5589,6 +5599,9 @@ - have_copy_file_range=yes - fi - -+# Limbo: no copy_file_range for Android -+have_copy_file_range=no -+ - ########################################## - # check if struct fsxattr is available via linux/fs.h - -diff -ru --no-dereference /tmp/qemu-4.0.0/include/ui/console.h ./include/ui/console.h ---- /tmp/qemu-4.0.0/include/ui/console.h 2019-04-23 21:14:46.000000000 +0300 -+++ ./include/ui/console.h 2019-05-16 12:46:26.839691439 +0300 -@@ -26,7 +26,13 @@ - #define QEMU_CAPS_LOCK_LED (1 << 2) - - /* in ms */ -+#ifdef __LIMBO__ -+extern int limbo_sdl_scale_hint; -+extern int gui_refresh_interval_default; -+#define GUI_REFRESH_INTERVAL_DEFAULT gui_refresh_interval_default -+#else - #define GUI_REFRESH_INTERVAL_DEFAULT 30 -+#endif //__LIMBO__ - #define GUI_REFRESH_INTERVAL_IDLE 3000 - - /* Color number is match to standard vga palette */ -diff -ru --no-dereference /tmp/qemu-4.0.0/Makefile ./Makefile ---- /tmp/qemu-4.0.0/Makefile 2019-04-23 21:14:45.000000000 +0300 -+++ ./Makefile 2019-05-16 12:49:19.991991417 +0300 -@@ -308,7 +308,8 @@ - - LIBS+=-lz $(LIBS_TOOLS) - --HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = qemu-bridge-helper$(EXESUF) -+# Limbo: no need for this -+#HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = qemu-bridge-helper$(EXESUF) - - ifdef BUILD_DOCS - DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8 -@@ -416,8 +417,8 @@ - audio-obj-y \ - audio-obj-m \ - trace-obj-y) -- --include $(SRC_PATH)/tests/Makefile.include -+# Limbo: no need for this -+#include $(SRC_PATH)/tests/Makefile.include - - all: $(DOCS) $(if $(BUILD_DOCS),sphinxdocs) $(TOOLS) $(HELPERS-y) recurse-all modules - -@@ -510,7 +511,8 @@ - qemu-nbd$(EXESUF): qemu-nbd.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) - qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) - --qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS) -+#Limbo: no need for this -+#qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS) - - qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS) - -diff -ru --no-dereference /tmp/qemu-4.0.0/Makefile.target ./Makefile.target ---- /tmp/qemu-4.0.0/Makefile.target 2019-04-23 21:14:45.000000000 +0300 -+++ ./Makefile.target 2019-05-16 12:51:04.236254663 +0300 -@@ -200,8 +200,11 @@ - COMMON_LDADDS = ../libqemuutil.a - - # build either PROG or PROGW --$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS) -- $(call LINK, $(filter-out %.mak, $^)) -+# Limbo: no need for the executable, will build the .so instead -+#$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS) -+# $(call LINK, $(filter-out %.mak, $^)) -+include ../../android-qemu-build.mak -+ - ifdef CONFIG_DARWIN - $(call quiet-command,Rez -append $(SRC_PATH)/pc-bios/qemu.rsrc -o $@,"REZ","$(TARGET_DIR)$@") - $(call quiet-command,SetFile -a C $@,"SETFILE","$(TARGET_DIR)$@") -diff -ru --no-dereference /tmp/qemu-4.0.0/migration/migration.c ./migration/migration.c ---- /tmp/qemu-4.0.0/migration/migration.c 2019-04-23 21:14:46.000000000 +0300 -+++ ./migration/migration.c 2019-05-16 23:09:55.680078614 +0300 -@@ -3448,11 +3448,16 @@ - qemu_mutex_destroy(&ms->qemu_file_lock); - g_free(params->tls_hostname); - g_free(params->tls_creds); -+ -+#ifndef __LIMBO__ -+//TODO: freeing causes crash for now we supress since the app is -+// about to close anyways - qemu_sem_destroy(&ms->rate_limit_sem); - qemu_sem_destroy(&ms->pause_sem); - qemu_sem_destroy(&ms->postcopy_pause_sem); - qemu_sem_destroy(&ms->postcopy_pause_rp_sem); - qemu_sem_destroy(&ms->rp_state.rp_sem); -+#endif //__LIMBO__ - error_free(ms->error); - } - -diff -ru --no-dereference /tmp/qemu-4.0.0/monitor.c ./monitor.c ---- /tmp/qemu-4.0.0/monitor.c 2019-04-23 21:14:46.000000000 +0300 -+++ ./monitor.c 2019-05-16 22:17:19.686597076 +0300 -@@ -2270,6 +2270,13 @@ - { - mon_fd_t *monfd; - -+#ifdef __LIMBO__ -+ //FIXME: The lookup for the fd fails below -+ // so for now we treat the fdname as the actual fd -+ int fd_tmp = atoi(fdname); -+ return fd_tmp; -+#endif //__LIMBO__ -+ - qemu_mutex_lock(&mon->mon_lock); - QLIST_FOREACH(monfd, &mon->fds, next) { - int fd; -diff -ru --no-dereference /tmp/qemu-4.0.0/ui/console.c ./ui/console.c ---- /tmp/qemu-4.0.0/ui/console.c 2019-04-23 21:14:46.000000000 +0300 -+++ ./ui/console.c 2019-05-16 13:09:33.262270480 +0300 -@@ -36,6 +36,11 @@ - #define DEFAULT_BACKSCROLL 512 - #define CONSOLE_CURSOR_PERIOD 500 - -+#ifdef __LIMBO__ -+int gui_refresh_interval_default = 30; -+#endif //__LIMBO__ -+ -+ - typedef struct TextAttributes { - uint8_t fgcol:4; - uint8_t bgcol:4; -diff -ru --no-dereference /tmp/qemu-4.0.0/ui/sdl2-2d.c ./ui/sdl2-2d.c ---- /tmp/qemu-4.0.0/ui/sdl2-2d.c 2019-04-23 21:14:46.000000000 +0300 -+++ ./ui/sdl2-2d.c 2019-05-16 13:01:26.138572680 +0300 -@@ -127,6 +127,11 @@ - surface_width(new_surface), - surface_height(new_surface)); - sdl2_2d_redraw(scon); -+ -+#ifdef __LIMBO__ -+ //TODO: Need to send the resolution to Limbo -+ Android_JNI_SetVMResolution(surface_width(new_surface), surface_height(new_surface)); -+#endif //__ANDROID__ - } - - void sdl2_2d_refresh(DisplayChangeListener *dcl) -diff -ru --no-dereference /tmp/qemu-4.0.0/ui/sdl2.c ./ui/sdl2.c ---- /tmp/qemu-4.0.0/ui/sdl2.c 2019-04-23 21:14:46.000000000 +0300 -+++ ./ui/sdl2.c 2019-05-16 13:04:41.834678642 +0300 -@@ -87,7 +87,15 @@ - surface_width(scon->surface), - surface_height(scon->surface), - flags); -+#if defined(__LIMBO_SDL_FORCE_SOFTWARE_RENDERING__) -+ //LIMBO: We can force SOFTWARE rendering when some devices don't HARDWARE acceleration -+ scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, SDL_RENDERER_SOFTWARE); -+#elif defined(__LIMBO_SDL_FORCE_HARDWARE_RENDERING__) -+ scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, SDL_RENDERER_ACCELERATED); -+#else - scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0); -+#endif -+ - if (scon->opengl) { - scon->winctx = SDL_GL_GetCurrentContext(); - } -@@ -752,6 +760,10 @@ - } - } - -+#ifdef __LIMBO__ -+int limbo_sdl_scale_hint = -1; -+#endif -+ - static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) - { - uint8_t data = 0; -@@ -761,6 +773,7 @@ - - assert(o->type == DISPLAY_TYPE_SDL); - -+#ifndef __ANDROID__ - #ifdef __linux__ - /* on Linux, SDL may use fbcon|directfb|svgalib when run without - * accessible $DISPLAY to open X11 window. This is often the case -@@ -773,6 +786,12 @@ - */ - setenv("SDL_VIDEODRIVER", "x11", 0); - #endif -+#else -+ if(limbo_sdl_scale_hint == 1) { -+ SDL_bool res = SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); -+ LOGI("Setting SDL_HINT_RENDER_SCALE_QUALITY to %d, code = %d", limbo_sdl_scale_hint, res); -+ } -+#endif - - if (SDL_Init(SDL_INIT_VIDEO)) { - fprintf(stderr, "Could not initialize SDL(%s) - exiting\n", -diff -ru --no-dereference /tmp/qemu-4.0.0/ui/vnc.c ./ui/vnc.c ---- /tmp/qemu-4.0.0/ui/vnc.c 2019-04-23 21:14:46.000000000 +0300 -+++ ./ui/vnc.c 2019-05-16 13:05:37.686527467 +0300 -@@ -47,8 +47,20 @@ - #include "qemu/cutils.h" - #include "io/dns-resolver.h" - -+//LIMBO: we can override the refresh rate for VNC here -+#ifdef __LIMBO__ -+int vnc_refresh_interval_base = 30; -+#define VNC_REFRESH_INTERVAL_BASE vnc_refresh_interval_base -+ -+int vnc_refresh_interval_inc = 30; -+#define VNC_REFRESH_INTERVAL_INC vnc_refresh_interval_inc -+#else -+ - #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT - #define VNC_REFRESH_INTERVAL_INC 50 -+ -+#endif //__LIMBO__ -+ - #define VNC_REFRESH_INTERVAL_MAX GUI_REFRESH_INTERVAL_IDLE - static const struct timeval VNC_REFRESH_STATS = { 0, 500000 }; - static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 }; -diff -ru --no-dereference /tmp/qemu-4.0.0/util/qemu-openpty.c ./util/qemu-openpty.c ---- /tmp/qemu-4.0.0/util/qemu-openpty.c 2019-04-23 21:14:46.000000000 +0300 -+++ ./util/qemu-openpty.c 2019-05-16 13:06:38.342406923 +0300 -@@ -108,6 +108,10 @@ - } - #endif - -+ -+#ifdef __ANDROID__ -+int qemu_openpty_raw(int *aslave, char *pty_name){return -1;} -+#else - int qemu_openpty_raw(int *aslave, char *pty_name) - { - int amaster; -@@ -135,3 +139,4 @@ - - return amaster; - } -+#endif //__ANDROID__ diff --git a/limbo-android-lib/src/main/jni/patches/qemu-5.1.0.patch b/limbo-android-lib/src/main/jni/patches/qemu-5.1.0.patch index a7c5cee7b..1eef3ef96 100644 --- a/limbo-android-lib/src/main/jni/patches/qemu-5.1.0.patch +++ b/limbo-android-lib/src/main/jni/patches/qemu-5.1.0.patch @@ -1,6 +1,6 @@ diff -ru --no-dereference /tmp/qemu-5.1.0/accel/kvm/kvm-all.c ./accel/kvm/kvm-all.c ---- /tmp/qemu-5.1.0/accel/kvm/kvm-all.c 2020-08-11 22:17:15.000000000 +0300 -+++ ./accel/kvm/kvm-all.c 2021-08-16 18:33:40.744081668 +0300 +--- /tmp/qemu-5.1.0/accel/kvm/kvm-all.c 2020-08-11 15:17:15.000000000 -0400 ++++ ./accel/kvm/kvm-all.c 2022-03-05 17:53:47.698530009 -0500 @@ -2015,7 +2015,12 @@ s->vmfd = -1; s->fd = qemu_open("/dev/kvm", O_RDWR); @@ -14,9 +14,39 @@ diff -ru --no-dereference /tmp/qemu-5.1.0/accel/kvm/kvm-all.c ./accel/kvm/kvm-al ret = -errno; goto err; } +diff -ru --no-dereference /tmp/qemu-5.1.0/audio/audio.c ./audio/audio.c +--- /tmp/qemu-5.1.0/audio/audio.c 2020-08-11 15:17:15.000000000 -0400 ++++ ./audio/audio.c 2022-03-05 17:53:47.718530383 -0500 +@@ -2005,7 +2005,11 @@ + + if (!pdo->has_frequency) { + pdo->has_frequency = true; ++#ifdef __LIMBO__ ++ pdo->frequency = 22050; ++#else + pdo->frequency = 44100; ++#endif + } + if (!pdo->has_channels) { + pdo->has_channels = true; +diff -ru --no-dereference /tmp/qemu-5.1.0/audio/audio_legacy.c ./audio/audio_legacy.c +--- /tmp/qemu-5.1.0/audio/audio_legacy.c 2020-08-11 15:17:15.000000000 -0400 ++++ ./audio/audio_legacy.c 2022-03-05 17:53:47.718530383 -0500 +@@ -105,7 +105,11 @@ + static uint32_t frames_to_usecs(uint32_t frames, + AudiodevPerDirectionOptions *pdo) + { ++#ifdef __LIMBO__ ++ uint32_t freq = pdo->has_frequency ? pdo->frequency : 22050; ++#else + uint32_t freq = pdo->has_frequency ? pdo->frequency : 44100; ++#endif + return (frames * 1000000 + freq / 2) / freq; + } + diff -ru --no-dereference /tmp/qemu-5.1.0/configure ./configure ---- /tmp/qemu-5.1.0/configure 2020-08-11 22:17:15.000000000 +0300 -+++ ./configure 2021-08-16 18:33:40.768081667 +0300 +--- /tmp/qemu-5.1.0/configure 2020-08-11 15:17:15.000000000 -0400 ++++ ./configure 2022-03-05 17:53:47.746530904 -0500 @@ -2501,7 +2501,8 @@ # pkg-config probe @@ -141,9 +171,70 @@ diff -ru --no-dereference /tmp/qemu-5.1.0/configure ./configure ########################################## # checks for sanitizers +diff -ru --no-dereference /tmp/qemu-5.1.0/exec.c ./exec.c +--- /tmp/qemu-5.1.0/exec.c 2022-03-05 15:50:10.824886801 -0500 ++++ ./exec.c 2022-03-05 17:56:20.141302931 -0500 +@@ -1003,6 +1003,9 @@ + tb_invalidate_phys_page_range(ram_addr, ram_addr + 1); + } + ++#ifdef __LIMBO__ ++int limbo_ignore_breakpoint_invalidate = 0; ++#endif + static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) + { + /* +@@ -1011,6 +1014,9 @@ + * Flush the whole TB cache to force re-translation of such TBs. + * This is heavyweight, but we're debugging anyway. + */ ++#ifdef __LIMBO__ ++ if(!limbo_ignore_breakpoint_invalidate) ++#endif + tb_flush(cpu); + } + #endif +diff -ru --no-dereference /tmp/qemu-5.1.0/hw/display/vga.c ./hw/display/vga.c +--- /tmp/qemu-5.1.0/hw/display/vga.c 2020-08-11 15:17:14.000000000 -0400 ++++ ./hw/display/vga.c 2022-03-05 17:53:47.774531425 -0500 +@@ -1458,6 +1458,12 @@ + memory_region_set_log(&s->vram, false, DIRTY_MEMORY_VGA); + } + ++#ifdef __LIMBO__ ++// LIMBO: we need to do a full screen refresh to align we can't do this via the SDL ++// layer so we pick the lesser evil and do it within the vga ++int limbo_vga_full_update; ++#endif ++ + /* + * graphic modes + */ +@@ -1674,7 +1680,11 @@ + if (!(s->cr[VGA_CRTC_MODE] & 2)) { + addr = (addr & ~0x8000) | ((y1 & 2) << 14); + } ++#ifdef __LIMBO__ ++ update = full_update || limbo_vga_full_update; ++#else + update = full_update; ++#endif + page0 = addr & s->vbe_size_mask; + page1 = (addr + bwidth - 1) & s->vbe_size_mask; + if (full_update) { +@@ -1726,6 +1736,9 @@ + /* flush to display */ + dpy_gfx_update(s->con, 0, y_start, + disp_width, y - y_start); ++#ifdef __LIMBO__ ++ limbo_vga_full_update = 0; ++#endif + } + g_free(snap); + memset(s->invalidated_y_table, 0, sizeof(s->invalidated_y_table)); diff -ru --no-dereference /tmp/qemu-5.1.0/include/qemu/osdep.h ./include/qemu/osdep.h ---- /tmp/qemu-5.1.0/include/qemu/osdep.h 2020-08-11 22:17:15.000000000 +0300 -+++ ./include/qemu/osdep.h 2021-08-16 18:33:40.780081667 +0300 +--- /tmp/qemu-5.1.0/include/qemu/osdep.h 2020-08-11 15:17:15.000000000 -0400 ++++ ./include/qemu/osdep.h 2022-03-05 17:53:47.790531723 -0500 @@ -36,6 +36,10 @@ #include "qemu/compiler.h" @@ -156,25 +247,28 @@ diff -ru --no-dereference /tmp/qemu-5.1.0/include/qemu/osdep.h ./include/qemu/os * stdlib.h unless we define these macros before first inclusion of * that system header. diff -ru --no-dereference /tmp/qemu-5.1.0/include/ui/console.h ./include/ui/console.h ---- /tmp/qemu-5.1.0/include/ui/console.h 2020-08-11 22:17:14.000000000 +0300 -+++ ./include/ui/console.h 2021-09-05 11:00:29.000925684 +0300 -@@ -26,7 +26,13 @@ +--- /tmp/qemu-5.1.0/include/ui/console.h 2020-08-11 15:17:14.000000000 -0400 ++++ ./include/ui/console.h 2022-03-05 17:53:47.806532021 -0500 +@@ -26,8 +26,16 @@ #define QEMU_CAPS_LOCK_LED (1 << 2) /* in ms */ +#ifdef __LIMBO__ +extern int limbo_sdl_scale_hint; +extern int gui_refresh_interval_default; ++extern int gui_refresh_interval_idle; +#define GUI_REFRESH_INTERVAL_DEFAULT gui_refresh_interval_default ++#define GUI_REFRESH_INTERVAL_IDLE gui_refresh_interval_idle +#else #define GUI_REFRESH_INTERVAL_DEFAULT 30 -+#endif //__LIMBO__ #define GUI_REFRESH_INTERVAL_IDLE 3000 ++#endif //__LIMBO__ /* Color number is match to standard vga palette */ + enum qemu_color_names { diff -ru --no-dereference /tmp/qemu-5.1.0/Makefile ./Makefile ---- /tmp/qemu-5.1.0/Makefile 2020-08-11 22:17:15.000000000 +0300 -+++ ./Makefile 2021-08-16 18:33:40.796081667 +0300 +--- /tmp/qemu-5.1.0/Makefile 2020-08-11 15:17:15.000000000 -0400 ++++ ./Makefile 2022-03-05 17:53:47.822532318 -0500 @@ -338,7 +338,8 @@ vhost-user-json-y = HELPERS-y = $(HELPERS) @@ -206,8 +300,8 @@ diff -ru --no-dereference /tmp/qemu-5.1.0/Makefile ./Makefile qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS) diff -ru --no-dereference /tmp/qemu-5.1.0/Makefile.target ./Makefile.target ---- /tmp/qemu-5.1.0/Makefile.target 2020-08-11 22:17:15.000000000 +0300 -+++ ./Makefile.target 2021-08-16 18:33:40.812081666 +0300 +--- /tmp/qemu-5.1.0/Makefile.target 2020-08-11 15:17:15.000000000 -0400 ++++ ./Makefile.target 2022-03-05 17:53:47.830532468 -0500 @@ -18,6 +18,12 @@ QEMU_CFLAGS+=-iquote $(SRC_PATH)/include @@ -235,8 +329,8 @@ diff -ru --no-dereference /tmp/qemu-5.1.0/Makefile.target ./Makefile.target $(call quiet-command,Rez -append $(SRC_PATH)/pc-bios/qemu.rsrc -o $@,"REZ","$(TARGET_DIR)$@") $(call quiet-command,SetFile -a C $@,"SETFILE","$(TARGET_DIR)$@") diff -ru --no-dereference /tmp/qemu-5.1.0/monitor/misc.c ./monitor/misc.c ---- /tmp/qemu-5.1.0/monitor/misc.c 2020-08-11 22:17:15.000000000 +0300 -+++ ./monitor/misc.c 2021-08-16 18:33:40.836081666 +0300 +--- /tmp/qemu-5.1.0/monitor/misc.c 2020-08-11 15:17:15.000000000 -0400 ++++ ./monitor/misc.c 2022-03-05 17:53:47.838532616 -0500 @@ -1299,6 +1299,14 @@ { mon_fd_t *monfd; @@ -253,22 +347,23 @@ diff -ru --no-dereference /tmp/qemu-5.1.0/monitor/misc.c ./monitor/misc.c QLIST_FOREACH(monfd, &mon->fds, next) { int fd; diff -ru --no-dereference /tmp/qemu-5.1.0/ui/console.c ./ui/console.c ---- /tmp/qemu-5.1.0/ui/console.c 2020-08-11 22:17:15.000000000 +0300 -+++ ./ui/console.c 2021-08-16 18:33:40.848081666 +0300 -@@ -38,6 +38,10 @@ +--- /tmp/qemu-5.1.0/ui/console.c 2020-08-11 15:17:15.000000000 -0400 ++++ ./ui/console.c 2022-03-05 17:53:47.854532914 -0500 +@@ -38,6 +38,11 @@ #define DEFAULT_BACKSCROLL 512 #define CONSOLE_CURSOR_PERIOD 500 +#ifdef __LIMBO__ +int gui_refresh_interval_default = 30; ++int gui_refresh_interval_idle = 300; +#endif //__LIMBO__ + typedef struct TextAttributes { uint8_t fgcol:4; uint8_t bgcol:4; diff -ru --no-dereference /tmp/qemu-5.1.0/ui/sdl2-2d.c ./ui/sdl2-2d.c ---- /tmp/qemu-5.1.0/ui/sdl2-2d.c 2020-08-11 22:17:15.000000000 +0300 -+++ ./ui/sdl2-2d.c 2021-08-16 18:33:40.848081666 +0300 +--- /tmp/qemu-5.1.0/ui/sdl2-2d.c 2020-08-11 15:17:15.000000000 -0400 ++++ ./ui/sdl2-2d.c 2022-03-05 17:53:47.862533063 -0500 @@ -125,6 +125,12 @@ surface_width(new_surface), surface_height(new_surface)); @@ -283,8 +378,8 @@ diff -ru --no-dereference /tmp/qemu-5.1.0/ui/sdl2-2d.c ./ui/sdl2-2d.c void sdl2_2d_refresh(DisplayChangeListener *dcl) diff -ru --no-dereference /tmp/qemu-5.1.0/ui/sdl2.c ./ui/sdl2.c ---- /tmp/qemu-5.1.0/ui/sdl2.c 2020-08-11 22:17:15.000000000 +0300 -+++ ./ui/sdl2.c 2021-09-06 00:42:21.782631272 +0300 +--- /tmp/qemu-5.1.0/ui/sdl2.c 2020-08-11 15:17:15.000000000 -0400 ++++ ./ui/sdl2.c 2022-03-05 17:53:47.878533361 -0500 @@ -89,7 +89,16 @@ surface_width(scon->surface), surface_height(scon->surface), @@ -303,7 +398,32 @@ diff -ru --no-dereference /tmp/qemu-5.1.0/ui/sdl2.c ./ui/sdl2.c if (scon->opengl) { scon->winctx = SDL_GL_GetCurrentContext(); } -@@ -789,6 +798,10 @@ +@@ -272,6 +281,13 @@ + }; + static uint32_t prev_state; + ++#ifdef __LIMBO__ ++// LIMBO: the console can be NULL when the mouse moves outside of the window ++// and it crashes the app ++ if(scon==NULL) ++ return; ++#endif ++ + if (prev_state != state) { + qemu_input_update_buttons(scon->dcl.con, bmap, prev_state, state); + prev_state = state; +@@ -627,6 +643,10 @@ + } + + while (SDL_PollEvent(ev)) { ++#ifdef __LIMBO__ ++ if(!ev) ++ continue; ++#endif + switch (ev->type) { + case SDL_KEYDOWN: + idle = 0; +@@ -789,6 +809,10 @@ } } @@ -314,7 +434,7 @@ diff -ru --no-dereference /tmp/qemu-5.1.0/ui/sdl2.c ./ui/sdl2.c static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) { uint8_t data = 0; -@@ -798,6 +811,7 @@ +@@ -798,6 +822,7 @@ assert(o->type == DISPLAY_TYPE_SDL); @@ -322,7 +442,7 @@ diff -ru --no-dereference /tmp/qemu-5.1.0/ui/sdl2.c ./ui/sdl2.c #ifdef __linux__ /* on Linux, SDL may use fbcon|directfb|svgalib when run without * accessible $DISPLAY to open X11 window. This is often the case -@@ -810,6 +824,12 @@ +@@ -810,6 +835,12 @@ */ g_setenv("SDL_VIDEODRIVER", "x11", 0); #endif @@ -336,8 +456,8 @@ diff -ru --no-dereference /tmp/qemu-5.1.0/ui/sdl2.c ./ui/sdl2.c if (SDL_Init(SDL_INIT_VIDEO)) { fprintf(stderr, "Could not initialize SDL(%s) - exiting\n", diff -ru --no-dereference /tmp/qemu-5.1.0/ui/vnc.c ./ui/vnc.c ---- /tmp/qemu-5.1.0/ui/vnc.c 2020-08-11 22:17:15.000000000 +0300 -+++ ./ui/vnc.c 2021-09-06 15:34:42.656266952 +0300 +--- /tmp/qemu-5.1.0/ui/vnc.c 2020-08-11 15:17:15.000000000 -0400 ++++ ./ui/vnc.c 2022-03-05 17:53:47.882533435 -0500 @@ -51,8 +51,19 @@ #include "qemu/cutils.h" #include "io/dns-resolver.h" @@ -371,7 +491,7 @@ diff -ru --no-dereference /tmp/qemu-5.1.0/ui/vnc.c ./ui/vnc.c + + if ((sym >= 123 && sym <= 126) + || (sym >= 33 && sym <= 38) -+ || (sym >= 40 && sym <= 44) ++ || (sym >= 40 && sym < 44) + || (sym >= 62 && sym <= 64) + || (sym >= 94 && sym <= 95) + || sym == 58 || sym == 60) { @@ -424,8 +544,8 @@ diff -ru --no-dereference /tmp/qemu-5.1.0/ui/vnc.c ./ui/vnc.c trace_vnc_key_event_ext(down, sym, keycode, code2name(keycode)); do_key_event(vs, down, keycode, sym); diff -ru --no-dereference /tmp/qemu-5.1.0/util/Makefile.objs ./util/Makefile.objs ---- /tmp/qemu-5.1.0/util/Makefile.objs 2020-08-11 22:17:15.000000000 +0300 -+++ ./util/Makefile.objs 2021-08-16 18:33:40.900081665 +0300 +--- /tmp/qemu-5.1.0/util/Makefile.objs 2020-08-11 15:17:15.000000000 -0400 ++++ ./util/Makefile.objs 2022-03-05 17:53:47.894533659 -0500 @@ -39,7 +39,8 @@ util-obj-y += range.o util-obj-y += stats64.o @@ -437,8 +557,8 @@ diff -ru --no-dereference /tmp/qemu-5.1.0/util/Makefile.objs ./util/Makefile.obj util-obj-$(CONFIG_GIO) += dbus.o dbus.o-cflags = $(GIO_CFLAGS) diff -ru --no-dereference /tmp/qemu-5.1.0/util/qemu-openpty.c ./util/qemu-openpty.c ---- /tmp/qemu-5.1.0/util/qemu-openpty.c 2020-08-11 22:17:15.000000000 +0300 -+++ ./util/qemu-openpty.c 2021-08-16 18:33:40.908081665 +0300 +--- /tmp/qemu-5.1.0/util/qemu-openpty.c 2020-08-11 15:17:15.000000000 -0400 ++++ ./util/qemu-openpty.c 2022-03-05 17:53:47.902533807 -0500 @@ -111,6 +111,9 @@ } #endif diff --git a/limbo-android-lib/src/main/jni/patches/sdl2-2.0.8.patch b/limbo-android-lib/src/main/jni/patches/sdl2-2.0.8.patch index a4e801cdb..74547eb3c 100644 --- a/limbo-android-lib/src/main/jni/patches/sdl2-2.0.8.patch +++ b/limbo-android-lib/src/main/jni/patches/sdl2-2.0.8.patch @@ -1,50 +1,117 @@ diff -ru --no-dereference /tmp/SDL2-2.0.8/Android.mk ./Android.mk ---- /tmp/SDL2-2.0.8/Android.mk 2018-03-01 18:34:41.000000000 +0200 -+++ ./Android.mk 2021-09-19 22:52:44.403970089 +0300 -@@ -51,6 +51,10 @@ +--- /tmp/SDL2-2.0.8/Android.mk 2018-03-01 11:34:41.000000000 -0500 ++++ ./Android.mk 2022-03-03 05:05:48.223192739 -0500 +@@ -51,6 +51,14 @@ LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES LOCAL_LDLIBS := -ldl -lGLESv1_CM -lGLESv2 -llog -landroid -+# LIMBO: extend to our audio library -+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../compat/sdl-addons -+LOCAL_SHARED_LIBRARIES += compat-SDL2-addons ++# LIMBO: ++LOCAL_CFLAGS += -D__LIMBO__ ++ifeq ($(USE_AAUDIO),true) ++ LOCAL_CFLAGS += -D__ENABLE_AAUDIO__ ++endif ++LOCAL_LDLIBS += -llog +#LIMBO ++ cmd-strip := include $(BUILD_SHARED_LIBRARY) diff -ru --no-dereference /tmp/SDL2-2.0.8/src/core/android/SDL_android.c ./src/core/android/SDL_android.c ---- /tmp/SDL2-2.0.8/src/core/android/SDL_android.c 2018-03-01 18:34:42.000000000 +0200 -+++ ./src/core/android/SDL_android.c 2021-09-19 23:15:28.204809157 +0300 -@@ -1001,8 +1001,23 @@ +--- /tmp/SDL2-2.0.8/src/core/android/SDL_android.c 2018-03-01 11:34:42.000000000 -0500 ++++ ./src/core/android/SDL_android.c 2022-03-02 17:18:20.593569237 -0500 +@@ -1001,8 +1001,86 @@ static jboolean captureBuffer16Bit = JNI_FALSE; static jobject captureBuffer = NULL; +#if defined(__LIMBO__) && defined(__ENABLE_AAUDIO__) -+#include "SDL_limboaudio.h" -+extern int isAaudioEnabled(); -+extern void createAAudioDevice(int sampleRate, int channelCount, int desiredBufferFrames); -+extern void destroyAaudioDevice(); -+extern void writeAaudio() ; -+extern void* getAaudioBuffer(); ++#include ++int enableAaudio = 0; ++typedef void (*createAAudioDevice_t)(int, int, int); ++createAAudioDevice_t createAAudioDevice = NULL; ++typedef void (*destroyAaudioDevice_t)(void); ++destroyAaudioDevice_t destroyAaudioDevice = NULL; ++typedef int (*writeAaudio_t)(void); ++writeAaudio_t writeAaudio = NULL; ++typedef void* (*getAaudioBuffer_t)(void); ++getAaudioBuffer_t getAaudioBuffer = NULL; ++void *ldhandle = NULL; ++const char *aaudioLib = NULL; ++const char *aaudioLibPath = NULL; ++const char *dlsym_error = NULL; ++ ++void* loadLib() { ++ dlerror(); ++ void* handle = dlopen(aaudioLib, RTLD_LAZY); ++ if(handle == NULL) { ++ printf("trying loading with full path: %s\n", aaudioLibPath); ++ handle = dlopen(aaudioLibPath, RTLD_LAZY); ++ } ++ dlsym_error = dlerror(); ++ if (dlsym_error) { ++ printf("Cannot load library %s: %s\n", aaudioLib, dlsym_error); ++ return NULL; ++ } ++ return handle; ++} ++ ++void* loadSymbol(const char *sym) { ++ dlerror(); ++ void *sym_func = dlsym(ldhandle, sym); ++ dlsym_error = dlerror(); ++ if (dlsym_error) { ++ LOGE("Cannot load symbol %s: %s\n", sym, dlsym_error); ++ return NULL; ++ } ++ return sym_func; ++} ++ ++void loadSymbols() { ++ createAAudioDevice = (createAAudioDevice_t) loadSymbol("createAAudioDevice"); ++ destroyAaudioDevice = (destroyAaudioDevice_t) loadSymbol("destroyAaudioDevice"); ++ writeAaudio = (writeAaudio_t) loadSymbol("writeAaudio"); ++ getAaudioBuffer = (getAaudioBuffer_t) loadSymbol("getAaudioBuffer"); ++} ++ ++JNIEXPORT void JNICALL Java_com_max2idea_android_limbo_jni_VMExecutor_nativeEnableAaudio( ++ JNIEnv* env, jobject thiz, ++ int value, jstring aaudioLibName, jstring aaudioLibFullpath) { ++ printf("set enable aaudio: %d\n", value); ++ if (aaudioLibName != NULL) ++ aaudioLib = (*env)->GetStringUTFChars(env, aaudioLibName, 0); ++ ++ if (aaudioLibFullpath != NULL) ++ aaudioLibPath = (*env)->GetStringUTFChars(env, aaudioLibFullpath, 0); ++ ++ enableAaudio = value; ++} ++ +#endif // LIMBO + int Android_JNI_OpenAudioDevice(int iscapture, int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames) { +#if defined(__LIMBO__) && defined(__ENABLE_AAUDIO__) -+ if(isAaudioEnabled()) { -+ createAAudioDevice(sampleRate, channelCount, desiredBufferFrames); -+ return desiredBufferFrames; ++ if(enableAaudio) { ++ ldhandle = loadLib(); ++ if(ldhandle != NULL) { ++ loadSymbols(); ++ if(createAAudioDevice != NULL) { ++ createAAudioDevice(sampleRate, channelCount, desiredBufferFrames); ++ return desiredBufferFrames; ++ } ++ } ++ enableAaudio = 0; ++ printf("Aaudio could not be loaded%d\n"); + } +#endif jboolean audioBufferStereo; int audioBufferFrames; jobject jbufobj = NULL; -@@ -1119,11 +1134,28 @@ +@@ -1119,11 +1197,28 @@ void * Android_JNI_GetAudioBuffer(void) { +#if defined(__LIMBO__) && defined(__ENABLE_AAUDIO__) -+ if(isAaudioEnabled()) ++ if(enableAaudio && getAaudioBuffer != NULL) + return getAaudioBuffer(); +#endif return audioBufferPinned; @@ -53,7 +120,7 @@ diff -ru --no-dereference /tmp/SDL2-2.0.8/src/core/android/SDL_android.c ./src/c void Android_JNI_WriteAudioBuffer(void) { +#if defined(__LIMBO__) && defined(__ENABLE_AAUDIO__) -+ if(isAaudioEnabled()) { ++ if(enableAaudio && writeAaudio != NULL) { + writeAaudio(); + return; + } @@ -68,19 +135,20 @@ diff -ru --no-dereference /tmp/SDL2-2.0.8/src/core/android/SDL_android.c ./src/c JNIEnv *mAudioEnv = Android_JNI_GetEnv(); if (audioBuffer16Bit) { -@@ -1203,6 +1235,9 @@ +@@ -1203,6 +1298,10 @@ audioBufferPinned = NULL; } } +#if defined(__LIMBO__) && defined(__ENABLE_AAUDIO__) -+ destroyAaudioDevice(); ++ if(enableAaudio && destroyAaudioDevice!=NULL) ++ destroyAaudioDevice(); +#endif } /* Test for an exception and call SDL_SetError with its detail if one occurs */ diff -ru --no-dereference /tmp/SDL2-2.0.8/src/events/SDL_mouse.c ./src/events/SDL_mouse.c ---- /tmp/SDL2-2.0.8/src/events/SDL_mouse.c 2018-03-01 18:34:42.000000000 +0200 -+++ ./src/events/SDL_mouse.c 2021-09-14 13:04:17.955954017 +0300 +--- /tmp/SDL2-2.0.8/src/events/SDL_mouse.c 2018-03-01 11:34:42.000000000 -0500 ++++ ./src/events/SDL_mouse.c 2022-03-02 17:18:20.597569238 -0500 @@ -655,6 +655,10 @@ void SDL_WarpMouseInWindow(SDL_Window * window, int x, int y) diff --git a/limbo-android-lib/src/main/res/color/spinner_color.xml b/limbo-android-lib/src/main/res/color/spinner_color.xml index 18825a14a..18b227078 100644 --- a/limbo-android-lib/src/main/res/color/spinner_color.xml +++ b/limbo-android-lib/src/main/res/color/spinner_color.xml @@ -1,8 +1,6 @@ - - - - - - + + + + \ No newline at end of file diff --git a/limbo-android-lib/src/main/res/drawable/border.xml b/limbo-android-lib/src/main/res/drawable/border.xml index 2efad6687..f95551145 100644 --- a/limbo-android-lib/src/main/res/drawable/border.xml +++ b/limbo-android-lib/src/main/res/drawable/border.xml @@ -1,7 +1,7 @@ + android:shape="rectangle"> + android:color="#808080" /> \ No newline at end of file diff --git a/limbo-android-lib/src/main/res/layout/advanced_layout.xml b/limbo-android-lib/src/main/res/layout/advanced_layout.xml new file mode 100644 index 000000000..760aff297 --- /dev/null +++ b/limbo-android-lib/src/main/res/layout/advanced_layout.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/limbo-android-lib/src/main/res/layout/audio_layout.xml b/limbo-android-lib/src/main/res/layout/audio_layout.xml new file mode 100644 index 000000000..7684357c8 --- /dev/null +++ b/limbo-android-lib/src/main/res/layout/audio_layout.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/limbo-android-lib/src/main/res/layout/board_layout.xml b/limbo-android-lib/src/main/res/layout/board_layout.xml new file mode 100644 index 000000000..d9619b216 --- /dev/null +++ b/limbo-android-lib/src/main/res/layout/board_layout.xml @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/limbo-android-lib/src/main/res/layout/boot_layout.xml b/limbo-android-lib/src/main/res/layout/boot_layout.xml new file mode 100644 index 000000000..e4ac1e701 --- /dev/null +++ b/limbo-android-lib/src/main/res/layout/boot_layout.xml @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/limbo-android-lib/src/main/res/layout/connection_list.xml b/limbo-android-lib/src/main/res/layout/connection_list.xml deleted file mode 100644 index 3f140ebbd..000000000 --- a/limbo-android-lib/src/main/res/layout/connection_list.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - diff --git a/limbo-android-lib/src/main/res/layout/controls_layout.xml b/limbo-android-lib/src/main/res/layout/controls_layout.xml new file mode 100644 index 000000000..14f33f7ce --- /dev/null +++ b/limbo-android-lib/src/main/res/layout/controls_layout.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/limbo-android-lib/src/main/res/layout/entertext.xml b/limbo-android-lib/src/main/res/layout/entertext.xml deleted file mode 100644 index 6bd5e8f96..000000000 --- a/limbo-android-lib/src/main/res/layout/entertext.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - -