1c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren/* 2c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * Copyright (C) 2012 The Android Open Source Project 3c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * 4c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * Licensed under the Apache License, Version 2.0 (the "License"); 5c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * you may not use this file except in compliance with the License. 6c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * You may obtain a copy of the License at 7c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * 8c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * http://www.apache.org/licenses/LICENSE-2.0 9c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * 10c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * Unless required by applicable law or agreed to in writing, software 11c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * distributed under the License is distributed on an "AS IS" BASIS, 12c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * See the License for the specific language governing permissions and 14c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * limitations under the License. 15c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren */ 16c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wrenpackage com.android.dreams.phototable; 17c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren 18d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport android.content.Context; 19d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport android.content.res.Resources; 20d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport android.graphics.Bitmap; 21d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport android.graphics.BitmapFactory; 22d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport android.graphics.PointF; 23eb70217554d14807d73cae033b4f756c9b80fe3bChris Wrenimport android.graphics.PorterDuff; 241dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wrenimport android.graphics.Rect; 25d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport android.graphics.drawable.BitmapDrawable; 26d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport android.graphics.drawable.Drawable; 27d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport android.graphics.drawable.LayerDrawable; 28d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport android.os.AsyncTask; 2988d80f4471c900628e2cb6eef23029b99af48e09Chris Wrenimport android.service.dreams.DreamService; 30d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport android.util.AttributeSet; 31d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport android.util.Log; 32eb70217554d14807d73cae033b4f756c9b80fe3bChris Wrenimport android.view.KeyEvent; 33d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport android.view.LayoutInflater; 34d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport android.view.MotionEvent; 35d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport android.view.View; 36f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wrenimport android.view.ViewGroup; 37eb70217554d14807d73cae033b4f756c9b80fe3bChris Wrenimport android.view.ViewPropertyAnimator; 38d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport android.view.animation.DecelerateInterpolator; 3976086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wrenimport android.view.animation.Interpolator; 40d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport android.widget.FrameLayout; 41d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport android.widget.ImageView; 4288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren 43f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wrenimport java.util.ArrayList; 4488d80f4471c900628e2cb6eef23029b99af48e09Chris Wrenimport java.util.Formatter; 45f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wrenimport java.util.HashSet; 46d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport java.util.LinkedList; 473910ecc7c3a93485d32f8c1eac79e47dbf3753fbChris Wrenimport java.util.List; 48d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport java.util.Random; 49f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wrenimport java.util.Set; 50c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren 51c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren/** 52d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren * A surface where photos sit. 53c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren */ 54d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenpublic class PhotoTable extends FrameLayout { 55c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren private static final String TAG = "PhotoTable"; 56d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private static final boolean DEBUG = false; 57d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 58d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren class Launcher implements Runnable { 59eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren @Override 60eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren public void run() { 61eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren PhotoTable.this.scheduleNext(mDropPeriod); 62eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren PhotoTable.this.launch(); 63d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 64eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 65d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 66eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren class FocusReaper implements Runnable { 67d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren @Override 68d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren public void run() { 69eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren PhotoTable.this.clearFocus(); 70d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 71d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 72d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 7384c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren class SelectionReaper implements Runnable { 7484c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren @Override 7584c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren public void run() { 7684c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren PhotoTable.this.clearSelection(); 7784c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren } 7884c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren } 7984c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren 8088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private static final int NEXT = 1; 8188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private static final int PREV = 0; 82d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private static Random sRNG = new Random(); 83d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 84d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private final Launcher mLauncher; 85eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren private final FocusReaper mFocusReaper; 8684c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren private final SelectionReaper mSelectionReaper; 87d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private final LinkedList<View> mOnTable; 88d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private final int mDropPeriod; 89d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private final int mFastDropPeriod; 90d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private final int mNowDropDelay; 91d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private final float mImageRatio; 92d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private final float mTableRatio; 93d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private final float mImageRotationLimit; 9476086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wren private final float mThrowRotation; 9576086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wren private final float mThrowSpeed; 96d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private final boolean mTapToExit; 97d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private final int mTableCapacity; 98f98687b22f4091083287ab7de7a0f3102bd9c1baChris Wren private final int mRedealCount; 99d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private final int mInset; 100f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren private final PhotoSource mPhotoSource; 101d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private final Resources mResources; 10276086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wren private final Interpolator mThrowInterpolator; 10376086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wren private final Interpolator mDropInterpolator; 10488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private final DragGestureDetector mDragGestureDetector; 10588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private final EdgeSwipeDetector mEdgeSwipeDetector; 10688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private final KeyboardInterpreter mKeyboardInterpreter; 10788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private final boolean mStoryModeEnabled; 108a924553fc2a5bf5ab88ac031adf154480b970bd9Chris Wren private final long mPickUpDuration; 10984c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren private final int mMaxSelectionTime; 11084c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren private final int mMaxFocusTime; 111f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren private final List<View> mAnimating; 11284c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren 1131c1faec579b96aeac7416a80a7ee7155a813fffeDianne Hackborn private DreamService mDream; 114d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private PhotoLaunchTask mPhotoLaunchTask; 11588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private LoadNaturalSiblingTask mLoadOnDeckTasks[]; 116d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private boolean mStarted; 117d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private boolean mIsLandscape; 118d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private int mLongSide; 119d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private int mShortSide; 120d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private int mWidth; 121d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private int mHeight; 12288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private View mSelection; 12388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private View mOnDeck[]; 12488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private View mFocus; 125eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren private int mHighlightColor; 126f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren private ViewGroup mBackground; 127f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren private ViewGroup mStageLeft; 128d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 129651ee92128e1899957758abeeb1c2c669f240093Chris Wren public PhotoTable(Context context, AttributeSet as) { 130651ee92128e1899957758abeeb1c2c669f240093Chris Wren super(context, as); 131d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mResources = getResources(); 132d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mInset = mResources.getDimensionPixelSize(R.dimen.photo_inset); 13370ef3e7da59212c3c4baedf92f418eb9012a4db3Chris Wren mDropPeriod = mResources.getInteger(R.integer.table_drop_period); 134d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mFastDropPeriod = mResources.getInteger(R.integer.fast_drop); 135d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mNowDropDelay = mResources.getInteger(R.integer.now_drop); 136d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mImageRatio = mResources.getInteger(R.integer.image_ratio) / 1000000f; 137d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mTableRatio = mResources.getInteger(R.integer.table_ratio) / 1000000f; 138d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mImageRotationLimit = (float) mResources.getInteger(R.integer.max_image_rotation); 13976086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wren mThrowSpeed = mResources.getDimension(R.dimen.image_throw_speed); 140a924553fc2a5bf5ab88ac031adf154480b970bd9Chris Wren mPickUpDuration = mResources.getInteger(R.integer.photo_pickup_duration); 14176086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wren mThrowRotation = (float) mResources.getInteger(R.integer.image_throw_rotatioan); 142d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mTableCapacity = mResources.getInteger(R.integer.table_capacity); 143f98687b22f4091083287ab7de7a0f3102bd9c1baChris Wren mRedealCount = mResources.getInteger(R.integer.redeal_count); 144d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mTapToExit = mResources.getBoolean(R.bool.enable_tap_to_exit); 14588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mStoryModeEnabled = mResources.getBoolean(R.bool.enable_story_mode); 146eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren mHighlightColor = mResources.getColor(R.color.highlight_color); 14784c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren mMaxSelectionTime = mResources.getInteger(R.integer.max_selection_time); 14884c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren mMaxFocusTime = mResources.getInteger(R.integer.max_focus_time); 14976086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wren mThrowInterpolator = new SoftLandingInterpolator( 15076086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wren mResources.getInteger(R.integer.soft_landing_time) / 1000000f, 15176086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wren mResources.getInteger(R.integer.soft_landing_distance) / 1000000f); 15276086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wren mDropInterpolator = new DecelerateInterpolator( 15376086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wren (float) mResources.getInteger(R.integer.drop_deceleration_exponent)); 154d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mOnTable = new LinkedList<View>(); 155d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mPhotoSource = new PhotoSourcePlexor(getContext(), 156d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren getContext().getSharedPreferences(PhotoTableDreamSettings.PREFS_NAME, 0)); 157f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren mAnimating = new ArrayList<View>(); 158eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren mLauncher = new Launcher(); 159eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren mFocusReaper = new FocusReaper(); 16084c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren mSelectionReaper = new SelectionReaper(); 1611dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren mDragGestureDetector = new DragGestureDetector(context, this); 16288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mEdgeSwipeDetector = new EdgeSwipeDetector(context, this); 16388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mKeyboardInterpreter = new KeyboardInterpreter(this); 16488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mLoadOnDeckTasks = new LoadNaturalSiblingTask[2]; 16588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mOnDeck = new View[2]; 166d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mStarted = false; 167d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 168d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 169f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren @Override 170f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren public void onFinishInflate() { 171f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren mBackground = (ViewGroup) findViewById(R.id.background); 172f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren mStageLeft = (ViewGroup) findViewById(R.id.stageleft); 173f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren } 174387363c426e097c358bd41367e0a8f4339fc8dc3Chris Wren 1751c1faec579b96aeac7416a80a7ee7155a813fffeDianne Hackborn public void setDream(DreamService dream) { 176651ee92128e1899957758abeeb1c2c669f240093Chris Wren mDream = dream; 177651ee92128e1899957758abeeb1c2c669f240093Chris Wren } 178651ee92128e1899957758abeeb1c2c669f240093Chris Wren 179d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren public boolean hasSelection() { 18088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren return mSelection != null; 181d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 182d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 18388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren public View getSelection() { 18488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren return mSelection; 185d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 186d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 187d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren public void clearSelection() { 188eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren if (hasSelection()) { 189bcfd4439d730a4d783a02596c8ab444796323aadChris Wren dropOnTable(mSelection); 190bcfd4439d730a4d783a02596c8ab444796323aadChris Wren mPhotoSource.donePaging(getBitmap(mSelection)); 1913910ecc7c3a93485d32f8c1eac79e47dbf3753fbChris Wren if (mStoryModeEnabled) { 192f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren fadeInBackground(mSelection); 1933910ecc7c3a93485d32f8c1eac79e47dbf3753fbChris Wren } 194bcfd4439d730a4d783a02596c8ab444796323aadChris Wren mSelection = null; 19588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 19688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren for (int slot = 0; slot < mOnDeck.length; slot++) { 19788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (mOnDeck[slot] != null) { 19888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren fadeAway(mOnDeck[slot], false); 19988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mOnDeck[slot] = null; 20088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 201a96a454c9084e98e20f263b435b536a194f25673Chris Wren if (mLoadOnDeckTasks[slot] != null && 202a96a454c9084e98e20f263b435b536a194f25673Chris Wren mLoadOnDeckTasks[slot].getStatus() != AsyncTask.Status.FINISHED) { 203a96a454c9084e98e20f263b435b536a194f25673Chris Wren mLoadOnDeckTasks[slot].cancel(true); 204a96a454c9084e98e20f263b435b536a194f25673Chris Wren mLoadOnDeckTasks[slot] = null; 205a96a454c9084e98e20f263b435b536a194f25673Chris Wren } 206eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 207d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 208d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 209d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren public void setSelection(View selected) { 21088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (selected != null) { 21188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren clearSelection(); 21288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mSelection = selected; 21388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren promoteSelection(); 2143910ecc7c3a93485d32f8c1eac79e47dbf3753fbChris Wren if (mStoryModeEnabled) { 215f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren fadeOutBackground(mSelection); 2163910ecc7c3a93485d32f8c1eac79e47dbf3753fbChris Wren } 21788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 21888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 21988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren 22088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren public void selectNext() { 22188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (mStoryModeEnabled) { 22288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren log("selectNext"); 22388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (hasSelection() && mOnDeck[NEXT] != null) { 22488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren placeOnDeck(mSelection, PREV); 22588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mSelection = mOnDeck[NEXT]; 22688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mOnDeck[NEXT] = null; 22788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren promoteSelection(); 22888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 22988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } else { 23088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren clearSelection(); 23188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 23288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 23388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren 23488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren public void selectPrevious() { 23588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (mStoryModeEnabled) { 23688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren log("selectPrevious"); 23788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (hasSelection() && mOnDeck[PREV] != null) { 23888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren placeOnDeck(mSelection, NEXT); 23988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mSelection = mOnDeck[PREV]; 24088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mOnDeck[PREV] = null; 24188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren promoteSelection(); 24288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 24388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } else { 24488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren clearSelection(); 24588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 24688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 24788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren 24888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private void promoteSelection() { 24988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (hasSelection()) { 25084c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren scheduleSelectionReaper(mMaxSelectionTime); 25188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mSelection.animate().cancel(); 25288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mSelection.setAlpha(1f); 25388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren moveToTopOfPile(mSelection); 25488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren pickUp(mSelection); 25588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (mStoryModeEnabled) { 25688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren for (int slot = 0; slot < mOnDeck.length; slot++) { 25788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (mLoadOnDeckTasks[slot] != null && 25888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mLoadOnDeckTasks[slot].getStatus() != AsyncTask.Status.FINISHED) { 25988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mLoadOnDeckTasks[slot].cancel(true); 26088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 26188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (mOnDeck[slot] == null) { 26288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mLoadOnDeckTasks[slot] = new LoadNaturalSiblingTask(slot); 26388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mLoadOnDeckTasks[slot].execute(mSelection); 26488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 26588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 26688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 26788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 268d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 269d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 270eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren public boolean hasFocus() { 27188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren return mFocus != null; 272eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 273eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren 27488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren public View getFocus() { 27588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren return mFocus; 276eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 277eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren 278eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren public void clearFocus() { 279eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren if (hasFocus()) { 28088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren setHighlight(getFocus(), false); 281eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 28288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mFocus = null; 28388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 28488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren 28588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren public void setDefaultFocus() { 28688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren setFocus(mOnTable.getLast()); 287eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 288eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren 289eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren public void setFocus(View focus) { 290eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren assert(focus != null); 291eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren clearFocus(); 29288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mFocus = focus; 293eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren moveToTopOfPile(focus); 294eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren setHighlight(focus, true); 29584c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren scheduleFocusReaper(mMaxFocusTime); 296eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 297eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren 298d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren static float lerp(float a, float b, float f) { 299d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren return (b-a)*f + a; 300d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 301d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 302d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren static float randfrange(float a, float b) { 303d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren return lerp(a, b, sRNG.nextFloat()); 304d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 305d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 306d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren static PointF randFromCurve(float t, PointF[] v) { 307d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren PointF p = new PointF(); 308d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren if (v.length == 4 && t >= 0f && t <= 1f) { 309d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren float a = (float) Math.pow(1f-t, 3f); 310d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren float b = (float) Math.pow(1f-t, 2f) * t; 311d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren float c = (1f-t) * (float) Math.pow(t, 2f); 312d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren float d = (float) Math.pow(t, 3f); 313d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 314d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren p.x = a * v[0].x + 3 * b * v[1].x + 3 * c * v[2].x + d * v[3].x; 315d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren p.y = a * v[0].y + 3 * b * v[1].y + 3 * c * v[2].y + d * v[3].y; 316d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 317d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren return p; 318d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 319d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 320f40bfb6272ceec0f3ec471495a8e1b739c5b9d4fChris Wren private static PointF randMultiDrop(int n, float i, float j, int width, int height) { 32188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren log("randMultiDrop (%d, %f, %f, %d, %d)", n, i, j, width, height); 322f40bfb6272ceec0f3ec471495a8e1b739c5b9d4fChris Wren final float[] cx = {0.3f, 0.3f, 0.5f, 0.7f, 0.7f}; 323f40bfb6272ceec0f3ec471495a8e1b739c5b9d4fChris Wren final float[] cy = {0.3f, 0.7f, 0.5f, 0.3f, 0.7f}; 324f40bfb6272ceec0f3ec471495a8e1b739c5b9d4fChris Wren n = Math.abs(n); 325f40bfb6272ceec0f3ec471495a8e1b739c5b9d4fChris Wren float x = cx[n % cx.length]; 326f40bfb6272ceec0f3ec471495a8e1b739c5b9d4fChris Wren float y = cy[n % cx.length]; 327f40bfb6272ceec0f3ec471495a8e1b739c5b9d4fChris Wren PointF p = new PointF(); 328f40bfb6272ceec0f3ec471495a8e1b739c5b9d4fChris Wren p.x = x * width + 0.05f * width * i; 329f40bfb6272ceec0f3ec471495a8e1b739c5b9d4fChris Wren p.y = y * height + 0.05f * height * j; 33088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren log("randInCenter returning %f, %f", p.x, p.y); 331f40bfb6272ceec0f3ec471495a8e1b739c5b9d4fChris Wren return p; 332f40bfb6272ceec0f3ec471495a8e1b739c5b9d4fChris Wren } 333f40bfb6272ceec0f3ec471495a8e1b739c5b9d4fChris Wren 334eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren private double cross(double[] a, double[] b) { 335eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren return a[0] * b[1] - a[1] * b[0]; 336eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 337eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren 338eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren private double norm(double[] a) { 339eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren return Math.hypot(a[0], a[1]); 340eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 341eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren 342eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren private double[] getCenter(View photo) { 343eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren float width = (float) ((Integer) photo.getTag(R.id.photo_width)).intValue(); 344eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren float height = (float) ((Integer) photo.getTag(R.id.photo_height)).intValue(); 345eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren double[] center = { photo.getX() + width / 2f, 346eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren - (photo.getY() + height / 2f) }; 347eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren return center; 348eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 349eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren 350eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren public View moveFocus(View focus, float direction) { 351eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren return moveFocus(focus, direction, 90f); 352eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 353eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren 354eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren public View moveFocus(View focus, float direction, float angle) { 355eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren if (focus == null) { 356eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren setFocus(mOnTable.getLast()); 357eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } else { 358eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren final double alpha = Math.toRadians(direction); 359eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren final double beta = Math.toRadians(Math.min(angle, 180f) / 2f); 360eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren final double[] left = { Math.sin(alpha - beta), 361eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren Math.cos(alpha - beta) }; 362eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren final double[] right = { Math.sin(alpha + beta), 363eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren Math.cos(alpha + beta) }; 364eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren final double[] a = getCenter(focus); 365eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren View bestFocus = null; 366eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren double bestDistance = Double.MAX_VALUE; 367eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren for (View candidate: mOnTable) { 368eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren if (candidate != focus) { 369eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren final double[] b = getCenter(candidate); 370eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren final double[] delta = { b[0] - a[0], 371eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren b[1] - a[1] }; 372eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren if (cross(delta, left) > 0.0 && cross(delta, right) < 0.0) { 373eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren final double distance = norm(delta); 374eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren if (bestDistance > distance) { 375eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren bestDistance = distance; 376eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren bestFocus = candidate; 377eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 378eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 379eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 380eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 381eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren if (bestFocus == null) { 382eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren if (angle < 180f) { 383eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren return moveFocus(focus, direction, 180f); 384eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 385eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } else { 386eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren setFocus(bestFocus); 387eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 388eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 38988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren return getFocus(); 390eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 391eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren 392eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren @Override 393eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren public boolean onKeyDown(int keyCode, KeyEvent event) { 39488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren return mKeyboardInterpreter.onKeyDown(keyCode, event); 395eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 396eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren 397d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren @Override 398bf44ca22d40dd60b4c30f6a979b80fbd2ffa0078Chris Wren public boolean onGenericMotionEvent(MotionEvent event) { 3991dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren return mEdgeSwipeDetector.onTouchEvent(event) || mDragGestureDetector.onTouchEvent(event); 400bf44ca22d40dd60b4c30f6a979b80fbd2ffa0078Chris Wren } 401bf44ca22d40dd60b4c30f6a979b80fbd2ffa0078Chris Wren 402bf44ca22d40dd60b4c30f6a979b80fbd2ffa0078Chris Wren @Override 403d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren public boolean onTouchEvent(MotionEvent event) { 404d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 405d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren if (hasSelection()) { 406d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren clearSelection(); 407d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } else { 408651ee92128e1899957758abeeb1c2c669f240093Chris Wren if (mTapToExit && mDream != null) { 409d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mDream.finish(); 410d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 411d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 412d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren return true; 413d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 414d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren return false; 415d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 416c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren 417c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren @Override 418d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren public void onLayout(boolean changed, int left, int top, int right, int bottom) { 419d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren super.onLayout(changed, left, top, right, bottom); 42088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren log("onLayout (%d, %d, %d, %d)", left, top, right, bottom); 421d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 422d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mHeight = bottom - top; 423d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mWidth = right - left; 424d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 425d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mLongSide = (int) (mImageRatio * Math.max(mWidth, mHeight)); 426d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mShortSide = (int) (mImageRatio * Math.min(mWidth, mHeight)); 427d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 428d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren boolean isLandscape = mWidth > mHeight; 429d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren if (mIsLandscape != isLandscape) { 430d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren for (View photo: mOnTable) { 43188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (photo != getSelection()) { 432d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren dropOnTable(photo); 433d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 434d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 43588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (hasSelection()) { 43688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren pickUp(getSelection()); 43788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren for (int slot = 0; slot < mOnDeck.length; slot++) { 43888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (mOnDeck[slot] != null) { 43988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren placeOnDeck(mOnDeck[slot], slot); 44088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 44188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 44288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 443d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mIsLandscape = isLandscape; 444d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 445d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren start(); 446c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren } 447c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren 448c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren @Override 449d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren public boolean isOpaque() { 450d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren return true; 451d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 452d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 45388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren /** Put a nice border on the bitmap. */ 45488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private static View applyFrame(final PhotoTable table, final BitmapFactory.Options options, 455f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren Bitmap decodedPhoto) { 45688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren LayoutInflater inflater = (LayoutInflater) table.getContext() 45788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 45888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren View photo = inflater.inflate(R.layout.photo, null); 45988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren ImageView image = (ImageView) photo; 46088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren Drawable[] layers = new Drawable[2]; 46188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren int photoWidth = options.outWidth; 46288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren int photoHeight = options.outHeight; 46388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (decodedPhoto == null || options.outWidth <= 0 || options.outHeight <= 0) { 46488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren photo = null; 46588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } else { 46688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren decodedPhoto.setHasMipMap(true); 46788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren layers[0] = new BitmapDrawable(table.mResources, decodedPhoto); 46888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren layers[1] = table.mResources.getDrawable(R.drawable.frame); 46988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren LayerDrawable layerList = new LayerDrawable(layers); 47088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren layerList.setLayerInset(0, table.mInset, table.mInset, 47188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren table.mInset, table.mInset); 47288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren image.setImageDrawable(layerList); 47388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren 47488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren photo.setTag(R.id.photo_width, Integer.valueOf(photoWidth)); 47588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren photo.setTag(R.id.photo_height, Integer.valueOf(photoHeight)); 47688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren 47788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren photo.setOnTouchListener(new PhotoTouchListener(table.getContext(), 47888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren table)); 47988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 48088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren return photo; 48188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 48288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren 48388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private class LoadNaturalSiblingTask extends AsyncTask<View, Void, View> { 484b6c75a5e1a9dfc7753a451e97607510d37264374Chris Wren private final BitmapFactory.Options mOptions; 48588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private final int mSlot; 486a96a454c9084e98e20f263b435b536a194f25673Chris Wren private View mParent; 487b6c75a5e1a9dfc7753a451e97607510d37264374Chris Wren 48888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren public LoadNaturalSiblingTask (int slot) { 489b6c75a5e1a9dfc7753a451e97607510d37264374Chris Wren mOptions = new BitmapFactory.Options(); 490b6c75a5e1a9dfc7753a451e97607510d37264374Chris Wren mOptions.inTempStorage = new byte[32768]; 49188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mSlot = slot; 492b6c75a5e1a9dfc7753a451e97607510d37264374Chris Wren } 493b6c75a5e1a9dfc7753a451e97607510d37264374Chris Wren 494d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren @Override 49588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren public View doInBackground(View... views) { 49688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren log("load natural %s", (mSlot == NEXT ? "next" : "previous")); 497d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren final PhotoTable table = PhotoTable.this; 498a96a454c9084e98e20f263b435b536a194f25673Chris Wren mParent = views[0]; 499a96a454c9084e98e20f263b435b536a194f25673Chris Wren final Bitmap current = getBitmap(mParent); 50088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren Bitmap decodedPhoto; 50188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (mSlot == NEXT) { 50288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren decodedPhoto = table.mPhotoSource.naturalNext(current, 50388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mOptions, table.mLongSide, table.mShortSide); 50488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } else { 50588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren decodedPhoto = table.mPhotoSource.naturalPrevious(current, 50688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mOptions, table.mLongSide, table.mShortSide); 50788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 50888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren return applyFrame(PhotoTable.this, mOptions, decodedPhoto); 50988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 510d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 51188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren @Override 51288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren public void onPostExecute(View photo) { 51388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (photo != null) { 514a96a454c9084e98e20f263b435b536a194f25673Chris Wren if (hasSelection() && getSelection() == mParent) { 515a96a454c9084e98e20f263b435b536a194f25673Chris Wren log("natural %s being rendered", (mSlot == NEXT ? "next" : "previous")); 516a96a454c9084e98e20f263b435b536a194f25673Chris Wren PhotoTable.this.addView(photo, new LayoutParams(LayoutParams.WRAP_CONTENT, 517a96a454c9084e98e20f263b435b536a194f25673Chris Wren LayoutParams.WRAP_CONTENT)); 518a96a454c9084e98e20f263b435b536a194f25673Chris Wren PhotoTable.this.mOnDeck[mSlot] = photo; 519a96a454c9084e98e20f263b435b536a194f25673Chris Wren float width = (float) ((Integer) photo.getTag(R.id.photo_width)).intValue(); 520a96a454c9084e98e20f263b435b536a194f25673Chris Wren float height = (float) ((Integer) photo.getTag(R.id.photo_height)).intValue(); 521a96a454c9084e98e20f263b435b536a194f25673Chris Wren photo.setX(mSlot == PREV ? -2 * width : mWidth + 2 * width); 522a96a454c9084e98e20f263b435b536a194f25673Chris Wren photo.setY((mHeight - height) / 2); 523a96a454c9084e98e20f263b435b536a194f25673Chris Wren photo.addOnLayoutChangeListener(new OnLayoutChangeListener() { 524a96a454c9084e98e20f263b435b536a194f25673Chris Wren @Override 525a96a454c9084e98e20f263b435b536a194f25673Chris Wren public void onLayoutChange(View v, int left, int top, int right, int bottom, 526a96a454c9084e98e20f263b435b536a194f25673Chris Wren int oldLeft, int oldTop, int oldRight, int oldBottom) { 527a96a454c9084e98e20f263b435b536a194f25673Chris Wren PhotoTable.this.placeOnDeck(v, mSlot); 528a96a454c9084e98e20f263b435b536a194f25673Chris Wren v.removeOnLayoutChangeListener(this); 529a96a454c9084e98e20f263b435b536a194f25673Chris Wren } 530a96a454c9084e98e20f263b435b536a194f25673Chris Wren }); 531a96a454c9084e98e20f263b435b536a194f25673Chris Wren } else { 532a96a454c9084e98e20f263b435b536a194f25673Chris Wren recycle(photo); 533a96a454c9084e98e20f263b435b536a194f25673Chris Wren } 534d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } else { 53588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren log("natural, %s was null!", (mSlot == NEXT ? "next" : "previous")); 536d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 53788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 53888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren }; 53988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren 54088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private class PhotoLaunchTask extends AsyncTask<Void, Void, View> { 54188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private final BitmapFactory.Options mOptions; 542d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 54388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren public PhotoLaunchTask () { 54488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mOptions = new BitmapFactory.Options(); 54588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mOptions.inTempStorage = new byte[32768]; 54688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 54788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren 54888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren @Override 54988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren public View doInBackground(Void... unused) { 55088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren log("load a new photo"); 55188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren final PhotoTable table = PhotoTable.this; 55288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren return applyFrame(PhotoTable.this, mOptions, 55388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren table.mPhotoSource.next(mOptions, 55488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren table.mLongSide, table.mShortSide)); 555d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 556d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 557d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren @Override 558d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren public void onPostExecute(View photo) { 559d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren if (photo != null) { 560d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren final PhotoTable table = PhotoTable.this; 561d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 562d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren table.addView(photo, new LayoutParams(LayoutParams.WRAP_CONTENT, 56388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren LayoutParams.WRAP_CONTENT)); 564d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren if (table.hasSelection()) { 56588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren for (int slot = 0; slot < mOnDeck.length; slot++) { 56688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (mOnDeck[slot] != null) { 56788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren table.moveToTopOfPile(mOnDeck[slot]); 56888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 56988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 57088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren table.moveToTopOfPile(table.getSelection()); 571d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 572d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 573d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren log("drop it"); 574d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren table.throwOnTable(photo); 575d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 576387363c426e097c358bd41367e0a8f4339fc8dc3Chris Wren if (mOnTable.size() > mTableCapacity) { 577387363c426e097c358bd41367e0a8f4339fc8dc3Chris Wren int targetSize = Math.max(0, mOnTable.size() - mRedealCount); 578387363c426e097c358bd41367e0a8f4339fc8dc3Chris Wren while (mOnTable.size() > targetSize) { 579387363c426e097c358bd41367e0a8f4339fc8dc3Chris Wren fadeAway(mOnTable.poll(), false); 580387363c426e097c358bd41367e0a8f4339fc8dc3Chris Wren } 581387363c426e097c358bd41367e0a8f4339fc8dc3Chris Wren } 582387363c426e097c358bd41367e0a8f4339fc8dc3Chris Wren 583d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren if(table.mOnTable.size() < table.mTableCapacity) { 584d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren table.scheduleNext(table.mFastDropPeriod); 585d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 586d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 587d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 588d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren }; 589d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 59088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren /** Bring a new photo onto the table. */ 591d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren public void launch() { 592d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren log("launching"); 59388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); 59484c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren if (!hasSelection()) { 595d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren log("inflate it"); 596d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren if (mPhotoLaunchTask == null || 597d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mPhotoLaunchTask.getStatus() == AsyncTask.Status.FINISHED) { 598d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mPhotoLaunchTask = new PhotoLaunchTask(); 599d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mPhotoLaunchTask.execute(); 600d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 601d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 602d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 60388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren 6043910ecc7c3a93485d32f8c1eac79e47dbf3753fbChris Wren /** De-emphasize the other photos on the table. */ 605f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren public void fadeOutBackground(final View photo) { 606f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren mBackground.animate() 607f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .withLayer() 608f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .setDuration(mPickUpDuration) 609f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .alpha(0f); 6103910ecc7c3a93485d32f8c1eac79e47dbf3753fbChris Wren } 6113910ecc7c3a93485d32f8c1eac79e47dbf3753fbChris Wren 6123910ecc7c3a93485d32f8c1eac79e47dbf3753fbChris Wren 6133910ecc7c3a93485d32f8c1eac79e47dbf3753fbChris Wren /** Return the other photos to foreground status. */ 614f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren public void fadeInBackground(final View photo) { 615f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren mAnimating.add(photo); 616f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren mBackground.animate() 617f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .withLayer() 618f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .setDuration(mPickUpDuration) 619f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .alpha(1f) 620f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .withEndAction(new Runnable() { 621f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren @Override 622f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren public void run() { 623f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren mAnimating.remove(photo); 624f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren if (!mAnimating.contains(photo)) { 625f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren moveToBackground(photo); 626f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren } 6273910ecc7c3a93485d32f8c1eac79e47dbf3753fbChris Wren } 628f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren }); 6293910ecc7c3a93485d32f8c1eac79e47dbf3753fbChris Wren } 6303910ecc7c3a93485d32f8c1eac79e47dbf3753fbChris Wren 63188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren /** Dispose of the photo gracefully, in case we can see some of it. */ 632d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren public void fadeAway(final View photo, final boolean replace) { 633d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren // fade out of view 634d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mOnTable.remove(photo); 635f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren exitStageLeft(photo); 636f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren photo.setOnTouchListener(null); 637d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren photo.animate().cancel(); 638d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren photo.animate() 639d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren .withLayer() 640d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren .alpha(0f) 6413910ecc7c3a93485d32f8c1eac79e47dbf3753fbChris Wren .setDuration(mPickUpDuration) 642d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren .withEndAction(new Runnable() { 643d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren @Override 644d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren public void run() { 64588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (photo == getFocus()) { 64688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren clearFocus(); 64788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 648f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren mStageLeft.removeView(photo); 649d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren recycle(photo); 650d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren if (replace) { 651d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren scheduleNext(mNowDropDelay); 652d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 653d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 654d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren }); 655d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 656d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 65788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren /** Visually on top, and also freshest, for the purposes of timeouts. */ 658eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren public void moveToTopOfPile(View photo) { 659d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren // make this photo the last to be removed. 660f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren if (isInBackground(photo)) { 661f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren mBackground.bringChildToFront(photo); 662f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren } else { 663f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren bringChildToFront(photo); 664f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren } 665d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren invalidate(); 666d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mOnTable.remove(photo); 667d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mOnTable.offer(photo); 668d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 669d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 67088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren /** On deck is to the left or right of the selected photo. */ 67188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private void placeOnDeck(final View photo, final int slot ) { 67288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (slot < mOnDeck.length) { 67388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (mOnDeck[slot] != null && mOnDeck[slot] != photo) { 67488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren fadeAway(mOnDeck[slot], false); 67588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 67688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren mOnDeck[slot] = photo; 67788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren float photoWidth = photo.getWidth(); 67888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren float photoHeight = photo.getHeight(); 67988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren float scale = Math.min(getHeight() / photoHeight, getWidth() / photoWidth); 68088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren 68188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren float x = (getWidth() - photoWidth) / 2f; 68288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren float y = (getHeight() - photoHeight) / 2f; 68388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren 68488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren float offset = (((float) mWidth + scale * (photoWidth - 2f * mInset)) / 2f); 68588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren x += (slot == NEXT? 1f : -1f) * offset; 68688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren 68788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren photo.animate() 688f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .withLayer() 68988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren .rotation(0f) 69088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren .rotationY(0f) 69188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren .scaleX(scale) 69288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren .scaleY(scale) 69388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren .x(x) 69488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren .y(y) 695a924553fc2a5bf5ab88ac031adf154480b970bd9Chris Wren .setDuration(mPickUpDuration) 69688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren .setInterpolator(new DecelerateInterpolator(2f)); 69788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 69888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 69988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren 70088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren /** Move in response to touch. */ 70188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren public void move(final View photo, float x, float y, float a) { 70288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren photo.animate().cancel(); 70388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren photo.setAlpha(1f); 70488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren photo.setX((int) x); 70588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren photo.setY((int) y); 70688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren photo.setRotation((int) a); 70788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 70888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren 70988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren /** Wind up off screen, so we can animate in. */ 710d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private void throwOnTable(final View photo) { 711d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mOnTable.offer(photo); 712d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren log("start offscreen"); 71376086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wren photo.setRotation(mThrowRotation); 714d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren photo.setX(-mLongSide); 715d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren photo.setY(-mLongSide); 71676086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wren 71776086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wren dropOnTable(photo, mThrowInterpolator); 718d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 719d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 7201dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren public void move(final View photo, float dx, float dy, boolean drop) { 7211dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren if (photo != null) { 7221dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren final float x = photo.getX() + dx; 7231dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren final float y = photo.getY() + dy; 7241dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren photo.setX(x); 7251dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren photo.setY(y); 7261dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren Log.d(TAG, "[" + photo.getX() + ", " + photo.getY() + "] + (" + dx + "," + dy + ")"); 7271dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren if (drop && photoOffTable(photo)) { 7281dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren fadeAway(photo, true); 7291dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren } 7301dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren } 7311dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren } 7321dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren 73388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren /** Fling with no touch hints, then land off screen. */ 734eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren public void fling(final View photo) { 735eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren final float[] o = { mWidth + mLongSide / 2f, 736eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren mHeight + mLongSide / 2f }; 737eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren final float[] a = { photo.getX(), photo.getY() }; 738eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren final float[] b = { o[0], a[1] + o[0] - a[0] }; 739eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren final float[] c = { a[0] + o[1] - a[1], o[1] }; 740eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren float[] delta = { 0f, 0f }; 741eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren if (Math.hypot(b[0] - a[0], b[1] - a[1]) < Math.hypot(c[0] - a[0], c[1] - a[1])) { 742eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren delta[0] = b[0] - a[0]; 743eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren delta[1] = b[1] - a[1]; 744eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } else { 745eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren delta[0] = c[0] - a[0]; 746eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren delta[1] = c[1] - a[1]; 747eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 748eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren 749eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren final float dist = (float) Math.hypot(delta[0], delta[1]); 750eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren final int duration = (int) (1000f * dist / mThrowSpeed); 7511dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren fling(photo, delta[0], delta[1], duration, true); 752eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 753eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren 75488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren /** Continue dynamically after a fling gesture, possibly off the screen. */ 7551dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren public void fling(final View photo, float dx, float dy, int duration, boolean spin) { 75688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (photo == getFocus()) { 757eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren if (moveFocus(photo, 0f) == null) { 758eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren moveFocus(photo, 180f); 759eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 760eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 761f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren moveToForeground(photo); 762eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren ViewPropertyAnimator animator = photo.animate() 763f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .withLayer() 764eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren .xBy(dx) 765eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren .yBy(dy) 766eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren .setDuration(duration) 767eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren .setInterpolator(new DecelerateInterpolator(2f)); 768eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren 769eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren if (spin) { 770eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren animator.rotation(mThrowRotation); 771eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 772eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren 7731dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren if (photoOffTable(photo, (int) dx, (int) dy)) { 774eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren log("fling away"); 775eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren animator.withEndAction(new Runnable() { 776eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren @Override 777eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren public void run() { 778eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren fadeAway(photo, true); 779eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 780eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren }); 781eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 782eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 7831dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren public boolean photoOffTable(final View photo) { 7841dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren return photoOffTable(photo, 0, 0); 7851dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren } 7861dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren 7871dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren public boolean photoOffTable(final View photo, final int dx, final int dy) { 7881dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren Rect hit = new Rect(); 7891dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren photo.getHitRect(hit); 7901dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren hit.offset(dx, dy); 7911dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren return (hit.bottom < 0f || hit.top > getHeight() || 7921dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren hit.right < 0f || hit.left > getWidth()); 7931dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren } 794eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren 79588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren /** Animate to a random place and orientation, down on the table (visually small). */ 796d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren public void dropOnTable(final View photo) { 79776086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wren dropOnTable(photo, mDropInterpolator); 79876086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wren } 79976086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wren 80088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren /** Animate to a random place and orientation, down on the table (visually small). */ 80176086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wren public void dropOnTable(final View photo, final Interpolator interpolator) { 802d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren float angle = randfrange(-mImageRotationLimit, mImageRotationLimit); 803f40bfb6272ceec0f3ec471495a8e1b739c5b9d4fChris Wren PointF p = randMultiDrop(sRNG.nextInt(), 804f40bfb6272ceec0f3ec471495a8e1b739c5b9d4fChris Wren (float) sRNG.nextGaussian(), (float) sRNG.nextGaussian(), 805f40bfb6272ceec0f3ec471495a8e1b739c5b9d4fChris Wren mWidth, mHeight); 806d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren float x = p.x; 807d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren float y = p.y; 808d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 80988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren log("drop it at %f, %f", x, y); 810d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 811d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren float x0 = photo.getX(); 812d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren float y0 = photo.getY(); 813d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 814f40bfb6272ceec0f3ec471495a8e1b739c5b9d4fChris Wren x -= mLongSide / 2f; 815f40bfb6272ceec0f3ec471495a8e1b739c5b9d4fChris Wren y -= mShortSide / 2f; 81688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren log("fixed offset is %f, %f ", x, y); 817d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 818d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren float dx = x - x0; 819d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren float dy = y - y0; 820d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 821eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren float dist = (float) Math.hypot(dx, dy); 82276086400f8e68b810bf1b3db0dc4f7133c8644aeChris Wren int duration = (int) (1000f * dist / mThrowSpeed); 823d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren duration = Math.max(duration, 1000); 824d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 825d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren log("animate it"); 826d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren // toss onto table 827f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren mAnimating.add(photo); 828d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren photo.animate() 829f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .withLayer() 830f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .scaleX(mTableRatio / mImageRatio) 831f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .scaleY(mTableRatio / mImageRatio) 832f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .rotation(angle) 833f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .x(x) 834f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .y(y) 835f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .setDuration(duration) 836f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .setInterpolator(interpolator) 837f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .withEndAction(new Runnable() { 838f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren @Override 839f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren public void run() { 840f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren mAnimating.remove(photo); 841f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren if (!mAnimating.contains(photo)) { 842f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren moveToBackground(photo); 843f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren } 844f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren } 845f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren }); 846f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren } 847f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren 848f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren private void moveToBackground(View photo) { 849f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren if (!isInBackground(photo)) { 850f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren removeView(photo); 851f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren mBackground.addView(photo, new LayoutParams(LayoutParams.WRAP_CONTENT, 852f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren LayoutParams.WRAP_CONTENT)); 853f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren } 854f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren } 855f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren 856f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren private void exitStageLeft(View photo) { 857f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren if (isInBackground(photo)) { 858f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren mBackground.removeView(photo); 859f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren } else { 860f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren removeView(photo); 861f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren } 862f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren mStageLeft.addView(photo, new LayoutParams(LayoutParams.WRAP_CONTENT, 863f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren LayoutParams.WRAP_CONTENT)); 864f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren } 865f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren 866f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren private void moveToForeground(View photo) { 867f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren if (isInBackground(photo)) { 868f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren mBackground.removeView(photo); 869f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren addView(photo, new LayoutParams(LayoutParams.WRAP_CONTENT, 870f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren LayoutParams.WRAP_CONTENT)); 871f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren } 872f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren } 873f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren 874f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren private boolean isInBackground(View photo) { 875f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren return mBackground.indexOfChild(photo) != -1; 876d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 877d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 878d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren /** wrap all orientations to the interval [-180, 180). */ 879d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private float wrapAngle(float angle) { 880d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren float result = angle + 180; 881d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren result = ((result % 360) + 360) % 360; // catch negative numbers 882d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren result -= 180; 883d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren return result; 884d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 885d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 88688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren /** Animate the selected photo to the foregound: zooming in to bring it foreward. */ 887d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren private void pickUp(final View photo) { 888d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren float photoWidth = photo.getWidth(); 889d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren float photoHeight = photo.getHeight(); 890d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 891d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren float scale = Math.min(getHeight() / photoHeight, getWidth() / photoWidth); 892d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 89388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren log("scale is %f", scale); 894d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren log("target it"); 895d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren float x = (getWidth() - photoWidth) / 2f; 896d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren float y = (getHeight() - photoHeight) / 2f; 897d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 898d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren photo.setRotation(wrapAngle(photo.getRotation())); 899d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 900d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren log("animate it"); 90188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren // lift up to the glass for a good look 902f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren moveToForeground(photo); 903d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren photo.animate() 904f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .withLayer() 905f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .rotation(0f) 906f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .rotationY(0f) 907f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .alpha(1f) 908f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .scaleX(scale) 909f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .scaleY(scale) 910f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .x(x) 911f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .y(y) 912f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .setDuration(mPickUpDuration) 913f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .setInterpolator(new DecelerateInterpolator(2f)) 914f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren .withEndAction(new Runnable() { 915f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren @Override 916f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren public void run() { 917f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren log("endtimes: %f", photo.getX()); 918f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren } 919f362b8f1d503df040c55ac04913dabdbb2cd7155Chris Wren }); 920d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 921d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 92288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private Bitmap getBitmap(View photo) { 92388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (photo == null) { 92488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren return null; 92588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 926d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren ImageView image = (ImageView) photo; 927d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren LayerDrawable layers = (LayerDrawable) image.getDrawable(); 92888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (layers == null) { 92988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren return null; 93088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 931d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren BitmapDrawable bitmap = (BitmapDrawable) layers.getDrawable(0); 93288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (bitmap == null) { 93388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren return null; 93488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 93588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren return bitmap.getBitmap(); 93688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 93788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren 93888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private void recycle(View photo) { 93988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren if (photo != null) { 94088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren removeView(photo); 941bcfd4439d730a4d783a02596c8ab444796323aadChris Wren mPhotoSource.recycle(getBitmap(photo)); 94288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren } 943d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 944d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 945eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren public void setHighlight(View photo, boolean highlighted) { 946eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren ImageView image = (ImageView) photo; 947eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren LayerDrawable layers = (LayerDrawable) image.getDrawable(); 948eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren if (highlighted) { 949eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren layers.getDrawable(1).setColorFilter(mHighlightColor, PorterDuff.Mode.SRC_IN); 950eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } else { 951eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren layers.getDrawable(1).clearColorFilter(); 952eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 953eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 954eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren 95588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren /** Schedule the first launch. Idempotent. */ 956d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren public void start() { 957d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren if (!mStarted) { 958d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren log("kick it"); 959d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren mStarted = true; 96084c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren scheduleNext(0); 961d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 962d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 963d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 96484c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren public void refreshSelection() { 96584c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren scheduleSelectionReaper(mMaxFocusTime); 96684c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren } 96784c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren 96884c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren public void scheduleSelectionReaper(int delay) { 96984c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren removeCallbacks(mSelectionReaper); 97084c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren postDelayed(mSelectionReaper, delay); 97184c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren } 97284c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren 9731dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren public void refreshFocus() { 97484c3d5580e7168d1d150c1dd24340b19eb501cd2Chris Wren scheduleFocusReaper(mMaxFocusTime); 9751dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren } 9761dc94b55d59f5c4dbf86788d0c32b7ac4449909aChris Wren 977eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren public void scheduleFocusReaper(int delay) { 978eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren removeCallbacks(mFocusReaper); 979eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren postDelayed(mFocusReaper, delay); 980eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren } 981eb70217554d14807d73cae033b4f756c9b80fe3bChris Wren 982d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren public void scheduleNext(int delay) { 983d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren removeCallbacks(mLauncher); 984d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren postDelayed(mLauncher, delay); 985d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 986d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren 98788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren private static void log(String message, Object... args) { 988d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren if (DEBUG) { 98988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren Formatter formatter = new Formatter(); 99088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren formatter.format(message, args); 99188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren Log.i(TAG, formatter.toString()); 992d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren } 993c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren } 994c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren} 995