1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.gallery3d.ui; 18 19 import android.graphics.Rect; 20 21 import com.android.gallery3d.app.GalleryActivity; 22 import com.android.gallery3d.common.Utils; 23 import com.android.gallery3d.data.MediaItem; 24 import com.android.gallery3d.data.MediaSet; 25 import com.android.gallery3d.ui.PositionRepository.Position; 26 27 import java.util.Random; 28 29 public class AlbumSetView extends SlotView { 30 @SuppressWarnings("unused") 31 private static final String TAG = "AlbumSetView"; 32 private static final int CACHE_SIZE = 32; 33 private static final float PHOTO_DISTANCE = 35f; 34 35 private int mVisibleStart; 36 private int mVisibleEnd; 37 38 private final Random mRandom = new Random(); 39 private final long mSeed = mRandom.nextLong(); 40 41 private AlbumSetSlidingWindow mDataWindow; 42 private final GalleryActivity mActivity; 43 private final LabelSpec mLabelSpec; 44 45 private SelectionDrawer mSelectionDrawer; 46 47 public static interface Model { 48 public MediaItem[] getCoverItems(int index); 49 public MediaSet getMediaSet(int index); 50 public int size(); 51 public void setActiveWindow(int start, int end); 52 public void setModelListener(ModelListener listener); 53 } 54 55 public static interface ModelListener { 56 public void onWindowContentChanged(int index); 57 public void onSizeChanged(int size); 58 } 59 60 public static class AlbumSetItem { 61 public DisplayItem[] covers; 62 public DisplayItem labelItem; 63 public long setDataVersion; 64 } 65 66 public static class LabelSpec { 67 public int labelBackgroundHeight; 68 public int titleOffset; 69 public int countOffset; 70 public int titleFontSize; 71 public int countFontSize; 72 public int leftMargin; 73 public int iconSize; 74 } 75 76 public AlbumSetView(GalleryActivity activity, SelectionDrawer drawer, 77 SlotView.Spec slotViewSpec, LabelSpec labelSpec) { 78 super(activity.getAndroidContext()); 79 mActivity = activity; 80 setSelectionDrawer(drawer); 81 setSlotSpec(slotViewSpec); 82 mLabelSpec = labelSpec; 83 } 84 85 public void setSelectionDrawer(SelectionDrawer drawer) { 86 mSelectionDrawer = drawer; 87 if (mDataWindow != null) { 88 mDataWindow.setSelectionDrawer(drawer); 89 } 90 } 91 92 public void setModel(AlbumSetView.Model model) { 93 if (mDataWindow != null) { 94 mDataWindow.setListener(null); 95 setSlotCount(0); 96 mDataWindow = null; 97 } 98 if (model != null) { 99 mDataWindow = new AlbumSetSlidingWindow(mActivity, mLabelSpec, 100 mSelectionDrawer, model, CACHE_SIZE); 101 mDataWindow.setListener(new MyCacheListener()); 102 setSlotCount(mDataWindow.size()); 103 updateVisibleRange(getVisibleStart(), getVisibleEnd()); 104 } 105 } 106 107 private void putSlotContent(int slotIndex, AlbumSetItem entry) { 108 // Get displayItems from mItemsetMap or create them from MediaSet. 109 Utils.assertTrue(entry != null); 110 Rect rect = getSlotRect(slotIndex); 111 112 DisplayItem[] items = entry.covers; 113 mRandom.setSeed(slotIndex ^ mSeed); 114 115 int x = (rect.left + rect.right) / 2; 116 int y = (rect.top + rect.bottom) / 2; 117 118 Position basePosition = new Position(x, y, 0); 119 120 // Put the cover items in reverse order, so that the first item is on 121 // top of the rest. 122 Position position = new Position(x, y, 0f); 123 putDisplayItem(position, position, entry.labelItem); 124 125 for (int i = 0, n = items.length; i < n; ++i) { 126 DisplayItem item = items[i]; 127 float dx = 0; 128 float dy = 0; 129 float dz = 0f; 130 float theta = 0; 131 if (i != 0) { 132 dz = i * PHOTO_DISTANCE; 133 } 134 position = new Position(x + dx, y + dy, dz); 135 position.theta = theta; 136 putDisplayItem(position, basePosition, item); 137 } 138 139 } 140 141 private void freeSlotContent(int index, AlbumSetItem entry) { 142 if (entry == null) return; 143 for (DisplayItem item : entry.covers) { 144 removeDisplayItem(item); 145 } 146 removeDisplayItem(entry.labelItem); 147 } 148 149 public int size() { 150 return mDataWindow.size(); 151 } 152 153 @Override 154 public void onLayoutChanged(int width, int height) { 155 updateVisibleRange(0, 0); 156 updateVisibleRange(getVisibleStart(), getVisibleEnd()); 157 } 158 159 @Override 160 public void onScrollPositionChanged(int position) { 161 super.onScrollPositionChanged(position); 162 updateVisibleRange(getVisibleStart(), getVisibleEnd()); 163 } 164 165 private void updateVisibleRange(int start, int end) { 166 if (start == mVisibleStart && end == mVisibleEnd) { 167 // we need to set the mDataWindow active range in any case. 168 mDataWindow.setActiveWindow(start, end); 169 return; 170 } 171 if (start >= mVisibleEnd || mVisibleStart >= end) { 172 for (int i = mVisibleStart, n = mVisibleEnd; i < n; ++i) { 173 freeSlotContent(i, mDataWindow.get(i)); 174 } 175 mDataWindow.setActiveWindow(start, end); 176 for (int i = start; i < end; ++i) { 177 putSlotContent(i, mDataWindow.get(i)); 178 } 179 } else { 180 for (int i = mVisibleStart; i < start; ++i) { 181 freeSlotContent(i, mDataWindow.get(i)); 182 } 183 for (int i = end, n = mVisibleEnd; i < n; ++i) { 184 freeSlotContent(i, mDataWindow.get(i)); 185 } 186 mDataWindow.setActiveWindow(start, end); 187 for (int i = start, n = mVisibleStart; i < n; ++i) { 188 putSlotContent(i, mDataWindow.get(i)); 189 } 190 for (int i = mVisibleEnd; i < end; ++i) { 191 putSlotContent(i, mDataWindow.get(i)); 192 } 193 } 194 mVisibleStart = start; 195 mVisibleEnd = end; 196 197 invalidate(); 198 } 199 200 @Override 201 protected void render(GLCanvas canvas) { 202 mSelectionDrawer.prepareDrawing(); 203 super.render(canvas); 204 } 205 206 private class MyCacheListener implements AlbumSetSlidingWindow.Listener { 207 208 public void onSizeChanged(int size) { 209 if (setSlotCount(size)) { 210 // If the layout parameters are changed, we need reput all items. 211 // We keep the visible range at the same center but with size 0. 212 // So that we can: 213 // 1.) flush all visible items 214 // 2.) keep the cached data 215 int center = (getVisibleStart() + getVisibleEnd()) / 2; 216 updateVisibleRange(center, center); 217 } 218 updateVisibleRange(getVisibleStart(), getVisibleEnd()); 219 invalidate(); 220 } 221 222 public void onWindowContentChanged(int slot, AlbumSetItem old, AlbumSetItem update) { 223 freeSlotContent(slot, old); 224 putSlotContent(slot, update); 225 invalidate(); 226 } 227 228 public void onContentInvalidated() { 229 invalidate(); 230 } 231 } 232 233 public void pause() { 234 for (int i = mVisibleStart, n = mVisibleEnd; i < n; ++i) { 235 freeSlotContent(i, mDataWindow.get(i)); 236 } 237 mDataWindow.pause(); 238 } 239 240 public void resume() { 241 mDataWindow.resume(); 242 for (int i = mVisibleStart, n = mVisibleEnd; i < n; ++i) { 243 putSlotContent(i, mDataWindow.get(i)); 244 } 245 } 246 } 247