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