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
17package com.android.gallery3d.ui;
18
19import android.graphics.Rect;
20
21import com.android.gallery3d.app.GalleryActivity;
22import com.android.gallery3d.common.Utils;
23import com.android.gallery3d.data.MediaItem;
24import com.android.gallery3d.data.MediaSet;
25import com.android.gallery3d.ui.PositionRepository.Position;
26
27import java.util.Random;
28
29public 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