Android Open Source - Masaccio Masaccio Image View






From Project

Back to project page Masaccio.

License

The source code is released under:

Apache License

If you think the Android project Masaccio 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 Subito.it S.r.l (www.subito.it)
 *// w  w w  . ja  v  a 2s.com
 * 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 it.subito.masaccio;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.FaceDetector;
import android.media.FaceDetector.Face;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.ImageView;

import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;

public class MasaccioImageView extends ImageView {

    public static final int FLAG_IF_FACE = 0x10;

    public static final int FLAG_LANDSCAPE = 0x2;

    public static final int FLAG_NO_FACE = 0x8;

    public static final int FLAG_PORTRAIT = 0x1;

    public static final int FLAG_SQUARE = 0x4;

    private static final float FACE_POSITION_RATIO_X = 0.5f;

    private static final float FACE_POSITION_RATIO_Y = 0.5f;

    private static final Object sMutex = new Object();

    private static DefaultMasaccioFaceDetector sFaceDetector;

    public final StepInterpolator mDefaultInterpolator = new StepInterpolator();

    private final Matrix mAnimMatrix = new Matrix();

    private int mActivateDetectionFlags;

    private int mActivateMatrixFlags;

    private long mAnimationDuration;

    private boolean mAutoFaceDetection;

    private CropRunnable mCropRunnable;

    private boolean mCyclicAnimation;

    private Face[] mDetectedFaces;

    private long mDuration;

    private float[] mEndCoeffs;

    private float mEndScale;

    private float mEndX;

    private float mEndY;

    private Interpolator mInterpolator;

    private Handler mMessageHandler;

    private ScaleType mOriginalScaleType;

    private float[] mStartCoeffs;

    private float mStartScale;

    private long mStartTime;

    private float mStartX;

    private float mStartY;

    public MasaccioImageView(final Context context) {

        super(context);
        init(null, 0);
    }

    public MasaccioImageView(final Context context, final AttributeSet attrs) {

        super(context, attrs);
        init(attrs, 0);
    }

    public MasaccioImageView(final Context context, final AttributeSet attrs, final int defStyle) {

        super(context, attrs, defStyle);
        init(attrs, defStyle);
    }

    public static MasaccioFaceDetector getFaceDetector() {

        synchronized (sMutex) {

            if (sFaceDetector == null) {

                sFaceDetector = new DefaultMasaccioFaceDetector();
            }
        }

        return sFaceDetector;
    }

    private static boolean enabledDimensions(final int width, final int height, final int flags) {

        boolean enabled = !hasAnyFlag(flags, FLAG_PORTRAIT | FLAG_LANDSCAPE | FLAG_SQUARE);

        if ((width > height) && hasAllFlags(flags, FLAG_LANDSCAPE)) {

            enabled = true;

        } else if ((width < height) && hasAllFlags(flags, FLAG_PORTRAIT)) {

            enabled = true;

        } else if (hasAllFlags(flags, FLAG_SQUARE)) {

            enabled = true;
        }

        return enabled;
    }

    private static boolean hasAllFlags(final int value, final int flags) {

        return (value & flags) == flags;
    }

    private static boolean hasAnyFlag(final int value, final int flags) {

        return (value & flags) != 0;
    }

    public void setActivateDetectionFlags(final int flags) {

        final boolean isUpdate = (mActivateDetectionFlags != flags);

        mActivateDetectionFlags = flags;

        if (isUpdate) {

            applyCrop();
        }
    }

    public void setActivateMatrixFlags(final int flags) {

        final boolean isUpdate = (mActivateMatrixFlags != flags);

        mActivateMatrixFlags = flags;

        if (isUpdate) {

            applyCrop();
        }
    }

    public void setAnimationDuration(final long durationMs) {

        mAnimationDuration = durationMs;
    }

    public void setAnimationInterpolator(final Interpolator interpolator) {

        if (interpolator != null) {

            mInterpolator = interpolator;

        } else {

            mInterpolator = mDefaultInterpolator;
        }
    }

