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