RemoteDesktop.java :  » Remote » gmote » org » gmote » client » android » Android Open Source

Android Open Source » Remote » gmote 
gmote » org » gmote » client » android » RemoteDesktop.java
package org.gmote.client.android;

import java.net.DatagramSocket;
import java.net.SocketException;

import org.gmote.common.Protocol.Command;
import org.gmote.common.Protocol.MouseEvent;
import org.gmote.common.packet.AbstractPacket;
import org.gmote.common.packet.KeyboardEventPacket;
import org.gmote.common.packet.SimplePacket;
import org.gmote.common.packet.TileClickReq;
import org.gmote.common.packet.TileInfoReply;
import org.gmote.common.packet.TileSetReq;
import org.gmote.common.packet.TileUpdatePacket;

import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;

public class RemoteDesktop extends Activity implements BaseActivity {
  static final String DEBUG_TAG = "Gmote";
  static ActivityUtil mUtil = null;

  Remote remoteInstance;
  int serverUdpPort;
  
  // bitmap
  Bitmap bitmap;
  int bmWidth;
  int bmHeight;
  BitmapFactory.Options bmOptions;
  Rect bmRect;
  
  // tile
  Canvas tileCanvas;
  Rect tileRect;
  Object waitForTile = new Object();
  int tileSize;
  int tileMaxX;
  int tileMaxY;
  int tileNumVisibleX;
  int tileNumVisibleY;
  int tile1X = -1;
  int tile1Y = -1;
  
  // view
  BitmapView main;
  int width;
  int height;

  // motion
  GestureDetector gestureDetector = null;
  private float mX = 0;
  private float mY = 0;
  private float prevX = -1;
  private float prevY = -1;
  private int clickX = -10;
  private int clickY = -10;
  private long timeOfLastClick = 0;
  private long timeOfLastPosX = 0;
  private long timeOfLastNegX = 0;
  private long timeOfLastPosY = 0;
  private long timeOfLastNegY = 0;

  private float posXAcceleration = 0;
  private float negXAcceleration = 0;
  private float posYAcceleration = 0;
  private float negYAcceleration = 0;

  private static final float ACCELERATION_DECAY = (float) 0.1;
  private static final float MOUSE_SENSITIVITY_DEFAULT = (float) -1.4;
  private static final float MOUSE_ACCELERATION_DEFAULT = (float) 0.5;

  private float mouseSensitivity = MOUSE_SENSITIVITY_DEFAULT;
  private float mouseAccelerationDamper = MOUSE_ACCELERATION_DEFAULT;

  // Object that we will wait on when the screen is not scrolling.
  private Object waitForScroll = new Object();

  @Override
  public void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    Log.d(DEBUG_TAG, "RemoteDesktop# oncreate");


    mUtil = new ActivityUtil();
    mUtil.onCreate(icicle, this);
    remoteInstance = Remote.getInstance();

    main = new BitmapView(this);

    tileCanvas = new Canvas();
    tileRect = new Rect();
    bmRect = new Rect();

    gestureDetector = new GestureDetector(gestureListener);
    gestureDetector.setIsLongpressEnabled(true);
    setContentView(main);
    