    public void setCenterFace(final boolean enabled) {

        final boolean isUpdate = (mAutoFaceDetection != enabled);

        mAutoFaceDetection = enabled;

        if (enabled) {

            // Force instantiation
            getFaceDetector();
        }

        if (isUpdate) {

            applyCrop();
        }
    }

    public void setCyclicAnimation(final boolean isCyclic) {

        mCyclicAnimation = isCyclic;
    }

    public void setFaces(final Face[] faces) {

        if ((faces != null) && (faces.length > 0)) {

            mDetectedFaces = faces;

        } else {

            mDetectedFaces = null;
        }

        applyCrop();
    }

    @Override
    public void setImageDrawable(final Drawable drawable) {

        super.setImageDrawable(drawable);

        if (drawable == null) {

            setImageMatrix(new Matrix());

            return;
        }

        if (drawable instanceof BitmapDrawable) {

            getDetectedFaces(((BitmapDrawable) drawable).getBitmap());
        }

        cropImage(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
    }

    @Override
    public void setImageBitmap(final Bitmap bitmap) {

        super.setImageBitmap(bitmap);

        if (bitmap == null) {

            setImageMatrix(new Matrix());

            return;
        }

        getDetectedFaces(bitmap);

        cropImage(bitmap.getWidth(), bitmap.getHeight());
    }

    @Override
    public void setScaleType(final ScaleType scaleType) {

        super.setScaleType(scaleType);

        mOriginalScaleType = getScaleType();
    }

    @Override
    protected void onDraw(final Canvas canvas) {

        final long now = System.currentTimeMillis();

        final long startTime = mStartTime;
        final long duration = mDuration;
        final long endTime = startTime + duration;

        if ((now >= startTime) && (now < endTime)) {

            final Interpolator interpolator = mInterpolator;

            final float[] coeffs = new float[9];

            for (int i = 0; i < 9; i++) {

                final float interpolation =
                        interpolator.getInterpolation((float) (now - startTime) / duration);

                final float start = mStartCoeffs[i];

                coeffs[i] = start + (interpolation * (mEndCoeffs[i] - start));
            }

            final Matrix matrix = mAnimMatrix;

            matrix.setValues(coeffs);

            setImageMatrix(matrix);

            invalidate();

        } else if ((startTime > 0) && (now >= endTime)) {

            if (mCyclicAnimation) {

                mStartTime = System.currentTimeMillis();

            } else {

                mStartTime = 0;
            }

            final Matrix matrix = mAnimMatrix;

            matrix.setValues(mEndCoeffs);

            setImageMatrix(matrix);
        }

        super.onDraw(canvas);
    }

    public void setPreScale(final float scale) {

        final boolean isUpdate = (mStartScale != scale);

        mStartScale = scale;

        if (isUpdate) {

            applyCrop();
        }
    }

    public void setPreTranslate(final float translateX, final float translateY) {

        final boolean isUpdate = (mStartX != translateX) || (mStartY != translateY);

        mStartX = translateX;
        mStartY = translateY;

        if (isUpdate) {

            applyCrop();
        }
    }

    public void setPreTranslateX(final float translateX) {

        final boolean isUpdate = (mStartX != translateX);

        mStartX = translateX;

        if (isUpdate) {

            applyCrop();
        }
    }

    public void setPreTranslateY(final float translateY) {

        final boolean isUpdate = (mStartY != translateY);

        mStartY = translateY;

        if (isUpdate) {

            applyCrop();
        }
    }

    public void setScale(final float scale) {

        final boolean isUpdate = (mEndScale != scale);

        mEndScale = scale;

        if (isUpdate) {

            applyCrop();
        }
    }

    public void setTranslate(final float translateX, final float translateY) {

        final boolean isUpdate = (mEndX != translateX) || (mEndY != translateY);

        mEndX = translateX;
        mEndY = translateY;

        if (isUpdate) {

            applyCrop();
        }
    }

    public void setTranslateX(final float translateX) {

        final boolean isUpdate = (mEndX != translateX);

        mEndX = translateX;

        if (isUpdate) {

            applyCrop();
        }
    }

    public void setTranslateY(final float translateY) {

        final boolean isUpdate = (mEndY != translateY);

        mEndY = translateY;

        if (isUpdate) {

            applyCrop();
        }
    }

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

        super.onLayout(changed, left, top, right, bottom);

        if (changed) {

            applyCrop();
        }
    }

