Home | History | Annotate | Download | only in media
      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 
    106     RatingCompat(@Style int ratingStyle, float rating) {
    107         mRatingStyle = ratingStyle;
    108         mRatingValue = rating;
    109     }
    110 
    111     @Override
    112     public String toString() {
    113         return "Rating:style=" + mRatingStyle + " rating="
    114                 + (mRatingValue < 0.0f ? "unrated" : String.valueOf(mRatingValue));
    115     }
    116 
    117     @Override
    118     public int describeContents() {
    119         return mRatingStyle;
    120     }
    121 
    122     @Override
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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