    new Thread(new PainterThread()).start();
    new Thread(new TilerThread()).start();
  }

  // ---------------------------------------------------------------------------------------------------
  // GRAPHICS
  
  public class PainterThread implements Runnable {
    public void run() {
      while (true) {
          synchronized (waitForScroll) {
            try {
              waitForScroll.wait();
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }
          //Log.d(DEBUG_TAG, "scrolled, main.postInvalidate");
          main.postInvalidate();
      }
    }
  }

  public class TilerThread implements Runnable {
    public void run() {
      while (true) {
          synchronized (waitForTile) {
            try {
              waitForTile.wait();
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }
          Log.d(DEBUG_TAG, "got tile, main.postInvalidate");
          main.postInvalidate();
      }
    }
  }
  private synchronized void updateTile(int idx, int idy, byte[] data) {
    Log.d(DEBUG_TAG, "updateTile: " + idx +","+ idy);
    tileRect.offsetTo(idx * tileSize, idy * tileSize);
    tileCanvas.drawBitmap(BitmapFactory.decodeByteArray(data, 0, data.length, bmOptions), null, tileRect, new Paint());
    synchronized(waitForTile) {
      waitForTile.notifyAll();
    }
    //main.postInvalidate(tileRect.left, tileRect.top, tileRect.right, tileRect.bottom);
    //main.invalidate();
  }

  private synchronized void doDraw(Canvas canvas, Paint paint) {
    if (bitmap!=null) {
      canvas.drawBitmap(bitmap, mX, mY, paint);
      //Log.d(DEBUG_TAG, "drawing " + mX + ", " + mY);
    }
  }

  class BitmapView extends View {
    Paint paint;

    public BitmapView(Context context) {
      super(context);
      paint = new Paint();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
      super.onLayout(changed, left, top, right, bottom);
      width = right;
      height = bottom;
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
      doDraw(canvas, paint);
    }
  }

  void getTileInfo() {
    mUtil.send(new SimplePacket(Command.TILE_INFO_REQ));
  }
  
  void initializeGraphics() {
    bmRect = new Rect(0, 0, bmWidth, bmHeight);
    tileRect = new Rect(0, 0, tileSize, tileSize);
    if (bitmap != null) {
      bitmap.recycle();
    }
    bitmap = Bitmap.createBitmap(bmWidth, bmHeight, Bitmap.Config.RGB_565);
    tileCanvas.setBitmap(bitmap);
    bmOptions = new BitmapFactory.Options();
    bmOptions.inPreferredConfig = Bitmap.Config.RGB_565;
    tileMaxX = bmWidth/tileSize;
    tileMaxY = bmHeight/tileSize;
    Log.d(DEBUG_TAG, "width="+width+" height="+height+" tileMaxX="+tileMaxX + " tileMaxY="+tileMaxY + " tileSize="+tileSize);
  }
  
  // ---------------------------------------------------------------------------------------------------
  // GENERAL
  @Override
  public void onStart() {
    super.onStart();
    Log.d(DEBUG_TAG, "RemoteDesktop# onstart");

    mUtil.onStart(this);
    serverUdpPort = remoteInstance.getServerUdpPort();
    getTileInfo();
  }

  @Override
  public void onResume() {
    super.onResume();
    Log.d(DEBUG_TAG, "RemoteDesktop# resume");

    mUtil.onResume();
    getTileInfo();
    //bitmap = ((BitmapDrawable) getResources().getDrawable(R.drawable.screenshot)).getBitmap();
  }
  
  @Override
  public void onPause() {
    super.onPause();
    Log.d(DEBUG_TAG, "RemoteDesktop# onpause");

    mUtil.onPause();
  }

  @Override
  public void onStop() {
    super.onStop();
    Log.d(DEBUG_TAG, "RemoteDesktop# onstop");
    mUtil.onStop();
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    mUtil.onCreateOptionsMenu(menu);
    return true;
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    super.onOptionsItemSelected(item);
    return mUtil.onOptionsItemSelected(item);
  }

  @Override
  protected Dialog onCreateDialog(int id) {
    return mUtil.onCreateDialog(id);
  }

  public void handleReceivedPacket(AbstractPacket reply) {
    if (reply.getCommand() == Command.TILE_INFO_REPLY) {
      Log.d(DEBUG_TAG, "RemoteDesktop# got tile info ");
      TileInfoReply ti = (TileInfoReply) reply;
      bmWidth = ti.getScreenWidth();
      bmHeight = ti.getScreenHeight();
      tileSize = ti.getTileSize();

      initializeGraphics();
      new Thread(new TileReqSenderThread()).start();
      //sendTcpTileReq();
    } else if (reply.getCommand() == Command.TILE_UPDATE) {
      Log.d(DEBUG_TAG, "RemoteDesktop# got tile update ");
      TileUpdatePacket update = (TileUpdatePacket)reply;
      updateTile(update.getTileIdX(), update.getTileIdY(), update.getImageData());
    } else {
      Log.e(DEBUG_TAG,"Unexpected packet in RemoteDesktop: " + reply.getCommand());
      return;
    }
  }

  // ---------------------------------------------------------------------------------------------------
  // MOTION
  GestureDetector.OnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener() {
    public boolean onDown(MotionEvent e) {
      return false;
    }

    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
        float velocityY) {
      return false;
    }

    public void onLongPress(MotionEvent e) {
      mouseClick((int)e.getRawX(), (int)e.getRawY(), MouseEvent.RIGHT_CLICK);
    }

    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
        float distanceY) {
      incrementDistance(distanceX * mouseSensitivity, distanceY
          * mouseSensitivity);
      synchronized (waitForScroll) {
        waitForScroll.notifyAll();
      }

      return true;
    }

    public void onShowPress(MotionEvent e) {
    }

    public boolean onSingleTapUp(MotionEvent e) {
      mouseClick((int)e.getRawX(), (int)e.getRawY(), MouseEvent.SINGLE_CLICK);
      return true;
    }
  };
  
  
  @Override
  public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
      finish();
    } else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
     // mouseClick(MouseEvent.SINGLE_CLICK);
    } else {
      KeyCharacterMap kmap = KeyCharacterMap.load(event.getDeviceId());

      int c = kmap.get(keyCode, event.getMetaState());
      
      if (c != 0) {
        mUtil.send(new KeyboardEventPacket(c));
      } else {
        if (keyCode == KeyEvent.KEYCODE_DEL) {
          mUtil.send(new KeyboardEventPacket(KeyboardEventPacket.DELETE_KEYCODE));
        } else if (keyCode == KeyEvent.KEYCODE_SEARCH || keyCode == KeyEvent.KEYCODE_SYM) {
          mUtil.send(new KeyboardEventPacket(KeyboardEventPacket.SEARCH_KEYCODE));
        }
      }
    }
    return false;
  }

  private synchronized void incrementDistance(float distanceX, float distanceY) {
    long currentTime = System.currentTimeMillis();
    if (distanceX > 0) {
      posXAcceleration = computeAcceleration(timeOfLastPosX, currentTime,
          posXAcceleration, distanceX);
      timeOfLastPosX = currentTime;
      mX += distanceX + posXAcceleration;
    } else {
      negXAcceleration = computeAcceleration(timeOfLastNegX, currentTime,
          negXAcceleration, Math.abs(distanceX));
      timeOfLastNegX = currentTime;
      mX += distanceX + (negXAcceleration * -1); // * distanceX * -1);
    }

    if (distanceY > 0) {
      posYAcceleration = computeAcceleration(timeOfLastPosY, currentTime,
          posYAcceleration, distanceY);
      timeOfLastPosY = currentTime;
      mY += distanceY + posYAcceleration;
    } else {
      negYAcceleration = computeAcceleration(timeOfLastNegY, currentTime,
          negYAcceleration, Math.abs(distanceY));
      timeOfLastNegY = currentTime;
      mY += distanceY + (negYAcceleration * -1); // * distanceX * -1);
    }
    clamp();
  }
  private void clamp() {
    if(mX > 0)
      mX = 0;
    else if (mX < -bmWidth + width)
      mX = -bmWidth + width;
    
    if(mY > 0)
      mY = 0;
    else if (mY < -bmHeight + height)
      mY = -bmHeight + height;
      
  }

  private float computeAcceleration(long timeOfLastMove, long currentTime,
      float lastAcceleration, float distanceMoved) {

    // Decay the current acceleration value.
    lastAcceleration -= (currentTime - timeOfLastMove) * ACCELERATION_DECAY;
    // Add acceleration based on the current movement.
    lastAcceleration += distanceMoved;

    if (lastAcceleration < 0) {
      lastAcceleration = 0;
    }

    return lastAcceleration * mouseAccelerationDamper;
  }
  
  @Override
  public boolean onTouchEvent(MotionEvent event) {
     return gestureDetector.onTouchEvent(event);
  }
  
  void mouseClick(int x, int y, MouseEvent evt) {
    int idx, idy, offsetx, offsety;
    long currentTime = System.currentTimeMillis();

    if ((currentTime - timeOfLastClick < 1400) && clickX > 0) {
      // for double-clicks
      x = clickX;
      y = clickY;
    } else {
      x -= mX;
      y -= mY;
      clickX = x;
      clickY = y;
    }
    
    idx = x/tileSize;
    idy = y/tileSize;
    offsetx = x - idx * tileSize;
    offsety = y - idy * tileSize;

    mUtil.send(new TileClickReq(idx, idy, offsetx, offsety, evt));
    
    timeOfLastClick = currentTime;
  }
  
  public class TileReqSenderThread implements Runnable {
    
    public void run() {
      DatagramSocket socket = null;
      try {
        socket = new DatagramSocket();
      } catch (SocketException e) {
        Log.e(DEBUG_TAG, e.getMessage(), e);
      }
      
      while (true) {
        sendTcpTileReq();
        //sendUdpTileReq(socket);
      }
    }
  }
  
  private void sendTcpTileReq() {
    if ((mX != prevX || mY != prevY) && (bmRect.contains((int)-mX, (int)-mY))) {
      int x, y, x1, y1, x2, y2;
      x = (int) -mX / tileSize;
      y = (int) -mY / tileSize;

      if (x != tile1X || y != tile1Y) {
        x1 = Math.max(x, 0);
        y1 = Math.max(y, 0);
        x2 = Math.min(x + width/tileSize + 1, tileMaxX);
        y2 = Math.min(y + height/tileSize + 1, tileMaxY);
        mUtil.send(new TileSetReq(x1, y1, x2, y2));
        tile1X = x;
        tile1Y = y;
      }
      
      prevX = mX;
      prevY = mY;
    }
  }