    private void applyCrop() {

        setImageDrawable(getDrawable());
    }

    private void cropImage(final int originalImageWidth, final int originalImageHeight) {

        final Handler messageHandler = mMessageHandler;

        if (messageHandler == null) {

            // We can't do anything right now.
            return;
        }

        if (mCropRunnable != null) {

            messageHandler.removeCallbacks(mCropRunnable);
        }

        if ((!mAutoFaceDetection && (mEndX == 0) && (mEndY == 0) && (mEndScale == 1)) || (
                originalImageWidth <= 0) || (originalImageHeight <= 0)) {

            final ScaleType scaleType = super.getScaleType();
            final ScaleType originalScaleType = mOriginalScaleType;

            if (scaleType != originalScaleType) {

                super.setScaleType(originalScaleType);
            }

            return;
        }

        mCropRunnable = new CropRunnable(originalImageWidth, originalImageHeight);

        if (Looper.getMainLooper() == Looper.myLooper()) {

            mCropRunnable.run();

        } else {

            messageHandler.post(mCropRunnable);
        }
    }

    private void getDefaultOffsets(final float[] offsets, final float maxOffsetX,
            final float maxOffsetY) {

        offsets[0] = maxOffsetX / 2;
        offsets[1] = maxOffsetY / 2;
    }

    private void getDetectedFaces(final Bitmap bitmap) {

        if (bitmap == null) {

            // Do nothing
            return;
        }

        final DefaultMasaccioFaceDetector faceDetector = sFaceDetector;

        if (faceDetector != null) {

            final int width = bitmap.getWidth();
            final int height = bitmap.getHeight();

            if (enabledDimensions(width, height, mActivateDetectionFlags)) {

                if (mAutoFaceDetection) {

                    mDetectedFaces = faceDetector.process(bitmap);

                } else {

                    mDetectedFaces = faceDetector.getFaces(bitmap);
                }

            } else {

                mDetectedFaces = null;
            }
        }
    }

    private void getFaceOffsets(final Face[] faces, final float[] offsets, final float scaleFactor,
            final float width, final float height, final float maxOffsetX, final float maxOffsetY) {

        try {

            Face bestFace = null;

            float maxConfidence = 0;

            for (final Face face : faces) {

                final float faceConfidence = face.confidence();

                if (faceConfidence > maxConfidence) {

                    maxConfidence = faceConfidence;
                    bestFace = face;
                }
            }

            if (bestFace == null) {

                getDefaultOffsets(offsets, maxOffsetX, maxOffsetY);

                return;
            }

            final PointF midPoint = new PointF();

            bestFace.getMidPoint(midPoint);

            final float scaledOffsetX =
                    (midPoint.x * scaleFactor) - ((width - maxOffsetX) * FACE_POSITION_RATIO_X);

            final float scaledOffsetY =
                    (midPoint.y * scaleFactor) - ((height - maxOffsetY) * FACE_POSITION_RATIO_Y);

            if (Math.round(maxOffsetX) >= 0) {

                offsets[0] = Math.min(Math.max(0, scaledOffsetX), maxOffsetX);

            } else {

                offsets[0] = scaledOffsetX;
            }

            if (Math.round(maxOffsetY) >= 0) {

                offsets[1] = Math.min(Math.max(0, scaledOffsetY), maxOffsetY);

            } else {

                offsets[1] = scaledOffsetY;
            }


        } catch (final Exception e) {

            getDefaultOffsets(offsets, maxOffsetX, maxOffsetY);
        }
    }

