Android Open Source - paperchains Paper Chains View






From Project

Back to project page paperchains.

License

The source code is released under:

Apache License

If you think the Android project paperchains listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (c) 2014 Simon Robinson// w  w  w  .  j a  va2 s .  co m
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ac.robinson.paperchains;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

import com.sonyericsson.zoom.AspectQuotient;
import com.sonyericsson.zoom.ZoomState;

import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;

public class PaperChainsView extends View implements Observer {

  private static final int AUDIO_RECT_ALPHA = 100;
  private static final int SCRIBBLE_ALPHA = 180;

  private Bitmap mBitmap;

  private final ArrayList<Rect> mAudioAreas = new ArrayList<>();
  private final Path mScribblePath = new Path();

  private DragCallback mDragCallback = null;
  private ScribbleCallback mScribbleCallback = null;
  private boolean mScribbleEnabled = false;
  private boolean mDrawAudioAreas = false;

  private final Paint mBitmapPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
  private final Paint mAudioAreaPaint = new Paint();
  private final Paint mScribblePaint = new Paint();

  private final Rect mRectSrc = new Rect();
  private final Rect mRectDst = new Rect();

  private final AspectQuotient mAspectQuotient = new AspectQuotient();
  private ZoomState mZoomState;

  // for retrieving touch points
  private float mTopPos = 0;
  private float mLeftPos = 0;
  private float mScaleFactorX = 1;
  private float mScaleFactorY = 1;

  private float mDownX;
  private float mDownY;
  private final int mScaledTouchSlop;
  private boolean mCanClick;

  public interface DragCallback {
    public void dragStarted();
  }

  public interface ScribbleCallback {
    public void scribbleCompleted(Path scribble);
  }

  public PaperChainsView(Context context, AttributeSet attrs) {
    super(context, attrs);

    Resources resources = getResources();

    mAudioAreaPaint.setStyle(Paint.Style.FILL);
    mAudioAreaPaint.setColor(resources.getColor(R.color.audio_area));
    mAudioAreaPaint.setAlpha(AUDIO_RECT_ALPHA);
    mAudioAreaPaint.setStrokeCap(Paint.Cap.ROUND);

    mScribblePaint.setStyle(Paint.Style.STROKE);
    mScribblePaint.setColor(resources.getColor(R.color.scribble));
    mScribblePaint.setAntiAlias(true);
    mScribblePaint.setAlpha(SCRIBBLE_ALPHA);
    mScribblePaint.setStrokeWidth(resources.getDimensionPixelSize(R.dimen.scribble_stroke_width));
    mScribblePaint.setStrokeCap(Paint.Cap.ROUND);
    mScribblePaint.setStrokeJoin(Paint.Join.ROUND);

    mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
  }

  public void setImage(Bitmap bitmap) {
    mBitmap = bitmap;

    if (mBitmap != null) {
      mAspectQuotient.updateAspectQuotient(getWidth(), getHeight(), mBitmap.getWidth(), mBitmap.getHeight());
      mAspectQuotient.notifyObservers();
    }

    invalidate();
  }

  public void setZoomState(ZoomState state) {
    if (mZoomState != null) {
      mZoomState.deleteObserver(this);
    }

    mZoomState = state;
    mZoomState.addObserver(this);

    invalidate();
  }

  public AspectQuotient getAspectQuotient() {
    return mAspectQuotient;
  }

  /**
   * Converts a touch on screen to a point in the image coordinates
   *
   * @param touchPoint the touch point
   * @return touchPoint in the image coordinates
   */
  public Point screenPointToImagePoint(PointF touchPoint) {
    return new Point((int) screenXToImageX(touchPoint.x), (int) screenYToImageY(touchPoint.y));
  }

  private float screenXToImageX(float screenX) {
    return (screenX - mLeftPos) / mScaleFactorX;
  }

  private float screenYToImageY(float screenY) {
    return (screenY - mTopPos) / mScaleFactorY;
  }

  /**
   * Converts a touch on the image to a point on screen
   *
   * @param imagePoint the image point
   * @return imagePoint in the screen coordinates
   */
  public PointF imagePointToScreenPoint(Point imagePoint) {
    return new PointF((imagePoint.x * mScaleFactorX) + mLeftPos, (imagePoint.y * mScaleFactorY) + mTopPos);
  }

