diff --git a/app/build.gradle b/app/build.gradle index 9fe7fe2..74fe705 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,8 +15,8 @@ android { applicationId "com.vectras.vm" minSdk minApi targetSdk targetApi - versionCode 63 - versionName "3.5.9" + versionCode 64 + versionName "3.6.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true diff --git a/app/src/main/java/android/androidVNC/AbstractBitmapData.java b/app/src/main/java/android/androidVNC/AbstractBitmapData.java index bf63352..593e9a9 100644 --- a/app/src/main/java/android/androidVNC/AbstractBitmapData.java +++ b/app/src/main/java/android/androidVNC/AbstractBitmapData.java @@ -15,189 +15,172 @@ 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; + 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; - } + AbstractBitmapData(RfbProto p, VncCanvas c) { + rfb = p; + vncCanvas = c; + framebufferwidth = rfb.framebufferWidth; + framebufferheight = rfb.framebufferHeight; + } - synchronized void doneWaiting() { - waitingForInput = false; - } + 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); - } - } + 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); - } + /** + * + * @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; + /** + * 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); + /** + * 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); + /** + * 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); + /** + * 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(); + /** + * 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(); - } + /** + * Call in UI thread; tell ImageView we've changed + * + * @param v ImageView displaying bitmap data + */ + void updateView(ImageView v) { + if (drawable == null) + drawable = createDrawable(); - /** - * 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); + if (drawable != null) { + v.setImageDrawable(drawable); + v.invalidate(); + } + } - /** - * 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); + /** + * 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); - /** - * 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); + /** + * 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); - /** - * Sync scroll -- called from network thread; copies scroll changes from UI - * to network state - */ - abstract void syncScroll(); + /** + * 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); - /** - * 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; - } + /** + * 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/app/src/main/java/android/androidVNC/VncCanvas.java b/app/src/main/java/android/androidVNC/VncCanvas.java index 554ac07..84b77b8 100644 --- a/app/src/main/java/android/androidVNC/VncCanvas.java +++ b/app/src/main/java/android/androidVNC/VncCanvas.java @@ -2055,6 +2055,17 @@ public class VncCanvas extends AppCompatImageView { activity.onDisconnected(); } + public float getFramebufferWidth() { + return bitmapData.framebufferwidth; + } + + public float getFramebufferHeight() { + return bitmapData.framebufferheight; + } + + public AbstractBitmapData getBitmapData() { + return bitmapData; + } class VNCOnTouchListener implements View.OnTouchListener { diff --git a/app/src/main/java/android/androidVNC/VncCanvasActivity.java b/app/src/main/java/android/androidVNC/VncCanvasActivity.java index 06a960a..fe89845 100644 --- a/app/src/main/java/android/androidVNC/VncCanvasActivity.java +++ b/app/src/main/java/android/androidVNC/VncCanvasActivity.java @@ -1,14 +1,14 @@ -/* +/* * 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, @@ -45,6 +45,8 @@ import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; +import android.widget.FrameLayout; +import android.widget.LinearLayout; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.ZoomControls; @@ -62,332 +64,333 @@ import androidx.appcompat.app.AppCompatActivity; public abstract class VncCanvasActivity extends AppCompatActivity { - static Display display = null; - public static Activity activity; + static Display display = null; + public static Activity activity; + public float scale = 0; - /** - * @author Michael A. MacDonald - */ - class ZoomInputHandler extends AbstractGestureInputHandler { + /** + * @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; + /** + * 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); - } + /** + * @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#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.AbstractInputHandler#getName() + */ + @Override + public String getName() { + return TOUCH_ZOOM_MODE; + } - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.VncCanvasActivity.ZoomInputHandler#onKeyDown(int, - * android.view.SimulateKeyEvent) - */ - @Override - public boolean onKeyDown(int keyCode, KeyEvent evt) { - return keyHandler.onKeyDown(keyCode, evt); - } + /* + * (non-Javadoc) + * + * @see + * android.androidVNC.VncCanvasActivity.ZoomInputHandler#onKeyDown(int, + * android.view.SimulateKeyEvent) + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent evt) { + return keyHandler.onKeyDown(keyCode, evt); + } - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.VncCanvasActivity.ZoomInputHandler#onKeyUp(int, - * android.view.SimulateKeyEvent) - */ - @Override - public boolean onKeyUp(int keyCode, KeyEvent evt) { - return keyHandler.onKeyUp(keyCode, evt); - } + /* + * (non-Javadoc) + * + * @see + * android.androidVNC.VncCanvasActivity.ZoomInputHandler#onKeyUp(int, + * android.view.SimulateKeyEvent) + */ + @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.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; - } + /* + * (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; + /** + * 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.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.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; + /* + * (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); - } - } + 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) { + /* + * (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#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# + * 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); - } - } + /* + * (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 { + 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; + /** + * 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); - } + 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#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.AbstractInputHandler#getName() + */ + @Override + public String getName() { + return TOUCHPAD_MODE; + } - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.VncCanvasActivity.ZoomInputHandler#onKeyDown(int, - * android.view.SimulateKeyEvent) - */ - @Override - public boolean onKeyDown(int keyCode, KeyEvent evt) { - return keyHandler.onKeyDown(keyCode, evt); - } + /* + * (non-Javadoc) + * + * @see + * android.androidVNC.VncCanvasActivity.ZoomInputHandler#onKeyDown(int, + * android.view.SimulateKeyEvent) + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent evt) { + return keyHandler.onKeyDown(keyCode, evt); + } - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.VncCanvasActivity.ZoomInputHandler#onKeyUp(int, - * android.view.SimulateKeyEvent) - */ - @Override - public boolean onKeyUp(int keyCode, KeyEvent evt) { - return keyHandler.onKeyUp(keyCode, evt); - } + /* + * (non-Javadoc) + * + * @see + * android.androidVNC.VncCanvasActivity.ZoomInputHandler#onKeyUp(int, + * android.view.SimulateKeyEvent) + */ + @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.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; - } + /** + * 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); - } + /* + * (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) { @@ -399,19 +402,18 @@ public abstract class VncCanvasActivity extends AppCompatActivity { // 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) { - //VECTRAS: Disable this for now + * (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) { + //VECTRAS: Disable this for now // if (BCFactory.getInstance().getBCMotionEvent().getPointerCount(e2) > 1) { // if (inScaling) { // return false; @@ -419,41 +421,43 @@ public abstract class VncCanvasActivity extends AppCompatActivity { // 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 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; + // 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); - } + if (dragMode) { + if (e2.getAction() == MotionEvent.ACTION_UP) { + dragMode = false; + } + dragX = e2.getX(); + dragY = e2.getY(); + e2.setLocation(newRemoteX, newRemoteY); + updateVirtualMouse(e2); + return vncCanvas.processPointerEvent(e2, true); + } else { + e2.setLocation(newRemoteX, newRemoteY); + updateVirtualMouse(e2); + vncCanvas.processPointerEvent(e2, false); + } // } - return true; - } + return true; + } - /* - * (non-Javadoc) - * - * @see - * android.androidVNC.AbstractGestureInputHandler#onTouchEvent(android - * .view.MotionEvent) - */ - @Override - public boolean onTouchEvent(MotionEvent e) { + /* + * (non-Javadoc) + * + * @see + * android.androidVNC.AbstractGestureInputHandler#onTouchEvent(android + * .view.MotionEvent) + */ + @Override + public boolean onTouchEvent(MotionEvent e) { // MK if (e.getAction() == MotionEvent.ACTION_CANCEL) @@ -489,116 +493,126 @@ public abstract class VncCanvasActivity extends AppCompatActivity { } e.setLocation(newRemoteX, newRemoteY); + updateVirtualMouse(e); vncCanvas.processPointerEvent(e, down); return super.onTouchEvent(e); } else if (!Config.enableDragOnLongPress && e.getAction() == MotionEvent.ACTION_MOVE) { e.setLocation(newRemoteX, newRemoteY); + updateVirtualMouse(e); return vncCanvas.processPointerEvent(e, false); } else { - return super.onTouchEvent(e); - } - } + 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; + public void updateVirtualMouse(MotionEvent e) { + if (cursorView.getVisibility() == View.VISIBLE) { + float viewX = e.getX(); + float viewY = e.getY(); + cursorView.update(viewX * scale, viewY * scale); + } + } - } + 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; - public boolean leftClick(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; + } - } + public boolean leftClick(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); + } - } + /** + * 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); - } + /* + * (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) + */ - 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; + @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; + + } public boolean middleClick(final MotionEvent e) { Thread t = new Thread(new Runnable() { @@ -624,23 +638,23 @@ public abstract class VncCanvasActivity extends AppCompatActivity { } - /* - * (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) + /* + * (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() { @@ -660,33 +674,33 @@ public abstract class VncCanvasActivity extends AppCompatActivity { } }); t.start(); - } + } /* - * (non-Javadoc) - * - * @see - * android.view.GestureDetector.SimpleOnGestureListener#onDown(android - * .view.MotionEvent) - */ - @Override - public boolean onDown(MotionEvent e) { - panner.stop(); - return true; - } + * (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 Object doubleClickLock = new Object(); - private boolean doubleClick(final MotionEvent e1) { - Thread t = new Thread(new Runnable() { - public void run() { + 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()); + e1.getEventTime(), e1.getAction(), + e1.getX(), e1.getY(), e1.getMetaState()); remoteMouseStayPut(event); // One @@ -716,1119 +730,1134 @@ public abstract class VncCanvasActivity extends AppCompatActivity { event.setAction(MotionEvent.ACTION_UP); vncCanvas.processPointerEvent(event, false, false); } - } - }); - // t.setPriority(Thread.MAX_PRIORITY); - t.start(); - return true; + } + }); + // t.setPriority(Thread.MAX_PRIORITY); + t.start(); + return true; - } - } + } + } - private final static String TAG = "VncCanvasActivity"; - public static AbstractInputHandler inputHandler; - public VncCanvas vncCanvas; + private final static String TAG = "VncCanvasActivity"; + public static AbstractInputHandler inputHandler; + public VncCanvas vncCanvas; + public VncCursorView cursorView; - 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; + 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) { + @Override + public void onCreate(Bundle icicle) { - super.onCreate(icicle); - activity = this; + 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 { + 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(); + Bundle extras = i.getExtras(); - if (connection.getPort() == 0) { - connection.setPort(5901); - } + 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(null); - setContentView(); + // 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(null); + setContentView(); - vncCanvas = (VncCanvas) findViewById(R.id.vnc_canvas); - zoomer = (ZoomControls) findViewById(R.id.zoomer); + vncCanvas = (VncCanvas) findViewById(R.id.vnc_canvas); + zoomer = (ZoomControls) findViewById(R.id.zoomer); + cursorView = findViewById(R.id.cursorView); - vncCanvas.initializeVncCanvas(connection, new Runnable() { - public void run() { - setModes(); - } - }); - zoomer.hide(); - zoomer.setOnZoomInClickListener(new View.OnClickListener() { + 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); + /* + * (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() { + } + }); + 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); + /* + * (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() { + } + }); + 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); + } + }); + 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); + /* + * (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); + inputHandler = getInputHandlerById(R.id.itemInputFitToScreen); - display = ((WindowManager) this.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); - } + display = ((WindowManager) this.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + } - public void setContentView() { - setContentView(R.layout.activity_vnc); - } + public void setContentView() { + setContentView(R.layout.activity_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(); - } + /** + * 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; - } + 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); - } - } + /* + * (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 + public void onConfigurationChanged(Configuration newConfig) { + // ignore orientation/keyboard change + super.onConfigurationChanged(newConfig); + } - @Override - protected void onStop() { - vncCanvas.disableRepaints(); - super.onStop(); - } + @Override + protected void onStop() { + vncCanvas.disableRepaints(); + super.onStop(); + } - @Override - protected void onRestart() { - vncCanvas.enableRepaints(); - super.onRestart(); - } + @Override + protected void onRestart() { + vncCanvas.enableRepaints(); + super.onRestart(); + } - /** - * {@inheritDoc} - */ - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.vnccanvasactivitymenu, menu); + /** + * {@inheritDoc} + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.vnccanvasactivitymenu, menu); - if (vncCanvas.scaling != null) { - menu.findItem(vncCanvas.scaling.getId()).setChecked(true); - } + if (vncCanvas.scaling != null) { + menu.findItem(vncCanvas.scaling.getId()).setChecked(true); + } - Menu inputMenu = menu.findItem(R.id.itemInputMode).getSubMenu(); + 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; - } + 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; - } - - private MetaKeyBean lastSentKey; - - @Override - protected void onDestroy() { - super.onDestroy(); - if (isFinishing()) { - vncCanvas.closeConnection(); - vncCanvas.onDestroy(); + /** + * 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(); - @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) { + 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; + } + + 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).leftClick(e); return true; } else if (keyCode == KeyEvent.KEYCODE_MENU) { - return super.onKeyDown(keyCode, evt); - } + 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); - } + return inputHandler.onKeyDown(keyCode, evt); + } @Override - public boolean onTouchEvent(MotionEvent event) { - // MK - if (event.getAction() == MotionEvent.ACTION_CANCEL) - return true; + public boolean onKeyUp(int keyCode, KeyEvent evt) { + if (keyCode == KeyEvent.KEYCODE_MENU) { + return super.onKeyUp(keyCode, evt); + } - if(event.getAction() == MotionEvent.ACTION_DOWN) + 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) + else if (event.getAction() == MotionEvent.ACTION_UP) vncCanvas.mouseDown = false; - return inputHandler.onTouchEvent(event); - } + return inputHandler.onTouchEvent(event); + } - protected void selectColorModel() { - // Stop repainting the desktop - // because the display is composited! - vncCanvas.disableRepaints(); + 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; - } - } + 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()); + 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.SimulateKeyEvent) - */ - @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.SimulateKeyEvent) - */ - @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.SimulateKeyEvent) - */ - @Override - public boolean onKeyDown(int keyCode, KeyEvent evt) { - return keyHandler.onKeyDown(keyCode, evt); - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#onKeyUp(int, - * android.view.SimulateKeyEvent) - */ - @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.SimulateKeyEvent) - */ - @Override - public boolean onKeyDown(int keyCode, KeyEvent evt) { - return keyHandler.onKeyDown(keyCode, evt); - } - - /* - * (non-Javadoc) - * - * @see android.androidVNC.AbstractInputHandler#onKeyUp(int, - * android.view.SimulateKeyEvent) - */ - @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.SimulateKeyEvent) - */ - @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.SimulateKeyEvent) - */ - @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.SimulateKeyEvent) - */ - @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.SimulateKeyEvent) - */ - @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"; - } - } + } + }); + 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.SimulateKeyEvent) + */ + @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.SimulateKeyEvent) + */ + @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.SimulateKeyEvent) + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent evt) { + return keyHandler.onKeyDown(keyCode, evt); + } + + /* + * (non-Javadoc) + * + * @see android.androidVNC.AbstractInputHandler#onKeyUp(int, + * android.view.SimulateKeyEvent) + */ + @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.SimulateKeyEvent) + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent evt) { + return keyHandler.onKeyDown(keyCode, evt); + } + + /* + * (non-Javadoc) + * + * @see android.androidVNC.AbstractInputHandler#onKeyUp(int, + * android.view.SimulateKeyEvent) + */ + @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.SimulateKeyEvent) + */ + @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.SimulateKeyEvent) + */ + @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.SimulateKeyEvent) + */ + @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.SimulateKeyEvent) + */ + @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() { } - public void onDisconnected() { + public void onDisconnected() { - } + } - public void reconnect() { - // Recreate canvas to change resolution as resolution when changed in some Linux OS may cause canvas to not resize accordingly. + public void reconnect() { + // Recreate canvas to change resolution as resolution when changed in some Linux OS may cause canvas to not resize accordingly. - // Remove old canvas - ViewGroup parent = findViewById(R.id.vnc_canvas_layout); - parent.removeView(vncCanvas); + // Remove old canvas + ViewGroup parent = findViewById(R.id.vnc_canvas_layout); + parent.removeView(vncCanvas); - // Create new canvas - VncCanvas newCanvas = new VncCanvas(this); - newCanvas.setId(R.id.vnc_canvas); // Reassign ID - newCanvas.setKeepScreenOn(true); + // Create new canvas + VncCanvas newCanvas = new VncCanvas(this); + newCanvas.setId(R.id.vnc_canvas); // Reassign ID + newCanvas.setKeepScreenOn(true); // Add to parent - RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ); - params.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE); + RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ); + params.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE); - parent.addView(newCanvas, params); + parent.addView(newCanvas, params); // Reassign reference variable - vncCanvas = newCanvas; + vncCanvas = newCanvas; - vncCanvas.initializeVncCanvas(connection, new Runnable() { - public void run() { - setModes(); - } - }); - } + vncCanvas.initializeVncCanvas(connection, new Runnable() { + public void run() { + setModes(); + } + }); + } + + public void syncCursorViewWithBitmap() { + scale = (float) vncCanvas.getHeight() / vncCanvas.getFramebufferHeight(); + + vncCanvas.post(() -> { + LinearLayout.LayoutParams lp = + (LinearLayout.LayoutParams) cursorView.getLayoutParams(); + + lp.width = Math.round(vncCanvas.getFramebufferWidth() * scale); + lp.height = Math.round(vncCanvas.getFramebufferHeight() * scale); + + cursorView.setLayoutParams(lp); + }); + } } diff --git a/app/src/main/java/android/androidVNC/VncCursorView.java b/app/src/main/java/android/androidVNC/VncCursorView.java new file mode 100644 index 0000000..72c676b --- /dev/null +++ b/app/src/main/java/android/androidVNC/VncCursorView.java @@ -0,0 +1,53 @@ +package android.androidVNC; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.util.AttributeSet; +import android.view.View; + +import androidx.annotation.NonNull; + +import com.vectras.vm.R; + +public class VncCursorView extends View { + + private Bitmap cursor; + private float x, y; + + public VncCursorView(Context context) { + super(context); + init(context); + } + + public VncCursorView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public VncCursorView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context); + } + + private void init(Context context) { + cursor = BitmapFactory.decodeResource( + context.getResources(), + R.drawable.xc_left_ptr + ); + } + + public void update(float x, float y) { + this.x = x; + this.y = y; + invalidate(); + } + + @Override + protected void onDraw(@NonNull Canvas canvas) { + if (cursor != null) { + canvas.drawBitmap(cursor, x, y, null); + } + } +} diff --git a/app/src/main/java/com/vectras/qemu/MainSettingsManager.java b/app/src/main/java/com/vectras/qemu/MainSettingsManager.java index c4ac0d1..7f42d1e 100644 --- a/app/src/main/java/com/vectras/qemu/MainSettingsManager.java +++ b/app/src/main/java/com/vectras/qemu/MainSettingsManager.java @@ -1143,4 +1143,16 @@ public class MainSettingsManager extends AppCompatActivity SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); return prefs.getBoolean("showLastCrashLog", false); } + + public static void setShowVirtualMouse(Context context, Boolean _boolean) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor edit = prefs.edit(); + edit.putBoolean("showVirtualMouse", _boolean); + edit.apply(); + } + + public static Boolean getShowVirtualMouse(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean("showVirtualMouse", false); + } } diff --git a/app/src/main/java/com/vectras/qemu/MainVNCActivity.java b/app/src/main/java/com/vectras/qemu/MainVNCActivity.java index 45f7007..431af89 100644 --- a/app/src/main/java/com/vectras/qemu/MainVNCActivity.java +++ b/app/src/main/java/com/vectras/qemu/MainVNCActivity.java @@ -79,7 +79,7 @@ public class MainVNCActivity extends VncCanvasActivity { private final String TAG = "MainVNCActivity"; public static MainVNCActivity getContext; private final int retryLimit = 3; - private ActivityVncBinding binding; + public ActivityVncBinding binding; private ControlsFragmentBinding bindingControls; private DesktopControlsBinding bindingDesktopControls; private GameControlsBinding bindingGameControls; @@ -163,6 +163,8 @@ public class MainVNCActivity extends VncCanvasActivity { // Do not attempt to reconnect while connected. reconnect(); }); + + binding.cursorView.setVisibility(MainSettingsManager.getShowVirtualMouse(this) || VMManager.isNeedUseVirtualMouse() ? View.VISIBLE : View.GONE); } private void setDefaulViewMode() { @@ -668,7 +670,7 @@ public class MainVNCActivity extends VncCanvasActivity { } else { try { return super.dispatchKeyEvent(event); - } catch (ClassCastException e) { + } catch (Exception e) { return true; } } @@ -767,6 +769,7 @@ public class MainVNCActivity extends VncCanvasActivity { binding.lnNosignal.setVisibility(View.GONE); this.vncCanvas.setFocusableInTouchMode(true); + syncCursorViewWithBitmap(); }); } @@ -1081,9 +1084,14 @@ public class MainVNCActivity extends VncCanvasActivity { }); bindingDesktopControls.middleBtn.setOnClickListener(v -> { - MotionEvent e = MotionEvent.obtain(1000, 1000, MotionEvent.ACTION_DOWN, vncCanvas.mouseX, vncCanvas.mouseY, - 0); - ((TouchpadInputHandler) VncCanvasActivity.inputHandler).middleClick(e); + try { + MotionEvent e = MotionEvent.obtain(1000, 1000, MotionEvent.ACTION_DOWN, vncCanvas.mouseX, vncCanvas.mouseY, + 0); + ((TouchpadInputHandler) VncCanvasActivity.inputHandler).middleClick(e); + } catch (Exception e) { + VMManager.sendMiddleMouseKey(); + VMManager.sendMiddleMouseKey(); + } }); bindingDesktopControls.leftClickBtn.setOnClickListener(v -> { diff --git a/app/src/main/java/com/vectras/vm/VMCreatorActivity.java b/app/src/main/java/com/vectras/vm/VMCreatorActivity.java index c04afb2..0eda2d3 100644 --- a/app/src/main/java/com/vectras/vm/VMCreatorActivity.java +++ b/app/src/main/java/com/vectras/vm/VMCreatorActivity.java @@ -627,7 +627,7 @@ public class VMCreatorActivity extends AppCompatActivity { .into(binding.ivIcon); } else { binding.ivAddThubnail.setImageResource(R.drawable.round_add_24); - VMManager.setIconWithName(binding.ivIcon, current.itemName); + VMManager.setIconWithName(binding.ivIcon, Objects.requireNonNull(binding.title.getText()).toString()); } } else { binding.ivAddThubnail.setImageResource(R.drawable.round_add_24); diff --git a/app/src/main/java/com/vectras/vm/VMManager.java b/app/src/main/java/com/vectras/vm/VMManager.java index 5cab167..5287432 100644 --- a/app/src/main/java/com/vectras/vm/VMManager.java +++ b/app/src/main/java/com/vectras/vm/VMManager.java @@ -222,6 +222,8 @@ public class VMManager { return result.toString(); } + //This can be removed because QMP currently uses sockets instead of open ports. + @Deprecated public static int startRandomPort() { int _result; Random _random = new Random(); @@ -229,13 +231,15 @@ public class VMManager { int _max = 65535; _result = _random.nextInt(_max - _min + 1) + _min; - if (FileUtils.isFileExists(AppConfig.romsdatajson)) { + if (FileUtils.isFileExists(AppConfig.romsdatajson) || FileUtils.canRead(AppConfig.romsdatajson)) { if (FileUtils.readAFile(AppConfig.romsdatajson).contains("\"qmpPort\":" + _result)) { _result = _random.nextInt(_max - _min + 1) + _min; } if (FileUtils.readAFile(AppConfig.romsdatajson).contains("\"qmpPort\":" + _result)) { _result = _random.nextInt(_max - _min + 1) + _min; } + } else { + _result = 8080; } return _result; @@ -985,6 +989,12 @@ public class VMManager { _dialog.dismiss(); }); + _view.findViewById(R.id.ln_virtualmouse).setOnClickListener(v -> { + MainVNCActivity.getContext.binding.cursorView.setVisibility(MainVNCActivity.getContext.binding.cursorView.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE); + MainSettingsManager.setShowVirtualMouse(_activity, MainVNCActivity.getContext.binding.cursorView.getVisibility() == View.VISIBLE); + _dialog.dismiss(); + }); + _view.findViewById(R.id.ln_mouse).setOnClickListener(v -> { MainVNCActivity.getContext.onMouseMode(); _dialog.dismiss(); @@ -1356,6 +1366,14 @@ public class VMManager { || _qemuCommand.contains("-machine pc-q35"); } + public static boolean isNeedUseVirtualMouse() { + return lastQemuCommand.contains("-vga qxl") || + lastQemuCommand.contains("-vga virtio") || + lastQemuCommand.contains("-device qxl-vga") || + lastQemuCommand.contains("-device virtio-vga") || + lastQemuCommand.contains("-device virtio-gpu"); + } + public static String addAudioDevSdl(String env) { final String audioDevParam = ",audiodev=defaultaudiodev -audiodev sdl,id=defaultaudiodev "; String result = env; diff --git a/app/src/main/java/com/vectras/vm/utils/FileUtils.java b/app/src/main/java/com/vectras/vm/utils/FileUtils.java index 7b93873..69b560e 100644 --- a/app/src/main/java/com/vectras/vm/utils/FileUtils.java +++ b/app/src/main/java/com/vectras/vm/utils/FileUtils.java @@ -598,18 +598,19 @@ public class FileUtils { public static boolean moveFile(String oldfilename, String newFolderPath, String newFilename) { File folder = new File(newFolderPath); if (!folder.exists()) - folder.mkdirs(); + folder.mkdirs(); File oldfile = new File(oldfilename); File newFile = new File(newFolderPath, newFilename); - if (!newFile.exists()) - try { - newFile.createNewFile(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } + if (!newFile.exists()) { + try { + newFile.createNewFile(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } return oldfile.renameTo(newFile); } @@ -739,6 +740,11 @@ public class FileUtils { } } + public static boolean canRead(String filePath) { + File file = new File(filePath); + return file.canRead(); + } + public static String readAFile(String filePath) { StringBuilder content = new StringBuilder(); try (FileInputStream inputStream = new FileInputStream(filePath); diff --git a/app/src/main/res/drawable/highlight_mouse_cursor_24px.xml b/app/src/main/res/drawable/highlight_mouse_cursor_24px.xml new file mode 100644 index 0000000..6c055b2 --- /dev/null +++ b/app/src/main/res/drawable/highlight_mouse_cursor_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/xc_left_ptr.png b/app/src/main/res/drawable/xc_left_ptr.png new file mode 100644 index 0000000..db2aada Binary files /dev/null and b/app/src/main/res/drawable/xc_left_ptr.png differ diff --git a/app/src/main/res/layout/activity_vnc.xml b/app/src/main/res/layout/activity_vnc.xml index a46c797..a96ad3a 100644 --- a/app/src/main/res/layout/activity_vnc.xml +++ b/app/src/main/res/layout/activity_vnc.xml @@ -1,7 +1,7 @@ - - + + + + + + @@ -41,9 +52,9 @@ android:id="@+id/ln_nosignal" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="#E6000000" android:gravity="center" - android:orientation="vertical" - android:background="#E6000000"> + android:orientation="vertical"> + android:textColor="@android:color/white" + android:textSize="20sp" /> + android:textColor="@android:color/white" /> diff --git a/app/src/main/res/layout/dialog_change_removable_devices.xml b/app/src/main/res/layout/dialog_change_removable_devices.xml index 3c98b2a..67b1160 100644 --- a/app/src/main/res/layout/dialog_change_removable_devices.xml +++ b/app/src/main/res/layout/dialog_change_removable_devices.xml @@ -219,6 +219,26 @@ android:text="@string/refresh"/> + + + + + 3dfx is not available. Refresh We are very sorry about an incident that occurred and app crashed earlier. + Show virtual mouse + Hide virtual mouse diff --git a/web/data/UpdateConfig.json b/web/data/UpdateConfig.json index aa52dcf..e62aacf 100644 --- a/web/data/UpdateConfig.json +++ b/web/data/UpdateConfig.json @@ -5,11 +5,11 @@ "url": "https://github.com/xoureldeen/Vectras-VM-Android/releases", "Message": "

3.5.0

\n3dfx is back!", "cancellable": true, - "versionCodeBeta":"63", - "versionNameBeta":"3.5.9", - "versionNameBetas":"3.0.0,3.1.0,3.2.1,3.2.2,3.2.3,3.2.4,3.2.5,3.2.6,3.2.7,3.2.8,3.2.9,3.2.10,3.3.1,3.3.2,3.3.3,3.3.4,3.3.5,3.3.6,3.3.7,3.3.8,3.3.9,3.4.1,3.4.2,3.4.3,3.4.4,3.4.5,3.4.6,3.4.7,3.4.8,3.4.9,3.5.1,3.5.2,3.5.3,3.5.4,3.5.5,3.5.6,3.5.7,3.5.8,3.5.9", + "versionCodeBeta":"64", + "versionNameBeta":"3.6.0", + "versionNameBetas":"3.0.0,3.1.0,3.2.1,3.2.2,3.2.3,3.2.4,3.2.5,3.2.6,3.2.7,3.2.8,3.2.9,3.2.10,3.3.1,3.3.2,3.3.3,3.3.4,3.3.5,3.3.6,3.3.7,3.3.8,3.3.9,3.4.1,3.4.2,3.4.3,3.4.4,3.4.5,3.4.6,3.4.7,3.4.8,3.4.9,3.5.1,3.5.2,3.5.3,3.5.4,3.5.5,3.5.6,3.5.7,3.5.8,3.5.9,3.6.0", "sizeBeta": "43 MB", "urlBeta": "https://github.com/AnBui2004/Vectras-VM-Emu-Android/releases", - "MessageBeta": "

3.5.9

Bugs fixed.", + "MessageBeta": "

3.6.0

Bugs fixed.", "cancellableBeta": true }