CarouselController.java revision fb179e7afd8f02be63061b478b0283e3085fc25f
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.ex.carousel; 18 19import com.android.ex.carousel.CarouselRS.CarouselCallback; 20import com.android.ex.carousel.CarouselView.DetailAlignment; 21 22import android.content.res.Resources; 23import android.graphics.Bitmap; 24import android.renderscript.FileA3D; 25import android.renderscript.Float4; 26import android.renderscript.Mesh; 27import android.renderscript.RenderScriptGL; 28import android.util.Log; 29import android.view.SurfaceHolder; 30 31/** 32 * <p> 33 * This class represents the basic building block for using a 3D Carousel. The Carousel is 34 * basically a scene of cards and slots. The spacing between cards is dictated by the number 35 * of slots and the radius. The number of visible cards dictates how far the Carousel can be moved. 36 * If the number of cards exceeds the number of slots, then the Carousel will continue to go 37 * around until the last card can be seen. 38 */ 39public class CarouselController { 40 private final int DEFAULT_SLOT_COUNT = 10; 41 private final float DEFAULT_RADIUS = 20.0f; 42 private final int DEFAULT_VISIBLE_DETAIL_COUNT = 3; 43 private final int DEFAULT_PREFETCH_CARD_COUNT = 2; 44 private final float DEFAULT_SWAY_SENSITIVITY = 0.0f; 45 private final float DEFAULT_FRICTION_COEFFICIENT = 10.0f; 46 private final float DEFAULT_DRAG_FACTOR = 0.25f; 47 private final int DEFAULT_DETAIL_ALIGNMENT = 48 DetailAlignment.VIEW_TOP | DetailAlignment.LEFT; 49 private CarouselRS mRenderScript; 50 private RenderScriptGL mRS; 51 private static final String TAG = "CarouselController"; 52 private static final boolean DBG = false; 53 private boolean mTracking; 54 55 // These shadow the state of the renderer in case the surface changes so the surface 56 // can be restored to its previous state. 57 private Bitmap mDefaultBitmap; 58 private Bitmap mLoadingBitmap; 59 private Bitmap mBackgroundBitmap; 60 private Bitmap mDefaultLineBitmap = Bitmap.createBitmap( 61 new int[] {0x00000000, 0xffffffff, 0x00000000}, 0, 3, 3, 1, Bitmap.Config.ARGB_4444); 62 private Mesh mDefaultGeometry; 63 private Mesh mLoadingGeometry; 64 private int mCardCount = 0; 65 private int mVisibleSlots = 0; 66 private int mVisibleDetails = DEFAULT_VISIBLE_DETAIL_COUNT; 67 private int mPrefetchCardCount = DEFAULT_PREFETCH_CARD_COUNT; 68 private int mDetailTextureAlignment = DEFAULT_DETAIL_ALIGNMENT; 69 private boolean mForceBlendCardsWithZ = false; 70 private boolean mDrawRuler = true; 71 private float mStartAngle; 72 private float mCarouselRotationAngle; 73 private float mRadius = DEFAULT_RADIUS; 74 private float mCardRotation = 0.0f; 75 private boolean mCardsFaceTangent = false; 76 private float mSwaySensitivity = DEFAULT_SWAY_SENSITIVITY; 77 private float mFrictionCoefficient = DEFAULT_FRICTION_COEFFICIENT; 78 private float mDragFactor = DEFAULT_DRAG_FACTOR; 79 private int mSlotCount = DEFAULT_SLOT_COUNT; 80 private float mEye[] = { 20.6829f, 2.77081f, 16.7314f }; 81 private float mAt[] = { 14.7255f, -3.40001f, -1.30184f }; 82 private float mUp[] = { 0.0f, 1.0f, 0.0f }; 83 private Float4 mBackgroundColor = new Float4(0.0f, 0.0f, 0.0f, 1.0f); 84 private CarouselCallback mCarouselCallback; 85 private float mRezInCardCount = 0.0f; 86 private long mFadeInDuration = 250L; 87 private Bitmap mDetailLoadingBitmap = Bitmap.createBitmap( 88 new int[] {0}, 0, 1, 1, 1, Bitmap.Config.ARGB_4444); 89 90 public CarouselController() { 91 boolean useDepthBuffer = true; 92 } 93 94 public void setRS(RenderScriptGL rs, CarouselRS renderScript) { 95 mRS = rs; 96 mRenderScript = renderScript; 97 } 98 99 public void onSurfaceChanged() { 100 setSlotCount(mSlotCount); 101 createCards(mCardCount); 102 setVisibleSlots(mVisibleSlots); 103 setVisibleDetails(mVisibleDetails); 104 setPrefetchCardCount(mPrefetchCardCount); 105 setDetailTextureAlignment(mDetailTextureAlignment); 106 setForceBlendCardsWithZ(mForceBlendCardsWithZ); 107 setDrawRuler(mDrawRuler); 108 setCallback(mCarouselCallback); 109 setDefaultBitmap(mDefaultBitmap); 110 setLoadingBitmap(mLoadingBitmap); 111 setDefaultGeometry(mDefaultGeometry); 112 setLoadingGeometry(mLoadingGeometry); 113 setBackgroundColor(mBackgroundColor.x, mBackgroundColor.y, mBackgroundColor.z, 114 mBackgroundColor.w); 115 setBackgroundBitmap(mBackgroundBitmap); 116 setDetailLineBitmap(mDefaultLineBitmap); 117 setStartAngle(mStartAngle); 118 setCarouselRotationAngle(mCarouselRotationAngle); 119 setRadius(mRadius); 120 setCardRotation(mCardRotation); 121 setCardsFaceTangent(mCardsFaceTangent); 122 setSwaySensitivity(mSwaySensitivity); 123 setFrictionCoefficient(mFrictionCoefficient); 124 setDragFactor(mDragFactor); 125 setLookAt(mEye, mAt, mUp); 126 setRezInCardCount(mRezInCardCount); 127 setFadeInDuration(mFadeInDuration); 128 setDetailLoadingBitmap(mDetailLoadingBitmap); 129 } 130 131 /** 132 * Loads geometry from a resource id. 133 * 134 * @param resId 135 * @return the loaded mesh or null if it cannot be loaded 136 */ 137 public Mesh loadGeometry(Resources res, int resId) { 138 FileA3D model = FileA3D.createFromResource(mRS, res, resId); 139 FileA3D.IndexEntry entry = model.getIndexEntry(0); 140 if(entry == null || entry.getClassID() != FileA3D.ClassID.MESH) { 141 return null; 142 } 143 return (Mesh) entry.getObject(); 144 } 145 146 /** 147 * Load A3D file from resource. If resId == 0, will clear geometry for this item. 148 * @param n 149 * @param resId 150 */ 151 public void setGeometryForItem(int n, Mesh mesh) { 152 if (mRenderScript != null) { 153 mRenderScript.setGeometry(n, mesh); 154 } 155 } 156 157 /** 158 * Set the number of slots around the Carousel. Basically equivalent to the poles horses 159 * might attach to on a real Carousel. 160 * 161 * @param n the number of slots 162 */ 163 public void setSlotCount(int n) { 164 mSlotCount = n; 165 if (mRenderScript != null) { 166 mRenderScript.setSlotCount(n); 167 } 168 } 169 170 /** 171 * Sets the number of visible slots around the Carousel. This is primarily used as a cheap 172 * form of clipping. The Carousel will never show more than this many cards. 173 * @param n the number of visible slots 174 */ 175 public void setVisibleSlots(int n) { 176 mVisibleSlots = n; 177 if (mRenderScript != null) { 178 mRenderScript.setVisibleSlots(n); 179 } 180 } 181 182 /** 183 * Set the number of detail textures that can be visible at one time. 184 * 185 * @param n the number of slots 186 */ 187 public void setVisibleDetails(int n) { 188 mVisibleDetails = n; 189 if (mRenderScript != null) { 190 mRenderScript.setVisibleDetails(n); 191 } 192 } 193 194 /** 195 * Set the number of cards to pre-load that are outside of the visible region, as determined by 196 * setVisibleSlots(). This number gets added to the number of visible slots and used to 197 * determine when resources for cards should be loaded. This number should be small (n <= 4) 198 * for systems with limited texture memory or views that show more than half dozen cards in the 199 * view. 200 * 201 * @param n the number of cards; should be even, so the count is the same on each side 202 */ 203 public void setPrefetchCardCount(int n) { 204 mPrefetchCardCount = n; 205 if (mRenderScript != null) { 206 mRenderScript.setPrefetchCardCount(n); 207 } 208 } 209 210 /** 211 * Sets how detail textures are aligned with respect to the card. 212 * 213 * @param alignment a bitmask of DetailAlignment flags. 214 */ 215 public void setDetailTextureAlignment(int alignment) { 216 int xBits = alignment & DetailAlignment.HORIZONTAL_ALIGNMENT_MASK; 217 if (xBits == 0 || ((xBits & (xBits - 1)) != 0)) { 218 throw new IllegalArgumentException( 219 "Must specify exactly one horizontal alignment flag"); 220 } 221 int yBits = alignment & DetailAlignment.VERTICAL_ALIGNMENT_MASK; 222 if (yBits == 0 || ((yBits & (yBits - 1)) != 0)) { 223 throw new IllegalArgumentException( 224 "Must specify exactly one vertical alignment flag"); 225 } 226 227 mDetailTextureAlignment = alignment; 228 if (mRenderScript != null) { 229 mRenderScript.setDetailTextureAlignment(alignment); 230 } 231 } 232 233 /** 234 * Set whether depth is enabled while blending. Generally, this is discouraged because 235 * it causes bad artifacts. Careful attention to geometry and alpha transparency of 236 * textures can mitigate much of this. Geometry for an individual item must be drawn 237 * back-to-front, for example. 238 * 239 * @param enabled True to enable depth while blending, and false to disable it. 240 */ 241 public void setForceBlendCardsWithZ(boolean enabled) { 242 mForceBlendCardsWithZ = enabled; 243 if (mRenderScript != null) { 244 mRenderScript.setForceBlendCardsWithZ(enabled); 245 } 246 } 247 248 /** 249 * Set whether to draw a ruler from the card to the detail texture 250 * 251 * @param drawRuler True to draw a ruler, false to draw nothing where the ruler would go. 252 */ 253 public void setDrawRuler(boolean drawRuler) { 254 mDrawRuler = drawRuler; 255 if (mRenderScript != null) { 256 mRenderScript.setDrawRuler(drawRuler); 257 } 258 } 259 260 /** 261 * This dictates how many cards are in the deck. If the number of cards is greater than the 262 * number of slots, then the Carousel goes around n / slot_count times. 263 * 264 * Can be called again to increase or decrease the number of cards. 265 * 266 * @param n the number of cards to create. 267 */ 268 public void createCards(int n) { 269 mCardCount = n; 270 if (mRenderScript != null) { 271 mRenderScript.createCards(n); 272 } 273 } 274 275 public int getCardCount() { 276 return mCardCount; 277 } 278 279 /** 280 * This sets the texture on card n. It should only be called in response to 281 * {@link CarouselCallback#onRequestTexture(int)}. Since there's no guarantee 282 * that a given texture is still on the screen, replacing this texture should be done 283 * by first setting it to null and then waiting for the next 284 * {@link CarouselCallback#onRequestTexture(int)} to swap it with the new one. 285 * 286 * @param n the card given by {@link CarouselCallback#onRequestTexture(int)} 287 * @param bitmap the bitmap image to show 288 */ 289 public void setTextureForItem(int n, Bitmap bitmap) { 290 // Also check against mRS, to handle the case where the result is being delivered by a 291 // background thread but the sender no longer exists. 292 if (mRenderScript != null && mRS != null) { 293 if (DBG) Log.v(TAG, "setTextureForItem(" + n + ")"); 294 mRenderScript.setTexture(n, bitmap); 295 if (DBG) Log.v(TAG, "done"); 296 } 297 } 298 299 /** 300 * This sets the detail texture that floats above card n. It should only be called in response 301 * to {@link CarouselCallback#onRequestDetailTexture(int)}. Since there's no guarantee 302 * that a given texture is still on the screen, replacing this texture should be done 303 * by first setting it to null and then waiting for the next 304 * {@link CarouselCallback#onRequestDetailTexture(int)} to swap it with the new one. 305 * 306 * @param n the card to set detail texture for 307 * @param offx an optional offset to apply to the texture (in pixels) from top of detail line 308 * @param offy an optional offset to apply to the texture (in pixels) from top of detail line 309 * @param loffx an optional offset to apply to the line (in pixels) from left edge of card 310 * @param loffy an optional offset to apply to the line (in pixels) from top of screen 311 * @param bitmap the bitmap to show as the detail 312 */ 313 public void setDetailTextureForItem(int n, float offx, float offy, float loffx, float loffy, 314 Bitmap bitmap) { 315 if (mRenderScript != null && mRS != null) { 316 if (DBG) Log.v(TAG, "setDetailTextureForItem(" + n + ")"); 317 mRenderScript.setDetailTexture(n, offx, offy, loffx, loffy, bitmap); 318 if (DBG) Log.v(TAG, "done"); 319 } 320 } 321 322 /** 323 * Sets the bitmap to show on a card when the card draws the very first time. 324 * Generally, this bitmap will only be seen during the first few frames of startup 325 * or when the number of cards are changed. It can be ignored in most cases, 326 * as the cards will generally only be in the loading or loaded state. 327 * 328 * @param bitmap 329 */ 330 public void setDefaultBitmap(Bitmap bitmap) { 331 mDefaultBitmap = bitmap; 332 if (mRenderScript != null) { 333 mRenderScript.setDefaultBitmap(bitmap); 334 } 335 } 336 337 /** 338 * Sets the bitmap to show on the card while the texture is loading. It is set to this 339 * value just before {@link CarouselCallback#onRequestTexture(int)} is called and changed 340 * when {@link CarouselView#setTextureForItem(int, Bitmap)} is called. It is shared by all 341 * cards. 342 * 343 * @param bitmap 344 */ 345 public void setLoadingBitmap(Bitmap bitmap) { 346 mLoadingBitmap = bitmap; 347 if (mRenderScript != null) { 348 mRenderScript.setLoadingBitmap(bitmap); 349 } 350 } 351 352 /** 353 * Sets background to specified color. If a background texture is specified with 354 * {@link CarouselView#setBackgroundBitmap(Bitmap)}, then this call has no effect. 355 * 356 * @param red the amount of red 357 * @param green the amount of green 358 * @param blue the amount of blue 359 * @param alpha the amount of alpha 360 */ 361 public void setBackgroundColor(float red, float green, float blue, float alpha) { 362 mBackgroundColor = new Float4(red, green, blue, alpha); 363 if (mRenderScript != null) { 364 mRenderScript.setBackgroundColor(mBackgroundColor); 365 } 366 } 367 368 /** 369 * Can be used to optionally set the background to a bitmap. When set to something other than 370 * null, this overrides {@link CarouselController#setBackgroundColor(Float4)}. 371 * 372 * @param bitmap 373 */ 374 public void setBackgroundBitmap(Bitmap bitmap) { 375 mBackgroundBitmap = bitmap; 376 if (mRenderScript != null) { 377 mRenderScript.setBackgroundTexture(bitmap); 378 } 379 } 380 381 /** 382 * Can be used to optionally set a "loading" detail bitmap. Typically, this is just a black 383 * texture with alpha = 0 to allow details to slowly fade in. 384 * 385 * @param bitmap 386 */ 387 public void setDetailLoadingBitmap(Bitmap bitmap) { 388 mDetailLoadingBitmap = bitmap; 389 if (mRenderScript != null) { 390 mRenderScript.setDetailLoadingTexture(bitmap); 391 } 392 } 393 394 /** 395 * This texture is used to draw a line from the card alongside the texture detail. The line 396 * will be as wide as the texture. It can be used to give the line glow effects as well as 397 * allowing other blending effects. It is typically one dimensional, e.g. 3x1. 398 * 399 * @param bitmap 400 */ 401 public void setDetailLineBitmap(Bitmap bitmap) { 402 mDefaultLineBitmap = bitmap; 403 if (mRenderScript != null) { 404 mRenderScript.setDetailLineTexture(bitmap); 405 } 406 } 407 408 /** 409 * This geometry will be shown when no geometry has been loaded for a given slot. If not set, 410 * a quad will be drawn in its place. It is shared for all cards. If something other than 411 * simple planar geometry is used, consider enabling depth test with 412 * {@link CarouselController#setForceBlendCardsWithZ(boolean)} 413 * 414 * @param mesh 415 */ 416 public void setDefaultGeometry(Mesh mesh) { 417 mDefaultGeometry = mesh; 418 if (mRenderScript != null) { 419 mRenderScript.setDefaultGeometry(mesh); 420 } 421 } 422 423 /** 424 * This is an intermediate version of the object to show while geometry is loading. If not set, 425 * a quad will be drawn in its place. It is shared for all cards. If something other than 426 * simple planar geometry is used, consider enabling depth test with 427 * {@link CarouselView#setForceBlendCardsWithZ(boolean)} 428 * 429 * @param mesh 430 */ 431 public void setLoadingGeometry(Mesh mesh) { 432 mLoadingGeometry = mesh; 433 if (mRenderScript != null) { 434 mRenderScript.setLoadingGeometry(mesh); 435 } 436 } 437 438 /** 439 * Sets the callback for receiving events from RenderScript. 440 * 441 * @param callback 442 */ 443 public void setCallback(CarouselCallback callback) 444 { 445 mCarouselCallback = callback; 446 if (mRenderScript != null) { 447 mRenderScript.setCallback(callback); 448 } 449 } 450 451 /** 452 * Gets the callback for receiving events from Renderscript. 453 */ 454 public CarouselCallback getCallback() { 455 return mCarouselCallback; 456 } 457 458 /** 459 * Sets the startAngle for the Carousel. The start angle is the first position of the first 460 * slot draw. Cards will be drawn from this angle in a counter-clockwise manner around the 461 * Carousel. 462 * 463 * @param angle the angle, in radians. 464 */ 465 public void setStartAngle(float angle) 466 { 467 mStartAngle = angle; 468 if (mRenderScript != null) { 469 mRenderScript.setStartAngle(angle); 470 } 471 } 472 473 /** 474 * Set the current carousel rotation angle, in card units. 475 * This is measured in card positions, not in radians or degrees. 476 * 477 * A value of 0.0 means that card 0 is in the home position. 478 * A value of 1.0 means that card 1 is in the home position, and so on. 479 * The maximum value will be somewhat less than the total number of cards. 480 * 481 * @param angle 482 */ 483 public void setCarouselRotationAngle(float angle) { 484 mCarouselRotationAngle = angle; 485 if (mRenderScript != null) { 486 mRenderScript.setCarouselRotationAngle(angle); 487 } 488 } 489 490 public void setRadius(float radius) { 491 mRadius = radius; 492 if (mRenderScript != null) { 493 mRenderScript.setRadius(radius); 494 } 495 } 496 497 public void setCardRotation(float cardRotation) { 498 mCardRotation = cardRotation; 499 if (mRenderScript != null) { 500 mRenderScript.setCardRotation(cardRotation); 501 } 502 } 503 504 public void setCardsFaceTangent(boolean faceTangent) { 505 mCardsFaceTangent = faceTangent; 506 if (mRenderScript != null) { 507 mRenderScript.setCardsFaceTangent(faceTangent); 508 } 509 } 510 511 public void setSwaySensitivity(float swaySensitivity) { 512 mSwaySensitivity = swaySensitivity; 513 if (mRenderScript != null) { 514 mRenderScript.setSwaySensitivity(swaySensitivity); 515 } 516 } 517 518 public void setFrictionCoefficient(float frictionCoefficient) { 519 mFrictionCoefficient = frictionCoefficient; 520 if (mRenderScript != null) { 521 mRenderScript.setFrictionCoefficient(frictionCoefficient); 522 } 523 } 524 525 public void setDragFactor(float dragFactor) { 526 mDragFactor = dragFactor; 527 if (mRenderScript != null) { 528 mRenderScript.setDragFactor(dragFactor); 529 } 530 } 531 532 public void setLookAt(float[] eye, float[] at, float[] up) { 533 mEye = eye; 534 mAt = at; 535 mUp = up; 536 if (mRenderScript != null) { 537 mRenderScript.setLookAt(eye, at, up); 538 } 539 } 540 541 /** 542 * This sets the number of cards in the distance that will be shown "rezzing in". 543 * These alpha values will be faded in from the background to the foreground over 544 * 'n' cards. A floating point value is used to allow subtly changing the rezzing in 545 * position. 546 * 547 * @param n the number of cards to rez in. 548 */ 549 public void setRezInCardCount(float n) { 550 mRezInCardCount = n; 551 if (mRenderScript != null) { 552 mRenderScript.setRezInCardCount(n); 553 } 554 } 555 556 /** 557 * This sets the duration (in ms) that a card takes to fade in when loaded via a call 558 * to {@link CarouselView#setTextureForItem(int, Bitmap)}. The timer starts the 559 * moment {@link CarouselView#setTextureForItem(int, Bitmap)} is called and continues 560 * until all of the cards have faded in. Note: using large values will extend the 561 * animation until all cards have faded in. 562 * 563 * @param t 564 */ 565 public void setFadeInDuration(long t) { 566 mFadeInDuration = t; 567 if (mRenderScript != null) { 568 mRenderScript.setFadeInDuration(t); 569 } 570 } 571 572 /** 573 * Tells the carousel that a touch event has started at the designated location. 574 * @param x The number of pixels from the left edge that the event occurred 575 * @param y The number of pixels from the top edge that the event occurred 576 */ 577 public void onTouchStarted(float x, float y) { 578 mRenderScript.doStart(x, y); 579 } 580 581 /** 582 * Tells the carousel that a touch event has moved to the designated location. 583 * @param x The number of pixels from the left edge that the event occurred 584 * @param y The number of pixels from the top edge that the event occurred 585 */ 586 public void onTouchMoved(float x, float y) { 587 mRenderScript.doMotion(x, y); 588 } 589 590 /** 591 * Tells the carousel that the user has long-pressed. 592 */ 593 public void onLongPress() { 594 mRenderScript.doLongPress(); 595 } 596 597 /** 598 * Tells the carousel that a touch event has stopped at the designated location. 599 * @param x The number of pixels from the left edge that the event occurred 600 * @param y The number of pixels from the top edge that the event occurred 601 */ 602 public void onTouchStopped(float x, float y) { 603 mRenderScript.doStop(x, y); 604 } 605} 606