  public void addAudioAreaRect(Rect rect) {
    mAudioAreas.add(rect);
    invalidate();
  }

  public void removeAudioAreaRect(Rect rect) {
    mAudioAreas.remove(rect);
    invalidate();
  }

  public void clearAudioAreaRects() {
    mAudioAreas.clear();
  }

  public void setDragCallback(DragCallback callback) {
    mDragCallback = callback;
  }

  public void setScribbleCallback(ScribbleCallback callback) {
    mScribbleCallback = callback;
  }

  public void setScribbleEnabled(boolean enabled) {
    mScribbleEnabled = enabled;
    if (!enabled) {
      mScribblePath.reset();
    }
    invalidate();
  }

  public void setDrawAudioRectsEnabled(boolean enabled) {
    mDrawAudioAreas = enabled;
    invalidate();
  }

  @Override
  public boolean onTouchEvent(@NonNull MotionEvent event) {
    if (mScribbleEnabled && mScribbleCallback != null) {
      // handle scribbling on the image
      final float x = screenXToImageX(event.getX());
      final float y = screenYToImageY(event.getY());

      switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
          mScribblePath.reset();
          mScribblePath.moveTo(x, y);
          break;

        case MotionEvent.ACTION_MOVE:
          mScribblePath.lineTo(x, y);
          break;

        case MotionEvent.ACTION_UP:
          mScribblePath.lineTo(x, y);
          mScribbleCallback.scribbleCompleted(mScribblePath);
          break;

        default:
          break;
      }
    } else if (isClickable()) {
      // handle click so we don't click after moving beyond the touch slop
      switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
          mDownX = event.getX();
          mDownY = event.getY();
          mCanClick = true;
          break;

        case MotionEvent.ACTION_MOVE:
          final float scrollX = mDownX - event.getX();
          final float scrollY = mDownY - event.getY();
          final float dist = (float) Math.sqrt(scrollX * scrollX + scrollY * scrollY);
          if (dist >= mScaledTouchSlop) {
            if (mCanClick && mDragCallback != null) {
              mDragCallback.dragStarted();
            }
            mCanClick = false;
          }
          break;

        case MotionEvent.ACTION_UP:
          if (mCanClick) {
            performClick();
          }
          break;
      }
    }

    invalidate();
    return true; //super.onTouchEvent(event);
  }

  /**
   * Apply the current zoom and scale to the given canvas
   *
   * @param canvas The Canvas to zoom and scale
   */
  private void applyZoomAndScale(Canvas canvas) {
    final float aspectQuotient = mAspectQuotient.get();

    final int viewWidth = getWidth();
    final int viewHeight = getHeight();
    final int bitmapWidth = mBitmap.getWidth();
    final int bitmapHeight = mBitmap.getHeight();

    final float panX = mZoomState.getPanX();
    final float panY = mZoomState.getPanY();
    final float zoomX = mZoomState.getZoomX(aspectQuotient) * viewWidth / bitmapWidth;
    final float zoomY = mZoomState.getZoomY(aspectQuotient) * viewHeight / bitmapHeight;

    // set up the source and destination rectangles
    mRectSrc.left = (int) (panX * bitmapWidth - viewWidth / (zoomX * 2));
    mRectSrc.top = (int) (panY * bitmapHeight - viewHeight / (zoomY * 2));
    mRectSrc.right = (int) (mRectSrc.left + viewWidth / zoomX);
    mRectSrc.bottom = (int) (mRectSrc.top + viewHeight / zoomY);
    mRectDst.left = getPaddingLeft();
    mRectDst.top = getPaddingTop();
    mRectDst.right = viewWidth - getPaddingRight();
    mRectDst.bottom = viewHeight - getPaddingBottom();

    // adjust the source rectangle so that it fits within the source image.
    if (mRectSrc.left < 0) {
      mRectDst.left += -mRectSrc.left * zoomX;
      mRectSrc.left = 0;
    }
    if (mRectSrc.right > bitmapWidth) {
      mRectDst.right -= (mRectSrc.right - bitmapWidth) * zoomX;
      mRectSrc.right = bitmapWidth;
    }
    if (mRectSrc.top < 0) {
      mRectDst.top += -mRectSrc.top * zoomY;
      mRectSrc.top = 0;
    }
    if (mRectSrc.bottom > bitmapHeight) {
      mRectDst.bottom -= (mRectSrc.bottom - bitmapHeight) * zoomY;
      mRectSrc.bottom = bitmapHeight;
    }

    // scale the canvas so the visible area is the zoomed part of the map
    final float leftPos = (-mRectSrc.left * zoomX) + mRectDst.left;
    final float topPos = (-mRectSrc.top * zoomY) + mRectDst.top;
    final float rightPos = ((bitmapWidth - mRectSrc.right) * zoomX) + mRectDst.right;
    final float bottomPos = ((bitmapHeight - mRectSrc.bottom) * zoomY) + mRectDst.bottom;

    final float scaleFactorX = (rightPos - leftPos) / bitmapWidth;
    final float scaleFactorY = (bottomPos - topPos) / bitmapHeight;

    canvas.translate(leftPos, topPos);
    canvas.scale(scaleFactorX, scaleFactorY, 0, 0);

    // save for touch interaction at this zoom level to avoid calculating again
    mLeftPos = leftPos;
    mTopPos = topPos;
    mScaleFactorX = scaleFactorX;
    mScaleFactorY = scaleFactorY;
  }

  private boolean isVisible(Canvas canvas, Rect r) {
    return !canvas.quickReject(r.left, r.top, r.right, r.bottom, Canvas.EdgeType.BW);
  }

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.save();

    if (mBitmap != null && mZoomState != null) {
      canvas.drawBitmap(mBitmap, mRectSrc, mRectDst, mBitmapPaint);

      // zoom/scale to the correct position
      if (!isInEditMode()) {
        applyZoomAndScale(canvas);
      }

      // draw audio areas and current scribble
      if (mDrawAudioAreas) {
        for (Rect rect : mAudioAreas) {
          if (!isVisible(canvas, rect)) {
            continue; // don't draw rects that aren't visible
          }
          canvas.drawRect(rect, mAudioAreaPaint);
        }
      }
      if (mScribbleEnabled) {
        canvas.drawPath(mScribblePath, mScribblePaint);
      }
    } else {
      canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
    }

    canvas.restore();
  }

  @Override
  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);

    if (mBitmap != null) {
      mAspectQuotient.updateAspectQuotient(right - left, bottom - top, mBitmap.getWidth(), mBitmap.getHeight());
      mAspectQuotient.notifyObservers();
    }
  }

  public void update(Observable observable, Object data) {
    invalidate();
  }
}