//
//  private void sendUdpTileReq(DatagramSocket socket) {
//    DatagramPacket packet = makeDatagramPacketIfNeeded();
//    if (packet != null) {
//      Log.i(DEBUG_TAG, "Sending packet");
//      try {
//        if (!remoteInstance.isConnected()) {
//          remoteInstance.connect(false);
//        }
//        socket.send(packet);
//        Log.i(DEBUG_TAG, "Packet sent");
//      } catch (IOException e) {
//        Log.e(DEBUG_TAG, e.getMessage(), e);
//      }
//    } else {
//      try {
//        synchronized (waitForScroll) {
//          waitForScroll.wait();
//        }
//      } catch (InterruptedException e) {
//        Log.e(DEBUG_TAG, e.getMessage(), e);
//        return;
//      }
//    }
//  }
//  private synchronized DatagramPacket makeDatagramPacketIfNeeded() {
//    DatagramPacket packet = null;
//    
//    // FIXME!
//    if (mX != prevX || mY != prevY) {
//      byte[] buf = new byte[5];
//      packet= new DatagramPacket(buf, buf.length, remoteInstance.getServerInetAddress(), serverUdpPort);
//      Log.i(DEBUG_TAG, remoteInstance.getServerIp());
//      prevX = mX; prevY = mY;
//    }
//    
//    return packet;
//  }

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.