Project: Android_1
package com.novoda.example.compass.view;
 
import java.util.ArrayList; 
 
import com.novoda.example.compass.R; 
 
import android.content.Context; 
import android.graphics.Canvas; 
import android.graphics.PointF; 
import android.graphics.drawable.Drawable; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.view.MotionEvent; 
import android.view.ScaleGestureDetector; 
import android.view.View; 
 
public class RotatedImageView extends View { 
 
    private static final int INVALID_POINTER_ID = -1
 
    private Drawable backgroundImage; 
    private float mPosX; 
    private float mPosY; 
 
    private ArrayList<PointF> touchPoints = null
 
    private float mLastTouchX; 
    private float mLastTouchY; 
    private int mActivePointerId = INVALID_POINTER_ID; 
 
    private ScaleGestureDetector mScaleDetector; 
    private float mScaleFactor = 1.f
 
    private float degrees; 
    private float old_degrees; 
    private boolean firstIterationMultitouch; 
    private float last_rotation_degrees; 
 
    private float last_center_rotation_position_x; 
    private float last_center_rotation_position_y; 
 
    private boolean isMultiTouch = false
 
    private boolean isCompassActivated; 
    private float compass_degrees; 
    private float original_compass_degrees; 
 
    private String TAG = "RotatedImage"
 
    public RotatedImageView(Context context) { 
        this(context, null0); 
    } 
 
    public RotatedImageView(Context context, AttributeSet attrs) { 
        this(context, attrs, 0); 
    } 
 
    public RotatedImageView(Context context, AttributeSet attrs, int defStyle) { 
        super(context, attrs, defStyle); 
        initialization(context); 
    } 
 
    private void initialization(Context context) { 
        initializeBackgroundImage(context); 
        initializeScaleDetector(context); 
        initializeVariables(); 
        last_rotation_degrees = 0
        last_center_rotation_position_x = 0
        last_center_rotation_position_y = 0
        isCompassActivated = false
    } 
 
    private void initializeBackgroundImage(Context context) { 
        backgroundImage = context.getResources().getDrawable(R.drawable.floorplan); 
        backgroundImage.setBounds(00, backgroundImage.getIntrinsicWidth(), backgroundImage.getIntrinsicHeight()); 
 
    } 
 
