blob: 26f5c7fe7fd919ed0bff0fc43a4b8ebbbdf5b28b [file] [log] [blame]
Ravi Banuri7a41c372015-10-19 16:03:03 +05301/*
Ravi Banuric963ee82015-10-20 19:41:58 +05302 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
3 * Not a Contribution
4 *
Ravi Banuri7a41c372015-10-19 16:03:03 +05305 * Copyright (C) 2010 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
Ravi Banuri7a41c372015-10-19 16:03:03 +053019package com.android.gallery3d.ui;
20
21import android.graphics.Rect;
Ravi Banuri7a41c372015-10-19 16:03:03 +053022import android.text.TextUtils;
23import android.view.GestureDetector;
24import android.view.MotionEvent;
25import android.view.View;
26import android.view.animation.DecelerateInterpolator;
27
28import com.android.gallery3d.anim.Animation;
29import com.android.gallery3d.app.AbstractGalleryActivity;
Likai Ding711aee72016-05-18 13:47:56 +080030import com.android.gallery3d.common.ApiHelper.SystemProperties;
Ravi Banuri7a41c372015-10-19 16:03:03 +053031import com.android.gallery3d.common.Utils;
32import com.android.gallery3d.glrenderer.GLCanvas;
33
34import java.util.Locale;
35
Ravi Banuric963ee82015-10-20 19:41:58 +053036public class TimeLineSlotView extends GLView {
Ravi Banuri7a41c372015-10-19 16:03:03 +053037 @SuppressWarnings("unused")
Ravi Banuric963ee82015-10-20 19:41:58 +053038 private static final String TAG = "TimeLineSlotView";
Ravi Banuri7a41c372015-10-19 16:03:03 +053039
Ravi Banuric963ee82015-10-20 19:41:58 +053040 public static final int INDEX_NONE = -1;
41 private static final int mainKey = SystemProperties.getInt("qemu.hw.mainkeys", 1);
Ravi Banuri7a41c372015-10-19 16:03:03 +053042
43 public static final int RENDER_MORE_PASS = 1;
44 public static final int RENDER_MORE_FRAME = 2;
45
Ravi Banuric963ee82015-10-20 19:41:58 +053046 private int mWidth = 0;
47
Ravi Banuri7a41c372015-10-19 16:03:03 +053048 public interface Listener {
49 public void onDown(int index);
50 public void onUp(boolean followedByLongPress);
Ravi Banuric963ee82015-10-20 19:41:58 +053051 public void onSingleTapUp(int index, boolean isTitle);
52 public void onLongTap(int index, boolean isTitle);
Ravi Banuri7a41c372015-10-19 16:03:03 +053053 public void onScrollPositionChanged(int position, int total);
54 }
55
56 public static class SimpleListener implements Listener {
57 @Override public void onDown(int index) {}
58 @Override public void onUp(boolean followedByLongPress) {}
Ravi Banuric963ee82015-10-20 19:41:58 +053059 @Override public void onSingleTapUp(int index, boolean isTitle) {}
60 @Override public void onLongTap(int index, boolean isTitle) {}
Ravi Banuri7a41c372015-10-19 16:03:03 +053061 @Override public void onScrollPositionChanged(int position, int total) {}
62 }
63
Ravi Banuri7a41c372015-10-19 16:03:03 +053064 private final GestureDetector mGestureDetector;
65 private final ScrollerHelper mScroller;
Ravi Banuri7a41c372015-10-19 16:03:03 +053066
67 private Listener mListener;
Ravi Banuri7a41c372015-10-19 16:03:03 +053068 private SlotAnimation mAnimation = null;
69 private final Layout mLayout = new Layout();
70 private int mStartIndex = INDEX_NONE;
71
72 // whether the down action happened while the view is scrolling.
73 private boolean mDownInScrolling;
Ravi Banuri7a41c372015-10-19 16:03:03 +053074
Ravi Banuric963ee82015-10-20 19:41:58 +053075 private TimeLineSlotRenderer mRenderer;
Ravi Banuri7a41c372015-10-19 16:03:03 +053076
77 private int[] mRequestRenderSlots = new int[16];
78
Ravi Banuri7a41c372015-10-19 16:03:03 +053079 // Flag to check whether it is come from Photo Page.
80 private boolean isFromPhotoPage = false;
81
Ravi Banuric963ee82015-10-20 19:41:58 +053082 public TimeLineSlotView(AbstractGalleryActivity activity, Spec spec) {
Ravi Banuri7a41c372015-10-19 16:03:03 +053083 mGestureDetector = new GestureDetector(activity, new MyGestureListener());
84 mScroller = new ScrollerHelper(activity);
Ravi Banuri7a41c372015-10-19 16:03:03 +053085 setSlotSpec(spec);
86 }
87
Ravi Banuric963ee82015-10-20 19:41:58 +053088 public void setSlotRenderer(TimeLineSlotRenderer slotDrawer) {
Ravi Banuri7a41c372015-10-19 16:03:03 +053089 mRenderer = slotDrawer;
90 if (mRenderer != null) {
Ravi Banuri7a41c372015-10-19 16:03:03 +053091 mRenderer.onVisibleRangeChanged(getVisibleStart(), getVisibleEnd());
92 }
93 }
94
95 public void setCenterIndex(int index) {
Likai Ding941b4592016-03-16 14:45:27 +080096 int size = mLayout.getSlotSize();
97 if (index < 0 || index >= size) {
Ravi Banuri7a41c372015-10-19 16:03:03 +053098 return;
99 }
Likai Ding941b4592016-03-16 14:45:27 +0800100 Rect rect = mLayout.getSlotRect(index);
zdi1162ee92016-03-30 17:28:17 +0800101 if (rect != null) {
102 int position = (rect.top + rect.bottom - getHeight()) / 2;
103 setScrollPosition(position);
104 }
Ravi Banuri7a41c372015-10-19 16:03:03 +0530105 }
106
107 public void makeSlotVisible(int index) {
Likai Ding941b4592016-03-16 14:45:27 +0800108 Rect rect = mLayout.getSlotRect(index);
109 if (rect == null) return;
Ravi Banuric963ee82015-10-20 19:41:58 +0530110 int visibleBegin = mScrollY;
111 int visibleLength = getHeight();
Ravi Banuri7a41c372015-10-19 16:03:03 +0530112 int visibleEnd = visibleBegin + visibleLength;
Ravi Banuric963ee82015-10-20 19:41:58 +0530113 int slotBegin = rect.top;
114 int slotEnd = rect.bottom;
Ravi Banuri7a41c372015-10-19 16:03:03 +0530115
116 int position = visibleBegin;
117 if (visibleLength < slotEnd - slotBegin) {
118 position = visibleBegin;
119 } else if (slotBegin < visibleBegin) {
120 position = slotBegin;
Ravi Banuric963ee82015-10-20 19:41:58 +0530121 } else if (slotEnd > visibleEnd && mainKey == 1) {
Ravi Banuri7a41c372015-10-19 16:03:03 +0530122 position = slotEnd - visibleLength;
Ravi Banuric963ee82015-10-20 19:41:58 +0530123 } else if (slotBegin > visibleEnd && mainKey == 0) {
124 position = slotBegin - visibleLength;
Ravi Banuri7a41c372015-10-19 16:03:03 +0530125 }
126
127 setScrollPosition(position);
128 }
129
130 /**
131 * Set the flag which used for check whether it is come from Photo Page.
132 */
133 public void setIsFromPhotoPage(boolean flag) {
134 isFromPhotoPage = flag;
135 }
136
137 public void setScrollPosition(int position) {
Ravi Banuri7a41c372015-10-19 16:03:03 +0530138 position = Utils.clamp(position, 0, mLayout.getScrollLimit());
139 mScroller.setPosition(position);
140 updateScrollPosition(position, false);
141 }
142
143 public void setSlotSpec(Spec spec) {
144 mLayout.setSlotSpec(spec);
145 }
146
147 @Override
Ravi Banuri7a41c372015-10-19 16:03:03 +0530148 protected void onLayout(boolean changeSize, int l, int t, int r, int b) {
149 if (!changeSize) return;
Ravi Banuric963ee82015-10-20 19:41:58 +0530150 mWidth = r - l;
Ravi Banuri7a41c372015-10-19 16:03:03 +0530151
Likai Ding941b4592016-03-16 14:45:27 +0800152 // Make sure we are still at a reasonable scroll position after the size
Ravi Banuri7a41c372015-10-19 16:03:03 +0530153 // is changed (like orientation change). We choose to keep the center
154 // visible slot still visible. This is arbitrary but reasonable.
155 int visibleIndex =
156 (mLayout.getVisibleStart() + mLayout.getVisibleEnd()) / 2;
157 mLayout.setSize(r - l, b - t);
158 makeSlotVisible(visibleIndex);
Ravi Banuri7a41c372015-10-19 16:03:03 +0530159 }
160
161 public void startScatteringAnimation(RelativePosition position) {
162 mAnimation = new ScatteringAnimation(position);
163 mAnimation.start();
Likai Ding941b4592016-03-16 14:45:27 +0800164 if (mLayout.getSlotSize() != 0) invalidate();
Ravi Banuri7a41c372015-10-19 16:03:03 +0530165 }
166
167 public void startRisingAnimation() {
168 mAnimation = new RisingAnimation();
169 mAnimation.start();
Likai Ding941b4592016-03-16 14:45:27 +0800170 if (mLayout.getSlotSize() != 0) invalidate();
Ravi Banuri7a41c372015-10-19 16:03:03 +0530171 }
172
173 private void updateScrollPosition(int position, boolean force) {
Ravi Banuric963ee82015-10-20 19:41:58 +0530174 if (!force && (position == mScrollY)) return;
175 mScrollY = position;
Ravi Banuri7a41c372015-10-19 16:03:03 +0530176 mLayout.setScrollPosition(position);
177 onScrollPositionChanged(position);
178 }
179
180 protected void onScrollPositionChanged(int newPosition) {
181 int limit = mLayout.getScrollLimit();
182 mListener.onScrollPositionChanged(newPosition, limit);
183 }
184
185 public Rect getSlotRect(int slotIndex) {
Likai Ding941b4592016-03-16 14:45:27 +0800186 return mLayout.getSlotRect(slotIndex);
Ravi Banuri7a41c372015-10-19 16:03:03 +0530187 }
188
189 @Override
190 protected boolean onTouch(MotionEvent event) {
Ravi Banuri7a41c372015-10-19 16:03:03 +0530191 mGestureDetector.onTouchEvent(event);
192 switch (event.getAction()) {
193 case MotionEvent.ACTION_DOWN:
194 mDownInScrolling = !mScroller.isFinished();
195 mScroller.forceFinished();
196 break;
197 case MotionEvent.ACTION_UP:
Ravi Banuri7a41c372015-10-19 16:03:03 +0530198 invalidate();
199 break;
200 }
201 return true;
202 }
203
204 public void setListener(Listener listener) {
205 mListener = listener;
206 }
207
Ravi Banuri7a41c372015-10-19 16:03:03 +0530208 private static int[] expandIntArray(int array[], int capacity) {
209 while (array.length < capacity) {
210 array = new int[array.length * 2];
211 }
212 return array;
213 }
214
215 @Override
216 protected void render(GLCanvas canvas) {
217 super.render(canvas);
218
219 if (mRenderer == null) return;
220 mRenderer.prepareDrawing();
221
222 long animTime = AnimationTime.get();
223 boolean more = mScroller.advanceAnimation(animTime);
Ravi Banuri7a41c372015-10-19 16:03:03 +0530224 int oldX = mScrollX;
225 updateScrollPosition(mScroller.getPosition(), false);
226
Ravi Banuri7a41c372015-10-19 16:03:03 +0530227 if (mAnimation != null) {
228 more |= mAnimation.calculate(animTime);
229 }
230
231 canvas.translate(-mScrollX, -mScrollY);
232
233 int requestCount = 0;
234 int requestedSlot[] = expandIntArray(mRequestRenderSlots,
Ravi Banuric963ee82015-10-20 19:41:58 +0530235 mLayout.getVisibleEnd() - mLayout.getVisibleStart());
Ravi Banuri7a41c372015-10-19 16:03:03 +0530236
Ravi Banuric963ee82015-10-20 19:41:58 +0530237 for (int i = mLayout.getVisibleEnd() - 1; i >= mLayout.getVisibleStart(); --i) {
Michael Bestasa4866ae2016-06-11 00:46:15 +0300238 int r = renderItem(canvas, i, 0);
Ravi Banuri7a41c372015-10-19 16:03:03 +0530239 if ((r & RENDER_MORE_FRAME) != 0) more = true;
240 if ((r & RENDER_MORE_PASS) != 0) requestedSlot[requestCount++] = i;
241 }
242
243 for (int pass = 1; requestCount != 0; ++pass) {
244 int newCount = 0;
245 for (int i = 0; i < requestCount; ++i) {
Michael Bestasa4866ae2016-06-11 00:46:15 +0300246 int r = renderItem(canvas, requestedSlot[i], pass);
Ravi Banuric963ee82015-10-20 19:41:58 +0530247 if ((r & RENDER_MORE_FRAME) != 0) more = false;
Ravi Banuri7a41c372015-10-19 16:03:03 +0530248 if ((r & RENDER_MORE_PASS) != 0) requestedSlot[newCount++] = i;
249 }
250 requestCount = newCount;
251 }
252
253 canvas.translate(mScrollX, mScrollY);
254
255 if (more) invalidate();
256
Ravi Banuri7a41c372015-10-19 16:03:03 +0530257 }
258
Michael Bestasa4866ae2016-06-11 00:46:15 +0300259 private int renderItem(GLCanvas canvas, int index, int pass) {
Likai Ding941b4592016-03-16 14:45:27 +0800260 Rect rect = mLayout.getSlotRect(index);
Ravi Banuric963ee82015-10-20 19:41:58 +0530261 if (rect == null) return 0;
huiyan5a3b4432016-02-03 16:09:04 +0800262 canvas.save(GLCanvas.SAVE_FLAG_ALPHA | GLCanvas.SAVE_FLAG_MATRIX);
Ravi Banuric963ee82015-10-20 19:41:58 +0530263 canvas.translate(rect.left, rect.top, 0);
Ravi Banuri7a41c372015-10-19 16:03:03 +0530264 if (mAnimation != null && mAnimation.isActive()) {
265 mAnimation.apply(canvas, index, rect);
266 }
267 int result = mRenderer.renderSlot(
268 canvas, index, pass, rect.right - rect.left, rect.bottom - rect.top);
269 canvas.restore();
270 return result;
271 }
272
273 public static abstract class SlotAnimation extends Animation {
274 protected float mProgress = 0;
275
276 public SlotAnimation() {
277 setInterpolator(new DecelerateInterpolator(4));
278 setDuration(1500);
279 }
280
281 @Override
282 protected void onCalculate(float progress) {
283 mProgress = progress;
284 }
285
286 abstract public void apply(GLCanvas canvas, int slotIndex, Rect target);
287 }
288
289 public static class RisingAnimation extends SlotAnimation {
290 private static final int RISING_DISTANCE = 128;
291
292 @Override
293 public void apply(GLCanvas canvas, int slotIndex, Rect target) {
294 canvas.translate(0, 0, RISING_DISTANCE * (1 - mProgress));
295 }
296 }
297
298 public static class ScatteringAnimation extends SlotAnimation {
299 private int PHOTO_DISTANCE = 1000;
300 private RelativePosition mCenter;
301
302 public ScatteringAnimation(RelativePosition center) {
303 mCenter = center;
304 }
305
306 @Override
307 public void apply(GLCanvas canvas, int slotIndex, Rect target) {
308 canvas.translate(
309 (mCenter.getX() - target.centerX()) * (1 - mProgress),
310 (mCenter.getY() - target.centerY()) * (1 - mProgress),
311 slotIndex * PHOTO_DISTANCE * (1 - mProgress));
312 canvas.setAlpha(mProgress);
313 }
314 }
315
Ravi Banuri7a41c372015-10-19 16:03:03 +0530316 private class MyGestureListener implements GestureDetector.OnGestureListener {
317 private boolean isDown;
318
319 // We call the listener's onDown() when our onShowPress() is called and
320 // call the listener's onUp() when we receive any further event.
321 @Override
322 public void onShowPress(MotionEvent e) {
323 GLRoot root = getGLRoot();
324 root.lockRenderThread();
325 try {
326 if (isDown) return;
Likai Ding941b4592016-03-16 14:45:27 +0800327 Slot slot = mLayout.getSlotByPosition(e.getX(), e.getY());
328 if (slot != null) {
Ravi Banuri7a41c372015-10-19 16:03:03 +0530329 isDown = true;
Likai Ding941b4592016-03-16 14:45:27 +0800330 mListener.onDown(slot.index);
Ravi Banuri7a41c372015-10-19 16:03:03 +0530331 }
332 } finally {
333 root.unlockRenderThread();
334 }
335 }
336
337 private void cancelDown(boolean byLongPress) {
338 if (!isDown) return;
339 isDown = false;
340 mListener.onUp(byLongPress);
341 }
342
343 @Override
344 public boolean onDown(MotionEvent e) {
345 return false;
346 }
347
348 @Override
349 public boolean onFling(MotionEvent e1,
350 MotionEvent e2, float velocityX, float velocityY) {
351 cancelDown(false);
352 int scrollLimit = mLayout.getScrollLimit();
353 if (scrollLimit == 0) return false;
Ravi Banuric963ee82015-10-20 19:41:58 +0530354 mScroller.fling((int) -velocityY, 0, scrollLimit);
Ravi Banuri7a41c372015-10-19 16:03:03 +0530355 invalidate();
356 return true;
357 }
358
359 @Override
360 public boolean onScroll(MotionEvent e1,
361 MotionEvent e2, float distanceX, float distanceY) {
362 cancelDown(false);
Ravi Banuri7a41c372015-10-19 16:03:03 +0530363 int overDistance = mScroller.startScroll(
Ravi Banuric963ee82015-10-20 19:41:58 +0530364 Math.round(distanceY), 0, mLayout.getScrollLimit());
Ravi Banuri7a41c372015-10-19 16:03:03 +0530365 invalidate();
366 return true;
367 }
368
369 @Override
370 public boolean onSingleTapUp(MotionEvent e) {
371 cancelDown(false);
372 if (mDownInScrolling) return true;
Likai Ding941b4592016-03-16 14:45:27 +0800373 Slot slot = mLayout.getSlotByPosition(e.getX(), e.getY());
374 if (slot != null) {
375 mListener.onSingleTapUp(slot.index, slot.isTitle);
376 }
377 return true;
Ravi Banuri7a41c372015-10-19 16:03:03 +0530378 }
379
380 @Override
381 public void onLongPress(MotionEvent e) {
382 cancelDown(true);
383 if (mDownInScrolling) return;
384 lockRendering();
385 try {
Likai Ding941b4592016-03-16 14:45:27 +0800386 Slot slot = mLayout.getSlotByPosition(e.getX(), e.getY());
387 if (slot != null) {
388 mListener.onLongTap(slot.index, slot.isTitle);
Ravi Banuric963ee82015-10-20 19:41:58 +0530389 }
Ravi Banuri7a41c372015-10-19 16:03:03 +0530390 } finally {
391 unlockRendering();
392 }
393 }
394 }
395
396 public void setStartIndex(int index) {
397 mStartIndex = index;
398 }
399
Likai Ding941b4592016-03-16 14:45:27 +0800400 public void setSlotCount(int[] count) {
401 mLayout.setSlotCount(count);
Ravi Banuri7a41c372015-10-19 16:03:03 +0530402
403 // mStartIndex is applied the first time setSlotCount is called.
404 if (mStartIndex != INDEX_NONE) {
405 setCenterIndex(mStartIndex);
406 mStartIndex = INDEX_NONE;
407 }
408 // Reset the scroll position to avoid scrolling over the updated limit.
Ravi Banuric963ee82015-10-20 19:41:58 +0530409 setScrollPosition(mScrollY);
Ravi Banuric963ee82015-10-20 19:41:58 +0530410 }
411
Ravi Banuri7a41c372015-10-19 16:03:03 +0530412 public int getVisibleStart() {
413 return mLayout.getVisibleStart();
414 }
415
416 public int getVisibleEnd() {
417 return mLayout.getVisibleEnd();
418 }
419
420 public int getScrollX() {
421 return mScrollX;
422 }
423
424 public int getScrollY() {
425 return mScrollY;
426 }
427
428 public Rect getSlotRect(int slotIndex, GLView rootPane) {
429 // Get slot rectangle relative to this root pane.
430 Rect offset = new Rect();
431 rootPane.getBoundsOf(this, offset);
432 Rect r = getSlotRect(slotIndex);
huiyan53796272016-05-05 11:33:07 +0800433 if (r != null) {
434 r.offset(offset.left - getScrollX(),
435 offset.top - getScrollY());
436 return r;
437 }
438 return offset;
Ravi Banuri7a41c372015-10-19 16:03:03 +0530439 }
440
Ravi Banuric963ee82015-10-20 19:41:58 +0530441 public int getTitleWidth() {
442 return mWidth;
443 }
Ravi Banuri7a41c372015-10-19 16:03:03 +0530444
Ravi Banuric963ee82015-10-20 19:41:58 +0530445 // This Spec class is used to specify the size of each slot in the SlotView.
446 // There are two ways to do it:
447 //
448 // Specify colsLand, colsPort, and slotGap: they specify the number
449 // of rows in landscape/portrait mode and the gap between slots. The
450 // width and height of each slot is determined automatically.
451 //
452 // The initial value of -1 means they are not specified.
453 public static class Spec {
454 public int colsLand = -1;
455 public int colsPort = -1;
456 public int titleHeight = -1;
457 public int slotGapPort = -1;
458 public int slotGapLand = -1;
459 }
460
461 public class Layout {
Ravi Banuric963ee82015-10-20 19:41:58 +0530462 private int mVisibleStart;
463 private int mVisibleEnd;
464
Likai Ding941b4592016-03-16 14:45:27 +0800465 public int mSlotSize;
Ravi Banuric963ee82015-10-20 19:41:58 +0530466 private int mSlotWidth;
467 private int mSlotHeight;
468 private int mSlotGap;
Likai Ding941b4592016-03-16 14:45:27 +0800469 private int[] mSlotCount;
Ravi Banuric963ee82015-10-20 19:41:58 +0530470
471 private Spec mSpec;
472
473 private int mWidth;
474 private int mHeight;
475
476 private int mUnitCount;
477 private int mContentLength;
478 private int mScrollPosition;
479
Ravi Banuric963ee82015-10-20 19:41:58 +0530480 public void setSlotSpec(TimeLineSlotView.Spec spec) {
481 mSpec = spec;
Ravi Banuri7a41c372015-10-19 16:03:03 +0530482 }
483
Likai Ding941b4592016-03-16 14:45:27 +0800484 public void setSlotCount(int[] count) {
485 mSlotCount = count;
486 if (mHeight != 0) {
487 initLayoutParameters();
488 createSlots();
489 }
Ravi Banuric963ee82015-10-20 19:41:58 +0530490 }
491
Likai Ding941b4592016-03-16 14:45:27 +0800492 public int getSlotSize() {
493 return mSlotSize;
494 }
495
496 public Rect getSlotRect(int index) {
Ravi Banuric963ee82015-10-20 19:41:58 +0530497 if (index >= mVisibleStart && index < mVisibleEnd && mVisibleEnd != 0) {
Likai Ding941b4592016-03-16 14:45:27 +0800498 int height = 0, base = 0, top = 0;
499 for (int count : mSlotCount) {
500 if (index == base) {
501 return getSlotRect(getSlot(true, index, index, top));
502 }
503 top += mSpec.titleHeight;
504 ++base;
505
506 if (index >= base && index < base + count) {
507 return getSlotRect(getSlot(false, index, base, top));
508 }
509 int rows = (count + mUnitCount - 1) / mUnitCount;
510 top += mSlotHeight * rows + mSlotGap * (rows > 0 ? rows - 1 : 0);
511 base += count;
Ravi Banuric963ee82015-10-20 19:41:58 +0530512 }
Ravi Banuri7a41c372015-10-19 16:03:03 +0530513 }
Likai Ding941b4592016-03-16 14:45:27 +0800514 return null;
Ravi Banuri7a41c372015-10-19 16:03:03 +0530515 }
516
Ravi Banuric963ee82015-10-20 19:41:58 +0530517 private void initLayoutParameters() {
518 mUnitCount = (mWidth > mHeight) ? mSpec.colsLand : mSpec.colsPort;
519 mSlotGap = (mWidth > mHeight) ? mSpec.slotGapLand: mSpec.slotGapPort;
520 mSlotWidth = Math.round((mWidth - (mUnitCount - 1) * mSlotGap) / mUnitCount);
521 mSlotHeight = mSlotWidth;
522 if (mRenderer != null) {
523 mRenderer.onSlotSizeChanged(mSlotWidth, mSlotHeight);
524 }
Ravi Banuri7a41c372015-10-19 16:03:03 +0530525 }
526
Ravi Banuric963ee82015-10-20 19:41:58 +0530527 private void setSize(int width, int height) {
Likai Ding941b4592016-03-16 14:45:27 +0800528 if (width != mWidth || height != mHeight) {
529 mWidth = width;
530 mHeight = height;
531 initLayoutParameters();
532 createSlots();
533 }
Ravi Banuri7a41c372015-10-19 16:03:03 +0530534 }
535
Ravi Banuric963ee82015-10-20 19:41:58 +0530536 public void setScrollPosition(int position) {
537 if (mScrollPosition == position) return;
538 mScrollPosition = position;
539 updateVisibleSlotRange();
540 }
541
Likai Ding941b4592016-03-16 14:45:27 +0800542 private Rect getSlotRect(Slot slot) {
543 int x, y, w, h;
544 if (slot.isTitle) {
545 x = 0;
546 y = slot.top;
547 w = mWidth;
548 h = mSpec.titleHeight;
Ravi Banuric963ee82015-10-20 19:41:58 +0530549 } else {
Likai Ding941b4592016-03-16 14:45:27 +0800550 x = slot.col * (mSlotWidth + mSlotGap);
551 y = slot.top;
552 w = mSlotWidth;
553 h = mSlotHeight;
Ravi Banuric963ee82015-10-20 19:41:58 +0530554 }
Likai Ding941b4592016-03-16 14:45:27 +0800555 return new Rect(x, y, x + w, y + h);
Ravi Banuric963ee82015-10-20 19:41:58 +0530556 }
557
558 private synchronized void updateVisibleSlotRange() {
559 int position = mScrollPosition;
Likai Ding941b4592016-03-16 14:45:27 +0800560 if (mSlotCount != null) {
561 Slot begin = getSlotByPosition(0, mScrollPosition, true, false),
562 end = getSlotByPosition(0, mScrollPosition + mHeight, true, true);
Kedi Xuf91a4b22016-09-08 11:11:11 +0800563 if (begin == null && end != null && end.index == 0) {
564 setVisibleRange(0, 0);
565 } else if (begin != null && end != null) {
Likai Ding941b4592016-03-16 14:45:27 +0800566 setVisibleRange(begin.index, end.index);
Ravi Banuric963ee82015-10-20 19:41:58 +0530567 }
Ravi Banuric963ee82015-10-20 19:41:58 +0530568 }
569 }
570
571 private void setVisibleRange(int start, int end) {
572 if (start == mVisibleStart && end == mVisibleEnd) return;
573 if (start < end) {
574 mVisibleStart = start;
575 mVisibleEnd = end;
576 } else {
577 mVisibleStart = mVisibleEnd = 0;
578 }
579 if (mRenderer != null) {
580 mRenderer.onVisibleRangeChanged(mVisibleStart, mVisibleEnd);
581 }
582 }
583
584 public int getVisibleStart() {
585 return mVisibleStart;
586 }
587
588 public int getVisibleEnd() {
589 return mVisibleEnd;
590 }
591
Likai Ding941b4592016-03-16 14:45:27 +0800592 private Slot getSlot(boolean isTitle, int index, int indexBase, int top) {
593 if (isTitle) {
594 return new Slot(true, index, 0, top);
595 } else {
596 int row = (index - indexBase) / mUnitCount;
597 return new Slot(false, index, (index - indexBase) % mUnitCount,
598 top + row * (mSlotHeight + mSlotGap));
599 }
Ravi Banuric963ee82015-10-20 19:41:58 +0530600 }
601
Likai Ding941b4592016-03-16 14:45:27 +0800602 public Slot getSlotByPosition(float x, float y, boolean rowStart, boolean roundUp) {
603 if (x < 0 || y < 0 || mSlotCount == null) {
Ravi Banuric963ee82015-10-20 19:41:58 +0530604 return null;
605 }
Likai Ding941b4592016-03-16 14:45:27 +0800606 int pos = (int) y, index = 0, top = 0;
607 for (int count : mSlotCount) {
608 int h = mSpec.titleHeight;
609 if (pos < top + h) {
610 if (roundUp) {
611 return getSlot(false, index + 1, index, top + h);
612 } else {
613 return getSlot(true, index, index, top);
yonggaf84c0532016-01-11 18:36:35 +0800614 }
Ravi Banuric963ee82015-10-20 19:41:58 +0530615 }
Likai Ding941b4592016-03-16 14:45:27 +0800616 top += h;
617 ++index;
618
619 int rows = (count + mUnitCount - 1) / mUnitCount;
620 h = mSlotHeight * rows + mSlotGap * (rows > 0 ? rows - 1 : 0);
621 if (pos < top + h) {
622 int row = ((int) pos - top) / (mSlotHeight + mSlotGap);
623 int col = 0;
624 if (roundUp) {
625 int idx = (row + 1) * mUnitCount;
626 if (idx > count)
627 idx = count + 1;
628 return getSlot(false, index + idx, index, top + mSlotHeight);
629 }
630 if (!rowStart) {
631 col = ((int) x) / (mSlotWidth + mSlotGap);
Likai Ding941b4592016-03-16 14:45:27 +0800632 if (row * mUnitCount + col >= count) {
633 break;
634 }
635 }
636 return getSlot(false, index + row * mUnitCount + col, index, top);
637 }
638 top += h;
639 index += count;
640 }
641 if (roundUp) {
642 return getSlot(false, index, index, top);
Ravi Banuric963ee82015-10-20 19:41:58 +0530643 }
644 return null;
645 }
646
Likai Ding941b4592016-03-16 14:45:27 +0800647 public Slot getSlotByPosition(float x, float y) {
648 return getSlotByPosition(x, mScrollPosition + y, false, false);
Ravi Banuric963ee82015-10-20 19:41:58 +0530649 }
650
Likai Ding941b4592016-03-16 14:45:27 +0800651 public int getScrollLimit() {
652 return Math.max(0, mContentLength - mHeight);
653 }
Ravi Banuric963ee82015-10-20 19:41:58 +0530654
655 public void createSlots() {
Likai Ding941b4592016-03-16 14:45:27 +0800656 int height = 0;
657 int size = 0;
658 if (mSlotCount != null) {
659 for (int count : mSlotCount) {
660 int rows = (count + mUnitCount - 1) / mUnitCount;
661 height += mSlotHeight * rows + mSlotGap * (rows > 0 ? rows - 1 : 0);
662 size += 1 + count;
Ravi Banuric963ee82015-10-20 19:41:58 +0530663 }
Likai Ding941b4592016-03-16 14:45:27 +0800664 height += mSpec.titleHeight * mSlotCount.length;
665 mContentLength = height;
666 mSlotSize = size;
667 updateVisibleSlotRange();
Ravi Banuric963ee82015-10-20 19:41:58 +0530668 }
Ravi Banuri7a41c372015-10-19 16:03:03 +0530669 }
670 }
671
Likai Ding941b4592016-03-16 14:45:27 +0800672 private static class Slot {
673 public boolean isTitle;
674 public int index;
675 public int col;
676 public int top;
Ravi Banuric963ee82015-10-20 19:41:58 +0530677
Likai Ding941b4592016-03-16 14:45:27 +0800678 public Slot(boolean isTitle, int index, int col, int top) {
679 this.isTitle = isTitle;
680 this.index = index;
681 this.col = col;
682 this.top = top;
Ravi Banuric963ee82015-10-20 19:41:58 +0530683 }
Ravi Banuri7a41c372015-10-19 16:03:03 +0530684 }
685}