/* * Copyright (C) 2015 Jorge Ruesga * * 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 com.ruesga.android.wallpapers.photophase.widgets; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask.Status; import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.view.MotionEventCompat; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.MotionEvent; import android.view.SoundEffectConstants; import android.view.View; import android.view.WindowManager; import android.view.animation.AccelerateInterpolator; import android.view.animation.Animation; import android.view.animation.ScaleAnimation; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.FrameLayout; import android.widget.ImageView; import com.ruesga.android.wallpapers.photophase.AndroidHelper; import com.ruesga.android.wallpapers.photophase.PhotoViewerActivity; import com.ruesga.android.wallpapers.photophase.R; import com.ruesga.android.wallpapers.photophase.model.Picture; import com.ruesga.android.wallpapers.photophase.tasks.AsyncPictureLoaderTask; import com.ruesga.android.wallpapers.photophase.tasks.AsyncPictureLoaderTask.AsyncPictureLoaderRunnable; import com.ruesga.android.wallpapers.photophase.tasks.AsyncPictureLoaderTask.OnPictureLoaded; import java.io.File; import java.util.ArrayList; import java.util.List; /** * A view that contains the view of the picture of an abbum */ public class PictureItemView extends FrameLayout { /** * A convenient listener for receive events of the PictureItemView class * */ public interface CallbacksListener { /** * Invoked when a picture was pressed * * @param v The view */ void onPictureItemViewPressed(View v); } private final OnCheckedChangeListener mSelectionListener = new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { for (CallbacksListener cb : mCallbacks) { cb.onPictureItemViewPressed(PictureItemView.this); } } }; private List<CallbacksListener> mCallbacks; private Picture mPicture; private AsyncPictureLoaderRunnable mTask; private Animation mScaleInAnimation; private Animation mScaleOutAnimation; private ImageView mIcon; private CheckBox mCheckbox; private View mExpand; private View mOverlay; private boolean mInEditMode; private boolean mLongClickFired; private DisplayMetrics mMetrics; /** * Constructor of <code>PictureItemView</code>. * * @param context The current context */ public PictureItemView(Context context) { super(context); init(); } /** * Constructor of <code>PictureItemView</code>. * * @param context The current context * @param attrs The attributes of the XML tag that is inflating the view. */ public PictureItemView(Context context, AttributeSet attrs) { super(context, attrs); init(); } /** * Constructor of <code>PictureItemView</code>. * * @param context The current context * @param attrs The attributes of the XML tag that is inflating the view. * @param defStyle The default style to apply to this view. If 0, no style * will be applied (beyond what is included in the theme). This may * either be an attribute resource, whose value will be retrieved * from the current theme, or an explicit style resource. */ public PictureItemView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } /** * Method that initializes the internal references */ private void init() { mCallbacks = new ArrayList<>(); mMetrics = new DisplayMetrics(); WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); wm.getDefaultDisplay().getMetrics(mMetrics); mScaleInAnimation = new ScaleAnimation( 1f, 0.98f, 1f, 0.98f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); mScaleInAnimation.setFillAfter(true); mScaleInAnimation.setDuration(100L); mScaleInAnimation.setInterpolator(new AccelerateInterpolator()); mScaleOutAnimation = new ScaleAnimation( 0.98f, 1f, 0.98f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); mScaleOutAnimation.setFillAfter(true); mScaleOutAnimation.setDuration(100L); mScaleOutAnimation.setInterpolator(new AccelerateInterpolator()); setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { mLongClickFired = true; mScaleInAnimation.cancel(); mScaleOutAnimation.cancel(); for (CallbacksListener cb : mCallbacks) { cb.onPictureItemViewPressed(PictureItemView.this); } return false; } }); setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (!mInEditMode) { final int action = MotionEventCompat.getActionMasked(event); switch (action) { case MotionEvent.ACTION_DOWN: mLongClickFired = false; mScaleOutAnimation.cancel(); startAnimation(mScaleInAnimation); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: mScaleInAnimation.cancel(); startAnimation(mScaleOutAnimation); if (action == MotionEvent.ACTION_UP && !mLongClickFired) { playSoundEffect(SoundEffectConstants.CLICK); performDisplayPicture(mIcon); } break; } } return false; } }); } /** * Method that adds the class that will be listen for events of this class * * @param callback The callback class */ public void addCallBackListener(CallbacksListener callback) { this.mCallbacks.add(callback); } /** * Method that removes the class from the current callbacks * * @param callback The callback class */ public void removeCallBackListener(CallbacksListener callback) { this.mCallbacks.remove(callback); } /** * {@inheritDoc} */ @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); // Cancel pending tasks if (mTask != null) { removeCallbacks(mTask); if (mTask.mTask.getStatus() != Status.FINISHED) { mTask.mTask.cancel(true); } } } /** * Method that returns the picture * * @return Picture The picture */ public Picture getPicture() { return mPicture; } /** * Method that sets the picture * * @param picture The picture */ public void setPicture(Picture picture) { mPicture = picture; } /** * Method that updates the view * * @param picture The picture data */ public void updateView(final Picture picture, boolean editMode, boolean refreshIcon) { // Destroy the update drawable task if (mTask != null) { removeCallbacks(mTask); if (mTask.mTask.getStatus() != Status.FINISHED) { mTask.mTask.cancel(true); } } // Retrieve the views references if (mIcon == null) { mIcon = (ImageView) findViewById(R.id.photo); } if (mCheckbox == null) { mCheckbox = (CheckBox) findViewById(R.id.picture_selector); } if (mExpand == null) { mExpand = findViewById(R.id.picture_expand); mExpand.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { performDisplayPicture(mIcon); } }); } if (mOverlay == null) { mOverlay = findViewById(R.id.picture_overlay); } // Update the views setPicture(picture); mInEditMode = editMode; if (picture != null) { setSelected(picture.isSelected()); mCheckbox.setOnCheckedChangeListener(null); mCheckbox.setChecked(picture.isSelected()); mCheckbox.setVisibility(editMode ? View.VISIBLE : View.GONE); mCheckbox.setOnCheckedChangeListener(mSelectionListener); mExpand.setVisibility(editMode ? View.VISIBLE : View.GONE); mOverlay.setVisibility(editMode ? View.VISIBLE : View.GONE); // Choose whether use the cache or just redraw the image to avoid to much // memory footprint (cache only in high-end devices) if (AndroidHelper.isHighEndDevice(getContext()) && picture.getBitmap() != null) { mIcon.setImageBitmap(picture.getBitmap()); } else if (refreshIcon) { mIcon.setImageBitmap(null); // Show as icon, the first picture File f = new File(picture.getPath()); AsyncPictureLoaderTask task = new AsyncPictureLoaderTask(getContext(), mIcon, mMetrics.widthPixels, mMetrics.heightPixels, new OnPictureLoaded() { @Override public void onPictureLoaded(Object o, Drawable drawable) { if (AndroidHelper.isHighEndDevice(getContext())) { if (drawable instanceof BitmapDrawable) { picture.setBitmap(((BitmapDrawable) drawable).getBitmap()); } } } }); task.mFactor = 8; mTask = new AsyncPictureLoaderRunnable(task, f); ViewCompat.postOnAnimation(this, mTask); } } } private void performDisplayPicture(ImageView photo) { Intent intent = new Intent(getContext(), PhotoViewerActivity.class); intent.putExtra(PhotoViewerActivity.EXTRA_PHOTO, getPicture().getPath()); if (AndroidHelper.isJellyBeanOrGreater()) { ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation( ((Activity) getContext()), photo, "photo"); getContext().startActivity(intent, options.toBundle()); } else { getContext().startActivity(intent); } } }