1 /* 2 * Copyright (C) 2013 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 android.support.v4.media; 18 19 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; 20 21 import android.os.Build; 22 import android.os.Parcel; 23 import android.os.Parcelable; 24 import android.support.annotation.IntDef; 25 import android.support.annotation.RestrictTo; 26 import android.util.Log; 27 28 import java.lang.annotation.Retention; 29 import java.lang.annotation.RetentionPolicy; 30 31 /** 32 * A class to encapsulate rating information used as content metadata. 33 * A rating is defined by its rating style (see {@link #RATING_HEART}, 34 * {@link #RATING_THUMB_UP_DOWN}, {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, 35 * {@link #RATING_5_STARS} or {@link #RATING_PERCENTAGE}) and the actual rating value (which may 36 * be defined as "unrated"), both of which are defined when the rating instance is constructed 37 * through one of the factory methods. 38 */ 39 public final class RatingCompat implements Parcelable { 40 private final static String TAG = "Rating"; 41 42 /** 43 * @hide 44 */ 45 @RestrictTo(LIBRARY_GROUP) 46 @IntDef({RATING_NONE, RATING_HEART, RATING_THUMB_UP_DOWN, RATING_3_STARS, RATING_4_STARS, 47 RATING_5_STARS, RATING_PERCENTAGE}) 48 @Retention(RetentionPolicy.SOURCE) 49 public @interface Style {} 50 51 /** 52 * @hide 53 */ 54 @RestrictTo(LIBRARY_GROUP) 55 @IntDef({RATING_3_STARS, RATING_4_STARS, RATING_5_STARS}) 56 @Retention(RetentionPolicy.SOURCE) 57 public @interface StarStyle {} 58 59 /** 60 * Indicates a rating style is not supported. A Rating will never have this 61 * type, but can be used by other classes to indicate they do not support 62 * Rating. 63 */ 64 public final static int RATING_NONE = 0; 65 66 /** 67 * A rating style with a single degree of rating, "heart" vs "no heart". Can be used to 68 * indicate the content referred to is a favorite (or not). 69 */ 70 public final static int RATING_HEART = 1; 71 72 /** 73 * A rating style for "thumb up" vs "thumb down". 74 */ 75 public final static int RATING_THUMB_UP_DOWN = 2; 76 77 /** 78 * A rating style with 0 to 3 stars. 79 */ 80 public final static int RATING_3_STARS = 3; 81 82 /** 83 * A rating style with 0 to 4 stars. 84 */ 85 public final static int RATING_4_STARS = 4; 86 87 /** 88 * A rating style with 0 to 5 stars. 89 */ 90 public final static int RATING_5_STARS = 5; 91 92 /** 93 * A rating style expressed as a percentage. 94 */ 95 public final static int RATING_PERCENTAGE = 6; 96 97 private final static float RATING_NOT_RATED = -1.0f; 98 99 private final int mRatingStyle; 100 private final float mRatingValue; 101 102 private Object mRatingObj; // framework Rating object 103 RatingCompat(@tyle int ratingStyle, float rating)104 RatingCompat(@Style int ratingStyle, float rating) { 105 mRatingStyle = ratingStyle; 106 mRatingValue = rating; 107 } 108 109 @Override toString()110 public String toString() { 111 return "Rating:style=" + mRatingStyle + " rating=" 112 + (mRatingValue < 0.0f ? "unrated" : String.valueOf(mRatingValue)); 113 } 114 115 @Override describeContents()116 public int describeContents() { 117 return mRatingStyle; 118 } 119 120 @Override writeToParcel(Parcel dest, int flags)121 public void writeToParcel(Parcel dest, int flags) { 122 dest.writeInt(mRatingStyle); 123 dest.writeFloat(mRatingValue); 124 } 125 126 public static final Parcelable.Creator<RatingCompat> CREATOR 127 = new Parcelable.Creator<RatingCompat>() { 128 /** 129 * Rebuilds a Rating previously stored with writeToParcel(). 130 * @param p Parcel object to read the Rating from 131 * @return a new Rating created from the data in the parcel 132 */ 133 @Override 134 public RatingCompat createFromParcel(Parcel p) { 135 return new RatingCompat(p.readInt(), p.readFloat()); 136 } 137 138 @Override 139 public RatingCompat[] newArray(int size) { 140 return new RatingCompat[size]; 141 } 142 }; 143 144 /** 145 * Return a Rating instance with no rating. 146 * Create and return a new Rating instance with no rating known for the given 147 * rating style. 148 * @param ratingStyle one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN}, 149 * {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS}, 150 * or {@link #RATING_PERCENTAGE}. 151 * @return null if an invalid rating style is passed, a new Rating instance otherwise. 152 */ newUnratedRating(@tyle int ratingStyle)153 public static RatingCompat newUnratedRating(@Style int ratingStyle) { 154 switch(ratingStyle) { 155 case RATING_HEART: 156 case RATING_THUMB_UP_DOWN: 157 case RATING_3_STARS: 158 case RATING_4_STARS: 159 case RATING_5_STARS: 160 case RATING_PERCENTAGE: 161 return new RatingCompat(ratingStyle, RATING_NOT_RATED); 162 default: 163 return null; 164 } 165 } 166 167 /** 168 * Return a Rating instance with a heart-based rating. 169 * Create and return a new Rating instance with a rating style of {@link #RATING_HEART}, 170 * and a heart-based rating. 171 * @param hasHeart true for a "heart selected" rating, false for "heart unselected". 172 * @return a new Rating instance. 173 */ newHeartRating(boolean hasHeart)174 public static RatingCompat newHeartRating(boolean hasHeart) { 175 return new RatingCompat(RATING_HEART, hasHeart ? 1.0f : 0.0f); 176 } 177 178 /** 179 * Return a Rating instance with a thumb-based rating. 180 * Create and return a new Rating instance with a {@link #RATING_THUMB_UP_DOWN} 181 * rating style, and a "thumb up" or "thumb down" rating. 182 * @param thumbIsUp true for a "thumb up" rating, false for "thumb down". 183 * @return a new Rating instance. 184 */ newThumbRating(boolean thumbIsUp)185 public static RatingCompat newThumbRating(boolean thumbIsUp) { 186 return new RatingCompat(RATING_THUMB_UP_DOWN, thumbIsUp ? 1.0f : 0.0f); 187 } 188 189 /** 190 * Return a Rating instance with a star-based rating. 191 * Create and return a new Rating instance with one of the star-base rating styles 192 * and the given integer or fractional number of stars. Non integer values can for instance 193 * be used to represent an average rating value, which might not be an integer number of stars. 194 * @param starRatingStyle one of {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, 195 * {@link #RATING_5_STARS}. 196 * @param starRating a number ranging from 0.0f to 3.0f, 4.0f or 5.0f according to 197 * the rating style. 198 * @return null if the rating style is invalid, or the rating is out of range, 199 * a new Rating instance otherwise. 200 */ newStarRating(@tarStyle int starRatingStyle, float starRating)201 public static RatingCompat newStarRating(@StarStyle int starRatingStyle, 202 float starRating) { 203 float maxRating = -1.0f; 204 switch(starRatingStyle) { 205 case RATING_3_STARS: 206 maxRating = 3.0f; 207 break; 208 case RATING_4_STARS: 209 maxRating = 4.0f; 210 break; 211 case RATING_5_STARS: 212 maxRating = 5.0f; 213 break; 214 default: 215 Log.e(TAG, "Invalid rating style (" + starRatingStyle + ") for a star rating"); 216 return null; 217 } 218 if ((starRating < 0.0f) || (starRating > maxRating)) { 219 Log.e(TAG, "Trying to set out of range star-based rating"); 220 return null; 221 } 222 return new RatingCompat(starRatingStyle, starRating); 223 } 224 225 /** 226 * Return a Rating instance with a percentage-based rating. 227 * Create and return a new Rating instance with a {@link #RATING_PERCENTAGE} 228 * rating style, and a rating of the given percentage. 229 * @param percent the value of the rating 230 * @return null if the rating is out of range, a new Rating instance otherwise. 231 */ newPercentageRating(float percent)232 public static RatingCompat newPercentageRating(float percent) { 233 if ((percent < 0.0f) || (percent > 100.0f)) { 234 Log.e(TAG, "Invalid percentage-based rating value"); 235 return null; 236 } else { 237 return new RatingCompat(RATING_PERCENTAGE, percent); 238 } 239 } 240 241 /** 242 * Return whether there is a rating value available. 243 * @return true if the instance was not created with {@link #newUnratedRating(int)}. 244 */ isRated()245 public boolean isRated() { 246 return mRatingValue >= 0.0f; 247 } 248 249 /** 250 * Return the rating style. 251 * @return one of {@link #RATING_HEART}, {@link #RATING_THUMB_UP_DOWN}, 252 * {@link #RATING_3_STARS}, {@link #RATING_4_STARS}, {@link #RATING_5_STARS}, 253 * or {@link #RATING_PERCENTAGE}. 254 */ 255 @Style getRatingStyle()256 public int getRatingStyle() { 257 return mRatingStyle; 258 } 259 260 /** 261 * Return whether the rating is "heart selected". 262 * @return true if the rating is "heart selected", false if the rating is "heart unselected", 263 * if the rating style is not {@link #RATING_HEART} or if it is unrated. 264 */ hasHeart()265 public boolean hasHeart() { 266 if (mRatingStyle != RATING_HEART) { 267 return false; 268 } else { 269 return (mRatingValue == 1.0f); 270 } 271 } 272 273 /** 274 * Return whether the rating is "thumb up". 275 * @return true if the rating is "thumb up", false if the rating is "thumb down", 276 * if the rating style is not {@link #RATING_THUMB_UP_DOWN} or if it is unrated. 277 */ isThumbUp()278 public boolean isThumbUp() { 279 if (mRatingStyle != RATING_THUMB_UP_DOWN) { 280 return false; 281 } else { 282 return (mRatingValue == 1.0f); 283 } 284 } 285 286 /** 287 * Return the star-based rating value. 288 * @return a rating value greater or equal to 0.0f, or a negative value if the rating style is 289 * not star-based, or if it is unrated. 290 */ getStarRating()291 public float getStarRating() { 292 switch (mRatingStyle) { 293 case RATING_3_STARS: 294 case RATING_4_STARS: 295 case RATING_5_STARS: 296 if (isRated()) { 297 return mRatingValue; 298 } 299 // fall through 300 default: 301 return -1.0f; 302 } 303 } 304 305 /** 306 * Return the percentage-based rating value. 307 * @return a rating value greater or equal to 0.0f, or a negative value if the rating style is 308 * not percentage-based, or if it is unrated. 309 */ getPercentRating()310 public float getPercentRating() { 311 if ((mRatingStyle != RATING_PERCENTAGE) || !isRated()) { 312 return -1.0f; 313 } else { 314 return mRatingValue; 315 } 316 } 317 318 /** 319 * Creates an instance from a framework {@link android.media.Rating} object. 320 * <p> 321 * This method is only supported on API 19+. 322 * </p> 323 * 324 * @param ratingObj A {@link android.media.Rating} object, or null if none. 325 * @return An equivalent {@link RatingCompat} object, or null if none. 326 */ fromRating(Object ratingObj)327 public static RatingCompat fromRating(Object ratingObj) { 328 if (ratingObj != null && Build.VERSION.SDK_INT >= 19) { 329 final int ratingStyle = RatingCompatKitkat.getRatingStyle(ratingObj); 330 final RatingCompat rating; 331 if (RatingCompatKitkat.isRated(ratingObj)) { 332 switch (ratingStyle) { 333 case RATING_HEART: 334 rating = newHeartRating(RatingCompatKitkat.hasHeart(ratingObj)); 335 break; 336 case RATING_THUMB_UP_DOWN: 337 rating = newThumbRating(RatingCompatKitkat.isThumbUp(ratingObj)); 338 break; 339 case RATING_3_STARS: 340 case RATING_4_STARS: 341 case RATING_5_STARS: 342 rating = newStarRating(ratingStyle, 343 RatingCompatKitkat.getStarRating(ratingObj)); 344 break; 345 case RATING_PERCENTAGE: 346 rating = newPercentageRating( 347 RatingCompatKitkat.getPercentRating(ratingObj)); 348 break; 349 default: 350 return null; 351 } 352 } else { 353 rating = newUnratedRating(ratingStyle); 354 } 355 rating.mRatingObj = ratingObj; 356 return rating; 357 } else { 358 return null; 359 } 360 } 361 362 /** 363 * Gets the underlying framework {@link android.media.Rating} object. 364 * <p> 365 * This method is only supported on API 19+. 366 * </p> 367 * 368 * @return An equivalent {@link android.media.Rating} object, or null if none. 369 */ getRating()370 public Object getRating() { 371 if (mRatingObj == null && Build.VERSION.SDK_INT >= 19) { 372 if (isRated()) { 373 switch (mRatingStyle) { 374 case RATING_HEART: 375 mRatingObj = RatingCompatKitkat.newHeartRating(hasHeart()); 376 break; 377 case RATING_THUMB_UP_DOWN: 378 mRatingObj = RatingCompatKitkat.newThumbRating(isThumbUp()); 379 break; 380 case RATING_3_STARS: 381 case RATING_4_STARS: 382 case RATING_5_STARS: 383 mRatingObj = RatingCompatKitkat.newStarRating(mRatingStyle, 384 getStarRating()); 385 break; 386 case RATING_PERCENTAGE: 387 mRatingObj = RatingCompatKitkat.newPercentageRating(getPercentRating()); 388 break; 389 default: 390 return null; 391 } 392 } else { 393 mRatingObj = RatingCompatKitkat.newUnratedRating(mRatingStyle); 394 } 395 } 396 return mRatingObj; 397 } 398 } 399