From 4effddfe30fd045834232f6bb66070edf079578d Mon Sep 17 00:00:00 2001 From: Jorge Ruesga Date: Sat, 2 Nov 2013 02:20:56 +0100 Subject: Multiples fixes - Fully rewrite the album selection preference - Fix multiple style - Fix lints - Resources cleanup Signed-off-by: Jorge Ruesga --- .../photophase/MediaPictureDiscoverer.java | 2 +- .../wallpapers/photophase/PhotoPhaseRenderer.java | 28 +- .../wallpapers/photophase/PhotoPhaseWallpaper.java | 2 +- .../wallpapers/photophase/TextureManager.java | 10 +- .../photophase/adapters/AlbumCardUiAdapter.java | 139 ++++ .../photophase/adapters/AlbumPictureAdapter.java | 127 ++++ .../AlbumsFlip3dAnimationController.java | 164 ----- .../animations/Flip3dAnimationController.java | 153 ++++ .../photophase/animations/Rotate3dAnimation.java | 93 +++ .../wallpapers/photophase/model/Album.java | 23 +- .../wallpapers/photophase/model/Picture.java | 64 ++ .../preferences/ChoosePicturesFragment.java | 805 ++++++++++++++++----- .../preferences/DispositionFragment.java | 2 +- .../preferences/PhotoPhasePreferences.java | 3 +- .../preferences/PreferencesProvider.java | 13 +- .../photophase/tasks/AsyncPictureLoaderTask.java | 52 +- .../wallpapers/photophase/widgets/AlbumInfo.java | 284 -------- .../photophase/widgets/AlbumInfoView.java | 367 ++++++++++ .../photophase/widgets/AlbumPictures.java | 345 --------- .../wallpapers/photophase/widgets/CardLayout.java | 91 --- .../photophase/widgets/DispositionView.java | 8 +- .../photophase/widgets/PictureItemView.java | 204 ++++++ .../photophase/widgets/PicturesView.java | 164 ----- .../widgets/VerticalEndlessScroller.java | 114 --- 24 files changed, 1858 insertions(+), 1399 deletions(-) create mode 100644 src/org/cyanogenmod/wallpapers/photophase/adapters/AlbumCardUiAdapter.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/adapters/AlbumPictureAdapter.java delete mode 100644 src/org/cyanogenmod/wallpapers/photophase/animations/AlbumsFlip3dAnimationController.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/animations/Flip3dAnimationController.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/animations/Rotate3dAnimation.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/model/Picture.java delete mode 100644 src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumInfo.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumInfoView.java delete mode 100644 src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumPictures.java delete mode 100644 src/org/cyanogenmod/wallpapers/photophase/widgets/CardLayout.java create mode 100644 src/org/cyanogenmod/wallpapers/photophase/widgets/PictureItemView.java delete mode 100644 src/org/cyanogenmod/wallpapers/photophase/widgets/PicturesView.java delete mode 100644 src/org/cyanogenmod/wallpapers/photophase/widgets/VerticalEndlessScroller.java (limited to 'src') diff --git a/src/org/cyanogenmod/wallpapers/photophase/MediaPictureDiscoverer.java b/src/org/cyanogenmod/wallpapers/photophase/MediaPictureDiscoverer.java index 4228abf..f45dcd6 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/MediaPictureDiscoverer.java +++ b/src/org/cyanogenmod/wallpapers/photophase/MediaPictureDiscoverer.java @@ -264,7 +264,7 @@ public class MediaPictureDiscoverer { } } - /*package*/ final Context mContext; + final Context mContext; private final OnMediaPictureDiscoveredListener mCallback; private AsyncDiscoverTask mTask; diff --git a/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseRenderer.java b/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseRenderer.java index f57d53b..c307023 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseRenderer.java +++ b/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseRenderer.java @@ -62,25 +62,25 @@ public class PhotoPhaseRenderer implements GLSurfaceView.Renderer { private final long mInstance; private static long sInstances; - /*package*/ final Context mContext; - /*package*/ EffectContext mEffectContext; + final Context mContext; + EffectContext mEffectContext; private final Handler mHandler; - /*package*/ final GLESSurfaceDispatcher mDispatcher; - /*package*/ TextureManager mTextureManager; + final GLESSurfaceDispatcher mDispatcher; + TextureManager mTextureManager; - /*package*/ PhotoPhaseWallpaperWorld mWorld; - /*package*/ ColorShape mOverlay; - /*package*/ OopsShape mOopsShape; + PhotoPhaseWallpaperWorld mWorld; + ColorShape mOverlay; + OopsShape mOopsShape; - /*package*/ long mLastRunningTransition; + long mLastRunningTransition; private long mLastTouchTime; private static final long TOUCH_BARRIER_TIME = 1000L; - /*package*/ int mWidth = -1; - /*package*/ int mHeight = -1; + int mWidth = -1; + int mHeight = -1; private int mStatusBarHeight = 0; - /*package*/ int mMeasuredHeight = -1; + int mMeasuredHeight = -1; private final float[] mMVPMatrix = new float[16]; private final float[] mProjMatrix = new float[16]; @@ -88,7 +88,7 @@ public class PhotoPhaseRenderer implements GLSurfaceView.Renderer { private final Object mDrawing = new Object(); - /*package*/ final Object mMediaSync = new Object(); + final Object mMediaSync = new Object(); private PendingIntent mMediaScanIntent; private final BroadcastReceiver mSettingsChangedReceiver = new BroadcastReceiver() { @@ -383,13 +383,13 @@ public class PhotoPhaseRenderer implements GLSurfaceView.Renderer { /** * Method that deselect the current transition */ - /*package*/ synchronized void deselectCurrentTransition() { + synchronized void deselectCurrentTransition() { mHandler.removeCallbacks(mTransitionThread); mWorld.deselectTransition(mMVPMatrix); mLastRunningTransition = 0; } - /*package*/ void scheduleOrCancelMediaScan() { + void scheduleOrCancelMediaScan() { int interval = Preferences.Media.getRefreshFrecuency(); if (interval != Preferences.Media.MEDIA_RELOAD_DISABLED) { scheduleMediaScan(interval); diff --git a/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseWallpaper.java b/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseWallpaper.java index 625212c..5152bc8 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseWallpaper.java +++ b/src/org/cyanogenmod/wallpapers/photophase/PhotoPhaseWallpaper.java @@ -96,7 +96,7 @@ public class PhotoPhaseWallpaper class PhotoPhaseWallpaperEngine extends GLES20WallpaperService.GLES20Engine { private final Handler mHandler; - /*package*/ final ActivityManager mActivityManager; + final ActivityManager mActivityManager; /** * Constructor of PhotoPhaseWallpaperEngine diff --git a/src/org/cyanogenmod/wallpapers/photophase/TextureManager.java b/src/org/cyanogenmod/wallpapers/photophase/TextureManager.java index 6c8de05..3731a1b 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/TextureManager.java +++ b/src/org/cyanogenmod/wallpapers/photophase/TextureManager.java @@ -59,8 +59,8 @@ public class TextureManager implements OnMediaPictureDiscoveredListener { BackgroundPictureLoaderThread mBackgroundTask; /*protected*/ final MediaPictureDiscoverer mPictureDiscoverer; - /*package*/ Rect mScreenDimensions; - /*package*/ Rect mDimensions; + Rect mScreenDimensions; + Rect mDimensions; final GLESSurfaceDispatcher mDispatcher; @@ -73,7 +73,7 @@ public class TextureManager implements OnMediaPictureDiscoveredListener { /** * A private runnable that will run in the GLThread */ - /*package*/ class PictureDispatcher implements Runnable { + class PictureDispatcher implements Runnable { File mImage; GLESTextureInfo ti = null; final Object mWait = new Object(); @@ -428,7 +428,7 @@ public class TextureManager implements OnMediaPictureDiscoveredListener { * @param ti The original texture information * @param effect The effect to apply to the destination picture */ - /*package*/ void fixAspectRatio(TextureRequestor requestor, GLESTextureInfo ti) { + void fixAspectRatio(TextureRequestor requestor, GLESTextureInfo ti) { // Check if we have to apply any correction to the image if (Preferences.General.isFixAspectRatio()) { // Transform requestor dimensions to screen dimensions @@ -472,7 +472,7 @@ public class TextureManager implements OnMediaPictureDiscoveredListener { boolean mRun; boolean mTaskPaused; - /*package*/ boolean mEmpty; + boolean mEmpty; private final List mNewImages; private final List mUsedImages; diff --git a/src/org/cyanogenmod/wallpapers/photophase/adapters/AlbumCardUiAdapter.java b/src/org/cyanogenmod/wallpapers/photophase/adapters/AlbumCardUiAdapter.java new file mode 100644 index 0000000..b321b73 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/adapters/AlbumCardUiAdapter.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project + * + * 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 org.cyanogenmod.wallpapers.photophase.adapters; + +import android.animation.Animator; +import android.animation.AnimatorInflater; +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; + +import org.cyanogenmod.wallpapers.photophase.R; +import org.cyanogenmod.wallpapers.photophase.model.Album; +import org.cyanogenmod.wallpapers.photophase.widgets.AlbumInfoView; + +import java.util.ArrayList; +import java.util.List; + +/** + * An implementation of {@link ArrayAdapter} supporting "Google Now Card Layout" like layout + */ +public class AlbumCardUiAdapter extends ArrayAdapter { + + /** + * A class that conforms with the ViewHolder pattern to performance + * the list view rendering. + */ + private static class ViewHolder { + public ViewHolder() { + super(); + } + AlbumInfoView mAlbumInfoView; + Animation mCardAnimation; + Animator mCardAnimator; + } + + private List mData = new ArrayList(); + + private AlbumInfoView.CallbacksListener mAlbumInfoCallback; + + /** + * Constructor of AlbumCardUiAdapter. + * + * @param context The current context + * @param parent The adapter view + * @param data The array with all the album data + * @param callback The album info view callback + */ + public AlbumCardUiAdapter(Context context, AdapterView parent, List data, + AlbumInfoView.CallbacksListener callback) { + super(context, R.id.album_name, data); + mData = data; + mAlbumInfoCallback = callback; + } + + /** + * Method that dispose the elements of the adapter. + */ + public void dispose() { + clear(); + this.mData = null; + } + + /** + * {@inheritDoc} + */ + @Override + public View getView(int position, View convertView, ViewGroup parent) { + + // Retrieve album + final Album album = this.mData.get(position); + + // Check to reuse view + View v = convertView; + if (v == null) { + //Create the view holder + LayoutInflater li = + (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + v = li.inflate(R.layout.album_info, parent, false); + + // Create the controller for the view + ViewHolder viewHolder = new ViewHolder(); + viewHolder.mAlbumInfoView = (AlbumInfoView)v.findViewById(R.id.album_info); + viewHolder.mAlbumInfoView.setAlbum(album); + viewHolder.mAlbumInfoView.addCallBackListener(mAlbumInfoCallback); + v.setTag(viewHolder); + } + + // Retrieve the view holder + ViewHolder viewHolder = (ViewHolder)v.getTag(); + + // Retrieve the view holder and update the view + viewHolder.mAlbumInfoView.updateView(album); + + // Animate the view? + if (!album.isDisplayed()) { + album.setDisplayed(true); + + // Reset the animation + if (viewHolder.mCardAnimation != null) { + viewHolder.mCardAnimation.cancel(); + } + if (viewHolder.mCardAnimator != null) { + viewHolder.mCardAnimator.cancel(); + } + + // Card Animation + viewHolder.mCardAnimation = AnimationUtils.loadAnimation( + getContext(), R.anim.cards_slide_up); + v.startAnimation(viewHolder.mCardAnimation); + viewHolder.mCardAnimator = AnimatorInflater.loadAnimator(getContext(), + R.animator.cards_rotate_animator); + viewHolder.mCardAnimator.setTarget(v); + viewHolder.mCardAnimator.start(); + } + + // Return the view + return v; + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/adapters/AlbumPictureAdapter.java b/src/org/cyanogenmod/wallpapers/photophase/adapters/AlbumPictureAdapter.java new file mode 100644 index 0000000..addc298 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/adapters/AlbumPictureAdapter.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project + * + * 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 org.cyanogenmod.wallpapers.photophase.adapters; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; + +import org.cyanogenmod.wallpapers.photophase.R; +import org.cyanogenmod.wallpapers.photophase.model.Picture; +import org.cyanogenmod.wallpapers.photophase.widgets.PictureItemView; + +import java.util.ArrayList; +import java.util.List; + +/** + * An implementation of {@link ArrayAdapter} an album picture + */ +public class AlbumPictureAdapter extends ArrayAdapter { + + /** + * A class that conforms with the ViewHolder pattern to performance + * the list view rendering. + */ + private static class ViewHolder { + public ViewHolder() { + super(); + } + PictureItemView mPictureItemView; + } + + private List mData = new ArrayList(); + private AdapterView mParent; + + /** + * Constructor of AlbumPictureAdapter. + * + * @param context The current context + * @param data The pictures data + */ + public AlbumPictureAdapter(Context context, List data, AdapterView parent) { + super(context, R.id.picture_thumbnail, data); + mData = data; + mParent = parent; + } + + /** + * Method that dispose the elements of the adapter. + */ + public void dispose() { + clear(); + this.mData = null; + } + + @Override + public void notifyDataSetChanged() { + int start = mParent.getFirstVisiblePosition(); + int last = mParent.getLastVisiblePosition(); + for (int i = start; i <= last; i++) { + View v = mParent.getChildAt(i); + getView(i, v, mParent, false); + } + } + + /** + * {@inheritDoc} + */ + @Override + public View getView(int position, View convertView, ViewGroup parent) { + return getView(position, convertView, parent, true); + } + + /** + * Method that returns a view for a position + * + * @param position The position + * @param convertView A reusable view or null + * @param parent The parent of the view + * @param refreshIcon If the view should refresh its icon + * @return View The view + */ + private View getView(int position, View convertView, ViewGroup parent, boolean refreshIcon) { + final Picture picture = this.mData.get(position); + + // Check to reuse view + View v = convertView; + if (v == null) { + //Create the view holder + LayoutInflater li = + (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + v = li.inflate(R.layout.picture_item, parent, false); + + // Create the controller for the view + ViewHolder viewHolder = new ViewHolder(); + viewHolder.mPictureItemView = (PictureItemView)v.findViewById(R.id.picture); + viewHolder.mPictureItemView.setPicture(picture); + v.setTag(viewHolder); + } + + // Retrieve the view holder + ViewHolder viewHolder = (ViewHolder)v.getTag(); + + // Retrieve the view holder and update the view + viewHolder.mPictureItemView.updateView(picture, refreshIcon); + + // Return the view + return v; + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/animations/AlbumsFlip3dAnimationController.java b/src/org/cyanogenmod/wallpapers/photophase/animations/AlbumsFlip3dAnimationController.java deleted file mode 100644 index 228f827..0000000 --- a/src/org/cyanogenmod/wallpapers/photophase/animations/AlbumsFlip3dAnimationController.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2013 The CyanogenMod Project - * - * 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 org.cyanogenmod.wallpapers.photophase.animations; - -import android.view.View; -import android.view.View.OnClickListener; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.Animation; -import android.view.animation.Animation.AnimationListener; - -import org.cyanogenmod.wallpapers.photophase.model.Album; -import org.cyanogenmod.wallpapers.photophase.widgets.AlbumInfo; -import org.cyanogenmod.wallpapers.photophase.widgets.AlbumPictures; -import org.cyanogenmod.wallpapers.photophase.widgets.AlbumPictures.CallbacksListener; - -/** - * A class that manages a flip 3d effect of an album - */ -public class AlbumsFlip3dAnimationController { - - private static final int DURATION = 200; - - View mFront; - View mBack; - boolean mFrontFace; - - /** - * Constructor of AlbumsFlip3dAnimationController - * - * @param front The front view - * @param back The back view - */ - public AlbumsFlip3dAnimationController(AlbumInfo front, AlbumPictures back) { - super(); - mFront = front; - mBack = back; - mBack.setVisibility(View.GONE); - mFrontFace = true; - } - - /** - * Method that register the controller - */ - public void register() { - getFrontView().setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - getBackView().setVisibility(View.INVISIBLE); - applyAnimation(false); - } - }); - ((AlbumPictures)getBackView()).addCallBackListener(new CallbacksListener() { - @Override - public void onBackButtonClick(View v) { - getBackView().setVisibility(View.INVISIBLE); - applyAnimation(true); - } - - @Override - public void onSelectionChanged(Album album) { - // Ignore - } - }); - } - - /** - * Method that unregister the controller - */ - public void unregister() { - getFrontView().setOnClickListener(null); - getBackView().setOnClickListener(null); - } - - /** - * Method that reset the controller - */ - public void reset() { - if (!mFrontFace) { - applyAnimation(true); - } - } - - /** - * Method that applies the animation over the views - * - * @param inverse Applies the inverse animation - */ - /*package*/ void applyAnimation(boolean inverse) { - applyTransformation(getFrontView(), 0, 90 * (inverse ? -1 : 1), true); - } - - /*package*/ void applyTransformation(final View v, float start, float end, final boolean step1) { - final float centerX = v.getWidth() / 2.0f; - final float centerY = v.getHeight() / 2.0f; - - final Flip3dAnimation anim = new Flip3dAnimation(start, end, centerX, centerY); - anim.setDuration(DURATION); - anim.setFillAfter(true); - anim.setInterpolator(new AccelerateInterpolator()); - - anim.setAnimationListener(new AnimationListener() { - @Override - public void onAnimationStart(Animation animation) { - if (!step1) { - getBackView().setVisibility(View.VISIBLE); - } - getFrontView().setOnClickListener(null); - getBackView().setOnClickListener(null); - } - - @Override - public void onAnimationRepeat(Animation animation) { - // Ignore - } - - @Override - public void onAnimationEnd(Animation animation) { - getFrontView().setAnimation(null); - getBackView().setAnimation(null); - if (step1) { - getFrontView().setVisibility(View.INVISIBLE); - applyTransformation(getBackView(), -90 * (!mFrontFace ? -1 : 1), 0, false); - } else { - mFrontFace = !mFrontFace; - getBackView().setVisibility(View.GONE); - if (mFrontFace) { - getFrontView().setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - getBackView().setVisibility(View.INVISIBLE); - applyAnimation(false); - } - }); - } else { - ((AlbumPictures)getFrontView()).onShow(); - } - } - } - }); - v.startAnimation(anim); - } - - /*package*/ View getFrontView() { - return mFrontFace ? mFront : mBack; - } - - /*package*/ View getBackView() { - return !mFrontFace ? mFront : mBack; - } -} - diff --git a/src/org/cyanogenmod/wallpapers/photophase/animations/Flip3dAnimationController.java b/src/org/cyanogenmod/wallpapers/photophase/animations/Flip3dAnimationController.java new file mode 100644 index 0000000..69f023d --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/animations/Flip3dAnimationController.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project + * + * 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 org.cyanogenmod.wallpapers.photophase.animations; + +import android.view.View; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.Animation.AnimationListener; + +/** + * A class that manages a flip 3d effect of two views + */ +public class Flip3dAnimationController { + + private static final int DURATION = 200; + + View mFront; + View mBack; + boolean mFrontFace; + + final Object mLock = new Object(); + + /** + * Constructor of Flip3dAnimationController + * + * @param front The front view + * @param back The back view + */ + public Flip3dAnimationController(View front, View back) { + super(); + mFront = front; + mBack = back; + mBack.setVisibility(View.GONE); + mFrontFace = true; + } + + public boolean isFrontFace() { + return mFrontFace; + } + + /** + * Method that reset the controller + */ + public void reset() { + changeToFrontFace(true); + } + + /** + * Method that change the view to the front face + * + * @param animate Do with animation + */ + public void changeToFrontFace(boolean animate) { + if (!mFrontFace) { + if (animate) { + applyAnimation(true); + } else { + mBack.setVisibility(View.GONE); + mFront.setVisibility(View.VISIBLE); + mFrontFace = true; + } + } + } + + /** + * Method that change the view to the back face + * + * @param animate Do with animation + */ + public void changeToBackFace(boolean animate) { + if (mFrontFace) { + if (animate) { + applyAnimation(true); + } else { + mFront.setVisibility(View.GONE); + mBack.setVisibility(View.VISIBLE); + mFrontFace = false; + } + } + } + + /** + * Method that applies the animation over the views + * + * @param inverse Applies the inverse animation + */ + void applyAnimation(boolean inverse) { + applyTransformation(getFrontView(), 0, 90 * (inverse ? -1 : 1), true); + } + + void applyTransformation(final View v, float start, float end, final boolean step1) { + final float centerX = v.getWidth() / 2.0f; + final float centerY = v.getHeight() / 2.0f; + + final Flip3dAnimation anim = new Flip3dAnimation(start, end, centerX, centerY); + anim.setDuration(DURATION); + anim.setFillAfter(true); + anim.setInterpolator(new AccelerateInterpolator()); + + anim.setAnimationListener(new AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + if (!step1) { + getBackView().setVisibility(View.VISIBLE); + } + } + + @Override + public void onAnimationRepeat(Animation animation) { + // Ignore + } + + @Override + public void onAnimationEnd(Animation animation) { + synchronized (mLock) { + getFrontView().setAnimation(null); + getBackView().setAnimation(null); + if (step1) { + getFrontView().setVisibility(View.INVISIBLE); + applyTransformation(getBackView(), + -90 * (!mFrontFace ? -1 : 1), 0, false); + } else { + mFrontFace = !mFrontFace; + getBackView().setVisibility(View.GONE); + } + } + } + }); + v.startAnimation(anim); + } + + View getFrontView() { + return mFrontFace ? mFront : mBack; + } + + View getBackView() { + return !mFrontFace ? mFront : mBack; + } +} + diff --git a/src/org/cyanogenmod/wallpapers/photophase/animations/Rotate3dAnimation.java b/src/org/cyanogenmod/wallpapers/photophase/animations/Rotate3dAnimation.java new file mode 100644 index 0000000..814ef0f --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/animations/Rotate3dAnimation.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * 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 org.cyanogenmod.wallpapers.photophase.animations; + +import android.view.animation.Animation; +import android.view.animation.Transformation; +import android.graphics.Camera; +import android.graphics.Matrix; + +/** + * An animation that rotates the view on the Y axis between two specified angles. + * This animation also adds a translation on the Z axis (depth) to improve the effect. + */ +public class Rotate3dAnimation extends Animation { + private final float mFromDegrees; + private final float mToDegrees; + private final float mCenterX; + private final float mCenterY; + private final float mDepthZ; + private final boolean mReverse; + private Camera mCamera; + + /** + * Creates a new 3D rotation on the Y axis. The rotation is defined by its + * start angle and its end angle. Both angles are in degrees. The rotation + * is performed around a center point on the 2D space, definied by a pair + * of X and Y coordinates, called centerX and centerY. When the animation + * starts, a translation on the Z axis (depth) is performed. The length + * of the translation can be specified, as well as whether the translation + * should be reversed in time. + * + * @param fromDegrees the start angle of the 3D rotation + * @param toDegrees the end angle of the 3D rotation + * @param centerX the X center of the 3D rotation + * @param centerY the Y center of the 3D rotation + * @param reverse true if the translation should be reversed, false otherwise + */ + public Rotate3dAnimation(float fromDegrees, float toDegrees, + float centerX, float centerY, float depthZ, boolean reverse) { + mFromDegrees = fromDegrees; + mToDegrees = toDegrees; + mCenterX = centerX; + mCenterY = centerY; + mDepthZ = depthZ; + mReverse = reverse; + } + + @Override + public void initialize(int width, int height, int parentWidth, int parentHeight) { + super.initialize(width, height, parentWidth, parentHeight); + mCamera = new Camera(); + } + + @Override + protected void applyTransformation(float interpolatedTime, Transformation t) { + final float fromDegrees = mFromDegrees; + float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime); + + final float centerX = mCenterX; + final float centerY = mCenterY; + final Camera camera = mCamera; + + final Matrix matrix = t.getMatrix(); + + camera.save(); + if (mReverse) { + camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime); + } else { + camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime)); + } + camera.rotateY(degrees); + camera.getMatrix(matrix); + camera.restore(); + + matrix.preTranslate(-centerX, -centerY); + matrix.postTranslate(centerX, centerY); + } +} + diff --git a/src/org/cyanogenmod/wallpapers/photophase/model/Album.java b/src/org/cyanogenmod/wallpapers/photophase/model/Album.java index 83f067d..0c1c2a2 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/model/Album.java +++ b/src/org/cyanogenmod/wallpapers/photophase/model/Album.java @@ -24,16 +24,20 @@ import java.util.List; /** * A class that represents an album */ -public class Album implements Comparable, Cloneable { +public class Album implements Comparable, Cloneable { private Drawable mIcon; private String mPath; private String mName; private String mDate; private boolean mSelected; - private List mItems; + private List mItems; + // We have this array for performance access. Do not forget to sync with Picture#selected private List mSelectedItems; + // Ui properties + private boolean mDisplayed = false; + public Drawable getIcon() { return mIcon; } @@ -74,11 +78,11 @@ public class Album implements Comparable, Cloneable { this.mSelected = selected; } - public List getItems() { + public List getItems() { return mItems; } - public void setItems(List items) { + public void setItems(List items) { this.mItems = items; } @@ -90,6 +94,14 @@ public class Album implements Comparable, Cloneable { this.mSelectedItems = selectedItems; } + public boolean isDisplayed() { + return mDisplayed; + } + + public void setDisplayed(boolean mDisplayed) { + this.mDisplayed = mDisplayed; + } + @Override public int compareTo(Album another) { return mPath.compareTo(another.mPath); @@ -102,9 +114,10 @@ public class Album implements Comparable, Cloneable { album.mPath = mPath; album.mName = mName; album.mDate = mDate; - album.mItems = new ArrayList(mItems); + album.mItems = new ArrayList(mItems); album.mSelectedItems = new ArrayList(mSelectedItems); album.mSelected = mSelected; + album.mDisplayed = mDisplayed; return album; } } diff --git a/src/org/cyanogenmod/wallpapers/photophase/model/Picture.java b/src/org/cyanogenmod/wallpapers/photophase/model/Picture.java new file mode 100644 index 0000000..76b1bb6 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/model/Picture.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project + * + * 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 org.cyanogenmod.wallpapers.photophase.model; + +import java.io.File; + +/** + * A class that represents a picture + */ +public class Picture implements Comparable, Cloneable { + + private String mPath; + private boolean mSelected; + + public Picture(String path, boolean selected) { + super(); + this.mPath = path; + this.mSelected = selected; + } + + public String getPath() { + return mPath; + } + + public void setPath(String path) { + this.mPath = path; + } + + public boolean isSelected() { + return mSelected; + } + + public void setSelected(boolean selected) { + this.mSelected = selected; + } + + public String getName() { + return new File(mPath).getName(); + } + + @Override + public int compareTo(Picture another) { + return mPath.compareTo(another.mPath); + } + + @Override + public Object clone() { + return new Picture(mPath, mSelected); + } +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/ChoosePicturesFragment.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/ChoosePicturesFragment.java index 6e41308..00d75a6 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/preferences/ChoosePicturesFragment.java +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/ChoosePicturesFragment.java @@ -16,7 +16,9 @@ package org.cyanogenmod.wallpapers.photophase.preferences; -import android.content.ContentResolver; +import android.animation.Animator; +import android.animation.Animator.AnimatorListener; +import android.animation.ObjectAnimator; import android.content.Context; import android.content.Intent; import android.content.res.Resources; @@ -24,6 +26,8 @@ import android.database.Cursor; import android.os.AsyncTask; import android.os.AsyncTask.Status; import android.os.Bundle; +import android.os.Handler; +import android.os.Message; import android.preference.PreferenceFragment; import android.provider.MediaStore; import android.util.Log; @@ -32,19 +36,30 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; +import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.FrameLayout; +import android.widget.GridView; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; import org.cyanogenmod.wallpapers.photophase.R; -import org.cyanogenmod.wallpapers.photophase.animations.AlbumsFlip3dAnimationController; +import org.cyanogenmod.wallpapers.photophase.adapters.AlbumCardUiAdapter; +import org.cyanogenmod.wallpapers.photophase.adapters.AlbumPictureAdapter; import org.cyanogenmod.wallpapers.photophase.model.Album; +import org.cyanogenmod.wallpapers.photophase.model.Picture; import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider.Preferences; -import org.cyanogenmod.wallpapers.photophase.widgets.AlbumInfo; -import org.cyanogenmod.wallpapers.photophase.widgets.AlbumPictures; -import org.cyanogenmod.wallpapers.photophase.widgets.CardLayout; -import org.cyanogenmod.wallpapers.photophase.widgets.VerticalEndlessScroller; -import org.cyanogenmod.wallpapers.photophase.widgets.VerticalEndlessScroller.OnEndScrollListener; +import org.cyanogenmod.wallpapers.photophase.widgets.AlbumInfoView; +import org.cyanogenmod.wallpapers.photophase.widgets.PictureItemView; import java.io.File; +import java.io.IOException; import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; @@ -56,25 +71,28 @@ import java.util.Set; /** * A fragment class for select the picture that will be displayed on the wallpaper */ -public class ChoosePicturesFragment extends PreferenceFragment implements OnEndScrollListener { +public class ChoosePicturesFragment extends PreferenceFragment + implements AlbumInfoView.CallbacksListener, OnClickListener { private static final String TAG = "ChoosePicturesFragment"; private static final boolean DEBUG = false; - private static final int AMOUNT_OF_ADDED_STEPS = 5; + private static final int PROGRESS_STEPS = 5; + + // The album loader task + private final AsyncTask mTask = new AsyncTask() { + + private DateFormat mDateFormat; - private final AsyncTask mAlbumsLoaderTask = new AsyncTask() { /** * {@inheritDoc} */ @Override protected Void doInBackground(Void... params) { // Query all the external content and classify the pictures in albums and load the cards - DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); - Album album = null; - unregister(); - Cursor c = mContentResolver.query( + mDateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); + Cursor c = getActivity().getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{ MediaStore.MediaColumns.DATA }, null, @@ -84,40 +102,31 @@ public class ChoosePicturesFragment extends PreferenceFragment implements OnEndS try { long start = System.currentTimeMillis(); if (DEBUG) Log.v(TAG, "Media library:"); + int count = 0; + List pending = new ArrayList(); + List all = new ArrayList(); + Album album = null; while (c.moveToNext()) { - // Only valid files (those i can read) - String p = c.getString(0); - if (DEBUG) Log.v(TAG, "\t" + p); - if (p != null) { - File f = new File(p); - if (f.isFile() && f.canRead()) { - File path = f.getParentFile(); - String name = path.getName(); - if (album == null || album.getPath().compareTo(path.getAbsolutePath()) != 0) { - if (album != null) { - mAlbums.add(album); - mOriginalAlbums.add((Album)album.clone()); - } - album = new Album(); - album.setPath(path.getAbsolutePath()); - album.setName(name); - album.setDate(df.format(new Date(path.lastModified()))); - album.setSelected(isSelectedItem(album.getPath())); - album.setItems(new ArrayList()); - album.setSelectedItems(new ArrayList()); - } - album.getItems().add(f.getAbsolutePath()); - if (isSelectedItem(f.getAbsolutePath())) { - album.getSelectedItems().add(f.getAbsolutePath()); - } - } + album = processPath(all, pending, album, c.getString(0)); + count++; + if (count % PROGRESS_STEPS == 0) { + // Notify and clean + publishProgress(pending.toArray(new Album[pending.size()])); + pending.clear(); } } - // Add the last album + // Add the last albums if (album != null) { - mAlbums.add(album); + // Add to global structures + all.add(album); mOriginalAlbums.add((Album)album.clone()); + + // Add to local structures and notify + pending.add(album); + + // Notify + publishProgress(pending.toArray(new Album[pending.size()])); } long end = System.currentTimeMillis(); if (DEBUG) Log.v(TAG, "Library loaded in " + (end - start) + " miliseconds"); @@ -129,47 +138,143 @@ public class ChoosePicturesFragment extends PreferenceFragment implements OnEndS return null; } + /** + * {@inheritDoc} + */ + @Override + protected void onProgressUpdate(Album... albums) { + for(Album album : albums) { + mAlbums.add(album); + } + mAlbumAdapter.notifyDataSetChanged(); + } + /** * {@inheritDoc} */ @Override protected void onPostExecute(Void result) { - Resources res = getActivity().getResources(); - int size = (int)(res.getDimension(R.dimen.album_size) + - res.getDimension(R.dimen.small_margin)); - mScroller.setEndPadding(size * AMOUNT_OF_ADDED_STEPS); - int height = mScroller.getMeasuredHeight(); - int steps = (height / size) + AMOUNT_OF_ADDED_STEPS; - - // Create the views an force a redraw the items - mAlbumViews = new ArrayList(mAlbums.size()); - for (Album item : mAlbums) { - mAlbumViews.add(createAlbumView(item)); + mAlbumAdapter.notifyDataSetChanged(); + } + + /** + * Method that process a album path + * + * @param all All the albums + * @param pending Pending albums to notify + * @param data The current album data + * @param path The path to analyze + * @return Album The new current album + */ + private Album processPath(List all, List pending, Album data, String path) { + // Only valid files (those i can read) + if (DEBUG) Log.v(TAG, "\t" + path); + Album album = data; + if (path != null) { + File f = new File(path); + if (f.isFile() && f.canRead()) { + File p = f.getParentFile(); + String name = p.getName(); + if (album == null || album.getPath().compareTo(p.getAbsolutePath()) != 0) { + if (album != null) { + // Add to global structures + all.add(album); + mOriginalAlbums.add((Album)album.clone()); + + // Add to local structures and notify + pending.add(album); + } + album = new Album(); + album.setPath(p.getAbsolutePath()); + album.setName(name); + album.setDate(mDateFormat.format(new Date(p.lastModified()))); + album.setSelected(isSelectedItem(album.getPath())); + album.setItems(new ArrayList()); + album.setSelectedItems(new ArrayList()); + } + boolean selected = isSelectedItem(f.getAbsolutePath()); + album.getItems().add(new Picture(f.getAbsolutePath(), selected)); + if (selected) { + album.getSelectedItems().add(f.getAbsolutePath()); + } + } } - doEndScroll(steps, true); + return album; + } - // We not need Hardware acceleration anymore (no more animations) - // Disable drawing cache - mScroller.setLayerType(View.LAYER_TYPE_SOFTWARE, null); - mScroller.setDrawingCacheEnabled(false); - mScroller.setSmoothScrollingEnabled(true); + /** + * Method that checks if an item is selected + * + * @param item The item + * @return boolean if an item is selected + */ + private boolean isSelectedItem(String item) { + Iterator it = mSelectedAlbums.iterator(); + while (it.hasNext()) { + String albumPath = it.next(); + if (item.compareTo(albumPath) == 0) { + return true; + } + } + return false; } }; - /*package*/ ContentResolver mContentResolver; + private final Handler.Callback mCallback = new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + if (msg.what == MSG_LOAD_PICTURES) { + loadPictures((Album)msg.obj); + return true; + } + return false; + } + }; - /*package*/ List mAlbums; - /*package*/ List mAlbumViews; - /*package*/ List mOriginalAlbums; - /*package*/ List mAnimationControllers; + OnItemClickListener mOnItemClickListener = new OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (mShowingAlbums) { + onHeaderPressed(parent, view, position); + } else { + onPicturePressed(view); + } + } + }; + + private static final int MSG_LOAD_PICTURES = 1; - /*package*/ Set mSelectedAlbums; + List mAlbums; + List mOriginalAlbums; + + Set mSelectedAlbums; private Set mOriginalSelectedAlbums; - /*package*/ VerticalEndlessScroller mScroller; - private CardLayout mAlbumsPanel; + ViewGroup mContainer; + + ListView mAlbumsPanel; + AlbumCardUiAdapter mAlbumAdapter; - /*package*/ boolean mSelectionChanged; + GridView mPicturesPanel; + AlbumPictureAdapter mPictureAdapter; + + private boolean mSelectionChanged; + + // Animation references + ViewGroup mSrcParent; + View mSrcView; + ViewGroup mDstParent; + View mDstView; + + Album mAlbum; + + private int mPicturesAnimDurationIn; + private int mPicturesAnimDurationOut; + + Handler mHandler; + LayoutInflater mInflater; + + boolean mShowingAlbums; /** * {@inheritDoc} @@ -177,22 +282,26 @@ public class ChoosePicturesFragment extends PreferenceFragment implements OnEndS @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mContentResolver = getActivity().getContentResolver(); + mHandler = new Handler(mCallback); + mShowingAlbums = true; // Create an empty album mAlbums = new ArrayList(); mOriginalAlbums = new ArrayList(); - mAnimationControllers = new ArrayList(); // Change the preference manager getPreferenceManager().setSharedPreferencesName(PreferencesProvider.PREFERENCES_FILE); getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE); // Load the albums user selection - mOriginalSelectedAlbums = Preferences.Media.getSelectedMedia(); + mOriginalSelectedAlbums = removeObsoleteAlbumsData(Preferences.Media.getSelectedMedia()); mSelectedAlbums = new HashSet(mOriginalSelectedAlbums); mSelectionChanged = false; + Resources res = getActivity().getResources(); + mPicturesAnimDurationIn = res.getInteger(R.integer.pictures_anim_in); + mPicturesAnimDurationOut = res.getInteger(R.integer.pictures_anim_out); + setHasOptionsMenu(true); } @@ -202,12 +311,19 @@ public class ChoosePicturesFragment extends PreferenceFragment implements OnEndS @Override public void onDestroy() { super.onDestroy(); - if (mAlbumsLoaderTask.getStatus().compareTo(Status.PENDING) == 0) { - mAlbumsLoaderTask.cancel(true); + if (mTask.getStatus().compareTo(Status.PENDING) == 0) { + mTask.cancel(true); } unbindDrawables(mAlbumsPanel); unregister(); + if (!mShowingAlbums) { + mPicturesPanel.setVisibility(View.GONE); + mDstView.setVisibility(View.GONE); + mDstParent.removeView(mPicturesPanel); + mDstParent.removeView(mDstView); + } + // Notify that the settings was changed Intent intent = new Intent(PreferencesProvider.ACTION_SETTINGS_CHANGED); if (mSelectionChanged) { @@ -218,10 +334,9 @@ public class ChoosePicturesFragment extends PreferenceFragment implements OnEndS getActivity().sendBroadcast(intent); } - /*package*/ void unregister() { + private void unregister() { mAlbums.clear(); mOriginalAlbums.clear(); - mAnimationControllers.clear(); } /** @@ -229,7 +344,7 @@ public class ChoosePicturesFragment extends PreferenceFragment implements OnEndS * * @param view The root view */ - private void unbindDrawables(View view) { + void unbindDrawables(View view) { if (view.getBackground() != null) { view.getBackground().setCallback(null); } @@ -237,7 +352,6 @@ public class ChoosePicturesFragment extends PreferenceFragment implements OnEndS for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { unbindDrawables(((ViewGroup) view).getChildAt(i)); } - ((ViewGroup) view).removeAllViews(); } } @@ -245,26 +359,43 @@ public class ChoosePicturesFragment extends PreferenceFragment implements OnEndS * {@inheritDoc} */ @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(final LayoutInflater inflater, final ViewGroup container, + Bundle savedInstanceState) { + + mContainer = container; + mInflater = inflater; + // Inflate the layout for this fragment - mScroller = - (VerticalEndlessScroller)inflater.inflate( + FrameLayout root = + (FrameLayout)mInflater.inflate( R.layout.choose_picture_fragment, container, false); - mScroller.setCallback(this); - mAlbumsPanel = (CardLayout)mScroller.findViewById(R.id.albums_panel); + mAlbumsPanel = (ListView)root.findViewById(R.id.albums_panel); + mAlbumsPanel.setSmoothScrollbarEnabled(true); + mAlbumAdapter = new AlbumCardUiAdapter(getActivity(), mAlbumsPanel, mAlbums, this); + mAlbumsPanel.setAdapter(mAlbumAdapter); + mAlbumsPanel.setOnItemClickListener(mOnItemClickListener); // Force Hardware acceleration - if (!mScroller.isHardwareAccelerated()) { - mScroller.setLayerType(View.LAYER_TYPE_HARDWARE, null); + if (!root.isHardwareAccelerated()) { + root.setLayerType(View.LAYER_TYPE_HARDWARE, null); } if (!mAlbumsPanel.isHardwareAccelerated()) { mAlbumsPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null); } // Load the albums - mAlbumsLoaderTask.execute(); + unregister(); + mTask.execute(); + + return root; + } - return mScroller; + @Override + public void onClick(View v) { + // Hide the albums picture with animation + if (v.equals(mDstView)) { + hideAlbumPictures(mDstParent, mDstView, mSrcParent, mSrcView); + } } /** @@ -287,8 +418,12 @@ public class ChoosePicturesFragment extends PreferenceFragment implements OnEndS case R.id.mnu_restore: restoreData(); return true; - case R.id.mnu_invert_all: - invertAll(); + case R.id.mnu_invert: + if (mShowingAlbums) { + invertAll(); + } else { + invertAlbum(mAlbum); + } return true; default: return super.onOptionsItemSelected(item); @@ -301,14 +436,25 @@ public class ChoosePicturesFragment extends PreferenceFragment implements OnEndS private void restoreData() { // Restore and the albums the selection mSelectedAlbums = new HashSet(mOriginalSelectedAlbums); - mAlbums.clear(); - for (Album album : mOriginalAlbums) { - mAlbums.add((Album)album.clone()); + int count = mAlbums.size(); + for (int i = 0; i < count ; i++) { + Album album = mAlbums.get(i); + Album originalAlbum = mOriginalAlbums.get(i); + + // Update selected status + album.setSelected(originalAlbum.isSelected()); + album.setItems(new ArrayList(originalAlbum.getItems())); + album.setSelectedItems(new ArrayList(originalAlbum.getSelectedItems())); } + mAlbumAdapter.notifyDataSetChanged(); - // Update all the views + // Update settings Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); - updateAll(); + mSelectionChanged = true; + + if (!mShowingAlbums) { + hideAlbumPictures(mDstParent, mDstView, mSrcParent, mSrcView); + } } /** @@ -325,172 +471,431 @@ public class ChoosePicturesFragment extends PreferenceFragment implements OnEndS } else { mSelectedAlbums.addAll(album.getSelectedItems()); } + for (Picture picture : album.getItems()) { + picture.setSelected(false); + } } + mAlbumAdapter.notifyDataSetChanged(); - // Update all the views + // Update settings Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); - updateAll(); + mSelectionChanged = true; } /** - * Method that updates the current state of all the albums + * Method that inverts the selection of an album + * + * @param album The album which to invert its selection */ - private void updateAll() { - // Update every view (albums and views should have the same size) - int count = mAlbumsPanel.getChildCount(); - for (int i = 0; i < count; i++) { - Album album = mAlbums.get(i); - View v = mAlbumsPanel.getChildAt(i); - AlbumInfo albumInfo = (AlbumInfo)v.findViewById(R.id.album_info); - AlbumPictures albumPictures = (AlbumPictures)v.findViewById(R.id.album_pictures); - albumInfo.updateView(album); - albumPictures.updateView(album, true); + private void invertAlbum(Album album) { + // Remove all pictures of the album + removeAlbumItems(album); + List origSelectedItems = new ArrayList(album.getSelectedItems()); + List selectedItems = album.getSelectedItems(); + album.getSelectedItems().clear(); + for (Picture picture : album.getItems()) { + boolean selected = !origSelectedItems.contains(picture.getPath()); + if (selected) { + selectedItems.add(picture.getPath()); + } + picture.setSelected(selected); } - // Restore the preference + // Notify pictures dataset changed + mPictureAdapter.notifyDataSetChanged(); + updateAlbumInfo(mDstView, album); + + mSelectedAlbums.addAll(album.getSelectedItems()); Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); mSelectionChanged = true; + } + + /** + * {@inheritDoc} + */ + @Override + public void onAlbumSelected(Album album) { + updateAlbumSelection(album, true); + } + + /** + * {@inheritDoc} + */ + @Override + public void onAlbumDeselected(Album album) { + updateAlbumSelection(album, false); + } + + /** + * {@inheritDoc} + */ + @Override + public void onAllPicturesSelected(Album album) { + updateAllPicturesSelection(album, true); + } - // Restore all the animations states - for (AlbumsFlip3dAnimationController controller : mAnimationControllers) { - controller.reset(); + /** + * {@inheritDoc} + */ + @Override + public void onAllPicturesDeselected(Album album) { + updateAllPicturesSelection(album, false); + } + + /** + * Method that update the album selection + * + * @param album The album to update + * @param selected If the album is selected + */ + private void updateAlbumSelection(Album album, boolean selected) { + // Remove all pictures of the album + removeAlbumItems(album); + album.setSelected(selected); + album.getSelectedItems().clear(); + for (Picture picture : album.getItems()) { + picture.setSelected(false); + } + if (selected) { + mSelectedAlbums.add(album.getPath()); } + + if (!mShowingAlbums) { + // Notify pictures dataset changed + updateAlbumInfo(mDstView, album); + mPictureAdapter.notifyDataSetChanged(); + } + updateAlbumInfo(mSrcView, album); + + Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); + mSelectionChanged = true; } /** - * Method that creates a new album to the card layout + * Method that update the whole picture selection * - * @param album The album to create - * @return View The view create + * @param album The album to update + * @param selected If all the picture were selected */ - View createAlbumView(final Album album) { - LayoutInflater li = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - final View albumView = li.inflate(R.layout.album, mAlbumsPanel, false); - final AlbumInfo albumInfo = (AlbumInfo)albumView.findViewById(R.id.album_info); - final AlbumPictures albumPictures = (AlbumPictures)albumView.findViewById(R.id.album_pictures); - - // Load the album info - albumInfo.post(new Runnable() { - @Override - public void run() { - albumInfo.updateView(album); + private void updateAllPicturesSelection(Album album, boolean selected) { + // Remove all pictures of the album + removeAlbumItems(album); + List selectedItems = album.getSelectedItems(); + selectedItems.clear(); + for (Picture picture : album.getItems()) { + if (selected) { + selectedItems.add(picture.getPath()); + } + picture.setSelected(selected); + } + album.setSelected(false); + + // Notify pictures dataset changed + mPictureAdapter.notifyDataSetChanged(); + updateAlbumInfo(mDstView, album); + + mSelectedAlbums.addAll(album.getSelectedItems()); + Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); + mSelectionChanged = true; + } + + /** + * Method that removes the reference to all the items and itself + * + * @param ref The album + */ + private void removeAlbumItems(Album ref) { + Iterator it = mSelectedAlbums.iterator(); + while (it.hasNext()) { + String item = it.next(); + String parent = new File(item).getParent(); + if (parent.compareTo(ref.getPath()) == 0) { + it.remove(); + } else if (item.compareTo(ref.getPath()) == 0) { + it.remove(); } - }); - if (album.isSelected()) { - albumInfo.setSelected(true); } - albumInfo.addCallBackListener(new AlbumInfo.CallbacksListener() { + } + + /** + * Method that shows the album pictures while animating the view + * + * @param srcParent The source parent view + * @param srcView The source view + * @param dstParent The destination parent view + * @param dstView The destination view + */ + void showAlbumPictures(final ViewGroup srcParent, final View srcView, + final ViewGroup dstParent, final View dstView) { + + // Hide the source view + srcView.setAlpha(0.0f); + + // Animation from bottom to top + ObjectAnimator anim1 = ObjectAnimator.ofFloat(dstView, "translationY", + srcView.getY(), srcParent.getPaddingTop()); + anim1.setDuration(mPicturesAnimDurationIn); + anim1.setInterpolator(new AccelerateDecelerateInterpolator()); + anim1.addListener(new AnimatorListener() { @Override - public void onAlbumSelected(Album ref) { - // Remove all pictures of the album and add the album reference - removeAlbumItems(ref); - mSelectedAlbums.add(ref.getPath()); - ref.setSelected(true); - albumPictures.updateView(ref, true); - - Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); - mSelectionChanged = true; + public void onAnimationStart(Animator animation) { + // Ignore } @Override - public void onAlbumDeselected(Album ref) { - // Remove all pictures of the album - removeAlbumItems(ref); - ref.setSelected(false); - albumPictures.updateView(ref, true); - - Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); - mSelectionChanged = true; + public void onAnimationRepeat(Animator animation) { + // Ignore } + @Override + public void onAnimationEnd(Animator animation) { + // Re-layout the view in its new position + dstView.setOnClickListener(ChoosePicturesFragment.this); + + // And now finally show the new album pictures layout + // and fill it + mPicturesPanel.setVisibility(View.VISIBLE); + mHandler.sendMessage(mHandler.obtainMessage(MSG_LOAD_PICTURES, mAlbum)); + mShowingAlbums = false; + } + @Override + public void onAnimationCancel(Animator animation) { + // Ignore + } }); + anim1.start(); + + // Hide the parent view + AlphaAnimation anim2 = new AlphaAnimation(1.0f, 0.0f); + anim2.setDuration(mPicturesAnimDurationIn); + anim2.setFillAfter(true); + anim2.setZAdjustment(Animation.ZORDER_BOTTOM); + anim2.setInterpolator(new AccelerateDecelerateInterpolator()); + srcParent.setEnabled(false); + srcParent.startAnimation(anim2); + + // Save the references + mSrcParent = srcParent; + mSrcView = srcView; + mDstParent = dstParent; + mDstView = dstView; + } + + /** + * Method that hides the album pictures while animating the view + * + * @param srcParent The source parent view + * @param srcView The source view + * @param dstParent The destination parent view + * @param dstView The destination view + */ + void hideAlbumPictures(final ViewGroup srcParent, final View srcView, + final ViewGroup dstParent, final View dstView) { + + mPicturesPanel.setVisibility(View.INVISIBLE); + + // Animation from top to bottom + ObjectAnimator anim1 = ObjectAnimator.ofFloat(srcView, "translationY", + dstParent.getPaddingTop(), dstView.getY()); + anim1.setDuration(mPicturesAnimDurationOut); + anim1.setInterpolator(new AccelerateDecelerateInterpolator()); + anim1.addListener(new AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + // Ignore + } - // Load the album picture data - albumPictures.updateView(album, false); - albumPictures.addCallBackListener(new AlbumPictures.CallbacksListener() { @Override - public void onBackButtonClick(View v) { - // Ignored + public void onAnimationRepeat(Animator animation) { + // Ignore } @Override - public void onSelectionChanged(Album ref) { - // Remove, add, and persist the selection - removeAlbumItems(ref); - mSelectedAlbums.addAll(ref.getSelectedItems()); - ref.setSelected(false); - albumInfo.updateView(ref); - - Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); - mSelectionChanged = true; + public void onAnimationEnd(Animator animation) { + // Remove the source view and show the destination view + srcParent.removeView(srcView); + dstView.setAlpha(1.0f); + dstParent.setEnabled(true); + unbindDrawables(mPicturesPanel); + srcParent.removeView(mPicturesPanel); + + mShowingAlbums = true; + } + + @Override + public void onAnimationCancel(Animator animation) { + // Ignore } }); + anim1.start(); + + // Hide the parent view + AlphaAnimation anim2 = new AlphaAnimation(0.0f, 1.0f); + anim2.setDuration(mPicturesAnimDurationOut); + anim2.setFillAfter(true); + anim2.setZAdjustment(Animation.ZORDER_BOTTOM); + anim2.setInterpolator(new AccelerateDecelerateInterpolator()); + dstParent.startAnimation(anim2); + } + + /** + * Method that updates an album info + * + * @param v The header view + * @param album The album data + */ + void updateAlbumInfo(View v, Album album) { + Resources res = getActivity().getResources(); + + AlbumInfoView info = (AlbumInfoView)v.findViewById(R.id.album_info); + info.setAlbum(album); - // Register the animation controller - AlbumsFlip3dAnimationController controller = new AlbumsFlip3dAnimationController(albumInfo, albumPictures); - controller.register(); - mAnimationControllers.add(controller); + ImageView icon = (ImageView)info.findViewById(R.id.album_thumbnail); + TextView name = (TextView)info.findViewById(R.id.album_name); + TextView items = (TextView)info.findViewById(R.id.album_items); + TextView selectedItems = (TextView)info.findViewById(R.id.album_selected_items); - return albumView; + icon.setImageDrawable(album.getIcon()); + name.setText(album.getName()); + + int size = album.getItems().size(); + items.setText(String.format(res.getQuantityText( + R.plurals.album_number_of_pictures, size).toString(), Integer.valueOf(size))); + + int selected = album.getSelectedItems().size(); + String count = String.valueOf(selected); + if (selected > 99) { + count = "99+"; + } + selectedItems.setText(count); + selectedItems.setVisibility(!album.isSelected() ? View.VISIBLE : View.INVISIBLE); + info.setSelected(album.isSelected()); } /** - * Method that checks if an item is selected + * Method that load all the pictures of the album * - * @param item The item - * @return boolean if an item is selected + * @param album */ - /*package*/ boolean isSelectedItem(String item) { - Iterator it = mSelectedAlbums.iterator(); - while (it.hasNext()) { - String albumPath = it.next(); - if (item.compareTo(albumPath) == 0) { - return true; - } + void loadPictures(Album album) { + List items = album.getItems(); + + // Calculate the grid dimensions + Resources res = getActivity().getResources(); + int pictureWidth = (int)res.getDimension(R.dimen.picture_size); + int gridWidth = mPicturesPanel.getWidth(); + int columns = gridWidth / pictureWidth; + int space = (gridWidth / pictureWidth) / (columns - 1); + if (columns < items.size()) { + space = (int)res.getDimension(R.dimen.small_padding); } - return false; + mPicturesPanel.setHorizontalSpacing(space); + mPicturesPanel.setVerticalSpacing(space); + mPicturesPanel.setStretchMode(GridView.STRETCH_SPACING); + mPicturesPanel.setColumnWidth(pictureWidth); + mPicturesPanel.setNumColumns(columns); + + mPictureAdapter = new AlbumPictureAdapter(getActivity(), items, mPicturesPanel); + mPicturesPanel.setAdapter(mPictureAdapter); } /** - * Method that removes the reference to all the items and itself + * Method invoked when an album header was pressed * - * @param ref The album + * @param parent The parent view + * @param view The header view + * @param position The position */ - /*package*/ void removeAlbumItems(Album ref) { - Iterator it = mSelectedAlbums.iterator(); - while (it.hasNext()) { - String item = it.next(); - String parent = new File(item).getParent(); - if (parent.compareTo(ref.getPath()) == 0) { - it.remove(); - } else if (item.compareTo(ref.getPath()) == 0) { - it.remove(); - } + void onHeaderPressed(AdapterView parent, View view, int position) { + mAlbum = mAlbumAdapter.getItem(position); + + // Header view + View header = mInflater.inflate(R.layout.album_info, null); + header.setTranslationY(view.getY() + parent.getPaddingTop()); + header.setLayoutParams(new ViewGroup.LayoutParams(view.getWidth(), view.getHeight())); + + // Pictures view + mPicturesPanel = (GridView) mInflater.inflate(R.layout.pictures_view, null); + if (!mPicturesPanel.isHardwareAccelerated()) { + mPicturesPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null); } + mPicturesPanel.setY(view.getHeight()); + mPicturesPanel.setLayoutParams(new ViewGroup.LayoutParams(view.getWidth(), + parent.getHeight() - view.getHeight() - + parent.getPaddingTop() - parent.getPaddingBottom())); + mPicturesPanel.setVisibility(View.INVISIBLE); + mPicturesPanel.setOnItemClickListener(mOnItemClickListener); + + // Add to the container + mContainer.addView(mPicturesPanel); + mContainer.addView(header); + + // Update and display the album pictures view + AlbumInfoView info = (AlbumInfoView)header.findViewById(R.id.album_info); + info.addCallBackListener(ChoosePicturesFragment.this); + info.setAlbumMode(false); + updateAlbumInfo(header, mAlbum); + showAlbumPictures(parent, view, mContainer, header); } /** - * {@inheritDoc} + * Method invoked when a picture view was pressed + * + * @param view The picture view */ - @Override - public void onEndScroll() { - doEndScroll(AMOUNT_OF_ADDED_STEPS, false); + void onPicturePressed(View view) { + PictureItemView pictureView = (PictureItemView)view.findViewById(R.id.picture); + if (pictureView != null) { + Picture picture = pictureView.getPicture(); + removeAlbumItems(mAlbum); + List selectedItems = mAlbum.getSelectedItems(); + if (selectedItems.contains(picture.getPath())) { + selectedItems.remove(picture.getPath()); + picture.setSelected(false); + } else { + selectedItems.add(picture.getPath()); + picture.setSelected(true); + mAlbum.setSelected(false); + } + + // Notify all the views + pictureView.updateView(picture, false); + updateAlbumInfo(mDstView, mAlbum); + mAlbumAdapter.notifyDataSetChanged(); + + // Update settings + mSelectedAlbums.addAll(mAlbum.getSelectedItems()); + Preferences.Media.setSelectedMedia(getActivity(), mSelectedAlbums); + mSelectionChanged = true; + } } /** - * Method that performs a scroll creating new items + * Method that removes all the inexistent albums and pictures * - * @param amount The amount of items to create - * @param animate If the add should be animated + * @param data The data to filter + * @return Set The data filtered */ - /*package*/ synchronized void doEndScroll(int amount, boolean animate) { - for (int i = 0; i < amount; i++) { - //Add to the panel of cards - if (mAlbumViews == null || mAlbumViews.isEmpty()) { - break; + private Set removeObsoleteAlbumsData(Set data) { + Set validDataList = new HashSet(); + Iterator it = data.iterator(); + while (it.hasNext()) { + File f = new File(it.next()); + if (f.exists()) { + try { + validDataList.add(f.getCanonicalPath()); + } catch (IOException ioex) { + // Ignore + } } - mAlbumsPanel.addCard(mAlbumViews.remove(0), animate); } + if (data.size() != validDataList.size()) { + // Obsolete entries were removed + data.clear(); + data.addAll(validDataList); + Preferences.Media.setSelectedMedia(getActivity(), mOriginalSelectedAlbums); + } + return data; } } diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/DispositionFragment.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/DispositionFragment.java index b0204c3..7071e76 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/preferences/DispositionFragment.java +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/DispositionFragment.java @@ -53,7 +53,7 @@ public abstract class DispositionFragment } }; - /*package*/ DispositionView mDispositionView; + DispositionView mDispositionView; private MenuItem mDeleteMenu; diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/PhotoPhasePreferences.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/PhotoPhasePreferences.java index 4e8c46e..b5c0d83 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/preferences/PhotoPhasePreferences.java +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/PhotoPhasePreferences.java @@ -47,7 +47,8 @@ public class PhotoPhasePreferences extends PreferenceActivity { private void initTitleActionBar() { //Configure the action bar options getActionBar().setDisplayOptions( - ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE); + ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME | + ActionBar.DISPLAY_SHOW_TITLE); getActionBar().setDisplayHomeAsUpEnabled(true); } diff --git a/src/org/cyanogenmod/wallpapers/photophase/preferences/PreferencesProvider.java b/src/org/cyanogenmod/wallpapers/photophase/preferences/PreferencesProvider.java index 1bf8359..0edc8b7 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/preferences/PreferencesProvider.java +++ b/src/org/cyanogenmod/wallpapers/photophase/preferences/PreferencesProvider.java @@ -96,7 +96,7 @@ public final class PreferencesProvider { /** * @hide */ - /*package*/ static int[] TRANSITIONS_INTERVALS; + static int[] TRANSITIONS_INTERVALS; /** * Method that loads the all the preferences of the application @@ -350,7 +350,12 @@ public final class PreferencesProvider { * @return Set The list of albums and pictures to be displayed */ public static Set getLastDiscorevedAlbums() { - return getStringSet("media_last_disvored_albums", new HashSet()); + // FIXME Typo. Remove when version 1005 is obsolete and unused + Set oldKey = getStringSet("media_last_disvored_albums", new HashSet()); + if (oldKey.size() > 0) { + return oldKey; + } + return getStringSet("media_last_discovered_albums", new HashSet()); } /** @@ -364,7 +369,9 @@ public final class PreferencesProvider { SharedPreferences preferences = context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE); Editor editor = preferences.edit(); - editor.putStringSet("media_last_disvored_albums", albums); + // FIXME Typo. Remove when version 1005 is obsolete and unused + editor.remove("media_last_disvored_albums"); + editor.putStringSet("media_last_discovered_albums", albums); editor.commit(); reload(context); } diff --git a/src/org/cyanogenmod/wallpapers/photophase/tasks/AsyncPictureLoaderTask.java b/src/org/cyanogenmod/wallpapers/photophase/tasks/AsyncPictureLoaderTask.java index bc07b17..3ea1885 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/tasks/AsyncPictureLoaderTask.java +++ b/src/org/cyanogenmod/wallpapers/photophase/tasks/AsyncPictureLoaderTask.java @@ -32,8 +32,34 @@ import java.io.File; */ public class AsyncPictureLoaderTask extends AsyncTask { + /** + * Notify whether the picture was loaded + */ + public static abstract class OnPictureLoaded { + Object[] mRefs; + + /** + * Constructor of OnPictureLoaded + * + * @param refs References to notify + */ + public OnPictureLoaded(Object...refs) { + super(); + mRefs = refs; + } + + /** + * Invoked when a picture is loaded + * + * @param o The original object reference + * @param drawable The drawable + */ + public abstract void onPictureLoaded(Object o, Drawable drawable); + } + private final Context mContext; private final ImageView mView; + private final OnPictureLoaded mCallback; /** * Constructor of AsyncPictureLoaderTask @@ -42,9 +68,21 @@ public class AsyncPictureLoaderTask extends AsyncTask { * @param v The associated view */ public AsyncPictureLoaderTask(Context context, ImageView v) { + this(context, v, null); + } + + /** + * Constructor of AsyncPictureLoaderTask + * + * @param context The current context + * @param v The associated view + * @param callback A callback to notify when the picture was loaded + */ + public AsyncPictureLoaderTask(Context context, ImageView v, OnPictureLoaded callback) { super(); mContext = context; mView = v; + mCallback = callback; } /** @@ -56,7 +94,15 @@ public class AsyncPictureLoaderTask extends AsyncTask { int height = mView.getMeasuredHeight(); Bitmap bitmap = BitmapUtils.decodeBitmap(params[0], width, height); if (bitmap != null) { - return new BitmapDrawable(mContext.getResources(), bitmap); + Drawable dw = new BitmapDrawable(mContext.getResources(), bitmap); + if (mCallback != null) { + for (Object o : mCallback.mRefs) { + if (!isCancelled()) { + mCallback.onPictureLoaded(o, dw); + } + } + } + return dw; } return null; } @@ -66,6 +112,8 @@ public class AsyncPictureLoaderTask extends AsyncTask { */ @Override protected void onPostExecute(Drawable result) { - mView.setImageDrawable(result); + if (!isCancelled()) { + mView.setImageDrawable(result); + } } } diff --git a/src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumInfo.java b/src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumInfo.java deleted file mode 100644 index 8e335c3..0000000 --- a/src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumInfo.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 2013 The CyanogenMod Project - * - * 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 org.cyanogenmod.wallpapers.photophase.widgets; - -import android.content.Context; -import android.content.res.Resources; -import android.os.AsyncTask.Status; -import android.util.AttributeSet; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.ImageView; -import android.widget.PopupMenu; -import android.widget.PopupMenu.OnMenuItemClickListener; -import android.widget.RelativeLayout; -import android.widget.TextView; - -import org.cyanogenmod.wallpapers.photophase.R; -import org.cyanogenmod.wallpapers.photophase.model.Album; -import org.cyanogenmod.wallpapers.photophase.tasks.AsyncPictureLoaderTask; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -/** - * A view that contains the info about an album - */ -public class AlbumInfo extends RelativeLayout - implements OnClickListener, OnMenuItemClickListener { - - /** - * A convenient listener for receive events of the AlbumPictures class - * - */ - public interface CallbacksListener { - /** - * Invoked when an album was selected - * - * @param album The album - */ - void onAlbumSelected(Album album); - - /** - * Invoked when an album was deselected - * - * @param album The album - */ - void onAlbumDeselected(Album album); - } - - private List mCallbacks; - - /*package*/ Album mAlbum; - - /*package*/ AsyncPictureLoaderTask mTask; - - /*package*/ ImageView mIcon; - private TextView mSelectedItems; - private TextView mName; - private TextView mItems; - private View mOverflowButton; - - /** - * Constructor of AlbumInfo. - * - * @param context The current context - */ - public AlbumInfo(Context context) { - super(context); - init(); - } - - /** - * Constructor of AlbumInfo. - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the view. - */ - public AlbumInfo(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - /** - * Constructor of AlbumInfo. - * - * @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 AlbumInfo(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(); - } - - /** - * Method that initializes the internal references - */ - private void init() { - mCallbacks = new ArrayList(); - } - - /** - * 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 onAttachedToWindow() { - super.onAttachedToWindow(); - - mIcon = (ImageView)findViewById(R.id.album_thumbnail); - mSelectedItems = (TextView)findViewById(R.id.album_selected_items); - mName = (TextView)findViewById(R.id.album_name); - mItems = (TextView)findViewById(R.id.album_items); - mOverflowButton = findViewById(R.id.overflow); - mOverflowButton.setOnClickListener(this); - - updateView(mAlbum); - } - - /** - * {@inheritDoc} - */ - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - - // Cancel pending tasks - if (mTask != null && mTask.getStatus().compareTo(Status.PENDING) == 0) { - mTask.cancel(true); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onClick(View v) { - if (v.equals(mOverflowButton)) { - PopupMenu popup = new PopupMenu(getContext(), v); - MenuInflater inflater = popup.getMenuInflater(); - inflater.inflate(R.menu.album_actions, popup.getMenu()); - onPreparePopupMenu(popup.getMenu()); - popup.setOnMenuItemClickListener(this); - popup.show(); - return; - } - } - - /** - * Method called prior to show the popup menu - * - * @param popup The popup menu - */ - public void onPreparePopupMenu(Menu popup) { - if (isSelected()) { - popup.findItem(R.id.mnu_select_album).setVisible(false); - } else { - popup.findItem(R.id.mnu_deselect_album).setVisible(false); - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean onMenuItemClick(MenuItem item) { - switch (item.getItemId()) { - case R.id.mnu_select_album: - doSelection(true); - break; - - case R.id.mnu_deselect_album: - doSelection(false); - break; - - default: - return false; - } - return true; - } - - /** - * Method that select/deselect the album - * - * @param selected whether the album is selected - */ - public void doSelection(boolean selected) { - setSelected(selected); - mAlbum.setSelected(selected); - mAlbum.setSelectedItems(new ArrayList()); - updateView(mAlbum); - notifySelectionChanged(); - } - - /** - * Method that notifies to all the registered callbacks that the selection - * was changed - */ - private void notifySelectionChanged() { - for (CallbacksListener callback : mCallbacks) { - if (mAlbum.isSelected()) { - callback.onAlbumSelected(mAlbum); - } else { - callback.onAlbumDeselected(mAlbum); - } - } - } - - /** - * Method that updates the view - * - * @param album The album data - */ - @SuppressWarnings("boxing") - public void updateView(Album album) { - mAlbum = album; - - if (mAlbum != null && mIcon != null) { - Resources res = getContext().getResources(); - - int selectedItems = mAlbum.getSelectedItems().size(); - String count = String.valueOf(selectedItems); - if (selectedItems > 99) { - count += "+"; - } - mSelectedItems.setText(count); - mSelectedItems.setVisibility(mAlbum.isSelected() ? View.INVISIBLE : View.VISIBLE); - mName.setText(mAlbum.getName()); - int items = mAlbum.getItems().size(); - mItems.setText(String.format(res.getQuantityText( - R.plurals.album_number_of_pictures, items).toString(), items)); - setSelected(album.isSelected()); - - if (mTask == null) { - post(new Runnable() { - @Override - public void run() { - // Show as icon, the first picture - mTask = new AsyncPictureLoaderTask(getContext(), mIcon); - mTask.execute(new File(mAlbum.getItems().get(0))); - } - }); - } - } - } -} diff --git a/src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumInfoView.java b/src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumInfoView.java new file mode 100644 index 0000000..3cfe4d3 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumInfoView.java @@ -0,0 +1,367 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project + * + * 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 org.cyanogenmod.wallpapers.photophase.widgets; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.os.AsyncTask.Status; +import android.util.AttributeSet; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.ImageView; +import android.widget.PopupMenu; +import android.widget.PopupMenu.OnMenuItemClickListener; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import org.cyanogenmod.wallpapers.photophase.R; +import org.cyanogenmod.wallpapers.photophase.model.Album; +import org.cyanogenmod.wallpapers.photophase.tasks.AsyncPictureLoaderTask; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * A view that contains the view of the info of an album + */ +public class AlbumInfoView extends RelativeLayout + implements OnClickListener, OnMenuItemClickListener { + + /** + * A convenient listener for receive events of the AlbumPictures class + * + */ + public interface CallbacksListener { + /** + * Invoked when an album was selected + * + * @param album The album + */ + void onAlbumSelected(Album album); + + /** + * Invoked when an album was deselected + * + * @param album The album + */ + void onAlbumDeselected(Album album); + + /** + * Invoked when an all of the picture of the album were selected + * + * @param album The album + */ + void onAllPicturesSelected(Album album); + + /** + * Invoked when an all of the picture of the album were deselected + * + * @param album The album + */ + void onAllPicturesDeselected(Album album); + } + + private class OnPictureLoaded extends AsyncPictureLoaderTask.OnPictureLoaded { + public OnPictureLoaded(Album album) { + super(new Object[]{album}); + } + + @Override + public void onPictureLoaded(Object o, Drawable drawable) { + ((Album)o).setIcon(drawable); + } + } + + private List mCallbacks; + + private Album mAlbum; + + private AsyncPictureLoaderTask mTask; + + private ImageView mIcon; + private TextView mSelectedItems; + private TextView mName; + private TextView mItems; + private View mOverflowButton; + + private boolean mAlbumMode; + + /** + * Constructor of AlbumInfoView. + * + * @param context The current context + */ + public AlbumInfoView(Context context) { + super(context); + init(); + } + + /** + * Constructor of AlbumInfoView. + * + * @param context The current context + * @param attrs The attributes of the XML tag that is inflating the view. + */ + public AlbumInfoView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + /** + * Constructor of AlbumInfoView. + * + * @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 AlbumInfoView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + /** + * Method that initializes the internal references + */ + private void init() { + mCallbacks = new ArrayList(); + mAlbumMode = true; + } + + /** + * Method that set the album mode + * + * @param albumMode The album mode + */ + public void setAlbumMode(boolean albumMode) { + this.mAlbumMode = albumMode; + } + + /** + * 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 onAttachedToWindow() { + super.onAttachedToWindow(); + + updateView(mAlbum); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + // Cancel pending tasks + if (mTask != null && mTask.getStatus().compareTo(Status.PENDING) == 0) { + mTask.cancel(true); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onClick(View v) { + if (v.equals(mOverflowButton)) { + PopupMenu popup = new PopupMenu(getContext(), v); + MenuInflater inflater = popup.getMenuInflater(); + inflater.inflate(R.menu.album_actions, popup.getMenu()); + onPreparePopupMenu(popup.getMenu()); + popup.setOnMenuItemClickListener(this); + popup.show(); + } + } + + /** + * Method called prior to show the popup menu + * + * @param popup The popup menu + */ + public void onPreparePopupMenu(Menu popup) { + if (isSelected()) { + popup.findItem(R.id.mnu_select_album).setVisible(false); + } else { + popup.findItem(R.id.mnu_deselect_album).setVisible(false); + } + if (mAlbumMode) { + popup.findItem(R.id.mnu_select_all).setVisible(false); + popup.findItem(R.id.mnu_deselect_all).setVisible(false); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.mnu_select_album: + doSelection(true); + break; + + case R.id.mnu_deselect_album: + doSelection(false); + break; + + case R.id.mnu_select_all: + notifyPictureSelectionChanged(true); + break; + + case R.id.mnu_deselect_all: + notifyPictureSelectionChanged(false); + break; + + default: + return false; + } + return true; + } + + /** + * Method that select/deselect the album + * + * @param selected whether the album is selected + */ + public void doSelection(boolean selected) { + setSelected(selected); + mAlbum.setSelected(selected); + mAlbum.setSelectedItems(new ArrayList()); + notifySelectionChanged(); + updateView(mAlbum); + } + + /** + * Method that notifies to all the registered callbacks that the selection + * was changed + */ + private void notifySelectionChanged() { + for (CallbacksListener callback : mCallbacks) { + if (mAlbum.isSelected()) { + callback.onAlbumSelected(mAlbum); + } else { + callback.onAlbumDeselected(mAlbum); + } + } + } + + /** + * Method that notifies to all the registered callbacks that the selection + * was changed + */ + private void notifyPictureSelectionChanged(boolean selected) { + for (CallbacksListener callback : mCallbacks) { + if (selected) { + callback.onAllPicturesSelected(mAlbum); + } else { + callback.onAllPicturesDeselected(mAlbum); + } + } + } + + /** + * Method that sets the album + * + * @param album The album + */ + public void setAlbum(Album album) { + mAlbum = album; + } + + /** + * Method that updates the view + * + * @param album The album data + */ + @SuppressWarnings("boxing") + public void updateView(Album album) { + // Destroy the update drawable task + if (mTask != null && (mTask.getStatus() == AsyncTask.Status.RUNNING || + mTask.getStatus() == AsyncTask.Status.PENDING)) { + mTask.cancel(true); + } + + // Retrieve the views references + if (mIcon == null) { + mIcon = (ImageView)findViewById(R.id.album_thumbnail); + mSelectedItems = (TextView)findViewById(R.id.album_selected_items); + mName = (TextView)findViewById(R.id.album_name); + mItems = (TextView)findViewById(R.id.album_items); + mOverflowButton = findViewById(R.id.overflow_button); + mOverflowButton.setOnClickListener(this); + } + + // Update the views + if (album != null) { + Resources res = getContext().getResources(); + + setAlbum(album); + + int selectedItems = album.getSelectedItems().size(); + String count = String.valueOf(selectedItems); + if (selectedItems > 99) { + count = "99+"; + } + mSelectedItems.setText(count); + mSelectedItems.setVisibility(album.isSelected() ? View.INVISIBLE : View.VISIBLE); + mName.setText(album.getName()); + int items = album.getItems().size(); + mItems.setText(String.format(res.getQuantityText( + R.plurals.album_number_of_pictures, items).toString(), items)); + setSelected(album.isSelected()); + + Drawable dw = album.getIcon(); + mIcon.setImageDrawable(dw); + if (dw == null) { + mIcon.setImageDrawable(null); + + // Show as icon, the first picture + mTask = new AsyncPictureLoaderTask(getContext(), mIcon, new OnPictureLoaded(album)); + mTask.execute(new File(album.getItems().get(0).getPath())); + } + } + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumPictures.java b/src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumPictures.java deleted file mode 100644 index 5d0c60f..0000000 --- a/src/org/cyanogenmod/wallpapers/photophase/widgets/AlbumPictures.java +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright (C) 2013 The CyanogenMod Project - * - * 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 org.cyanogenmod.wallpapers.photophase.widgets; - -import android.content.Context; -import android.os.Handler; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.LinearLayout; -import android.widget.PopupMenu; -import android.widget.PopupMenu.OnMenuItemClickListener; -import android.widget.RelativeLayout; -import android.widget.TextView; - -import org.cyanogenmod.wallpapers.photophase.R; -import org.cyanogenmod.wallpapers.photophase.model.Album; - -import java.util.ArrayList; -import java.util.List; - -/** - * A view that contains the pictures of an album - */ -public class AlbumPictures extends RelativeLayout - implements OnClickListener, OnMenuItemClickListener { - - private static final int SELECTION_SELECT_ALL = 1; - private static final int SELECTION_DESELECT_ALL = 2; - private static final int SELECTION_INVERT = 3; - - /** - * A convenient listener for receive events of the AlbumPictures class - * - */ - public interface CallbacksListener { - /** - * Invoked when the user pressed the back button - */ - void onBackButtonClick(View v); - - /** - * Invoked when the selection was changed - * - * @param album The album - */ - void onSelectionChanged(Album album); - } - - private List mCallbacks; - - private Handler mHandler; - - /*package*/ PicturesView mScroller; - /*package*/ LinearLayout mHolder; - private View mBackButton; - private View mOverflowButton; - - private boolean mInitialized; - - /*package*/ Album mAlbum; - - /** - * Constructor of AlbumPictures. - * - * @param context The current context - */ - public AlbumPictures(Context context) { - super(context); - init(); - } - - /** - * Constructor of AlbumPictures. - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the view. - */ - public AlbumPictures(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - /** - * Constructor of AlbumPictures. - * - * @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 AlbumPictures(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(); - } - - /** - * Method that initializes the internal references - */ - private void init() { - mCallbacks = new ArrayList(); - mHandler = new Handler(); - mInitialized = false; - } - - /** - * {@inheritDoc} - */ - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - mScroller = (PicturesView)findViewById(R.id.album_pictures_scroller); - mHolder = (LinearLayout)findViewById(R.id.album_pictures_holder); - mBackButton = findViewById(R.id.back); - mBackButton.setOnClickListener(this); - mOverflowButton = findViewById(R.id.overflow); - mOverflowButton.setOnClickListener(this); - TextView title = (TextView)findViewById(R.id.album_pictures_title); - title.setText(mAlbum.getName()); - - updateView(mAlbum, 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); - } - - /** - * Method that set the data of the view - * - * @param album The album data - * @param recreate If the view should be recreated - */ - public void updateView(Album album, boolean recreate) { - mAlbum = album; - recreateView(false); - } - - /** - * Method that recreates the the view - * - * @param propagateShow If should propagate the show event - */ - private void recreateView(final boolean propagateShow) { - if (mHolder != null) { - mHandler.post(new Runnable() { - @Override - public void run() { - int pictures = mHolder.getChildCount(); - if (pictures != mAlbum.getItems().size()) { - // Recreate the pictures - final LayoutInflater inflater = (LayoutInflater) getContext(). - getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mScroller.cancelTasks(); - mHolder.removeAllViews(); - for (final String picture : mAlbum.getItems()) { - View v = createPicture(inflater, picture, isPictureSelected(picture)); - mHolder.addView(v); - } - } else { - int i = 0; - for (final String picture : mAlbum.getItems()) { - View v = mHolder.getChildAt(i); - v.setSelected(isPictureSelected(picture)); - i++; - } - } - if (propagateShow) { - mScroller.onShow(); - } - } - }); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onClick(View v) { - // Check which is the view pressed - if (v.equals(mBackButton)) { - for (CallbacksListener callback : mCallbacks) { - callback.onBackButtonClick(v); - } - return; - } - if (v.equals(mOverflowButton)) { - PopupMenu popup = new PopupMenu(getContext(), v); - MenuInflater inflater = popup.getMenuInflater(); - inflater.inflate(R.menu.pictures_actions, popup.getMenu()); - popup.setOnMenuItemClickListener(this); - popup.show(); - return; - } - - // A picture view - v.setSelected(!v.isSelected()); - notifySelectionChanged(); - } - - /** - * Method that notifies to all the registered callbacks that the selection - * was changed - */ - private void notifySelectionChanged() { - List selection = new ArrayList(); - int count = mHolder.getChildCount(); - for (int i = 0; i < count; i++) { - View v = mHolder.getChildAt(i); - if (v.isSelected()) { - selection.add((String)v.getTag()); - } - } - mAlbum.setSelectedItems(selection); - mAlbum.setSelected(false); - - for (CallbacksListener callback : mCallbacks) { - callback.onSelectionChanged(mAlbum); - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean onMenuItemClick(MenuItem item) { - switch (item.getItemId()) { - case R.id.mnu_select_all: - doSelection(SELECTION_SELECT_ALL); - break; - - case R.id.mnu_deselect_all: - doSelection(SELECTION_DESELECT_ALL); - break; - - case R.id.mnu_invert_selection: - doSelection(SELECTION_INVERT); - break; - - default: - return false; - } - return true; - } - - /** - * Operate over the selection of the pictures of this album. - * - * @param action Takes the next values: - *
    - *
  • SELECTION_SELECT_ALL: select all
  • - *
  • SELECTION_DESELECT_ALL: deselect all
  • - *
  • SELECTION_INVERT: invert selection
  • - *
- */ - private void doSelection(int action) { - int count = mHolder.getChildCount(); - for (int i = 0; i < count; i++) { - View v = mHolder.getChildAt(i); - - boolean selected = true; - if (action == SELECTION_DESELECT_ALL) { - selected = false; - } else if (action == SELECTION_INVERT) { - selected = !v.isSelected(); - } - v.setSelected(selected); - } - notifySelectionChanged(); - } - - /** - * Method invoked when the view is displayed - */ - public void onShow() { - if (!mInitialized) { - mInitialized = true; - recreateView(true); - } - } - - /** - * Method that creates a new picture view - * - * @param inflater The inflater of the parent view - * @param picture The path of the picture - * @param selected If the picture is selected - */ - /*package*/ View createPicture(LayoutInflater inflater, String picture, boolean selected) { - final View v = inflater.inflate(R.layout.picture_item, mHolder, false); - v.setTag(picture); - v.setSelected(selected); - v.setOnClickListener(this); - return v; - } - - /** - * Method that check if a picture is selected - * - * @param picture The picture to check - * @return boolean whether the picture is selected - */ - /*package*/ boolean isPictureSelected(String picture) { - for (String item : mAlbum.getSelectedItems()) { - if (item.compareTo(picture) == 0) { - return true; - } - } - return false; - } -} diff --git a/src/org/cyanogenmod/wallpapers/photophase/widgets/CardLayout.java b/src/org/cyanogenmod/wallpapers/photophase/widgets/CardLayout.java deleted file mode 100644 index e1953cd..0000000 --- a/src/org/cyanogenmod/wallpapers/photophase/widgets/CardLayout.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2013 The CyanogenMod Project - * - * 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 org.cyanogenmod.wallpapers.photophase.widgets; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.view.animation.AnimationUtils; -import android.widget.LinearLayout; - -import org.cyanogenmod.wallpapers.photophase.R; - -/** - * A "Google Now Card Layout" like layout - */ -public class CardLayout extends LinearLayout { - - boolean inverted = false; - - /** - * Constructor of CardLayout. - * - * @param context The current context - */ - public CardLayout(Context context) { - super(context); - } - - /** - * Constructor of CardLayout. - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the view. - */ - public CardLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } - - /** - * Constructor of CardLayout. - * - * @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 CardLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - /** - * Add a new card to the layout - * - * @param card The card view to add - * @param animate If the add should be animated - */ - public void addCard(final View card, final boolean animate) { - post(new Runnable() { - @Override - public void run() { - addView(card); - if (animate) { - if (inverted) { - card.startAnimation(AnimationUtils.loadAnimation( - getContext(), R.anim.cards_animation_up_right)); - } else { - card.startAnimation(AnimationUtils.loadAnimation( - getContext(), R.anim.cards_animation_up_left)); - } - inverted = !inverted; - } - } - }); - } -} diff --git a/src/org/cyanogenmod/wallpapers/photophase/widgets/DispositionView.java b/src/org/cyanogenmod/wallpapers/photophase/widgets/DispositionView.java index 22d00a6..0bc462f 100644 --- a/src/org/cyanogenmod/wallpapers/photophase/widgets/DispositionView.java +++ b/src/org/cyanogenmod/wallpapers/photophase/widgets/DispositionView.java @@ -74,7 +74,7 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen private int mCols; private int mRows; - /*package*/ View mTarget; + View mTarget; private ResizeFrame mResizeFrame; private int mInternalPadding; private Rect mOldResizeFrameLocation; @@ -323,7 +323,7 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen * * @param target The disposition target */ - /*package*/ void finishDeleteAnimation(Disposition target) { + void finishDeleteAnimation(Disposition target) { removeView(mTarget); mDispositions.remove(target); Collections.sort(mDispositions); @@ -514,7 +514,7 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen * * @return The target view */ - /*package*/ View findTargetFromResizeFrame() { + View findTargetFromResizeFrame() { int count = getChildCount(); for (int i = 0; i < count; i++) { View v = getChildAt(i); @@ -552,7 +552,7 @@ public class DispositionView extends RelativeLayout implements OnLongClickListen * * @param v The target view */ - /*package*/ boolean selectTarget(View v) { + boolean selectTarget(View v) { //Do not do long click if we do not have a target if (mTarget != null && v.equals(mTarget)) return false; diff --git a/src/org/cyanogenmod/wallpapers/photophase/widgets/PictureItemView.java b/src/org/cyanogenmod/wallpapers/photophase/widgets/PictureItemView.java new file mode 100644 index 0000000..aac7e24 --- /dev/null +++ b/src/org/cyanogenmod/wallpapers/photophase/widgets/PictureItemView.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2013 The CyanogenMod Project + * + * 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 org.cyanogenmod.wallpapers.photophase.widgets; + +import android.content.Context; +import android.os.AsyncTask; +import android.os.AsyncTask.Status; +import android.util.AttributeSet; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import org.cyanogenmod.wallpapers.photophase.R; +import org.cyanogenmod.wallpapers.photophase.model.Picture; +import org.cyanogenmod.wallpapers.photophase.tasks.AsyncPictureLoaderTask; + +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 an Picture was selected + * + * @param Picture The Picture + */ + void onPictureSelected(Picture Picture); + + /** + * Invoked when an Picture was deselected + * + * @param Picture The Picture + */ + void onPictureDeselected(Picture Picture); + } + + private List mCallbacks; + + private Picture mPicture; + + private AsyncPictureLoaderTask mTask; + + private ImageView mIcon; + + /** + * Constructor of PictureItemView. + * + * @param context The current context + */ + public PictureItemView(Context context) { + super(context); + init(); + } + + /** + * Constructor of PictureItemView. + * + * @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 PictureItemView. + * + * @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(); + } + + /** + * 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 onAttachedToWindow() { + super.onAttachedToWindow(); + + updateView(mPicture, true); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + // Cancel pending tasks + if (mTask != null && mTask.getStatus().compareTo(Status.PENDING) == 0) { + 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(Picture picture, boolean refreshIcon) { + // Destroy the update drawable task + if (mTask != null && (mTask.getStatus() == AsyncTask.Status.RUNNING || + mTask.getStatus() == AsyncTask.Status.PENDING)) { + mTask.cancel(true); + } + + // Retrieve the views references + if (mIcon == null) { + mIcon = (ImageView)findViewById(R.id.picture_thumbnail); + } + + // Update the views + if (picture != null) { + setPicture(picture); + + setSelected(picture.isSelected()); + + // Do no try to cache the images (this generates a lot of memory an we want + // to have a low memory footprint) + if (refreshIcon) { + mIcon.setImageDrawable(null); + + // Show as icon, the first picture + mTask = new AsyncPictureLoaderTask(getContext(), mIcon); + mTask.execute(new File(picture.getPath())); + } + } + } + +} diff --git a/src/org/cyanogenmod/wallpapers/photophase/widgets/PicturesView.java b/src/org/cyanogenmod/wallpapers/photophase/widgets/PicturesView.java deleted file mode 100644 index 0744751..0000000 --- a/src/org/cyanogenmod/wallpapers/photophase/widgets/PicturesView.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2013 The CyanogenMod Project - * - * 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 org.cyanogenmod.wallpapers.photophase.widgets; - -import android.content.Context; -import android.graphics.Rect; -import android.os.AsyncTask.Status; -import android.os.Handler; -import android.util.AttributeSet; -import android.view.ViewGroup; -import android.widget.HorizontalScrollView; -import android.widget.ImageView; - -import org.cyanogenmod.wallpapers.photophase.R; -import org.cyanogenmod.wallpapers.photophase.tasks.AsyncPictureLoaderTask; - -import java.io.File; -import java.util.HashMap; -import java.util.Iterator; - -/** - * A view that contains all the pictures of an album - */ -public class PicturesView extends HorizontalScrollView { - - private HashMap mTasks; - private Handler mHandler; - - /** - * Constructor of PicturesView. - * - * @param context The current context - */ - public PicturesView(Context context) { - super(context); - init(); - } - - /** - * Constructor of PicturesView. - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the view. - */ - public PicturesView(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - /** - * Constructor of PicturesView. - * - * @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 PicturesView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(); - } - - /** - * Method that initializes the structures of this class - */ - private void init() { - mTasks = new HashMap(); - mHandler = new Handler(); - } - - /** - * {@inheritDoc} - */ - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - cancelTasks(); - } - - /** - * Method that removes all tasks - */ - public void cancelTasks() { - // Cancel all the pending task - Iterator it = mTasks.values().iterator(); - while (it.hasNext()) { - AsyncPictureLoaderTask task = it.next(); - if (task.getStatus().compareTo(Status.PENDING) == 0) { - task.cancel(true); - } - } - mTasks.clear(); - } - - /** - * {@inheritDoc} - */ - @Override - protected void onScrollChanged(int l, int t, int oldl, int oldt) { - super.onScrollChanged(l, t, oldl, oldt); - // Estimated velocity (in some moment we must obtain some scrolling with an estimated - // velocity below of 3) - int velocity = Math.abs(l - oldl); - if (velocity <= 3) { - mHandler.post(new Runnable() { - @Override - public void run() { - requestLoadOfPendingPictures(); - } - }); - } - } - - /** - * Method invoked when the view is displayed - */ - public void onShow() { - mHandler.post(new Runnable() { - @Override - public void run() { - requestLoadOfPendingPictures(); - } - }); - } - - /** - * Method that load in background all visible and pending pictures - */ - /*package*/ void requestLoadOfPendingPictures() { - // Get the visible rect - Rect r = new Rect(); - getHitRect(r); - - // Get all the image views - ViewGroup vg = (ViewGroup)getChildAt(0); - int count = vg.getChildCount(); - for (int i = 0; i < count; i++) { - ViewGroup picView = (ViewGroup)vg.getChildAt(i); - File image = new File((String)picView.getTag()); - if (picView.getLocalVisibleRect(r) && !mTasks.containsKey(image)) { - ImageView iv = (ImageView)picView.findViewById(R.id.picture_thumbnail); - AsyncPictureLoaderTask task = new AsyncPictureLoaderTask(getContext(), iv); - task.execute(image); - mTasks.put(image, task); - } - } - } -} diff --git a/src/org/cyanogenmod/wallpapers/photophase/widgets/VerticalEndlessScroller.java b/src/org/cyanogenmod/wallpapers/photophase/widgets/VerticalEndlessScroller.java deleted file mode 100644 index 67a1f17..0000000 --- a/src/org/cyanogenmod/wallpapers/photophase/widgets/VerticalEndlessScroller.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2013 The CyanogenMod Project - * - * 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 org.cyanogenmod.wallpapers.photophase.widgets; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.widget.ScrollView; - -/** - * A scroll view that notifies the end of the scroll to create new views - * dynamically. - */ -public class VerticalEndlessScroller extends ScrollView { - - /** - * Interface to communicate end-scroll events - */ - public interface OnEndScrollListener { - /** - * Called when the scroll reachs the end of the scroll - */ - void onEndScroll(); - } - - private OnEndScrollListener mCallback; - private int mEndPadding = 0; - private boolean mSwitch = false; - - /** - * Constructor of VerticalEndlessScroller. - * - * @param context The current context - */ - public VerticalEndlessScroller(Context context) { - super(context); - } - - /** - * Constructor of VerticalEndlessScroller. - * - * @param context The current context - * @param attrs The attributes of the XML tag that is inflating the view. - */ - public VerticalEndlessScroller(Context context, AttributeSet attrs) { - super(context, attrs); - } - - /** - * Constructor of VerticalEndlessScroller. - * - * @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 VerticalEndlessScroller(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - /** - * Method that set the callback for notify end-scroll events - * - * @param callback The callback - */ - public void setCallback(OnEndScrollListener callback) { - mCallback = callback; - } - - /** - * Method that set the end padding for fired the event - * - * @param endPadding The end padding - */ - public void setEndPadding(int endPadding) { - this.mEndPadding = endPadding; - } - - /** - * {@inheritDoc} - */ - @Override - protected void onScrollChanged(int l, int t, int oldl, int oldt) { - // We take the last child in the scrollview - View view = getChildAt(getChildCount() - 1); - int diff = (view.getBottom() - (getHeight() + getScrollY())); - if ((!mSwitch && diff <= mEndPadding)) { - if (mCallback != null) { - mCallback.onEndScroll(); - mSwitch = true; - return; - } - } else if (diff > mEndPadding) { - mSwitch = false; - } - } - -} -- cgit v1.2.3