    private Matrix getOriginalMatrix(final int originalImageWidth, final int originalImageHeight) {

        final float frameWidth = getWidth();
        final float frameHeight = getHeight();

        final float fitHorizontallyScaleFactor = frameWidth / originalImageWidth;
        final float fitVerticallyScaleFactor = frameHeight / originalImageHeight;

        final float minScaleFactor = Math.min(fitHorizontallyScaleFactor, fitVerticallyScaleFactor);

        final float minOffsetX = frameWidth - (originalImageWidth * minScaleFactor);
        final float minOffsetY = frameHeight - (originalImageHeight * minScaleFactor);

        final Matrix matrix = new Matrix();

        switch (mOriginalScaleType) {

            case CENTER:

                matrix.postTranslate((frameWidth - originalImageWidth) / 2,
                                     (frameHeight - originalImageHeight) / 2);

                break;

            case CENTER_CROP:

                final float maxScaleFactor =
                        Math.max(fitHorizontallyScaleFactor, fitVerticallyScaleFactor);

                final float maxOffsetX = frameWidth - (originalImageWidth * maxScaleFactor);
                final float maxOffsetY = frameHeight - (originalImageHeight * maxScaleFactor);

                matrix.setScale(maxScaleFactor, maxScaleFactor);
                matrix.postTranslate(maxOffsetX / 2, maxOffsetY / 2);

                break;

            case CENTER_INSIDE:

                if (Math.max(fitHorizontallyScaleFactor, fitVerticallyScaleFactor) < 1) {

                    matrix.postTranslate((frameWidth - originalImageWidth) / 2,
                                         (frameHeight - originalImageHeight) / 2);

                    break;
                }

            case FIT_CENTER:

                matrix.setScale(minScaleFactor, minScaleFactor);
                matrix.postTranslate(minOffsetX / 2, minOffsetY / 2);

                break;

            case FIT_END:

                matrix.setScale(minScaleFactor, minScaleFactor);
                matrix.postTranslate(minOffsetX, minOffsetY);

                break;

            case FIT_START:

                matrix.setScale(minScaleFactor, minScaleFactor);

                break;

            case FIT_XY:

                matrix.setScale(fitHorizontallyScaleFactor, fitVerticallyScaleFactor);

                break;

            case MATRIX:

                matrix.set(MasaccioImageView.super.getImageMatrix());

                break;
        }

        return matrix;
    }

    private void init(final AttributeSet attrs, final int defStyle) {

        // Read attributes

        final TypedArray typedArray =
                getContext().obtainStyledAttributes(attrs, R.styleable.MasaccioImageView, defStyle,
                                                    0);

        mActivateDetectionFlags =
                typedArray.getInt(R.styleable.MasaccioImageView_activate_face_detection, 0);
        mActivateMatrixFlags = typedArray.getInt(R.styleable.MasaccioImageView_activate_matrix, 0);

        final boolean autoFaceDetection =
                typedArray.getBoolean(R.styleable.MasaccioImageView_center_face, false);

        mStartScale = typedArray.getFloat(R.styleable.MasaccioImageView_pre_scale, -1);
        mStartX = typedArray.getFloat(R.styleable.MasaccioImageView_pre_translate_x, 0);
        mStartY = typedArray.getFloat(R.styleable.MasaccioImageView_pre_translate_y, 0);

        mEndScale = typedArray.getFloat(R.styleable.MasaccioImageView_scale, -1);
        mEndX = typedArray.getFloat(R.styleable.MasaccioImageView_translate_x, 0);
        mEndY = typedArray.getFloat(R.styleable.MasaccioImageView_translate_y, 0);

        mAnimationDuration = typedArray.getInt(R.styleable.MasaccioImageView_animation_duration, 0);

        final int interpolatorId =
                typedArray.getResourceId(R.styleable.MasaccioImageView_animation_interpolator,
                                         NO_ID);

        mCyclicAnimation =
                typedArray.getBoolean(R.styleable.MasaccioImageView_cyclic_animation, false);

        mOriginalScaleType = getScaleType();
        mMessageHandler = new Handler();

        if (interpolatorId != NO_ID) {

            mInterpolator = AnimationUtils.loadInterpolator(getContext(), interpolatorId);

        } else {

            mInterpolator = mDefaultInterpolator;
        }

        setCenterFace(autoFaceDetection);
    }