    private void initializeScaleDetector(Context context) { 
        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); 
    } 
 
    private void initializeVariables() { 
        touchPoints = new ArrayList<PointF>(); 
        firstIterationMultitouch = true
    } 
 
    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
 
        activateScaleDetector(ev); 
 
        final int action = ev.getAction(); 
 
        switch (action & MotionEvent.ACTION_MASK) { 
 
        case MotionEvent.ACTION_DOWN: { 
            updateXYPositionSimpleTouch(ev); 
            updatePointerId(ev); 
            mActivePointerId = ev.getPointerId(0); 
            // invalidate(); 
            break
        } 
 
        case MotionEvent.ACTION_POINTER_DOWN: { 
            Log.d(TAG, "Pointer down"); 
            isMultiTouch = true
            firstIterationMultitouch = true
            setInitalPositionMultiPoints(ev); 
            invalidate(); 
            break
        } 
 
        case MotionEvent.ACTION_MOVE: { 
 
            if (isMultiTouch) { 
                setMultitouchPoints(ev); 
            } else { 
                updatePositionToMoveTheImage(ev); 
            } 
            break
        } 
 
        case MotionEvent.ACTION_UP: { 
            mActivePointerId = INVALID_POINTER_ID; 
            Log.d(TAG, "Action up"); 
            break
        } 
 
        case MotionEvent.ACTION_CANCEL: { 
            mActivePointerId = INVALID_POINTER_ID; 
            break
        } 
 
        case MotionEvent.ACTION_POINTER_UP: { 
            Log.d(TAG, "Pointer up"); 
            isMultiTouch = false
            last_rotation_degrees = degrees + last_rotation_degrees; 
            Log.d(TAG, "last_rotation_degrees: " + last_rotation_degrees); 
            updateXYPositionSimpleTouch(ev); 
            updatePointerId(ev); 
 
            break
        } 
 
        } 
 
        return true
    } 
 
    private void activateScaleDetector(MotionEvent ev) { 
        mScaleDetector.onTouchEvent(ev); 
    } 
 
    private void updateXYPositionSimpleTouch(MotionEvent ev) { 
        mLastTouchX = ev.getX(); 
        mLastTouchY = ev.getY(); 
 
    } 
 
    private void updatePointerId(MotionEvent ev) { 
        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 
        final int pointerId = ev.getPointerId(pointerIndex); 
        if (pointerId == mActivePointerId) { 
            // This was our active pointer going up. Choose a new 
            // active pointer and adjust accordingly. 
            final int newPointerIndex = pointerIndex == 0 ? 1 : 0
            mLastTouchX = ev.getX(newPointerIndex); 
            mLastTouchY = ev.getY(newPointerIndex); 
            mActivePointerId = ev.getPointerId(newPointerIndex); 
        } 
    } 
 
    @Override 
    public void onDraw(Canvas canvas) { 
        super.onDraw(canvas); 
 
         
        if (isCompassActivated) { 
            canvas.rotate(original_compass_degrees - compass_degrees, 300600); 
        } 
 
        else { 
 
            if (!isMultiTouch) { 
                canvas.translate(mPosX, mPosY); 
                canvas.rotate(last_rotation_degrees, last_center_rotation_position_x, last_center_rotation_position_y); 
            } 
 
            if (isMultiTouch) { 
 
                if (touchPoints.size() > 1) { 
                    // canvas.scale(mScaleFactor, mScaleFactor); 
                    rotateImageFromMultitouch(touchPoints, canvas); 
                } 
            } 
        } 
 
        drawCanvas(canvas); 
 
    } 
 
    private void rotateImageFromMultitouch(ArrayList<PointF> touchPoints, Canvas canvas) { 
 
        PointF midpt; 
 
        for (int index = 1; index < touchPoints.size(); ++index) { 
 
            if (firstIterationMultitouch) { 
 
                double r = Math.atan2(touchPoints.get(index).x - touchPoints.get(index - 1).x, touchPoints.get(index).y 
                        - touchPoints.get(index - 1).y); 
                old_degrees = -(int) Math.toDegrees(r); 
                firstIterationMultitouch = false
                canvas.translate(mPosX, mPosY); 
                canvas.rotate(last_rotation_degrees, last_center_rotation_position_x, last_center_rotation_position_y); 
 
            } 
 
            else { 
 
                double r = Math.atan2(touchPoints.get(index).x - touchPoints.get(index - 1).x, touchPoints.get(index).y 
                        - touchPoints.get(index - 1).y); 
                int currentDegrees = -(int) Math.toDegrees(r); 
 
                degrees = currentDegrees - old_degrees; 
 
                 
 
                midpt = getMidPoint(touchPoints.get(index - 1).x, touchPoints.get(index - 1).y, 
                        touchPoints.get(index).x, touchPoints.get(index).y); 
 
                last_center_rotation_position_x = midpt.x; 
                last_center_rotation_position_y = midpt.y; 
 
                canvas.translate(mPosX, mPosY); 
                canvas.rotate(degrees + last_rotation_degrees, -mPosX + midpt.x, -mPosY + midpt.y); 
 
            } 
        } 
    } 
 
    private void drawCanvas(Canvas canvas) { 
        canvas.save(); 
        backgroundImage.draw(canvas); 
        canvas.restore(); 
    } 
 
    // Still not used 
    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { 
        @Override 
        public boolean onScale(ScaleGestureDetector detector) { 
            mScaleFactor *= detector.getScaleFactor(); 
 
            // Don't let the object get too small or too large. 
            mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f)); 
 
            invalidate(); 
            return true
        } 
    } 
 
    private PointF getMidPoint(float x1, float y1, float x2, float y2) { 
        PointF point = new PointF(); 
 
        float x = x1 + x2; 
        float y = y1 + y2; 
 
        point.set(x / 2, y / 2); 
 
        return point; 
    } 
 
    public void setMultitouchPoints(MotionEvent event) { 
 
        touchPoints.clear(); 
 
        int pointerIndex = 0
 
        for (int index = 0; index < event.getPointerCount(); ++index) { 
            pointerIndex = event.getPointerId(index); 
 
            touchPoints.add(new PointF(event.getX(pointerIndex), event.getY(pointerIndex))); 
        } 
    } 
 
    public void setInitalPositionMultiPoints(MotionEvent event) { 
 
        touchPoints.clear(); 
 
        int pointerIndex = 0
 
        for (int index = 0; index < event.getPointerCount(); ++index) { 
            pointerIndex = event.getPointerId(index); 
 
            touchPoints.add(new PointF(event.getX(pointerIndex), event.getY(pointerIndex))); 
        } 
 
        double r = Math.atan2(touchPoints.get(1).x - touchPoints.get(1 - 1).x, 
                touchPoints.get(1).y - touchPoints.get(1 - 1).y); 
        old_degrees = -(int) Math.toDegrees(r); 
    } 
 
    public void updatePositionToMoveTheImage(MotionEvent event) { 
 
        final int pointerIndex = event.findPointerIndex(mActivePointerId); 
        final float x = event.getX(pointerIndex); 
        final float y = event.getY(pointerIndex); 
 
        if (!mScaleDetector.isInProgress()) { 
 
            final float dx = x - mLastTouchX; 
            final float dy = y - mLastTouchY; 
 
            mPosX += dx; 
            mPosY += dy; 
 
            invalidate(); 
        } 
 
        mLastTouchX = x; 
        mLastTouchY = y; 
 
    } 
 
    public void calculateNewPoints() { 
 
    } 
 
    public void checkIfCompassIsActivated() { 
 
    } 
 
    public void setCompassActivated(boolean activated, float originalDegrees) { 
        isCompassActivated = activated; 
        original_compass_degrees = originalDegrees; 
    } 
 
    public void updateCompassDegrees(float degrees) { 
        compass_degrees = -degrees; 
        invalidate(); 
    } 
 
 
}