package com.marshalchen.common.uimodule.patio; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.content.res.TypedArray; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; import android.provider.MediaStore; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.HorizontalScrollView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.marshalchen.common.uimodule.animation.R; import com.marshalchen.common.uimodule.easyandroidanimations.Animation; import com.marshalchen.common.uimodule.easyandroidanimations.SlideInAnimation; import com.marshalchen.common.uimodule.easyandroidanimations.SlideOutAnimation; import com.squareup.picasso.Picasso; import java.io.File; import java.io.IOException; import java.util.ArrayList; public class Patio extends LinearLayout implements View.OnClickListener { /** * Constants */ public final static String TAG = Patio.class.getSimpleName(); public final static int DEFAULT_MAX_PICTURES = 3; /** * Variables */ private final int WIDGET_LAYOUT_RES_ID = R.layout.patio_patio_layout; private final int THUMBNAIL_LAYOUT_RES_ID = R.layout.patio_patio_thumbnail; private Context mContext; private int mMaxPictures; private PatioCallbacks mListener; private ArrayList<PatioThumbnail> mPatioThumbnails; private String mTakePicturePath; //Resources private float mThumbnailWidth; private float mThumbnailHeight; private String mThumbnailsMessageString; /** * Controls */ //Actions public Button mTakePicture; public Button mAttachPicture; public Button mRemovePicture; public Button mCancel; //Containers public HorizontalScrollView mThumbnailsWrapper; public LinearLayout mThumbnailsContainer; public TextView mThumbnailsCount; //Toolbars public LinearLayout mToolbarAddActions; public LinearLayout mToolbarRemoveActions; //TODO: http://ryanharter.com/blog/2014/08/29/building-dynamic-custom-views/ /** * Constructor */ public Patio(Context context) { super(context); init(context, null); } public Patio(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public Patio(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } /** * Lifecycle methods * https://speakerdeck.com/cyrilmottier/deep-dive-into-android-state-restoration * https://github.com/CharlesHarley/Example-Android-SavingInstanceState/blob/master/src/com/example/android/savinginstancestate/views/LockCombinationPicker.java * https://gist.github.com/granoeste/4037468 */ @Override protected Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState savedState = new SavedState(superState); savedState.setThumbnailsPaths(getThumbnailsPaths()); savedState.setTakePicturePath(mTakePicturePath); return savedState; } @Override protected void onRestoreInstanceState(Parcelable state) { SavedState savedState = (SavedState) state; super.onRestoreInstanceState(savedState.getSuperState()); ArrayList<String> thumbnailsPaths = savedState.getThumbnailsPaths(); String takePicturePath = savedState.getTakePicturePath(); restoreState(thumbnailsPaths, takePicturePath); } /** * Custom methods */ public void init(Context context, AttributeSet attributeSet) { //Setup defaults mContext = context; mMaxPictures = DEFAULT_MAX_PICTURES; mThumbnailHeight = mContext.getResources().getDimension(R.dimen.patio_default_thumbnail_height); mThumbnailWidth = mContext.getResources().getDimension(R.dimen.patio_default_thumbnail_width); mThumbnailsMessageString = "patio_thumbnails_message"; mPatioThumbnails = new ArrayList<PatioThumbnail>(); setOrientation(VERTICAL); //Local defaults float thumbnailsContainerPadding = mContext.getResources().getDimension(R.dimen.patio_default_thumbnails_container_padding); int actionsTextColor = mContext.getResources().getColor(R.color.patio_default_action_text_color); int thumbnailsWrapperBackground = mContext.getResources().getColor(R.color.patio_default_thumbnails_container_background); //Inflate view and setup controls LayoutInflater inflater = LayoutInflater.from(context); inflater.inflate(WIDGET_LAYOUT_RES_ID, this, true); //Buttons mTakePicture = (Button) findViewById(R.id.patio_action_take_picture); mAttachPicture = (Button) findViewById(R.id.patio_action_attach_picture); mRemovePicture = (Button) findViewById(R.id.patio_action_remove_picture); mCancel = (Button) findViewById(R.id.patio_action_cancel); //Containers mThumbnailsWrapper = (HorizontalScrollView) findViewById(R.id.patio_thumbnails_wrapper); mThumbnailsContainer = (LinearLayout) findViewById(R.id.patio_thumbnails_container); mThumbnailsCount = (TextView) findViewById(R.id.patio_thumbnails_count_message); //Toolbars mToolbarAddActions = (LinearLayout) findViewById(R.id.patio_add_actions_toolbar); mToolbarRemoveActions = (LinearLayout) findViewById(R.id.patio_remove_actions_toolbar); mToolbarRemoveActions.setVisibility(View.GONE); //Set actions listeners if(!isInEditMode()) { mTakePicture.setOnClickListener(this); mAttachPicture.setOnClickListener(this); mRemovePicture.setOnClickListener(this); mCancel.setOnClickListener(this); } //Get defined attributes if(attributeSet != null) { TypedArray a = mContext.getTheme().obtainStyledAttributes(attributeSet, R.styleable.Patio, 0, 0); try { //Runtime mMaxPictures = a.getInt(R.styleable.Patio_patio_maxPictures, DEFAULT_MAX_PICTURES); mThumbnailHeight = a.getDimension(R.styleable.Patio_patio_thumbnailHeight, mThumbnailHeight); mThumbnailWidth = a.getDimension(R.styleable.Patio_patio_thumbnailWidth, mThumbnailWidth); mThumbnailsMessageString = a.getString(R.styleable.Patio_patio_thumbnailsMessage); //Local thumbnailsContainerPadding = a.getDimension(R.styleable.Patio_patio_thumbnailsContainerPadding, thumbnailsContainerPadding); actionsTextColor = a.getColor(R.styleable.Patio_patio_actionsTextColor, actionsTextColor); thumbnailsWrapperBackground = a.getColor(R.styleable.Patio_patio_thumbnailsContainerBackground, thumbnailsWrapperBackground); } finally { a.recycle(); } } //Check Max Pictures if(mMaxPictures <= 0) { mMaxPictures = DEFAULT_MAX_PICTURES; } //Check Thumbnail Message if(mThumbnailsMessageString == null) { mThumbnailsMessageString = "patio_thumbnails_message"; } //Setup actions text color mTakePicture.setTextColor(actionsTextColor); mAttachPicture.setTextColor(actionsTextColor); mRemovePicture.setTextColor(actionsTextColor); mCancel.setTextColor(actionsTextColor); //Setup thumbnails container background mThumbnailsWrapper.setBackgroundColor(thumbnailsWrapperBackground); //Setup dimensions ViewGroup.LayoutParams layoutParams; int paddingTop, paddingBottom, paddingLeft, paddingRight; //Thumbnails Wrapper height layoutParams = mThumbnailsWrapper.getLayoutParams(); layoutParams.height = Float.valueOf(mThumbnailHeight).intValue(); mThumbnailsWrapper.setLayoutParams(layoutParams); //Thumbnails Container padding paddingTop = paddingBottom = paddingLeft = paddingRight = Float.valueOf(thumbnailsContainerPadding).intValue(); mThumbnailsContainer.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom); //Init Thumbnails Message updateThumbnailsMessage(); } public void restoreState(ArrayList<String> thumbnailsPaths, String takePicturePath) { for(String thumbnailPath : thumbnailsPaths) { addThumbnail(thumbnailPath); } mTakePicturePath = takePicturePath; } public void addThumbnail(String thumbnailPath) { LayoutInflater inflater = LayoutInflater.from(mContext); ImageView imageView = (ImageView) inflater.inflate(THUMBNAIL_LAYOUT_RES_ID, mThumbnailsContainer, false); int resizeDimension = mThumbnailWidth > mThumbnailHeight ? Float.valueOf(mThumbnailWidth).intValue() : Float.valueOf(mThumbnailHeight).intValue(); Picasso.with(mContext) .load(new File(thumbnailPath)) .resize(resizeDimension, resizeDimension) .centerCrop() .into(imageView); ViewGroup.LayoutParams layoutParams = imageView.getLayoutParams(); layoutParams.width = Float.valueOf(mThumbnailWidth).intValue(); layoutParams.height = Float.valueOf(mThumbnailHeight).intValue(); mThumbnailsContainer.addView(imageView, 0, layoutParams); imageView.setOnClickListener(this); mPatioThumbnails.add(new PatioThumbnail(thumbnailPath, imageView)); updateThumbnailsMessage(); } public void updateThumbnailsMessage() { int count = mPatioThumbnails.size(); Resources res = mContext.getResources(); String thumbnailsCountMessage = String.format(mThumbnailsMessageString, count, mMaxPictures); mThumbnailsCount.setText(thumbnailsCountMessage); //Check actions button if max pictures reached boolean actionsEnabled = mPatioThumbnails.size() < mMaxPictures; mTakePicture.setEnabled(actionsEnabled); mAttachPicture.setEnabled(actionsEnabled); } public void setCallbacksListener(PatioCallbacks listener) { mListener = listener; } public Intent getTakePictureIntent() { mTakePicturePath = null; File pictureFile = null; try { pictureFile = PatioUtils.getNewImageFile(); } catch (IOException ex) { ex.printStackTrace(); } if(pictureFile == null) return null; mTakePicturePath = pictureFile.getAbsolutePath(); Log.d(TAG, "Path: " + mTakePicturePath); Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(pictureFile)); return takePictureIntent; } public Intent getAttachPictureIntent() { Intent attachPictureIntent = new Intent(Intent.ACTION_PICK); attachPictureIntent.setType("image/*"); return attachPictureIntent; } public void handleTakePictureResult(Intent data) { Log.d(TAG, "File Path: " + mTakePicturePath); addThumbnail(mTakePicturePath); PatioUtils.addNewImageToGallery(mContext, mTakePicturePath); } public void handleAttachPictureResult(Intent data) { Uri uri = data.getData(); String filePath = PatioUtils.getRealPathFromURI(mContext, uri); Log.d(TAG, "File Path: " + filePath); addThumbnail(filePath); } public void showAddToolbar() { if(mToolbarAddActions.getVisibility() == View.VISIBLE) return; new SlideOutAnimation(mToolbarRemoveActions).setDirection(Animation.DIRECTION_UP).animate(); new SlideInAnimation(mToolbarAddActions).setDirection(Animation.DIRECTION_UP).animate(); } public void showRemoveToolbar() { if(mToolbarRemoveActions.getVisibility() == View.VISIBLE) return; new SlideOutAnimation(mToolbarAddActions).setDirection(Animation.DIRECTION_UP).animate(); new SlideInAnimation(mToolbarRemoveActions).setDirection(Animation.DIRECTION_UP).animate(); } public void cancelThumbnailSelection() { for(PatioThumbnail patioThumbnail : mPatioThumbnails) { patioThumbnail.setSelected(false); } checkToolbarsStatus(); } public void removeSelectedThumbnails() { for(int i = mPatioThumbnails.size() - 1; i >= 0; i--) { if(mPatioThumbnails.get(i).isSelected()) { ImageView thumbnailView = mPatioThumbnails.get(i).getThumbnailView(); mThumbnailsContainer.removeView(thumbnailView); mPatioThumbnails.remove(i); } } updateThumbnailsMessage(); checkToolbarsStatus(); } public void checkToolbarsStatus() { boolean thumbnailsSelected = false; for(PatioThumbnail patioThumbnail : mPatioThumbnails) { if (patioThumbnail.getThumbnailView().getAlpha() == 0.5f) { thumbnailsSelected = true; break; } } if(thumbnailsSelected) showRemoveToolbar(); else showAddToolbar(); } public ArrayList<String> getThumbnailsPaths() { ArrayList<String> thumbnailsPaths = new ArrayList<String>(); for(PatioThumbnail patioThumbnail : mPatioThumbnails) { thumbnailsPaths.add(patioThumbnail.getThumbnailPath()); } return thumbnailsPaths; } /** * OnClick buttons methods */ @Override public void onClick(View view) { //Buttons if(view instanceof Button) { if(view.getId() == R.id.patio_action_take_picture) { mListener.onTakePictureClick(); } if(view.getId() == R.id.patio_action_attach_picture) { mListener.onAddPictureClick(); } if(view.getId() == R.id.patio_action_remove_picture) { removeSelectedThumbnails(); } if(view.getId() == R.id.patio_action_cancel) { cancelThumbnailSelection(); } } //Thumbnails if(view instanceof ImageView) { //Check for PatioThumbnail for(PatioThumbnail patioThumbnail : mPatioThumbnails) { //Inverse selection for selected thumbnail if(patioThumbnail.getThumbnailView().equals(view)) { patioThumbnail.setSelected(!patioThumbnail.isSelected()); break; } } checkToolbarsStatus(); } } /** * Interface */ public interface PatioCallbacks { public void onTakePictureClick(); public void onAddPictureClick(); } /** * SavedState class for restoring view state */ protected static class SavedState extends BaseSavedState { public ArrayList<String> mThumbnailsPaths; private String mTakePictureFilePath; public SavedState(Parcelable superState) { super(superState); } public SavedState(Parcel in) { super(in); mThumbnailsPaths = in.readArrayList(String.class.getClassLoader()); mTakePictureFilePath = in.readString(); } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeList(mThumbnailsPaths); out.writeString(mTakePictureFilePath); } public static final Creator<SavedState> CREATOR = new Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; public void setThumbnailsPaths(ArrayList<String> thumbnailsPaths) { mThumbnailsPaths = thumbnailsPaths; } public ArrayList<String> getThumbnailsPaths() { return mThumbnailsPaths; } public void setTakePicturePath(String takePictureFilePath) { mTakePictureFilePath = takePictureFilePath; } public String getTakePicturePath() { return mTakePictureFilePath; } } }