    private void startMatrixAnimation(final Matrix start, final Matrix end, final long timeMs) {

        setImageMatrix(start);

        mStartCoeffs = new float[9];
        start.getValues(mStartCoeffs);

        mEndCoeffs = new float[9];
        end.getValues(mEndCoeffs);

        mStartTime = System.currentTimeMillis();
        mDuration = timeMs;
    }

    public interface MasaccioFaceDetector {

        public Face[] process(Bitmap bitmap);
    }

    private static class DefaultMasaccioFaceDetector implements MasaccioFaceDetector {

        private static final int MAX_FACES = 4;

        private static final Face[] NO_FACES = new Face[0];

        private final Map<Bitmap, Face[]> mFacesMap =
                Collections.synchronizedMap(new WeakHashMap<Bitmap, Face[]>());

        @Override
        public Face[] process(final Bitmap bitmap) {

            final Map<Bitmap, Face[]> facesMap = mFacesMap;

            final Face[] preProcessed = facesMap.get(bitmap);

            if (preProcessed != null) {

                if (preProcessed == NO_FACES) {

                    return null;
                }

                return preProcessed;
            }

            final Face[] faces = new Face[MAX_FACES];

            final Bitmap bitmap565 = convertTo565(bitmap);

            if (bitmap565 != null) {

                final FaceDetector faceDetector =
                        new FaceDetector(bitmap565.getWidth(), bitmap565.getHeight(), MAX_FACES);

                final int faceCount = faceDetector.findFaces(bitmap565, faces);

                if (faceCount > 0) {

                    final Face[] detected = new Face[faceCount];

                    System.arraycopy(faces, 0, detected, 0, faceCount);

                    facesMap.put(bitmap, detected);

                    return detected;
                }
            }

            facesMap.put(bitmap, NO_FACES);

            return null;
        }

        private Bitmap convertTo565(final Bitmap origin) {

            if (origin == null) {

                return null;
            }

            Bitmap bitmap = origin;

            if (bitmap.getConfig() != Bitmap.Config.RGB_565) {

                bitmap = bitmap.copy(Bitmap.Config.RGB_565, true);
            }

            if ((bitmap.getWidth() & 0x1) != 0) {

                bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth() & ~0x1,
                                             bitmap.getHeight());
            }

            return bitmap;
        }

