1666ea1b28a76aeba74744148b15099254d918671Owen Lin/* 2666ea1b28a76aeba74744148b15099254d918671Owen Lin * Copyright (C) 2009 The Android Open Source Project 3666ea1b28a76aeba74744148b15099254d918671Owen Lin * 4666ea1b28a76aeba74744148b15099254d918671Owen Lin * Licensed under the Apache License, Version 2.0 (the "License"); 5666ea1b28a76aeba74744148b15099254d918671Owen Lin * you may not use this file except in compliance with the License. 6666ea1b28a76aeba74744148b15099254d918671Owen Lin * You may obtain a copy of the License at 7666ea1b28a76aeba74744148b15099254d918671Owen Lin * 8666ea1b28a76aeba74744148b15099254d918671Owen Lin * http://www.apache.org/licenses/LICENSE-2.0 9666ea1b28a76aeba74744148b15099254d918671Owen Lin * 10666ea1b28a76aeba74744148b15099254d918671Owen Lin * Unless required by applicable law or agreed to in writing, software 11666ea1b28a76aeba74744148b15099254d918671Owen Lin * distributed under the License is distributed on an "AS IS" BASIS, 12666ea1b28a76aeba74744148b15099254d918671Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13666ea1b28a76aeba74744148b15099254d918671Owen Lin * See the License for the specific language governing permissions and 14666ea1b28a76aeba74744148b15099254d918671Owen Lin * limitations under the License. 15666ea1b28a76aeba74744148b15099254d918671Owen Lin */ 16666ea1b28a76aeba74744148b15099254d918671Owen Lin 17666ea1b28a76aeba74744148b15099254d918671Owen Linpackage com.android.camera; 18666ea1b28a76aeba74744148b15099254d918671Owen Lin 19d6c2fb7a38fcdb58742fcfffd67a4594487ec71cOwen Linimport com.android.gallery.R; 20d6c2fb7a38fcdb58742fcfffd67a4594487ec71cOwen Lin 21666ea1b28a76aeba74744148b15099254d918671Owen Linimport static com.android.camera.Util.Assert; 22666ea1b28a76aeba74744148b15099254d918671Owen Lin 23666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.app.Activity; 24666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.content.Context; 25666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.Bitmap; 26666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.Canvas; 27666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.Paint; 28666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.Rect; 29666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.graphics.drawable.Drawable; 30666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.media.AudioManager; 31666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.os.Handler; 32666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.util.AttributeSet; 33666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.util.DisplayMetrics; 34666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.view.GestureDetector; 35666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.view.KeyEvent; 36666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.view.MotionEvent; 37666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.view.View; 38666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.view.ViewConfiguration; 39666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.view.GestureDetector.SimpleOnGestureListener; 40666ea1b28a76aeba74744148b15099254d918671Owen Linimport android.widget.Scroller; 41666ea1b28a76aeba74744148b15099254d918671Owen Lin 42666ea1b28a76aeba74744148b15099254d918671Owen Linimport com.android.camera.gallery.IImage; 43666ea1b28a76aeba74744148b15099254d918671Owen Linimport com.android.camera.gallery.IImageList; 44666ea1b28a76aeba74744148b15099254d918671Owen Lin 45666ea1b28a76aeba74744148b15099254d918671Owen Linimport java.util.HashMap; 46666ea1b28a76aeba74744148b15099254d918671Owen Lin 47666ea1b28a76aeba74744148b15099254d918671Owen Linclass GridViewSpecial extends View { 48666ea1b28a76aeba74744148b15099254d918671Owen Lin @SuppressWarnings("unused") 49666ea1b28a76aeba74744148b15099254d918671Owen Lin private static final String TAG = "GridViewSpecial"; 50666ea1b28a76aeba74744148b15099254d918671Owen Lin private static final float MAX_FLING_VELOCITY = 2500; 51666ea1b28a76aeba74744148b15099254d918671Owen Lin 52666ea1b28a76aeba74744148b15099254d918671Owen Lin public static interface Listener { 53666ea1b28a76aeba74744148b15099254d918671Owen Lin public void onImageClicked(int index); 54666ea1b28a76aeba74744148b15099254d918671Owen Lin public void onImageTapped(int index); 55666ea1b28a76aeba74744148b15099254d918671Owen Lin public void onLayoutComplete(boolean changed); 56666ea1b28a76aeba74744148b15099254d918671Owen Lin 57666ea1b28a76aeba74744148b15099254d918671Owen Lin /** 58666ea1b28a76aeba74744148b15099254d918671Owen Lin * Invoked when the <code>GridViewSpecial</code> scrolls. 59666ea1b28a76aeba74744148b15099254d918671Owen Lin * 60666ea1b28a76aeba74744148b15099254d918671Owen Lin * @param scrollPosition the position of the scroller in the range 61666ea1b28a76aeba74744148b15099254d918671Owen Lin * [0, 1], when 0 means on the top and 1 means on the buttom 62666ea1b28a76aeba74744148b15099254d918671Owen Lin */ 63666ea1b28a76aeba74744148b15099254d918671Owen Lin public void onScroll(float scrollPosition); 64666ea1b28a76aeba74744148b15099254d918671Owen Lin } 65666ea1b28a76aeba74744148b15099254d918671Owen Lin 66666ea1b28a76aeba74744148b15099254d918671Owen Lin public static interface DrawAdapter { 67666ea1b28a76aeba74744148b15099254d918671Owen Lin public void drawImage(Canvas canvas, IImage image, 68666ea1b28a76aeba74744148b15099254d918671Owen Lin Bitmap b, int xPos, int yPos, int w, int h); 69666ea1b28a76aeba74744148b15099254d918671Owen Lin public void drawDecoration(Canvas canvas, IImage image, 70666ea1b28a76aeba74744148b15099254d918671Owen Lin int xPos, int yPos, int w, int h); 71666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean needsDecoration(); 72666ea1b28a76aeba74744148b15099254d918671Owen Lin } 73666ea1b28a76aeba74744148b15099254d918671Owen Lin 74666ea1b28a76aeba74744148b15099254d918671Owen Lin public static final int INDEX_NONE = -1; 75666ea1b28a76aeba74744148b15099254d918671Owen Lin 76666ea1b28a76aeba74744148b15099254d918671Owen Lin // There are two cell size we will use. It can be set by setSizeChoice(). 77666ea1b28a76aeba74744148b15099254d918671Owen Lin // The mLeftEdgePadding fields is filled in onLayout(). See the comments 78666ea1b28a76aeba74744148b15099254d918671Owen Lin // in onLayout() for details. 79666ea1b28a76aeba74744148b15099254d918671Owen Lin static class LayoutSpec { 80666ea1b28a76aeba74744148b15099254d918671Owen Lin LayoutSpec(int w, int h, int intercellSpacing, int leftEdgePadding, 81666ea1b28a76aeba74744148b15099254d918671Owen Lin DisplayMetrics metrics) { 82666ea1b28a76aeba74744148b15099254d918671Owen Lin mCellWidth = dpToPx(w, metrics); 83666ea1b28a76aeba74744148b15099254d918671Owen Lin mCellHeight = dpToPx(h, metrics); 84666ea1b28a76aeba74744148b15099254d918671Owen Lin mCellSpacing = dpToPx(intercellSpacing, metrics); 85666ea1b28a76aeba74744148b15099254d918671Owen Lin mLeftEdgePadding = dpToPx(leftEdgePadding, metrics); 86666ea1b28a76aeba74744148b15099254d918671Owen Lin } 87666ea1b28a76aeba74744148b15099254d918671Owen Lin int mCellWidth, mCellHeight; 88666ea1b28a76aeba74744148b15099254d918671Owen Lin int mCellSpacing; 89666ea1b28a76aeba74744148b15099254d918671Owen Lin int mLeftEdgePadding; 90666ea1b28a76aeba74744148b15099254d918671Owen Lin } 91666ea1b28a76aeba74744148b15099254d918671Owen Lin 92666ea1b28a76aeba74744148b15099254d918671Owen Lin private LayoutSpec [] mCellSizeChoices; 93666ea1b28a76aeba74744148b15099254d918671Owen Lin 94666ea1b28a76aeba74744148b15099254d918671Owen Lin private void initCellSize() { 95666ea1b28a76aeba74744148b15099254d918671Owen Lin Activity a = (Activity) getContext(); 96666ea1b28a76aeba74744148b15099254d918671Owen Lin DisplayMetrics metrics = new DisplayMetrics(); 97666ea1b28a76aeba74744148b15099254d918671Owen Lin a.getWindowManager().getDefaultDisplay().getMetrics(metrics); 98666ea1b28a76aeba74744148b15099254d918671Owen Lin mCellSizeChoices = new LayoutSpec[] { 99666ea1b28a76aeba74744148b15099254d918671Owen Lin new LayoutSpec(67, 67, 8, 0, metrics), 100666ea1b28a76aeba74744148b15099254d918671Owen Lin new LayoutSpec(92, 92, 8, 0, metrics), 101666ea1b28a76aeba74744148b15099254d918671Owen Lin }; 102666ea1b28a76aeba74744148b15099254d918671Owen Lin } 103666ea1b28a76aeba74744148b15099254d918671Owen Lin 104666ea1b28a76aeba74744148b15099254d918671Owen Lin // Converts dp to pixel. 105666ea1b28a76aeba74744148b15099254d918671Owen Lin private static int dpToPx(int dp, DisplayMetrics metrics) { 106666ea1b28a76aeba74744148b15099254d918671Owen Lin return (int) (metrics.density * dp); 107666ea1b28a76aeba74744148b15099254d918671Owen Lin } 108666ea1b28a76aeba74744148b15099254d918671Owen Lin 109666ea1b28a76aeba74744148b15099254d918671Owen Lin // These are set in init(). 110666ea1b28a76aeba74744148b15099254d918671Owen Lin private final Handler mHandler = new Handler(); 111666ea1b28a76aeba74744148b15099254d918671Owen Lin private GestureDetector mGestureDetector; 112666ea1b28a76aeba74744148b15099254d918671Owen Lin private ImageBlockManager mImageBlockManager; 113666ea1b28a76aeba74744148b15099254d918671Owen Lin 114666ea1b28a76aeba74744148b15099254d918671Owen Lin // These are set in set*() functions. 115666ea1b28a76aeba74744148b15099254d918671Owen Lin private ImageLoader mLoader; 116666ea1b28a76aeba74744148b15099254d918671Owen Lin private Listener mListener = null; 117666ea1b28a76aeba74744148b15099254d918671Owen Lin private DrawAdapter mDrawAdapter = null; 118666ea1b28a76aeba74744148b15099254d918671Owen Lin private IImageList mAllImages = ImageManager.makeEmptyImageList(); 119666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mSizeChoice = 1; // default is big cell size 120666ea1b28a76aeba74744148b15099254d918671Owen Lin 121666ea1b28a76aeba74744148b15099254d918671Owen Lin // These are set in onLayout(). 122666ea1b28a76aeba74744148b15099254d918671Owen Lin private LayoutSpec mSpec; 123666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mColumns; 124666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mMaxScrollY; 125666ea1b28a76aeba74744148b15099254d918671Owen Lin 126666ea1b28a76aeba74744148b15099254d918671Owen Lin // We can handle events only if onLayout() is completed. 127666ea1b28a76aeba74744148b15099254d918671Owen Lin private boolean mLayoutComplete = false; 128666ea1b28a76aeba74744148b15099254d918671Owen Lin 129666ea1b28a76aeba74744148b15099254d918671Owen Lin // Selection state 130666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mCurrentSelection = INDEX_NONE; 131666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mCurrentPressState = 0; 132666ea1b28a76aeba74744148b15099254d918671Owen Lin private static final int TAPPING_FLAG = 1; 133666ea1b28a76aeba74744148b15099254d918671Owen Lin private static final int CLICKING_FLAG = 2; 134666ea1b28a76aeba74744148b15099254d918671Owen Lin 135666ea1b28a76aeba74744148b15099254d918671Owen Lin // These are cached derived information. 136666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mCount; // Cache mImageList.getCount(); 137666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mRows; // Cache (mCount + mColumns - 1) / mColumns 138666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mBlockHeight; // Cache mSpec.mCellSpacing + mSpec.mCellHeight 139666ea1b28a76aeba74744148b15099254d918671Owen Lin 140666ea1b28a76aeba74744148b15099254d918671Owen Lin private boolean mRunning = false; 141666ea1b28a76aeba74744148b15099254d918671Owen Lin private Scroller mScroller = null; 142666ea1b28a76aeba74744148b15099254d918671Owen Lin 143666ea1b28a76aeba74744148b15099254d918671Owen Lin public GridViewSpecial(Context context, AttributeSet attrs) { 144666ea1b28a76aeba74744148b15099254d918671Owen Lin super(context, attrs); 145666ea1b28a76aeba74744148b15099254d918671Owen Lin init(context); 146666ea1b28a76aeba74744148b15099254d918671Owen Lin } 147666ea1b28a76aeba74744148b15099254d918671Owen Lin 148666ea1b28a76aeba74744148b15099254d918671Owen Lin private void init(Context context) { 149666ea1b28a76aeba74744148b15099254d918671Owen Lin setVerticalScrollBarEnabled(true); 150666ea1b28a76aeba74744148b15099254d918671Owen Lin initializeScrollbars(context.obtainStyledAttributes( 151666ea1b28a76aeba74744148b15099254d918671Owen Lin android.R.styleable.View)); 152666ea1b28a76aeba74744148b15099254d918671Owen Lin mGestureDetector = new GestureDetector(context, 153666ea1b28a76aeba74744148b15099254d918671Owen Lin new MyGestureDetector()); 154666ea1b28a76aeba74744148b15099254d918671Owen Lin setFocusableInTouchMode(true); 155666ea1b28a76aeba74744148b15099254d918671Owen Lin initCellSize(); 156666ea1b28a76aeba74744148b15099254d918671Owen Lin } 157666ea1b28a76aeba74744148b15099254d918671Owen Lin 158666ea1b28a76aeba74744148b15099254d918671Owen Lin private final Runnable mRedrawCallback = new Runnable() { 159666ea1b28a76aeba74744148b15099254d918671Owen Lin public void run() { 160666ea1b28a76aeba74744148b15099254d918671Owen Lin invalidate(); 161666ea1b28a76aeba74744148b15099254d918671Owen Lin } 162666ea1b28a76aeba74744148b15099254d918671Owen Lin }; 163666ea1b28a76aeba74744148b15099254d918671Owen Lin 164666ea1b28a76aeba74744148b15099254d918671Owen Lin public void setLoader(ImageLoader loader) { 165666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(mRunning == false); 166666ea1b28a76aeba74744148b15099254d918671Owen Lin mLoader = loader; 167666ea1b28a76aeba74744148b15099254d918671Owen Lin } 168666ea1b28a76aeba74744148b15099254d918671Owen Lin 169666ea1b28a76aeba74744148b15099254d918671Owen Lin public void setListener(Listener listener) { 170666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(mRunning == false); 171666ea1b28a76aeba74744148b15099254d918671Owen Lin mListener = listener; 172666ea1b28a76aeba74744148b15099254d918671Owen Lin } 173666ea1b28a76aeba74744148b15099254d918671Owen Lin 174666ea1b28a76aeba74744148b15099254d918671Owen Lin public void setDrawAdapter(DrawAdapter adapter) { 175666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(mRunning == false); 176666ea1b28a76aeba74744148b15099254d918671Owen Lin mDrawAdapter = adapter; 177666ea1b28a76aeba74744148b15099254d918671Owen Lin } 178666ea1b28a76aeba74744148b15099254d918671Owen Lin 179666ea1b28a76aeba74744148b15099254d918671Owen Lin public void setImageList(IImageList list) { 180666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(mRunning == false); 181666ea1b28a76aeba74744148b15099254d918671Owen Lin mAllImages = list; 182666ea1b28a76aeba74744148b15099254d918671Owen Lin mCount = mAllImages.getCount(); 183666ea1b28a76aeba74744148b15099254d918671Owen Lin } 184666ea1b28a76aeba74744148b15099254d918671Owen Lin 185666ea1b28a76aeba74744148b15099254d918671Owen Lin public void setSizeChoice(int choice) { 186666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(mRunning == false); 187666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mSizeChoice == choice) return; 188666ea1b28a76aeba74744148b15099254d918671Owen Lin mSizeChoice = choice; 189666ea1b28a76aeba74744148b15099254d918671Owen Lin } 190666ea1b28a76aeba74744148b15099254d918671Owen Lin 191666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 192666ea1b28a76aeba74744148b15099254d918671Owen Lin public void onLayout(boolean changed, int left, int top, 193666ea1b28a76aeba74744148b15099254d918671Owen Lin int right, int bottom) { 194666ea1b28a76aeba74744148b15099254d918671Owen Lin super.onLayout(changed, left, top, right, bottom); 195666ea1b28a76aeba74744148b15099254d918671Owen Lin 196666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!mRunning) { 197666ea1b28a76aeba74744148b15099254d918671Owen Lin return; 198666ea1b28a76aeba74744148b15099254d918671Owen Lin } 199666ea1b28a76aeba74744148b15099254d918671Owen Lin 200666ea1b28a76aeba74744148b15099254d918671Owen Lin mSpec = mCellSizeChoices[mSizeChoice]; 201666ea1b28a76aeba74744148b15099254d918671Owen Lin 202666ea1b28a76aeba74744148b15099254d918671Owen Lin int width = right - left; 203666ea1b28a76aeba74744148b15099254d918671Owen Lin 204666ea1b28a76aeba74744148b15099254d918671Owen Lin // The width is divided into following parts: 205666ea1b28a76aeba74744148b15099254d918671Owen Lin // 206666ea1b28a76aeba74744148b15099254d918671Owen Lin // LeftEdgePadding CellWidth (CellSpacing CellWidth)* RightEdgePadding 207666ea1b28a76aeba74744148b15099254d918671Owen Lin // 208666ea1b28a76aeba74744148b15099254d918671Owen Lin // We determine number of cells (columns) first, then the left and right 209666ea1b28a76aeba74744148b15099254d918671Owen Lin // padding are derived. We make left and right paddings the same size. 210666ea1b28a76aeba74744148b15099254d918671Owen Lin // 211666ea1b28a76aeba74744148b15099254d918671Owen Lin // The height is divided into following parts: 212666ea1b28a76aeba74744148b15099254d918671Owen Lin // 213666ea1b28a76aeba74744148b15099254d918671Owen Lin // CellSpacing (CellHeight CellSpacing)+ 214666ea1b28a76aeba74744148b15099254d918671Owen Lin 215666ea1b28a76aeba74744148b15099254d918671Owen Lin mColumns = 1 + (width - mSpec.mCellWidth) 216666ea1b28a76aeba74744148b15099254d918671Owen Lin / (mSpec.mCellWidth + mSpec.mCellSpacing); 217666ea1b28a76aeba74744148b15099254d918671Owen Lin 218666ea1b28a76aeba74744148b15099254d918671Owen Lin mSpec.mLeftEdgePadding = (width 219666ea1b28a76aeba74744148b15099254d918671Owen Lin - ((mColumns - 1) * mSpec.mCellSpacing) 220666ea1b28a76aeba74744148b15099254d918671Owen Lin - (mColumns * mSpec.mCellWidth)) / 2; 221666ea1b28a76aeba74744148b15099254d918671Owen Lin 222666ea1b28a76aeba74744148b15099254d918671Owen Lin mRows = (mCount + mColumns - 1) / mColumns; 223666ea1b28a76aeba74744148b15099254d918671Owen Lin mBlockHeight = mSpec.mCellSpacing + mSpec.mCellHeight; 224666ea1b28a76aeba74744148b15099254d918671Owen Lin mMaxScrollY = mSpec.mCellSpacing + (mRows * mBlockHeight) 225666ea1b28a76aeba74744148b15099254d918671Owen Lin - (bottom - top); 226666ea1b28a76aeba74744148b15099254d918671Owen Lin 227666ea1b28a76aeba74744148b15099254d918671Owen Lin // Put mScrollY in the valid range. This matters if mMaxScrollY is 228666ea1b28a76aeba74744148b15099254d918671Owen Lin // changed. For example, orientation changed from portrait to landscape. 229666ea1b28a76aeba74744148b15099254d918671Owen Lin mScrollY = Math.max(0, Math.min(mMaxScrollY, mScrollY)); 230666ea1b28a76aeba74744148b15099254d918671Owen Lin 231666ea1b28a76aeba74744148b15099254d918671Owen Lin generateOutlineBitmap(); 232666ea1b28a76aeba74744148b15099254d918671Owen Lin 233666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mImageBlockManager != null) { 234666ea1b28a76aeba74744148b15099254d918671Owen Lin mImageBlockManager.recycle(); 235666ea1b28a76aeba74744148b15099254d918671Owen Lin } 236666ea1b28a76aeba74744148b15099254d918671Owen Lin 237666ea1b28a76aeba74744148b15099254d918671Owen Lin mImageBlockManager = new ImageBlockManager(mHandler, mRedrawCallback, 238666ea1b28a76aeba74744148b15099254d918671Owen Lin mAllImages, mLoader, mDrawAdapter, mSpec, mColumns, width, 239666ea1b28a76aeba74744148b15099254d918671Owen Lin mOutline[OUTLINE_EMPTY]); 240666ea1b28a76aeba74744148b15099254d918671Owen Lin 241666ea1b28a76aeba74744148b15099254d918671Owen Lin mListener.onLayoutComplete(changed); 242666ea1b28a76aeba74744148b15099254d918671Owen Lin 243666ea1b28a76aeba74744148b15099254d918671Owen Lin moveDataWindow(); 244666ea1b28a76aeba74744148b15099254d918671Owen Lin 245666ea1b28a76aeba74744148b15099254d918671Owen Lin mLayoutComplete = true; 246666ea1b28a76aeba74744148b15099254d918671Owen Lin } 247666ea1b28a76aeba74744148b15099254d918671Owen Lin 248666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 249666ea1b28a76aeba74744148b15099254d918671Owen Lin protected int computeVerticalScrollRange() { 250666ea1b28a76aeba74744148b15099254d918671Owen Lin return mMaxScrollY + getHeight(); 251666ea1b28a76aeba74744148b15099254d918671Owen Lin } 252666ea1b28a76aeba74744148b15099254d918671Owen Lin 253666ea1b28a76aeba74744148b15099254d918671Owen Lin // We cache the three outlines from NinePatch to Bitmap to speed up 254666ea1b28a76aeba74744148b15099254d918671Owen Lin // drawing. The cache must be updated if the cell size is changed. 255666ea1b28a76aeba74744148b15099254d918671Owen Lin public static final int OUTLINE_EMPTY = 0; 256666ea1b28a76aeba74744148b15099254d918671Owen Lin public static final int OUTLINE_PRESSED = 1; 257666ea1b28a76aeba74744148b15099254d918671Owen Lin public static final int OUTLINE_SELECTED = 2; 258666ea1b28a76aeba74744148b15099254d918671Owen Lin 259666ea1b28a76aeba74744148b15099254d918671Owen Lin public Bitmap mOutline[] = new Bitmap[3]; 260666ea1b28a76aeba74744148b15099254d918671Owen Lin 261666ea1b28a76aeba74744148b15099254d918671Owen Lin private void generateOutlineBitmap() { 262666ea1b28a76aeba74744148b15099254d918671Owen Lin int w = mSpec.mCellWidth; 263666ea1b28a76aeba74744148b15099254d918671Owen Lin int h = mSpec.mCellHeight; 264666ea1b28a76aeba74744148b15099254d918671Owen Lin 265666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int i = 0; i < mOutline.length; i++) { 266666ea1b28a76aeba74744148b15099254d918671Owen Lin mOutline[i] = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); 267666ea1b28a76aeba74744148b15099254d918671Owen Lin } 268666ea1b28a76aeba74744148b15099254d918671Owen Lin 269666ea1b28a76aeba74744148b15099254d918671Owen Lin Drawable cellOutline; 270666ea1b28a76aeba74744148b15099254d918671Owen Lin cellOutline = GridViewSpecial.this.getResources() 271666ea1b28a76aeba74744148b15099254d918671Owen Lin .getDrawable(android.R.drawable.gallery_thumb); 272666ea1b28a76aeba74744148b15099254d918671Owen Lin cellOutline.setBounds(0, 0, w, h); 273666ea1b28a76aeba74744148b15099254d918671Owen Lin Canvas canvas = new Canvas(); 274666ea1b28a76aeba74744148b15099254d918671Owen Lin 275666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.setBitmap(mOutline[OUTLINE_EMPTY]); 276666ea1b28a76aeba74744148b15099254d918671Owen Lin cellOutline.setState(EMPTY_STATE_SET); 277666ea1b28a76aeba74744148b15099254d918671Owen Lin cellOutline.draw(canvas); 278666ea1b28a76aeba74744148b15099254d918671Owen Lin 279666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.setBitmap(mOutline[OUTLINE_PRESSED]); 280666ea1b28a76aeba74744148b15099254d918671Owen Lin cellOutline.setState( 281666ea1b28a76aeba74744148b15099254d918671Owen Lin PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET); 282666ea1b28a76aeba74744148b15099254d918671Owen Lin cellOutline.draw(canvas); 283666ea1b28a76aeba74744148b15099254d918671Owen Lin 284666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.setBitmap(mOutline[OUTLINE_SELECTED]); 285666ea1b28a76aeba74744148b15099254d918671Owen Lin cellOutline.setState(ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET); 286666ea1b28a76aeba74744148b15099254d918671Owen Lin cellOutline.draw(canvas); 287666ea1b28a76aeba74744148b15099254d918671Owen Lin } 288666ea1b28a76aeba74744148b15099254d918671Owen Lin 289666ea1b28a76aeba74744148b15099254d918671Owen Lin private void moveDataWindow() { 290666ea1b28a76aeba74744148b15099254d918671Owen Lin // Calculate visible region according to scroll position. 291666ea1b28a76aeba74744148b15099254d918671Owen Lin int startRow = (mScrollY - mSpec.mCellSpacing) / mBlockHeight; 292666ea1b28a76aeba74744148b15099254d918671Owen Lin int endRow = (mScrollY + getHeight() - mSpec.mCellSpacing - 1) 293666ea1b28a76aeba74744148b15099254d918671Owen Lin / mBlockHeight + 1; 294666ea1b28a76aeba74744148b15099254d918671Owen Lin 295666ea1b28a76aeba74744148b15099254d918671Owen Lin // Limit startRow and endRow to the valid range. 296666ea1b28a76aeba74744148b15099254d918671Owen Lin // Make sure we handle the mRows == 0 case right. 297666ea1b28a76aeba74744148b15099254d918671Owen Lin startRow = Math.max(Math.min(startRow, mRows - 1), 0); 298666ea1b28a76aeba74744148b15099254d918671Owen Lin endRow = Math.max(Math.min(endRow, mRows), 0); 299666ea1b28a76aeba74744148b15099254d918671Owen Lin mImageBlockManager.setVisibleRows(startRow, endRow); 300666ea1b28a76aeba74744148b15099254d918671Owen Lin } 301666ea1b28a76aeba74744148b15099254d918671Owen Lin 302666ea1b28a76aeba74744148b15099254d918671Owen Lin // In MyGestureDetector we have to check canHandleEvent() because 303666ea1b28a76aeba74744148b15099254d918671Owen Lin // GestureDetector could queue events and fire them later. At that time 304666ea1b28a76aeba74744148b15099254d918671Owen Lin // stop() may have already been called and we can't handle the events. 305666ea1b28a76aeba74744148b15099254d918671Owen Lin private class MyGestureDetector extends SimpleOnGestureListener { 306666ea1b28a76aeba74744148b15099254d918671Owen Lin private AudioManager mAudioManager; 307666ea1b28a76aeba74744148b15099254d918671Owen Lin 308666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 309666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean onDown(MotionEvent e) { 310666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!canHandleEvent()) return false; 311666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mScroller != null && !mScroller.isFinished()) { 312666ea1b28a76aeba74744148b15099254d918671Owen Lin mScroller.forceFinished(true); 313666ea1b28a76aeba74744148b15099254d918671Owen Lin return false; 314666ea1b28a76aeba74744148b15099254d918671Owen Lin } 315666ea1b28a76aeba74744148b15099254d918671Owen Lin int index = computeSelectedIndex(e.getX(), e.getY()); 316666ea1b28a76aeba74744148b15099254d918671Owen Lin if (index >= 0 && index < mCount) { 317666ea1b28a76aeba74744148b15099254d918671Owen Lin setSelectedIndex(index); 318666ea1b28a76aeba74744148b15099254d918671Owen Lin } else { 319666ea1b28a76aeba74744148b15099254d918671Owen Lin setSelectedIndex(INDEX_NONE); 320666ea1b28a76aeba74744148b15099254d918671Owen Lin } 321666ea1b28a76aeba74744148b15099254d918671Owen Lin return true; 322666ea1b28a76aeba74744148b15099254d918671Owen Lin } 323666ea1b28a76aeba74744148b15099254d918671Owen Lin 324666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 325666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean onFling(MotionEvent e1, MotionEvent e2, 326666ea1b28a76aeba74744148b15099254d918671Owen Lin float velocityX, float velocityY) { 327666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!canHandleEvent()) return false; 328666ea1b28a76aeba74744148b15099254d918671Owen Lin if (velocityY > MAX_FLING_VELOCITY) { 329666ea1b28a76aeba74744148b15099254d918671Owen Lin velocityY = MAX_FLING_VELOCITY; 330666ea1b28a76aeba74744148b15099254d918671Owen Lin } else if (velocityY < -MAX_FLING_VELOCITY) { 331666ea1b28a76aeba74744148b15099254d918671Owen Lin velocityY = -MAX_FLING_VELOCITY; 332666ea1b28a76aeba74744148b15099254d918671Owen Lin } 333666ea1b28a76aeba74744148b15099254d918671Owen Lin 334666ea1b28a76aeba74744148b15099254d918671Owen Lin setSelectedIndex(INDEX_NONE); 335666ea1b28a76aeba74744148b15099254d918671Owen Lin mScroller = new Scroller(getContext()); 336666ea1b28a76aeba74744148b15099254d918671Owen Lin mScroller.fling(0, mScrollY, 0, -(int) velocityY, 0, 0, 0, 337666ea1b28a76aeba74744148b15099254d918671Owen Lin mMaxScrollY); 338666ea1b28a76aeba74744148b15099254d918671Owen Lin computeScroll(); 339666ea1b28a76aeba74744148b15099254d918671Owen Lin 340666ea1b28a76aeba74744148b15099254d918671Owen Lin return true; 341666ea1b28a76aeba74744148b15099254d918671Owen Lin } 342666ea1b28a76aeba74744148b15099254d918671Owen Lin 343666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 344666ea1b28a76aeba74744148b15099254d918671Owen Lin public void onLongPress(MotionEvent e) { 345666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!canHandleEvent()) return; 346666ea1b28a76aeba74744148b15099254d918671Owen Lin performLongClick(); 347666ea1b28a76aeba74744148b15099254d918671Owen Lin } 348666ea1b28a76aeba74744148b15099254d918671Owen Lin 349666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 350666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean onScroll(MotionEvent e1, MotionEvent e2, 351666ea1b28a76aeba74744148b15099254d918671Owen Lin float distanceX, float distanceY) { 352666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!canHandleEvent()) return false; 353666ea1b28a76aeba74744148b15099254d918671Owen Lin setSelectedIndex(INDEX_NONE); 354666ea1b28a76aeba74744148b15099254d918671Owen Lin scrollBy(0, (int) distanceY); 355666ea1b28a76aeba74744148b15099254d918671Owen Lin invalidate(); 356666ea1b28a76aeba74744148b15099254d918671Owen Lin return true; 357666ea1b28a76aeba74744148b15099254d918671Owen Lin } 358666ea1b28a76aeba74744148b15099254d918671Owen Lin 359666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 360666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean onSingleTapConfirmed(MotionEvent e) { 361666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!canHandleEvent()) return false; 362666ea1b28a76aeba74744148b15099254d918671Owen Lin int index = computeSelectedIndex(e.getX(), e.getY()); 363666ea1b28a76aeba74744148b15099254d918671Owen Lin if (index >= 0 && index < mCount) { 364666ea1b28a76aeba74744148b15099254d918671Owen Lin // Play click sound. 365666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mAudioManager == null) { 366666ea1b28a76aeba74744148b15099254d918671Owen Lin mAudioManager = (AudioManager) getContext() 367666ea1b28a76aeba74744148b15099254d918671Owen Lin .getSystemService(Context.AUDIO_SERVICE); 368666ea1b28a76aeba74744148b15099254d918671Owen Lin } 369666ea1b28a76aeba74744148b15099254d918671Owen Lin mAudioManager.playSoundEffect(AudioManager.FX_KEY_CLICK); 370666ea1b28a76aeba74744148b15099254d918671Owen Lin 371666ea1b28a76aeba74744148b15099254d918671Owen Lin mListener.onImageTapped(index); 372666ea1b28a76aeba74744148b15099254d918671Owen Lin return true; 373666ea1b28a76aeba74744148b15099254d918671Owen Lin } 374666ea1b28a76aeba74744148b15099254d918671Owen Lin return false; 375666ea1b28a76aeba74744148b15099254d918671Owen Lin } 376666ea1b28a76aeba74744148b15099254d918671Owen Lin } 377666ea1b28a76aeba74744148b15099254d918671Owen Lin 378666ea1b28a76aeba74744148b15099254d918671Owen Lin public int getCurrentSelection() { 379666ea1b28a76aeba74744148b15099254d918671Owen Lin return mCurrentSelection; 380666ea1b28a76aeba74744148b15099254d918671Owen Lin } 381666ea1b28a76aeba74744148b15099254d918671Owen Lin 382666ea1b28a76aeba74744148b15099254d918671Owen Lin public void invalidateImage(int index) { 383666ea1b28a76aeba74744148b15099254d918671Owen Lin if (index != INDEX_NONE) { 384666ea1b28a76aeba74744148b15099254d918671Owen Lin mImageBlockManager.invalidateImage(index); 385666ea1b28a76aeba74744148b15099254d918671Owen Lin } 386666ea1b28a76aeba74744148b15099254d918671Owen Lin } 387666ea1b28a76aeba74744148b15099254d918671Owen Lin 388666ea1b28a76aeba74744148b15099254d918671Owen Lin /** 389666ea1b28a76aeba74744148b15099254d918671Owen Lin * 390666ea1b28a76aeba74744148b15099254d918671Owen Lin * @param index <code>INDEX_NONE</code> (-1) means remove selection. 391666ea1b28a76aeba74744148b15099254d918671Owen Lin */ 392666ea1b28a76aeba74744148b15099254d918671Owen Lin public void setSelectedIndex(int index) { 393666ea1b28a76aeba74744148b15099254d918671Owen Lin // A selection box will be shown for the image that being selected, 394666ea1b28a76aeba74744148b15099254d918671Owen Lin // (by finger or by the dpad center key). The selection box can be drawn 395666ea1b28a76aeba74744148b15099254d918671Owen Lin // in two colors. One color (yellow) is used when the the image is 396666ea1b28a76aeba74744148b15099254d918671Owen Lin // still being tapped or clicked (the finger is still on the touch 397666ea1b28a76aeba74744148b15099254d918671Owen Lin // screen or the dpad center key is not released). Another color 398666ea1b28a76aeba74744148b15099254d918671Owen Lin // (orange) is used after the finger leaves touch screen or the dpad 399666ea1b28a76aeba74744148b15099254d918671Owen Lin // center key is released. 400666ea1b28a76aeba74744148b15099254d918671Owen Lin 401666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mCurrentSelection == index) { 402666ea1b28a76aeba74744148b15099254d918671Owen Lin return; 403666ea1b28a76aeba74744148b15099254d918671Owen Lin } 404666ea1b28a76aeba74744148b15099254d918671Owen Lin // This happens when the last picture is deleted. 405666ea1b28a76aeba74744148b15099254d918671Owen Lin mCurrentSelection = Math.min(index, mCount - 1); 406666ea1b28a76aeba74744148b15099254d918671Owen Lin 407666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mCurrentSelection != INDEX_NONE) { 408666ea1b28a76aeba74744148b15099254d918671Owen Lin ensureVisible(mCurrentSelection); 409666ea1b28a76aeba74744148b15099254d918671Owen Lin } 410666ea1b28a76aeba74744148b15099254d918671Owen Lin invalidate(); 411666ea1b28a76aeba74744148b15099254d918671Owen Lin } 412666ea1b28a76aeba74744148b15099254d918671Owen Lin 413666ea1b28a76aeba74744148b15099254d918671Owen Lin public void scrollToImage(int index) { 414666ea1b28a76aeba74744148b15099254d918671Owen Lin Rect r = getRectForPosition(index); 415666ea1b28a76aeba74744148b15099254d918671Owen Lin scrollTo(0, r.top); 416666ea1b28a76aeba74744148b15099254d918671Owen Lin } 417666ea1b28a76aeba74744148b15099254d918671Owen Lin 418666ea1b28a76aeba74744148b15099254d918671Owen Lin public void scrollToVisible(int index) { 419666ea1b28a76aeba74744148b15099254d918671Owen Lin Rect r = getRectForPosition(index); 420666ea1b28a76aeba74744148b15099254d918671Owen Lin int top = getScrollY(); 421666ea1b28a76aeba74744148b15099254d918671Owen Lin int bottom = getScrollY() + getHeight(); 422666ea1b28a76aeba74744148b15099254d918671Owen Lin if (r.bottom > bottom) { 423666ea1b28a76aeba74744148b15099254d918671Owen Lin scrollTo(0, r.bottom - getHeight()); 424666ea1b28a76aeba74744148b15099254d918671Owen Lin } else if (r.top < top) { 425666ea1b28a76aeba74744148b15099254d918671Owen Lin scrollTo(0, r.top); 426666ea1b28a76aeba74744148b15099254d918671Owen Lin } 427666ea1b28a76aeba74744148b15099254d918671Owen Lin } 428666ea1b28a76aeba74744148b15099254d918671Owen Lin 429666ea1b28a76aeba74744148b15099254d918671Owen Lin private void ensureVisible(int pos) { 430666ea1b28a76aeba74744148b15099254d918671Owen Lin Rect r = getRectForPosition(pos); 431666ea1b28a76aeba74744148b15099254d918671Owen Lin int top = getScrollY(); 432666ea1b28a76aeba74744148b15099254d918671Owen Lin int bot = top + getHeight(); 433666ea1b28a76aeba74744148b15099254d918671Owen Lin 434666ea1b28a76aeba74744148b15099254d918671Owen Lin if (r.bottom > bot) { 435666ea1b28a76aeba74744148b15099254d918671Owen Lin mScroller = new Scroller(getContext()); 436666ea1b28a76aeba74744148b15099254d918671Owen Lin mScroller.startScroll(mScrollX, mScrollY, 0, 437666ea1b28a76aeba74744148b15099254d918671Owen Lin r.bottom - getHeight() - mScrollY, 200); 438666ea1b28a76aeba74744148b15099254d918671Owen Lin computeScroll(); 439666ea1b28a76aeba74744148b15099254d918671Owen Lin } else if (r.top < top) { 440666ea1b28a76aeba74744148b15099254d918671Owen Lin mScroller = new Scroller(getContext()); 441666ea1b28a76aeba74744148b15099254d918671Owen Lin mScroller.startScroll(mScrollX, mScrollY, 0, r.top - mScrollY, 200); 442666ea1b28a76aeba74744148b15099254d918671Owen Lin computeScroll(); 443666ea1b28a76aeba74744148b15099254d918671Owen Lin } 444666ea1b28a76aeba74744148b15099254d918671Owen Lin } 445666ea1b28a76aeba74744148b15099254d918671Owen Lin 446666ea1b28a76aeba74744148b15099254d918671Owen Lin public void start() { 447666ea1b28a76aeba74744148b15099254d918671Owen Lin // These must be set before start(). 448666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(mLoader != null); 449666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(mListener != null); 450666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(mDrawAdapter != null); 451666ea1b28a76aeba74744148b15099254d918671Owen Lin mRunning = true; 452666ea1b28a76aeba74744148b15099254d918671Owen Lin requestLayout(); 453666ea1b28a76aeba74744148b15099254d918671Owen Lin } 454666ea1b28a76aeba74744148b15099254d918671Owen Lin 455666ea1b28a76aeba74744148b15099254d918671Owen Lin // If the the underlying data is changed, for example, 456666ea1b28a76aeba74744148b15099254d918671Owen Lin // an image is deleted, or the size choice is changed, 457666ea1b28a76aeba74744148b15099254d918671Owen Lin // The following sequence is needed: 458666ea1b28a76aeba74744148b15099254d918671Owen Lin // 459666ea1b28a76aeba74744148b15099254d918671Owen Lin // mGvs.stop(); 460666ea1b28a76aeba74744148b15099254d918671Owen Lin // mGvs.set...(...); 461666ea1b28a76aeba74744148b15099254d918671Owen Lin // mGvs.set...(...); 462666ea1b28a76aeba74744148b15099254d918671Owen Lin // mGvs.start(); 463666ea1b28a76aeba74744148b15099254d918671Owen Lin public void stop() { 464666ea1b28a76aeba74744148b15099254d918671Owen Lin // Remove the long press callback from the queue if we are going to 465666ea1b28a76aeba74744148b15099254d918671Owen Lin // stop. 466666ea1b28a76aeba74744148b15099254d918671Owen Lin mHandler.removeCallbacks(mLongPressCallback); 467666ea1b28a76aeba74744148b15099254d918671Owen Lin mScroller = null; 468666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mImageBlockManager != null) { 469666ea1b28a76aeba74744148b15099254d918671Owen Lin mImageBlockManager.recycle(); 470666ea1b28a76aeba74744148b15099254d918671Owen Lin mImageBlockManager = null; 471666ea1b28a76aeba74744148b15099254d918671Owen Lin } 472666ea1b28a76aeba74744148b15099254d918671Owen Lin mRunning = false; 473666ea1b28a76aeba74744148b15099254d918671Owen Lin mCurrentSelection = INDEX_NONE; 474666ea1b28a76aeba74744148b15099254d918671Owen Lin } 475666ea1b28a76aeba74744148b15099254d918671Owen Lin 476666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 477666ea1b28a76aeba74744148b15099254d918671Owen Lin public void onDraw(Canvas canvas) { 478666ea1b28a76aeba74744148b15099254d918671Owen Lin super.onDraw(canvas); 479666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!canHandleEvent()) return; 480666ea1b28a76aeba74744148b15099254d918671Owen Lin mImageBlockManager.doDraw(canvas, getWidth(), getHeight(), mScrollY); 481666ea1b28a76aeba74744148b15099254d918671Owen Lin paintDecoration(canvas); 482666ea1b28a76aeba74744148b15099254d918671Owen Lin paintSelection(canvas); 483666ea1b28a76aeba74744148b15099254d918671Owen Lin moveDataWindow(); 484666ea1b28a76aeba74744148b15099254d918671Owen Lin } 485666ea1b28a76aeba74744148b15099254d918671Owen Lin 486666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 487666ea1b28a76aeba74744148b15099254d918671Owen Lin public void computeScroll() { 488666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mScroller != null) { 489666ea1b28a76aeba74744148b15099254d918671Owen Lin boolean more = mScroller.computeScrollOffset(); 490666ea1b28a76aeba74744148b15099254d918671Owen Lin scrollTo(0, mScroller.getCurrY()); 491666ea1b28a76aeba74744148b15099254d918671Owen Lin if (more) { 492666ea1b28a76aeba74744148b15099254d918671Owen Lin invalidate(); // So we draw again 493666ea1b28a76aeba74744148b15099254d918671Owen Lin } else { 494666ea1b28a76aeba74744148b15099254d918671Owen Lin mScroller = null; 495666ea1b28a76aeba74744148b15099254d918671Owen Lin } 496666ea1b28a76aeba74744148b15099254d918671Owen Lin } else { 497666ea1b28a76aeba74744148b15099254d918671Owen Lin super.computeScroll(); 498666ea1b28a76aeba74744148b15099254d918671Owen Lin } 499666ea1b28a76aeba74744148b15099254d918671Owen Lin } 500666ea1b28a76aeba74744148b15099254d918671Owen Lin 501666ea1b28a76aeba74744148b15099254d918671Owen Lin // Return the rectange for the thumbnail in the given position. 502666ea1b28a76aeba74744148b15099254d918671Owen Lin Rect getRectForPosition(int pos) { 503666ea1b28a76aeba74744148b15099254d918671Owen Lin int row = pos / mColumns; 504666ea1b28a76aeba74744148b15099254d918671Owen Lin int col = pos - (row * mColumns); 505666ea1b28a76aeba74744148b15099254d918671Owen Lin 506666ea1b28a76aeba74744148b15099254d918671Owen Lin int left = mSpec.mLeftEdgePadding 507666ea1b28a76aeba74744148b15099254d918671Owen Lin + (col * (mSpec.mCellWidth + mSpec.mCellSpacing)); 508666ea1b28a76aeba74744148b15099254d918671Owen Lin int top = row * mBlockHeight; 509666ea1b28a76aeba74744148b15099254d918671Owen Lin 510666ea1b28a76aeba74744148b15099254d918671Owen Lin return new Rect(left, top, 511666ea1b28a76aeba74744148b15099254d918671Owen Lin left + mSpec.mCellWidth + mSpec.mCellSpacing, 512666ea1b28a76aeba74744148b15099254d918671Owen Lin top + mSpec.mCellHeight + mSpec.mCellSpacing); 513666ea1b28a76aeba74744148b15099254d918671Owen Lin } 514666ea1b28a76aeba74744148b15099254d918671Owen Lin 515666ea1b28a76aeba74744148b15099254d918671Owen Lin // Inverse of getRectForPosition: from screen coordinate to image position. 516666ea1b28a76aeba74744148b15099254d918671Owen Lin int computeSelectedIndex(float xFloat, float yFloat) { 517666ea1b28a76aeba74744148b15099254d918671Owen Lin int x = (int) xFloat; 518666ea1b28a76aeba74744148b15099254d918671Owen Lin int y = (int) yFloat; 519666ea1b28a76aeba74744148b15099254d918671Owen Lin 520666ea1b28a76aeba74744148b15099254d918671Owen Lin int spacing = mSpec.mCellSpacing; 521666ea1b28a76aeba74744148b15099254d918671Owen Lin int leftSpacing = mSpec.mLeftEdgePadding; 522666ea1b28a76aeba74744148b15099254d918671Owen Lin 523666ea1b28a76aeba74744148b15099254d918671Owen Lin int row = (mScrollY + y - spacing) / (mSpec.mCellHeight + spacing); 524666ea1b28a76aeba74744148b15099254d918671Owen Lin int col = Math.min(mColumns - 1, 525666ea1b28a76aeba74744148b15099254d918671Owen Lin (x - leftSpacing) / (mSpec.mCellWidth + spacing)); 526666ea1b28a76aeba74744148b15099254d918671Owen Lin return (row * mColumns) + col; 527666ea1b28a76aeba74744148b15099254d918671Owen Lin } 528666ea1b28a76aeba74744148b15099254d918671Owen Lin 529666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 530666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean onTouchEvent(MotionEvent ev) { 531666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!canHandleEvent()) { 532666ea1b28a76aeba74744148b15099254d918671Owen Lin return false; 533666ea1b28a76aeba74744148b15099254d918671Owen Lin } 534666ea1b28a76aeba74744148b15099254d918671Owen Lin switch (ev.getAction()) { 535666ea1b28a76aeba74744148b15099254d918671Owen Lin case MotionEvent.ACTION_DOWN: 536666ea1b28a76aeba74744148b15099254d918671Owen Lin mCurrentPressState |= TAPPING_FLAG; 537666ea1b28a76aeba74744148b15099254d918671Owen Lin invalidate(); 538666ea1b28a76aeba74744148b15099254d918671Owen Lin break; 539666ea1b28a76aeba74744148b15099254d918671Owen Lin case MotionEvent.ACTION_UP: 540666ea1b28a76aeba74744148b15099254d918671Owen Lin mCurrentPressState &= ~TAPPING_FLAG; 541666ea1b28a76aeba74744148b15099254d918671Owen Lin invalidate(); 542666ea1b28a76aeba74744148b15099254d918671Owen Lin break; 543666ea1b28a76aeba74744148b15099254d918671Owen Lin } 544666ea1b28a76aeba74744148b15099254d918671Owen Lin mGestureDetector.onTouchEvent(ev); 545666ea1b28a76aeba74744148b15099254d918671Owen Lin // Consume all events 546666ea1b28a76aeba74744148b15099254d918671Owen Lin return true; 547666ea1b28a76aeba74744148b15099254d918671Owen Lin } 548666ea1b28a76aeba74744148b15099254d918671Owen Lin 549666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 550666ea1b28a76aeba74744148b15099254d918671Owen Lin public void scrollBy(int x, int y) { 551666ea1b28a76aeba74744148b15099254d918671Owen Lin scrollTo(mScrollX + x, mScrollY + y); 552666ea1b28a76aeba74744148b15099254d918671Owen Lin } 553666ea1b28a76aeba74744148b15099254d918671Owen Lin 554666ea1b28a76aeba74744148b15099254d918671Owen Lin public void scrollTo(float scrollPosition) { 555666ea1b28a76aeba74744148b15099254d918671Owen Lin scrollTo(0, Math.round(scrollPosition * mMaxScrollY)); 556666ea1b28a76aeba74744148b15099254d918671Owen Lin } 557666ea1b28a76aeba74744148b15099254d918671Owen Lin 558666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 559666ea1b28a76aeba74744148b15099254d918671Owen Lin public void scrollTo(int x, int y) { 560666ea1b28a76aeba74744148b15099254d918671Owen Lin y = Math.max(0, Math.min(mMaxScrollY, y)); 561666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mSpec != null) { 562666ea1b28a76aeba74744148b15099254d918671Owen Lin mListener.onScroll((float) mScrollY / mMaxScrollY); 563666ea1b28a76aeba74744148b15099254d918671Owen Lin } 564666ea1b28a76aeba74744148b15099254d918671Owen Lin super.scrollTo(x, y); 565666ea1b28a76aeba74744148b15099254d918671Owen Lin } 566666ea1b28a76aeba74744148b15099254d918671Owen Lin 567666ea1b28a76aeba74744148b15099254d918671Owen Lin private boolean canHandleEvent() { 568666ea1b28a76aeba74744148b15099254d918671Owen Lin return mRunning && mLayoutComplete; 569666ea1b28a76aeba74744148b15099254d918671Owen Lin } 570666ea1b28a76aeba74744148b15099254d918671Owen Lin 571666ea1b28a76aeba74744148b15099254d918671Owen Lin private final Runnable mLongPressCallback = new Runnable() { 572666ea1b28a76aeba74744148b15099254d918671Owen Lin public void run() { 573666ea1b28a76aeba74744148b15099254d918671Owen Lin mCurrentPressState &= ~CLICKING_FLAG; 574666ea1b28a76aeba74744148b15099254d918671Owen Lin showContextMenu(); 575666ea1b28a76aeba74744148b15099254d918671Owen Lin } 576666ea1b28a76aeba74744148b15099254d918671Owen Lin }; 577666ea1b28a76aeba74744148b15099254d918671Owen Lin 578666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 579666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean onKeyDown(int keyCode, KeyEvent event) { 580666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!canHandleEvent()) return false; 581666ea1b28a76aeba74744148b15099254d918671Owen Lin int sel = mCurrentSelection; 582666ea1b28a76aeba74744148b15099254d918671Owen Lin if (sel != INDEX_NONE) { 583666ea1b28a76aeba74744148b15099254d918671Owen Lin switch (keyCode) { 584666ea1b28a76aeba74744148b15099254d918671Owen Lin case KeyEvent.KEYCODE_DPAD_RIGHT: 585666ea1b28a76aeba74744148b15099254d918671Owen Lin if (sel != mCount - 1 && (sel % mColumns < mColumns - 1)) { 586666ea1b28a76aeba74744148b15099254d918671Owen Lin sel += 1; 587666ea1b28a76aeba74744148b15099254d918671Owen Lin } 588666ea1b28a76aeba74744148b15099254d918671Owen Lin break; 589666ea1b28a76aeba74744148b15099254d918671Owen Lin case KeyEvent.KEYCODE_DPAD_LEFT: 590666ea1b28a76aeba74744148b15099254d918671Owen Lin if (sel > 0 && (sel % mColumns != 0)) { 591666ea1b28a76aeba74744148b15099254d918671Owen Lin sel -= 1; 592666ea1b28a76aeba74744148b15099254d918671Owen Lin } 593666ea1b28a76aeba74744148b15099254d918671Owen Lin break; 594666ea1b28a76aeba74744148b15099254d918671Owen Lin case KeyEvent.KEYCODE_DPAD_UP: 595666ea1b28a76aeba74744148b15099254d918671Owen Lin if (sel >= mColumns) { 596666ea1b28a76aeba74744148b15099254d918671Owen Lin sel -= mColumns; 597666ea1b28a76aeba74744148b15099254d918671Owen Lin } 598666ea1b28a76aeba74744148b15099254d918671Owen Lin break; 599666ea1b28a76aeba74744148b15099254d918671Owen Lin case KeyEvent.KEYCODE_DPAD_DOWN: 600666ea1b28a76aeba74744148b15099254d918671Owen Lin sel = Math.min(mCount - 1, sel + mColumns); 601666ea1b28a76aeba74744148b15099254d918671Owen Lin break; 602666ea1b28a76aeba74744148b15099254d918671Owen Lin case KeyEvent.KEYCODE_DPAD_CENTER: 603666ea1b28a76aeba74744148b15099254d918671Owen Lin if (event.getRepeatCount() == 0) { 604666ea1b28a76aeba74744148b15099254d918671Owen Lin mCurrentPressState |= CLICKING_FLAG; 605666ea1b28a76aeba74744148b15099254d918671Owen Lin mHandler.postDelayed(mLongPressCallback, 606666ea1b28a76aeba74744148b15099254d918671Owen Lin ViewConfiguration.getLongPressTimeout()); 607666ea1b28a76aeba74744148b15099254d918671Owen Lin } 608666ea1b28a76aeba74744148b15099254d918671Owen Lin break; 609666ea1b28a76aeba74744148b15099254d918671Owen Lin default: 610666ea1b28a76aeba74744148b15099254d918671Owen Lin return super.onKeyDown(keyCode, event); 611666ea1b28a76aeba74744148b15099254d918671Owen Lin } 612666ea1b28a76aeba74744148b15099254d918671Owen Lin } else { 613666ea1b28a76aeba74744148b15099254d918671Owen Lin switch (keyCode) { 614666ea1b28a76aeba74744148b15099254d918671Owen Lin case KeyEvent.KEYCODE_DPAD_RIGHT: 615666ea1b28a76aeba74744148b15099254d918671Owen Lin case KeyEvent.KEYCODE_DPAD_LEFT: 616666ea1b28a76aeba74744148b15099254d918671Owen Lin case KeyEvent.KEYCODE_DPAD_UP: 617666ea1b28a76aeba74744148b15099254d918671Owen Lin case KeyEvent.KEYCODE_DPAD_DOWN: 618666ea1b28a76aeba74744148b15099254d918671Owen Lin int startRow = 619666ea1b28a76aeba74744148b15099254d918671Owen Lin (mScrollY - mSpec.mCellSpacing) / mBlockHeight; 620666ea1b28a76aeba74744148b15099254d918671Owen Lin int topPos = startRow * mColumns; 621666ea1b28a76aeba74744148b15099254d918671Owen Lin Rect r = getRectForPosition(topPos); 622666ea1b28a76aeba74744148b15099254d918671Owen Lin if (r.top < getScrollY()) { 623666ea1b28a76aeba74744148b15099254d918671Owen Lin topPos += mColumns; 624666ea1b28a76aeba74744148b15099254d918671Owen Lin } 625666ea1b28a76aeba74744148b15099254d918671Owen Lin topPos = Math.min(mCount - 1, topPos); 626666ea1b28a76aeba74744148b15099254d918671Owen Lin sel = topPos; 627666ea1b28a76aeba74744148b15099254d918671Owen Lin break; 628666ea1b28a76aeba74744148b15099254d918671Owen Lin default: 629666ea1b28a76aeba74744148b15099254d918671Owen Lin return super.onKeyDown(keyCode, event); 630666ea1b28a76aeba74744148b15099254d918671Owen Lin } 631666ea1b28a76aeba74744148b15099254d918671Owen Lin } 632666ea1b28a76aeba74744148b15099254d918671Owen Lin setSelectedIndex(sel); 633666ea1b28a76aeba74744148b15099254d918671Owen Lin return true; 634666ea1b28a76aeba74744148b15099254d918671Owen Lin } 635666ea1b28a76aeba74744148b15099254d918671Owen Lin 636666ea1b28a76aeba74744148b15099254d918671Owen Lin @Override 637666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean onKeyUp(int keyCode, KeyEvent event) { 638666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!canHandleEvent()) return false; 639666ea1b28a76aeba74744148b15099254d918671Owen Lin 640666ea1b28a76aeba74744148b15099254d918671Owen Lin if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { 641666ea1b28a76aeba74744148b15099254d918671Owen Lin mCurrentPressState &= ~CLICKING_FLAG; 642666ea1b28a76aeba74744148b15099254d918671Owen Lin invalidate(); 643666ea1b28a76aeba74744148b15099254d918671Owen Lin 644666ea1b28a76aeba74744148b15099254d918671Owen Lin // The keyUp doesn't get called when the longpress menu comes up. We 645666ea1b28a76aeba74744148b15099254d918671Owen Lin // only get here when the user lets go of the center key before the 646666ea1b28a76aeba74744148b15099254d918671Owen Lin // longpress menu comes up. 647666ea1b28a76aeba74744148b15099254d918671Owen Lin mHandler.removeCallbacks(mLongPressCallback); 648666ea1b28a76aeba74744148b15099254d918671Owen Lin 649666ea1b28a76aeba74744148b15099254d918671Owen Lin // open the photo 650666ea1b28a76aeba74744148b15099254d918671Owen Lin mListener.onImageClicked(mCurrentSelection); 651666ea1b28a76aeba74744148b15099254d918671Owen Lin return true; 652666ea1b28a76aeba74744148b15099254d918671Owen Lin } 653666ea1b28a76aeba74744148b15099254d918671Owen Lin return super.onKeyUp(keyCode, event); 654666ea1b28a76aeba74744148b15099254d918671Owen Lin } 655666ea1b28a76aeba74744148b15099254d918671Owen Lin 656666ea1b28a76aeba74744148b15099254d918671Owen Lin private void paintDecoration(Canvas canvas) { 657666ea1b28a76aeba74744148b15099254d918671Owen Lin if (!mDrawAdapter.needsDecoration()) return; 658666ea1b28a76aeba74744148b15099254d918671Owen Lin 659666ea1b28a76aeba74744148b15099254d918671Owen Lin // Calculate visible region according to scroll position. 660666ea1b28a76aeba74744148b15099254d918671Owen Lin int startRow = (mScrollY - mSpec.mCellSpacing) / mBlockHeight; 661666ea1b28a76aeba74744148b15099254d918671Owen Lin int endRow = (mScrollY + getHeight() - mSpec.mCellSpacing - 1) 662666ea1b28a76aeba74744148b15099254d918671Owen Lin / mBlockHeight + 1; 663666ea1b28a76aeba74744148b15099254d918671Owen Lin 664666ea1b28a76aeba74744148b15099254d918671Owen Lin // Limit startRow and endRow to the valid range. 665666ea1b28a76aeba74744148b15099254d918671Owen Lin // Make sure we handle the mRows == 0 case right. 666666ea1b28a76aeba74744148b15099254d918671Owen Lin startRow = Math.max(Math.min(startRow, mRows - 1), 0); 667666ea1b28a76aeba74744148b15099254d918671Owen Lin endRow = Math.max(Math.min(endRow, mRows), 0); 668666ea1b28a76aeba74744148b15099254d918671Owen Lin 669666ea1b28a76aeba74744148b15099254d918671Owen Lin int startIndex = startRow * mColumns; 670666ea1b28a76aeba74744148b15099254d918671Owen Lin int endIndex = Math.min(endRow * mColumns, mCount); 671666ea1b28a76aeba74744148b15099254d918671Owen Lin 672666ea1b28a76aeba74744148b15099254d918671Owen Lin int xPos = mSpec.mLeftEdgePadding; 673666ea1b28a76aeba74744148b15099254d918671Owen Lin int yPos = mSpec.mCellSpacing + startRow * mBlockHeight; 674666ea1b28a76aeba74744148b15099254d918671Owen Lin int off = 0; 675666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int i = startIndex; i < endIndex; i++) { 676666ea1b28a76aeba74744148b15099254d918671Owen Lin IImage image = mAllImages.getImageAt(i); 677666ea1b28a76aeba74744148b15099254d918671Owen Lin 678666ea1b28a76aeba74744148b15099254d918671Owen Lin mDrawAdapter.drawDecoration(canvas, image, xPos, yPos, 679666ea1b28a76aeba74744148b15099254d918671Owen Lin mSpec.mCellWidth, mSpec.mCellHeight); 680666ea1b28a76aeba74744148b15099254d918671Owen Lin 681666ea1b28a76aeba74744148b15099254d918671Owen Lin // Calculate next position 682666ea1b28a76aeba74744148b15099254d918671Owen Lin off += 1; 683666ea1b28a76aeba74744148b15099254d918671Owen Lin if (off == mColumns) { 684666ea1b28a76aeba74744148b15099254d918671Owen Lin xPos = mSpec.mLeftEdgePadding; 685666ea1b28a76aeba74744148b15099254d918671Owen Lin yPos += mBlockHeight; 686666ea1b28a76aeba74744148b15099254d918671Owen Lin off = 0; 687666ea1b28a76aeba74744148b15099254d918671Owen Lin } else { 688666ea1b28a76aeba74744148b15099254d918671Owen Lin xPos += mSpec.mCellWidth + mSpec.mCellSpacing; 689666ea1b28a76aeba74744148b15099254d918671Owen Lin } 690666ea1b28a76aeba74744148b15099254d918671Owen Lin } 691666ea1b28a76aeba74744148b15099254d918671Owen Lin } 692666ea1b28a76aeba74744148b15099254d918671Owen Lin 693666ea1b28a76aeba74744148b15099254d918671Owen Lin private void paintSelection(Canvas canvas) { 694666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mCurrentSelection == INDEX_NONE) return; 695666ea1b28a76aeba74744148b15099254d918671Owen Lin 696666ea1b28a76aeba74744148b15099254d918671Owen Lin int row = mCurrentSelection / mColumns; 697666ea1b28a76aeba74744148b15099254d918671Owen Lin int col = mCurrentSelection - (row * mColumns); 698666ea1b28a76aeba74744148b15099254d918671Owen Lin 699666ea1b28a76aeba74744148b15099254d918671Owen Lin int spacing = mSpec.mCellSpacing; 700666ea1b28a76aeba74744148b15099254d918671Owen Lin int leftSpacing = mSpec.mLeftEdgePadding; 701666ea1b28a76aeba74744148b15099254d918671Owen Lin int xPos = leftSpacing + (col * (mSpec.mCellWidth + spacing)); 702666ea1b28a76aeba74744148b15099254d918671Owen Lin int yTop = spacing + (row * mBlockHeight); 703666ea1b28a76aeba74744148b15099254d918671Owen Lin 704666ea1b28a76aeba74744148b15099254d918671Owen Lin int type = OUTLINE_SELECTED; 705666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mCurrentPressState != 0) { 706666ea1b28a76aeba74744148b15099254d918671Owen Lin type = OUTLINE_PRESSED; 707666ea1b28a76aeba74744148b15099254d918671Owen Lin } 708666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.drawBitmap(mOutline[type], xPos, yTop, null); 709666ea1b28a76aeba74744148b15099254d918671Owen Lin } 710666ea1b28a76aeba74744148b15099254d918671Owen Lin} 711666ea1b28a76aeba74744148b15099254d918671Owen Lin 712666ea1b28a76aeba74744148b15099254d918671Owen Linclass ImageBlockManager { 713666ea1b28a76aeba74744148b15099254d918671Owen Lin @SuppressWarnings("unused") 714666ea1b28a76aeba74744148b15099254d918671Owen Lin private static final String TAG = "ImageBlockManager"; 715666ea1b28a76aeba74744148b15099254d918671Owen Lin 716666ea1b28a76aeba74744148b15099254d918671Owen Lin // Number of rows we want to cache. 717666ea1b28a76aeba74744148b15099254d918671Owen Lin // Assume there are 6 rows per page, this caches 5 pages. 718666ea1b28a76aeba74744148b15099254d918671Owen Lin private static final int CACHE_ROWS = 30; 719666ea1b28a76aeba74744148b15099254d918671Owen Lin 720666ea1b28a76aeba74744148b15099254d918671Owen Lin // mCache maps from row number to the ImageBlock. 721666ea1b28a76aeba74744148b15099254d918671Owen Lin private final HashMap<Integer, ImageBlock> mCache; 722666ea1b28a76aeba74744148b15099254d918671Owen Lin 723666ea1b28a76aeba74744148b15099254d918671Owen Lin // These are parameters set in the constructor. 724666ea1b28a76aeba74744148b15099254d918671Owen Lin private final Handler mHandler; 725666ea1b28a76aeba74744148b15099254d918671Owen Lin private final Runnable mRedrawCallback; // Called after a row is loaded, 726666ea1b28a76aeba74744148b15099254d918671Owen Lin // so GridViewSpecial can draw 727666ea1b28a76aeba74744148b15099254d918671Owen Lin // again using the new images. 728666ea1b28a76aeba74744148b15099254d918671Owen Lin private final IImageList mImageList; 729666ea1b28a76aeba74744148b15099254d918671Owen Lin private final ImageLoader mLoader; 730666ea1b28a76aeba74744148b15099254d918671Owen Lin private final GridViewSpecial.DrawAdapter mDrawAdapter; 731666ea1b28a76aeba74744148b15099254d918671Owen Lin private final GridViewSpecial.LayoutSpec mSpec; 732666ea1b28a76aeba74744148b15099254d918671Owen Lin private final int mColumns; // Columns per row. 733666ea1b28a76aeba74744148b15099254d918671Owen Lin private final int mBlockWidth; // The width of an ImageBlock. 734666ea1b28a76aeba74744148b15099254d918671Owen Lin private final Bitmap mOutline; // The outline bitmap put on top of each 735666ea1b28a76aeba74744148b15099254d918671Owen Lin // image. 736666ea1b28a76aeba74744148b15099254d918671Owen Lin private final int mCount; // Cache mImageList.getCount(). 737666ea1b28a76aeba74744148b15099254d918671Owen Lin private final int mRows; // Cache (mCount + mColumns - 1) / mColumns 738666ea1b28a76aeba74744148b15099254d918671Owen Lin private final int mBlockHeight; // The height of an ImageBlock. 739666ea1b28a76aeba74744148b15099254d918671Owen Lin 740666ea1b28a76aeba74744148b15099254d918671Owen Lin // Visible row range: [mStartRow, mEndRow). Set by setVisibleRows(). 741666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mStartRow = 0; 742666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mEndRow = 0; 743666ea1b28a76aeba74744148b15099254d918671Owen Lin 744666ea1b28a76aeba74744148b15099254d918671Owen Lin ImageBlockManager(Handler handler, Runnable redrawCallback, 745666ea1b28a76aeba74744148b15099254d918671Owen Lin IImageList imageList, ImageLoader loader, 746666ea1b28a76aeba74744148b15099254d918671Owen Lin GridViewSpecial.DrawAdapter adapter, 747666ea1b28a76aeba74744148b15099254d918671Owen Lin GridViewSpecial.LayoutSpec spec, 748666ea1b28a76aeba74744148b15099254d918671Owen Lin int columns, int blockWidth, Bitmap outline) { 749666ea1b28a76aeba74744148b15099254d918671Owen Lin mHandler = handler; 750666ea1b28a76aeba74744148b15099254d918671Owen Lin mRedrawCallback = redrawCallback; 751666ea1b28a76aeba74744148b15099254d918671Owen Lin mImageList = imageList; 752666ea1b28a76aeba74744148b15099254d918671Owen Lin mLoader = loader; 753666ea1b28a76aeba74744148b15099254d918671Owen Lin mDrawAdapter = adapter; 754666ea1b28a76aeba74744148b15099254d918671Owen Lin mSpec = spec; 755666ea1b28a76aeba74744148b15099254d918671Owen Lin mColumns = columns; 756666ea1b28a76aeba74744148b15099254d918671Owen Lin mBlockWidth = blockWidth; 757666ea1b28a76aeba74744148b15099254d918671Owen Lin mOutline = outline; 758666ea1b28a76aeba74744148b15099254d918671Owen Lin mBlockHeight = mSpec.mCellSpacing + mSpec.mCellHeight; 759666ea1b28a76aeba74744148b15099254d918671Owen Lin mCount = imageList.getCount(); 760666ea1b28a76aeba74744148b15099254d918671Owen Lin mRows = (mCount + mColumns - 1) / mColumns; 761666ea1b28a76aeba74744148b15099254d918671Owen Lin mCache = new HashMap<Integer, ImageBlock>(); 762666ea1b28a76aeba74744148b15099254d918671Owen Lin mPendingRequest = 0; 763666ea1b28a76aeba74744148b15099254d918671Owen Lin initGraphics(); 764666ea1b28a76aeba74744148b15099254d918671Owen Lin } 765666ea1b28a76aeba74744148b15099254d918671Owen Lin 766666ea1b28a76aeba74744148b15099254d918671Owen Lin // Set the window of visible rows. Once set we will start to load them as 767666ea1b28a76aeba74744148b15099254d918671Owen Lin // soon as possible (if they are not already in cache). 768666ea1b28a76aeba74744148b15099254d918671Owen Lin public void setVisibleRows(int startRow, int endRow) { 769666ea1b28a76aeba74744148b15099254d918671Owen Lin if (startRow != mStartRow || endRow != mEndRow) { 770666ea1b28a76aeba74744148b15099254d918671Owen Lin mStartRow = startRow; 771666ea1b28a76aeba74744148b15099254d918671Owen Lin mEndRow = endRow; 772666ea1b28a76aeba74744148b15099254d918671Owen Lin startLoading(); 773666ea1b28a76aeba74744148b15099254d918671Owen Lin } 774666ea1b28a76aeba74744148b15099254d918671Owen Lin } 775666ea1b28a76aeba74744148b15099254d918671Owen Lin 776666ea1b28a76aeba74744148b15099254d918671Owen Lin int mPendingRequest; // Number of pending requests (sent to ImageLoader). 777666ea1b28a76aeba74744148b15099254d918671Owen Lin // We want to keep enough requests in ImageLoader's queue, but not too 778666ea1b28a76aeba74744148b15099254d918671Owen Lin // many. 779666ea1b28a76aeba74744148b15099254d918671Owen Lin static final int REQUESTS_LOW = 3; 780666ea1b28a76aeba74744148b15099254d918671Owen Lin static final int REQUESTS_HIGH = 6; 781666ea1b28a76aeba74744148b15099254d918671Owen Lin 782666ea1b28a76aeba74744148b15099254d918671Owen Lin // After clear requests currently in queue, start loading the thumbnails. 783666ea1b28a76aeba74744148b15099254d918671Owen Lin // We need to clear the queue first because the proper order of loading 784666ea1b28a76aeba74744148b15099254d918671Owen Lin // may have changed (because the visible region changed, or some images 785666ea1b28a76aeba74744148b15099254d918671Owen Lin // have been invalidated). 786666ea1b28a76aeba74744148b15099254d918671Owen Lin private void startLoading() { 787666ea1b28a76aeba74744148b15099254d918671Owen Lin clearLoaderQueue(); 788666ea1b28a76aeba74744148b15099254d918671Owen Lin continueLoading(); 789666ea1b28a76aeba74744148b15099254d918671Owen Lin } 790666ea1b28a76aeba74744148b15099254d918671Owen Lin 791666ea1b28a76aeba74744148b15099254d918671Owen Lin private void clearLoaderQueue() { 792666ea1b28a76aeba74744148b15099254d918671Owen Lin int[] tags = mLoader.clearQueue(); 793666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int pos : tags) { 794666ea1b28a76aeba74744148b15099254d918671Owen Lin int row = pos / mColumns; 795666ea1b28a76aeba74744148b15099254d918671Owen Lin int col = pos - row * mColumns; 796666ea1b28a76aeba74744148b15099254d918671Owen Lin ImageBlock blk = mCache.get(row); 797666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(blk != null); // We won't reuse the block if it has pending 798666ea1b28a76aeba74744148b15099254d918671Owen Lin // requests. See getEmptyBlock(). 799666ea1b28a76aeba74744148b15099254d918671Owen Lin blk.cancelRequest(col); 800666ea1b28a76aeba74744148b15099254d918671Owen Lin } 801666ea1b28a76aeba74744148b15099254d918671Owen Lin } 802666ea1b28a76aeba74744148b15099254d918671Owen Lin 803666ea1b28a76aeba74744148b15099254d918671Owen Lin // Scan the cache and send requests to ImageLoader if needed. 804666ea1b28a76aeba74744148b15099254d918671Owen Lin private void continueLoading() { 805666ea1b28a76aeba74744148b15099254d918671Owen Lin // Check if we still have enough requests in the queue. 806666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mPendingRequest >= REQUESTS_LOW) return; 807666ea1b28a76aeba74744148b15099254d918671Owen Lin 808666ea1b28a76aeba74744148b15099254d918671Owen Lin // Scan the visible rows. 809666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int i = mStartRow; i < mEndRow; i++) { 810666ea1b28a76aeba74744148b15099254d918671Owen Lin if (scanOne(i)) return; 811666ea1b28a76aeba74744148b15099254d918671Owen Lin } 812666ea1b28a76aeba74744148b15099254d918671Owen Lin 813666ea1b28a76aeba74744148b15099254d918671Owen Lin int range = (CACHE_ROWS - (mEndRow - mStartRow)) / 2; 814666ea1b28a76aeba74744148b15099254d918671Owen Lin // Scan other rows. 815666ea1b28a76aeba74744148b15099254d918671Owen Lin // d is the distance between the row and visible region. 816666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int d = 1; d <= range; d++) { 817666ea1b28a76aeba74744148b15099254d918671Owen Lin int after = mEndRow - 1 + d; 818666ea1b28a76aeba74744148b15099254d918671Owen Lin int before = mStartRow - d; 819666ea1b28a76aeba74744148b15099254d918671Owen Lin if (after >= mRows && before < 0) { 820666ea1b28a76aeba74744148b15099254d918671Owen Lin break; // Nothing more the scan. 821666ea1b28a76aeba74744148b15099254d918671Owen Lin } 822666ea1b28a76aeba74744148b15099254d918671Owen Lin if (after < mRows && scanOne(after)) return; 823666ea1b28a76aeba74744148b15099254d918671Owen Lin if (before >= 0 && scanOne(before)) return; 824666ea1b28a76aeba74744148b15099254d918671Owen Lin } 825666ea1b28a76aeba74744148b15099254d918671Owen Lin } 826666ea1b28a76aeba74744148b15099254d918671Owen Lin 827666ea1b28a76aeba74744148b15099254d918671Owen Lin // Returns true if we can stop scanning. 828666ea1b28a76aeba74744148b15099254d918671Owen Lin private boolean scanOne(int i) { 829666ea1b28a76aeba74744148b15099254d918671Owen Lin mPendingRequest += tryToLoad(i); 830666ea1b28a76aeba74744148b15099254d918671Owen Lin return mPendingRequest >= REQUESTS_HIGH; 831666ea1b28a76aeba74744148b15099254d918671Owen Lin } 832666ea1b28a76aeba74744148b15099254d918671Owen Lin 833666ea1b28a76aeba74744148b15099254d918671Owen Lin // Returns number of requests we issued for this row. 834666ea1b28a76aeba74744148b15099254d918671Owen Lin private int tryToLoad(int row) { 835666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(row >= 0 && row < mRows); 836666ea1b28a76aeba74744148b15099254d918671Owen Lin ImageBlock blk = mCache.get(row); 837666ea1b28a76aeba74744148b15099254d918671Owen Lin if (blk == null) { 838666ea1b28a76aeba74744148b15099254d918671Owen Lin // Find an empty block 839666ea1b28a76aeba74744148b15099254d918671Owen Lin blk = getEmptyBlock(); 840666ea1b28a76aeba74744148b15099254d918671Owen Lin blk.setRow(row); 841666ea1b28a76aeba74744148b15099254d918671Owen Lin blk.invalidate(); 842666ea1b28a76aeba74744148b15099254d918671Owen Lin mCache.put(row, blk); 843666ea1b28a76aeba74744148b15099254d918671Owen Lin } 844666ea1b28a76aeba74744148b15099254d918671Owen Lin return blk.loadImages(); 845666ea1b28a76aeba74744148b15099254d918671Owen Lin } 846666ea1b28a76aeba74744148b15099254d918671Owen Lin 847666ea1b28a76aeba74744148b15099254d918671Owen Lin // Get an empty block for the cache. 848666ea1b28a76aeba74744148b15099254d918671Owen Lin private ImageBlock getEmptyBlock() { 849666ea1b28a76aeba74744148b15099254d918671Owen Lin // See if we can allocate a new block. 850666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mCache.size() < CACHE_ROWS) { 851666ea1b28a76aeba74744148b15099254d918671Owen Lin return new ImageBlock(); 852666ea1b28a76aeba74744148b15099254d918671Owen Lin } 853666ea1b28a76aeba74744148b15099254d918671Owen Lin // Reclaim the old block with largest distance from the visible region. 854666ea1b28a76aeba74744148b15099254d918671Owen Lin int bestDistance = -1; 855666ea1b28a76aeba74744148b15099254d918671Owen Lin int bestIndex = -1; 856666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int index : mCache.keySet()) { 857666ea1b28a76aeba74744148b15099254d918671Owen Lin // Make sure we don't reclaim a block which still has pending 858666ea1b28a76aeba74744148b15099254d918671Owen Lin // request. 859666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mCache.get(index).hasPendingRequests()) { 860666ea1b28a76aeba74744148b15099254d918671Owen Lin continue; 861666ea1b28a76aeba74744148b15099254d918671Owen Lin } 862666ea1b28a76aeba74744148b15099254d918671Owen Lin int dist = 0; 863666ea1b28a76aeba74744148b15099254d918671Owen Lin if (index >= mEndRow) { 864666ea1b28a76aeba74744148b15099254d918671Owen Lin dist = index - mEndRow + 1; 865666ea1b28a76aeba74744148b15099254d918671Owen Lin } else if (index < mStartRow) { 866666ea1b28a76aeba74744148b15099254d918671Owen Lin dist = mStartRow - index; 867666ea1b28a76aeba74744148b15099254d918671Owen Lin } else { 868666ea1b28a76aeba74744148b15099254d918671Owen Lin // Inside the visible region. 869666ea1b28a76aeba74744148b15099254d918671Owen Lin continue; 870666ea1b28a76aeba74744148b15099254d918671Owen Lin } 871666ea1b28a76aeba74744148b15099254d918671Owen Lin if (dist > bestDistance) { 872666ea1b28a76aeba74744148b15099254d918671Owen Lin bestDistance = dist; 873666ea1b28a76aeba74744148b15099254d918671Owen Lin bestIndex = index; 874666ea1b28a76aeba74744148b15099254d918671Owen Lin } 875666ea1b28a76aeba74744148b15099254d918671Owen Lin } 876666ea1b28a76aeba74744148b15099254d918671Owen Lin return mCache.remove(bestIndex); 877666ea1b28a76aeba74744148b15099254d918671Owen Lin } 878666ea1b28a76aeba74744148b15099254d918671Owen Lin 879666ea1b28a76aeba74744148b15099254d918671Owen Lin public void invalidateImage(int index) { 880666ea1b28a76aeba74744148b15099254d918671Owen Lin int row = index / mColumns; 881666ea1b28a76aeba74744148b15099254d918671Owen Lin int col = index - (row * mColumns); 882666ea1b28a76aeba74744148b15099254d918671Owen Lin ImageBlock blk = mCache.get(row); 883666ea1b28a76aeba74744148b15099254d918671Owen Lin if (blk == null) return; 884666ea1b28a76aeba74744148b15099254d918671Owen Lin if ((blk.mCompletedMask & (1 << col)) != 0) { 885666ea1b28a76aeba74744148b15099254d918671Owen Lin blk.mCompletedMask &= ~(1 << col); 886666ea1b28a76aeba74744148b15099254d918671Owen Lin } 887666ea1b28a76aeba74744148b15099254d918671Owen Lin startLoading(); 888666ea1b28a76aeba74744148b15099254d918671Owen Lin } 889666ea1b28a76aeba74744148b15099254d918671Owen Lin 890666ea1b28a76aeba74744148b15099254d918671Owen Lin // After calling recycle(), the instance should not be used anymore. 891666ea1b28a76aeba74744148b15099254d918671Owen Lin public void recycle() { 892666ea1b28a76aeba74744148b15099254d918671Owen Lin for (ImageBlock blk : mCache.values()) { 893666ea1b28a76aeba74744148b15099254d918671Owen Lin blk.recycle(); 894666ea1b28a76aeba74744148b15099254d918671Owen Lin } 895666ea1b28a76aeba74744148b15099254d918671Owen Lin mCache.clear(); 896666ea1b28a76aeba74744148b15099254d918671Owen Lin mEmptyBitmap.recycle(); 897666ea1b28a76aeba74744148b15099254d918671Owen Lin } 898666ea1b28a76aeba74744148b15099254d918671Owen Lin 899666ea1b28a76aeba74744148b15099254d918671Owen Lin // Draw the images to the given canvas. 900666ea1b28a76aeba74744148b15099254d918671Owen Lin public void doDraw(Canvas canvas, int thisWidth, int thisHeight, 901666ea1b28a76aeba74744148b15099254d918671Owen Lin int scrollPos) { 902666ea1b28a76aeba74744148b15099254d918671Owen Lin final int height = mBlockHeight; 903666ea1b28a76aeba74744148b15099254d918671Owen Lin 904666ea1b28a76aeba74744148b15099254d918671Owen Lin // Note that currentBlock could be negative. 905666ea1b28a76aeba74744148b15099254d918671Owen Lin int currentBlock = (scrollPos < 0) 906666ea1b28a76aeba74744148b15099254d918671Owen Lin ? ((scrollPos - height + 1) / height) 907666ea1b28a76aeba74744148b15099254d918671Owen Lin : (scrollPos / height); 908666ea1b28a76aeba74744148b15099254d918671Owen Lin 909666ea1b28a76aeba74744148b15099254d918671Owen Lin while (true) { 910666ea1b28a76aeba74744148b15099254d918671Owen Lin final int yPos = currentBlock * height; 911666ea1b28a76aeba74744148b15099254d918671Owen Lin if (yPos >= scrollPos + thisHeight) { 912666ea1b28a76aeba74744148b15099254d918671Owen Lin break; 913666ea1b28a76aeba74744148b15099254d918671Owen Lin } 914666ea1b28a76aeba74744148b15099254d918671Owen Lin 915666ea1b28a76aeba74744148b15099254d918671Owen Lin ImageBlock blk = mCache.get(currentBlock); 916666ea1b28a76aeba74744148b15099254d918671Owen Lin if (blk != null) { 917666ea1b28a76aeba74744148b15099254d918671Owen Lin blk.doDraw(canvas, 0, yPos); 918666ea1b28a76aeba74744148b15099254d918671Owen Lin } else { 919666ea1b28a76aeba74744148b15099254d918671Owen Lin drawEmptyBlock(canvas, 0, yPos, currentBlock); 920666ea1b28a76aeba74744148b15099254d918671Owen Lin } 921666ea1b28a76aeba74744148b15099254d918671Owen Lin 922666ea1b28a76aeba74744148b15099254d918671Owen Lin currentBlock += 1; 923666ea1b28a76aeba74744148b15099254d918671Owen Lin } 924666ea1b28a76aeba74744148b15099254d918671Owen Lin } 925666ea1b28a76aeba74744148b15099254d918671Owen Lin 926666ea1b28a76aeba74744148b15099254d918671Owen Lin // Return number of columns in the given row. (This could be less than 927666ea1b28a76aeba74744148b15099254d918671Owen Lin // mColumns for the last row). 928666ea1b28a76aeba74744148b15099254d918671Owen Lin private int numColumns(int row) { 929666ea1b28a76aeba74744148b15099254d918671Owen Lin return Math.min(mColumns, mCount - row * mColumns); 930666ea1b28a76aeba74744148b15099254d918671Owen Lin } 931666ea1b28a76aeba74744148b15099254d918671Owen Lin 932666ea1b28a76aeba74744148b15099254d918671Owen Lin // Draw a block which has not been loaded. 933666ea1b28a76aeba74744148b15099254d918671Owen Lin private void drawEmptyBlock(Canvas canvas, int xPos, int yPos, int row) { 934666ea1b28a76aeba74744148b15099254d918671Owen Lin // Draw the background. 935666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.drawRect(xPos, yPos, xPos + mBlockWidth, yPos + mBlockHeight, 936666ea1b28a76aeba74744148b15099254d918671Owen Lin mBackgroundPaint); 937666ea1b28a76aeba74744148b15099254d918671Owen Lin 938666ea1b28a76aeba74744148b15099254d918671Owen Lin // Draw the empty images. 939666ea1b28a76aeba74744148b15099254d918671Owen Lin int x = xPos + mSpec.mLeftEdgePadding; 940666ea1b28a76aeba74744148b15099254d918671Owen Lin int y = yPos + mSpec.mCellSpacing; 941666ea1b28a76aeba74744148b15099254d918671Owen Lin int cols = numColumns(row); 942666ea1b28a76aeba74744148b15099254d918671Owen Lin 943666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int i = 0; i < cols; i++) { 944666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.drawBitmap(mEmptyBitmap, x, y, null); 945666ea1b28a76aeba74744148b15099254d918671Owen Lin x += (mSpec.mCellWidth + mSpec.mCellSpacing); 946666ea1b28a76aeba74744148b15099254d918671Owen Lin } 947666ea1b28a76aeba74744148b15099254d918671Owen Lin } 948666ea1b28a76aeba74744148b15099254d918671Owen Lin 949666ea1b28a76aeba74744148b15099254d918671Owen Lin // mEmptyBitmap is what we draw if we the wanted block hasn't been loaded. 950666ea1b28a76aeba74744148b15099254d918671Owen Lin // (If the user scrolls too fast). It is a gray image with normal outline. 951666ea1b28a76aeba74744148b15099254d918671Owen Lin // mBackgroundPaint is used to draw the (black) background outside 952666ea1b28a76aeba74744148b15099254d918671Owen Lin // mEmptyBitmap. 953666ea1b28a76aeba74744148b15099254d918671Owen Lin Paint mBackgroundPaint; 954666ea1b28a76aeba74744148b15099254d918671Owen Lin private Bitmap mEmptyBitmap; 955666ea1b28a76aeba74744148b15099254d918671Owen Lin 956666ea1b28a76aeba74744148b15099254d918671Owen Lin private void initGraphics() { 957666ea1b28a76aeba74744148b15099254d918671Owen Lin mBackgroundPaint = new Paint(); 958666ea1b28a76aeba74744148b15099254d918671Owen Lin mBackgroundPaint.setStyle(Paint.Style.FILL); 959666ea1b28a76aeba74744148b15099254d918671Owen Lin mBackgroundPaint.setColor(0xFF000000); // black 960666ea1b28a76aeba74744148b15099254d918671Owen Lin mEmptyBitmap = Bitmap.createBitmap(mSpec.mCellWidth, mSpec.mCellHeight, 961666ea1b28a76aeba74744148b15099254d918671Owen Lin Bitmap.Config.RGB_565); 962666ea1b28a76aeba74744148b15099254d918671Owen Lin Canvas canvas = new Canvas(mEmptyBitmap); 963666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.drawRGB(0xDD, 0xDD, 0xDD); 964666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.drawBitmap(mOutline, 0, 0, null); 965666ea1b28a76aeba74744148b15099254d918671Owen Lin } 966666ea1b28a76aeba74744148b15099254d918671Owen Lin 967666ea1b28a76aeba74744148b15099254d918671Owen Lin // ImageBlock stores bitmap for one row. The loaded thumbnail images are 968666ea1b28a76aeba74744148b15099254d918671Owen Lin // drawn to mBitmap. mBitmap is later used in onDraw() of GridViewSpecial. 969666ea1b28a76aeba74744148b15099254d918671Owen Lin private class ImageBlock { 970666ea1b28a76aeba74744148b15099254d918671Owen Lin private Bitmap mBitmap; 971666ea1b28a76aeba74744148b15099254d918671Owen Lin private final Canvas mCanvas; 972666ea1b28a76aeba74744148b15099254d918671Owen Lin 973666ea1b28a76aeba74744148b15099254d918671Owen Lin // Columns which have been requested to the loader 974666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mRequestedMask; 975666ea1b28a76aeba74744148b15099254d918671Owen Lin 976666ea1b28a76aeba74744148b15099254d918671Owen Lin // Columns which have been completed from the loader 977666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mCompletedMask; 978666ea1b28a76aeba74744148b15099254d918671Owen Lin 979666ea1b28a76aeba74744148b15099254d918671Owen Lin // The row number this block represents. 980666ea1b28a76aeba74744148b15099254d918671Owen Lin private int mRow; 981666ea1b28a76aeba74744148b15099254d918671Owen Lin 982666ea1b28a76aeba74744148b15099254d918671Owen Lin public ImageBlock() { 983666ea1b28a76aeba74744148b15099254d918671Owen Lin mBitmap = Bitmap.createBitmap(mBlockWidth, mBlockHeight, 984666ea1b28a76aeba74744148b15099254d918671Owen Lin Bitmap.Config.RGB_565); 985666ea1b28a76aeba74744148b15099254d918671Owen Lin mCanvas = new Canvas(mBitmap); 986666ea1b28a76aeba74744148b15099254d918671Owen Lin mRow = -1; 987666ea1b28a76aeba74744148b15099254d918671Owen Lin } 988666ea1b28a76aeba74744148b15099254d918671Owen Lin 989666ea1b28a76aeba74744148b15099254d918671Owen Lin public void setRow(int row) { 990666ea1b28a76aeba74744148b15099254d918671Owen Lin mRow = row; 991666ea1b28a76aeba74744148b15099254d918671Owen Lin } 992666ea1b28a76aeba74744148b15099254d918671Owen Lin 993666ea1b28a76aeba74744148b15099254d918671Owen Lin public void invalidate() { 994666ea1b28a76aeba74744148b15099254d918671Owen Lin // We do not change mRequestedMask or do cancelAllRequests() 995666ea1b28a76aeba74744148b15099254d918671Owen Lin // because the data coming from pending requests are valid. (We only 996666ea1b28a76aeba74744148b15099254d918671Owen Lin // invalidate data which has been drawn to the bitmap). 997666ea1b28a76aeba74744148b15099254d918671Owen Lin mCompletedMask = 0; 998666ea1b28a76aeba74744148b15099254d918671Owen Lin } 999666ea1b28a76aeba74744148b15099254d918671Owen Lin 1000666ea1b28a76aeba74744148b15099254d918671Owen Lin // After recycle, the ImageBlock instance should not be accessed. 1001666ea1b28a76aeba74744148b15099254d918671Owen Lin public void recycle() { 1002666ea1b28a76aeba74744148b15099254d918671Owen Lin cancelAllRequests(); 1003666ea1b28a76aeba74744148b15099254d918671Owen Lin mBitmap.recycle(); 1004666ea1b28a76aeba74744148b15099254d918671Owen Lin mBitmap = null; 1005666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1006666ea1b28a76aeba74744148b15099254d918671Owen Lin 1007666ea1b28a76aeba74744148b15099254d918671Owen Lin private boolean isVisible() { 1008666ea1b28a76aeba74744148b15099254d918671Owen Lin return mRow >= mStartRow && mRow < mEndRow; 1009666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1010666ea1b28a76aeba74744148b15099254d918671Owen Lin 1011666ea1b28a76aeba74744148b15099254d918671Owen Lin // Returns number of requests submitted to ImageLoader. 1012666ea1b28a76aeba74744148b15099254d918671Owen Lin public int loadImages() { 1013666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert(mRow != -1); 1014666ea1b28a76aeba74744148b15099254d918671Owen Lin 1015666ea1b28a76aeba74744148b15099254d918671Owen Lin int columns = numColumns(mRow); 1016666ea1b28a76aeba74744148b15099254d918671Owen Lin 1017666ea1b28a76aeba74744148b15099254d918671Owen Lin // Calculate what we need. 1018666ea1b28a76aeba74744148b15099254d918671Owen Lin int needMask = ((1 << columns) - 1) 1019666ea1b28a76aeba74744148b15099254d918671Owen Lin & ~(mCompletedMask | mRequestedMask); 1020666ea1b28a76aeba74744148b15099254d918671Owen Lin 1021666ea1b28a76aeba74744148b15099254d918671Owen Lin if (needMask == 0) { 1022666ea1b28a76aeba74744148b15099254d918671Owen Lin return 0; 1023666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1024666ea1b28a76aeba74744148b15099254d918671Owen Lin 1025666ea1b28a76aeba74744148b15099254d918671Owen Lin int retVal = 0; 1026666ea1b28a76aeba74744148b15099254d918671Owen Lin int base = mRow * mColumns; 1027666ea1b28a76aeba74744148b15099254d918671Owen Lin 1028666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int col = 0; col < columns; col++) { 1029666ea1b28a76aeba74744148b15099254d918671Owen Lin if ((needMask & (1 << col)) == 0) { 1030666ea1b28a76aeba74744148b15099254d918671Owen Lin continue; 1031666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1032666ea1b28a76aeba74744148b15099254d918671Owen Lin 1033666ea1b28a76aeba74744148b15099254d918671Owen Lin int pos = base + col; 1034666ea1b28a76aeba74744148b15099254d918671Owen Lin 1035666ea1b28a76aeba74744148b15099254d918671Owen Lin final IImage image = mImageList.getImageAt(pos); 1036666ea1b28a76aeba74744148b15099254d918671Owen Lin if (image != null) { 1037666ea1b28a76aeba74744148b15099254d918671Owen Lin // This callback is passed to ImageLoader. It will invoke 1038666ea1b28a76aeba74744148b15099254d918671Owen Lin // loadImageDone() in the main thread. We limit the callback 1039666ea1b28a76aeba74744148b15099254d918671Owen Lin // thread to be in this very short function. All other 1040666ea1b28a76aeba74744148b15099254d918671Owen Lin // processing is done in the main thread. 1041666ea1b28a76aeba74744148b15099254d918671Owen Lin final int colFinal = col; 1042666ea1b28a76aeba74744148b15099254d918671Owen Lin ImageLoader.LoadedCallback cb = 1043666ea1b28a76aeba74744148b15099254d918671Owen Lin new ImageLoader.LoadedCallback() { 1044666ea1b28a76aeba74744148b15099254d918671Owen Lin public void run(final Bitmap b) { 1045666ea1b28a76aeba74744148b15099254d918671Owen Lin mHandler.post(new Runnable() { 1046666ea1b28a76aeba74744148b15099254d918671Owen Lin public void run() { 1047666ea1b28a76aeba74744148b15099254d918671Owen Lin loadImageDone(image, b, 1048666ea1b28a76aeba74744148b15099254d918671Owen Lin colFinal); 1049666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1050666ea1b28a76aeba74744148b15099254d918671Owen Lin }); 1051666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1052666ea1b28a76aeba74744148b15099254d918671Owen Lin }; 1053666ea1b28a76aeba74744148b15099254d918671Owen Lin // Load Image 1054666ea1b28a76aeba74744148b15099254d918671Owen Lin mLoader.getBitmap(image, cb, pos); 1055666ea1b28a76aeba74744148b15099254d918671Owen Lin mRequestedMask |= (1 << col); 1056666ea1b28a76aeba74744148b15099254d918671Owen Lin retVal += 1; 1057666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1058666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1059666ea1b28a76aeba74744148b15099254d918671Owen Lin 1060666ea1b28a76aeba74744148b15099254d918671Owen Lin return retVal; 1061666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1062666ea1b28a76aeba74744148b15099254d918671Owen Lin 1063666ea1b28a76aeba74744148b15099254d918671Owen Lin // Whether this block has pending requests. 1064666ea1b28a76aeba74744148b15099254d918671Owen Lin public boolean hasPendingRequests() { 1065666ea1b28a76aeba74744148b15099254d918671Owen Lin return mRequestedMask != 0; 1066666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1067666ea1b28a76aeba74744148b15099254d918671Owen Lin 1068666ea1b28a76aeba74744148b15099254d918671Owen Lin // Called when an image is loaded. 1069666ea1b28a76aeba74744148b15099254d918671Owen Lin private void loadImageDone(IImage image, Bitmap b, 1070666ea1b28a76aeba74744148b15099254d918671Owen Lin int col) { 1071666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mBitmap == null) return; // This block has been recycled. 1072666ea1b28a76aeba74744148b15099254d918671Owen Lin 1073666ea1b28a76aeba74744148b15099254d918671Owen Lin int spacing = mSpec.mCellSpacing; 1074666ea1b28a76aeba74744148b15099254d918671Owen Lin int leftSpacing = mSpec.mLeftEdgePadding; 1075666ea1b28a76aeba74744148b15099254d918671Owen Lin final int yPos = spacing; 1076666ea1b28a76aeba74744148b15099254d918671Owen Lin final int xPos = leftSpacing 1077666ea1b28a76aeba74744148b15099254d918671Owen Lin + (col * (mSpec.mCellWidth + spacing)); 1078666ea1b28a76aeba74744148b15099254d918671Owen Lin 1079666ea1b28a76aeba74744148b15099254d918671Owen Lin drawBitmap(image, b, xPos, yPos); 1080666ea1b28a76aeba74744148b15099254d918671Owen Lin 1081666ea1b28a76aeba74744148b15099254d918671Owen Lin if (b != null) { 1082666ea1b28a76aeba74744148b15099254d918671Owen Lin b.recycle(); 1083666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1084666ea1b28a76aeba74744148b15099254d918671Owen Lin 1085666ea1b28a76aeba74744148b15099254d918671Owen Lin int mask = (1 << col); 1086666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert((mCompletedMask & mask) == 0); 1087666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert((mRequestedMask & mask) != 0); 1088666ea1b28a76aeba74744148b15099254d918671Owen Lin mRequestedMask &= ~mask; 1089666ea1b28a76aeba74744148b15099254d918671Owen Lin mCompletedMask |= mask; 1090666ea1b28a76aeba74744148b15099254d918671Owen Lin mPendingRequest--; 1091666ea1b28a76aeba74744148b15099254d918671Owen Lin 1092666ea1b28a76aeba74744148b15099254d918671Owen Lin if (isVisible()) { 1093666ea1b28a76aeba74744148b15099254d918671Owen Lin mRedrawCallback.run(); 1094666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1095666ea1b28a76aeba74744148b15099254d918671Owen Lin 1096666ea1b28a76aeba74744148b15099254d918671Owen Lin // Kick start next block loading. 1097666ea1b28a76aeba74744148b15099254d918671Owen Lin continueLoading(); 1098666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1099666ea1b28a76aeba74744148b15099254d918671Owen Lin 1100666ea1b28a76aeba74744148b15099254d918671Owen Lin // Draw the loaded bitmap to the block bitmap. 1101666ea1b28a76aeba74744148b15099254d918671Owen Lin private void drawBitmap( 1102666ea1b28a76aeba74744148b15099254d918671Owen Lin IImage image, Bitmap b, int xPos, int yPos) { 1103666ea1b28a76aeba74744148b15099254d918671Owen Lin mDrawAdapter.drawImage(mCanvas, image, b, xPos, yPos, 1104666ea1b28a76aeba74744148b15099254d918671Owen Lin mSpec.mCellWidth, mSpec.mCellHeight); 1105666ea1b28a76aeba74744148b15099254d918671Owen Lin mCanvas.drawBitmap(mOutline, xPos, yPos, null); 1106666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1107666ea1b28a76aeba74744148b15099254d918671Owen Lin 1108666ea1b28a76aeba74744148b15099254d918671Owen Lin // Draw the block bitmap to the specified canvas. 1109666ea1b28a76aeba74744148b15099254d918671Owen Lin public void doDraw(Canvas canvas, int xPos, int yPos) { 1110666ea1b28a76aeba74744148b15099254d918671Owen Lin int cols = numColumns(mRow); 1111666ea1b28a76aeba74744148b15099254d918671Owen Lin 1112666ea1b28a76aeba74744148b15099254d918671Owen Lin if (cols == mColumns) { 1113666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.drawBitmap(mBitmap, xPos, yPos, null); 1114666ea1b28a76aeba74744148b15099254d918671Owen Lin } else { 1115666ea1b28a76aeba74744148b15099254d918671Owen Lin 1116666ea1b28a76aeba74744148b15099254d918671Owen Lin // This must be the last row -- we draw only part of the block. 1117666ea1b28a76aeba74744148b15099254d918671Owen Lin // Draw the background. 1118666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.drawRect(xPos, yPos, xPos + mBlockWidth, 1119666ea1b28a76aeba74744148b15099254d918671Owen Lin yPos + mBlockHeight, mBackgroundPaint); 1120666ea1b28a76aeba74744148b15099254d918671Owen Lin // Draw part of the block. 1121666ea1b28a76aeba74744148b15099254d918671Owen Lin int w = mSpec.mLeftEdgePadding 1122666ea1b28a76aeba74744148b15099254d918671Owen Lin + cols * (mSpec.mCellWidth + mSpec.mCellSpacing); 1123666ea1b28a76aeba74744148b15099254d918671Owen Lin Rect srcRect = new Rect(0, 0, w, mBlockHeight); 1124666ea1b28a76aeba74744148b15099254d918671Owen Lin Rect dstRect = new Rect(srcRect); 1125666ea1b28a76aeba74744148b15099254d918671Owen Lin dstRect.offset(xPos, yPos); 1126666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.drawBitmap(mBitmap, srcRect, dstRect, null); 1127666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1128666ea1b28a76aeba74744148b15099254d918671Owen Lin 1129666ea1b28a76aeba74744148b15099254d918671Owen Lin // Draw the part which has not been loaded. 1130666ea1b28a76aeba74744148b15099254d918671Owen Lin int isEmpty = ((1 << cols) - 1) & ~mCompletedMask; 1131666ea1b28a76aeba74744148b15099254d918671Owen Lin 1132666ea1b28a76aeba74744148b15099254d918671Owen Lin if (isEmpty != 0) { 1133666ea1b28a76aeba74744148b15099254d918671Owen Lin int x = xPos + mSpec.mLeftEdgePadding; 1134666ea1b28a76aeba74744148b15099254d918671Owen Lin int y = yPos + mSpec.mCellSpacing; 1135666ea1b28a76aeba74744148b15099254d918671Owen Lin 1136666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int i = 0; i < cols; i++) { 1137666ea1b28a76aeba74744148b15099254d918671Owen Lin if ((isEmpty & (1 << i)) != 0) { 1138666ea1b28a76aeba74744148b15099254d918671Owen Lin canvas.drawBitmap(mEmptyBitmap, x, y, null); 1139666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1140666ea1b28a76aeba74744148b15099254d918671Owen Lin x += (mSpec.mCellWidth + mSpec.mCellSpacing); 1141666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1142666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1143666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1144666ea1b28a76aeba74744148b15099254d918671Owen Lin 1145666ea1b28a76aeba74744148b15099254d918671Owen Lin // Mark a request as cancelled. The request has already been removed 1146666ea1b28a76aeba74744148b15099254d918671Owen Lin // from the queue of ImageLoader, so we only need to mark the fact. 1147666ea1b28a76aeba74744148b15099254d918671Owen Lin public void cancelRequest(int col) { 1148666ea1b28a76aeba74744148b15099254d918671Owen Lin int mask = (1 << col); 1149666ea1b28a76aeba74744148b15099254d918671Owen Lin Assert((mRequestedMask & mask) != 0); 1150666ea1b28a76aeba74744148b15099254d918671Owen Lin mRequestedMask &= ~mask; 1151666ea1b28a76aeba74744148b15099254d918671Owen Lin mPendingRequest--; 1152666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1153666ea1b28a76aeba74744148b15099254d918671Owen Lin 1154666ea1b28a76aeba74744148b15099254d918671Owen Lin // Try to cancel all pending requests for this block. After this 1155666ea1b28a76aeba74744148b15099254d918671Owen Lin // completes there could still be requests not cancelled (because it is 1156666ea1b28a76aeba74744148b15099254d918671Owen Lin // already in progress). We deal with that situation by setting mBitmap 1157666ea1b28a76aeba74744148b15099254d918671Owen Lin // to null in recycle() and check this in loadImageDone(). 1158666ea1b28a76aeba74744148b15099254d918671Owen Lin private void cancelAllRequests() { 1159666ea1b28a76aeba74744148b15099254d918671Owen Lin for (int i = 0; i < mColumns; i++) { 1160666ea1b28a76aeba74744148b15099254d918671Owen Lin int mask = (1 << i); 1161666ea1b28a76aeba74744148b15099254d918671Owen Lin if ((mRequestedMask & mask) != 0) { 1162666ea1b28a76aeba74744148b15099254d918671Owen Lin int pos = (mRow * mColumns) + i; 1163666ea1b28a76aeba74744148b15099254d918671Owen Lin if (mLoader.cancel(mImageList.getImageAt(pos))) { 1164666ea1b28a76aeba74744148b15099254d918671Owen Lin mRequestedMask &= ~mask; 1165666ea1b28a76aeba74744148b15099254d918671Owen Lin mPendingRequest--; 1166666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1167666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1168666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1169666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1170666ea1b28a76aeba74744148b15099254d918671Owen Lin } 1171666ea1b28a76aeba74744148b15099254d918671Owen Lin} 1172