Java Source Code List

ac.robinson.paperchains.AudioRecorderCircleButton.java
ac.robinson.paperchains.PaperChainsActivity.java
ac.robinson.paperchains.PaperChainsView.java
ac.robinson.paperchains.SoundCloudLoginActivity.java
ac.robinson.paperchains.SoundCloudUploadTask.java
ac.robinson.paperchains.SoundCloudUrlFetcherTask.java
com.github.lassana.recorder.ApiHelper.java
com.github.lassana.recorder.AudioRecorder.java
com.github.lassana.recorder.Mp4ParserWrapper.java
com.sonyericsson.zoom.AspectQuotient.java
com.sonyericsson.zoom.DynamicZoomControl.java
com.sonyericsson.zoom.Dynamics.java
com.sonyericsson.zoom.LongPressZoomListener.java
com.sonyericsson.zoom.SpringDynamics.java
com.sonyericsson.zoom.ZoomState.java
com.soundcloud.playerapi.ApiWrapper.java
com.soundcloud.playerapi.CloudAPI.java
com.soundcloud.playerapi.CountingMultipartEntity.java
com.soundcloud.playerapi.DecompressingEntity.java
com.soundcloud.playerapi.Endpoints.java
com.soundcloud.playerapi.Env.java
com.soundcloud.playerapi.GzipDecompressingEntity.java
com.soundcloud.playerapi.Http.java
com.soundcloud.playerapi.OAuth2HttpRequestInterceptor.java
com.soundcloud.playerapi.OAuth2Scheme.java
com.soundcloud.playerapi.Params.java
com.soundcloud.playerapi.Request.java
com.soundcloud.playerapi.Stream.java
com.soundcloud.playerapi.Token.java