        private Face[] getFaces(final Bitmap bitmap) {

            return mFacesMap.get(bitmap);
        }
    }

    private class CropRunnable implements Runnable {

        private final int mOriginalImageHeight;

        private final int mOriginalImageWidth;

        public CropRunnable(final int originalImageWidth, final int originalImageHeight) {

            mOriginalImageWidth = originalImageWidth;
            mOriginalImageHeight = originalImageHeight;
        }

        @Override
        public void run() {

            final float frameWidth = getWidth();
            final float frameHeight = getHeight();

            if ((frameWidth <= 0) || (frameHeight <= 0)) {

                MasaccioImageView.super.setScaleType(mOriginalScaleType);

                mMessageHandler.post(this);

                return;
            }

            MasaccioImageView.super.setScaleType(ScaleType.MATRIX);

            final int originalImageWidth = mOriginalImageWidth;
            final int originalImageHeight = mOriginalImageHeight;

            final float fitHorizontallyScaleFactor = frameWidth / originalImageWidth;
            final float fitVerticallyScaleFactor = frameHeight / originalImageHeight;

            final float maxScaleFactor =
                    Math.max(fitHorizontallyScaleFactor, fitVerticallyScaleFactor);

            final float newImageWidth = originalImageWidth * maxScaleFactor;
            final float newImageHeight = originalImageHeight * maxScaleFactor;

            final float maxOffsetX = newImageWidth - frameWidth;
            final float maxOffsetY = newImageHeight - frameHeight;

            final Matrix matrix;
            final Face[] detectedFaces = mDetectedFaces;
            final float[] translateOffset = new float[2];
            final int matrixFlag = mActivateMatrixFlags;

            if (detectedFaces != null) {

                getFaceOffsets(detectedFaces, translateOffset, maxScaleFactor, newImageWidth,
                               newImageHeight, maxOffsetX, maxOffsetY);

                matrix = new Matrix();
                matrix.setScale(maxScaleFactor, maxScaleFactor);
                matrix.postTranslate(-translateOffset[0], -translateOffset[1]);

                if (hasAllFlags(matrixFlag, FLAG_NO_FACE) && !hasAllFlags(matrixFlag,
                                                                          FLAG_IF_FACE)) {

                    setImageMatrix(matrix);

                    return;
                }

            } else {

                matrix = getOriginalMatrix(originalImageWidth, originalImageHeight);

                if (!hasAllFlags(matrixFlag, FLAG_NO_FACE) && hasAllFlags(matrixFlag,
                                                                          FLAG_IF_FACE)) {

                    setImageMatrix(matrix);

                    return;
                }
            }

            if (!enabledDimensions(originalImageWidth, originalImageHeight, matrixFlag)) {

                setImageMatrix(matrix);

                return;
            }

            final float endScale = mEndScale;
            final float endX = mEndX;
            final float endY = mEndY;

            final Matrix endMatrix;

            if ((endScale < 0) && (endX == 0) && (endY == 0)) {

                endMatrix = matrix;

            } else {

                endMatrix = new Matrix();

                final float scale;

                if (endScale < 0) {

                    scale = 1;

                } else {

                    scale = endScale;
                }

                final float endScaleFactor = maxScaleFactor * scale;
                final float endImageWidth = newImageWidth * scale;
                final float endImageHeight = newImageHeight * scale;
                final float[] scaledTranslateOffset = new float[2];

                if (detectedFaces != null) {

                    getFaceOffsets(detectedFaces, scaledTranslateOffset, endScaleFactor,
                                   endImageWidth, endImageHeight, endImageWidth - frameWidth,
                                   endImageHeight - frameHeight);

                } else {

                    getDefaultOffsets(scaledTranslateOffset, endImageWidth - frameWidth,
                                      endImageHeight - frameHeight);
                }

                endMatrix.setScale(endScaleFactor, endScaleFactor);
                endMatrix.postTranslate(
                        -scaledTranslateOffset[0] + (Math.abs(endImageWidth - frameWidth) * endX),
                        -scaledTranslateOffset[1] + (Math.abs(endImageHeight - frameHeight)
                                * endY));
            }

            if (mAnimationDuration > 0) {

                final float startScale = mStartScale;
                final float startX = mStartX;
                final float startY = mStartY;

                final Matrix startMatrix;

                if ((startScale < 0) && (startX == 0) && (startY == 0)) {

                    startMatrix = getOriginalMatrix(originalImageWidth, originalImageHeight);

                } else {

                    startMatrix = new Matrix();

                    final float scale;

                    if (startScale < 0) {

                        scale = 1;

                    } else {

                        scale = startScale;
                    }

                    final float startScaleFactor = maxScaleFactor * scale;
                    final float startImageWidth = newImageWidth * scale;
                    final float startImageHeight = newImageHeight * scale;

                    startMatrix.setScale(startScaleFactor, startScaleFactor);

                    final float scaledOffsetX =
                            ((newImageWidth * (scale - 1)) / 2) + translateOffset[0];

                    final float scaledOffsetY =
                            ((newImageHeight * (scale - 1)) / 2) + translateOffset[1];

                    startMatrix.postTranslate(
                            -scaledOffsetX + (Math.abs(startImageWidth - frameWidth) * startX),
                            -scaledOffsetY + (Math.abs(startImageHeight - frameHeight) * startY));
                }

                startMatrixAnimation(startMatrix, endMatrix, mAnimationDuration);

            } else {

                setImageMatrix(endMatrix);
            }
        }
    }

    private class StepInterpolator implements Interpolator {

        @Override
        public float getInterpolation(final float input) {

            return 0;
        }
    }
}




Java Source Code List

it.subito.masaccio.ApplicationTest.java
it.subito.masaccio.MasaccioImageView.java
it.subito.masaccio.TestActivity.java
it.subito.masaccio.demo.DemoActivity.java