From 54440261b05f7f757f9bf5470552ae115084123f Mon Sep 17 00:00:00 2001 From: jaewan Date: Wed, 28 Apr 2021 15:57:56 +0100 Subject: [PATCH] Move Rating class to library-common Rating class should be in the same module as MediaMetadata. Tested: $ ./gradlew --stacktrace :exo-library-common:tDUT $ ./gradlew --stacktrace :media2-session:tDUT $ ./gradlew --stacktrace :media2-session-vct-current:cAT PiperOrigin-RevId: 370902917 --- .../android/exoplayer2/HeartRating.java | 115 +++++++++++++++ .../android/exoplayer2/PercentageRating.java | 108 ++++++++++++++ .../com/google/android/exoplayer2/Rating.java | 83 +++++++++++ .../google/android/exoplayer2/StarRating.java | 138 ++++++++++++++++++ .../android/exoplayer2/ThumbRating.java | 112 ++++++++++++++ .../google/android/exoplayer2/RatingTest.java | 101 +++++++++++++ 6 files changed, 657 insertions(+) create mode 100644 library/common/src/main/java/com/google/android/exoplayer2/HeartRating.java create mode 100644 library/common/src/main/java/com/google/android/exoplayer2/PercentageRating.java create mode 100644 library/common/src/main/java/com/google/android/exoplayer2/Rating.java create mode 100644 library/common/src/main/java/com/google/android/exoplayer2/StarRating.java create mode 100644 library/common/src/main/java/com/google/android/exoplayer2/ThumbRating.java create mode 100644 library/common/src/test/java/com/google/android/exoplayer2/RatingTest.java diff --git a/library/common/src/main/java/com/google/android/exoplayer2/HeartRating.java b/library/common/src/main/java/com/google/android/exoplayer2/HeartRating.java new file mode 100644 index 0000000000..e6610d6b7b --- /dev/null +++ b/library/common/src/main/java/com/google/android/exoplayer2/HeartRating.java @@ -0,0 +1,115 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2; + +import static com.google.android.exoplayer2.util.Assertions.checkArgument; + +import android.os.Bundle; +import androidx.annotation.IntDef; +import androidx.annotation.Nullable; +import com.google.common.base.Objects; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * A class for rating with a single degree of rating, "heart" vs "no heart". This can be used to + * indicate the content referred to is a favorite (or not). + */ +public final class HeartRating extends Rating { + + @RatingType private static final int TYPE = RATING_TYPE_HEART; + + private final boolean isRated; + + /** Whether the rating has a heart rating or not. */ + public final boolean hasHeart; + + /** Creates a unrated HeartRating instance. */ + public HeartRating() { + isRated = false; + hasHeart = false; + } + + /** + * Creates a HeartRating instance. + * + * @param hasHeart true for a "heart selected" rating, false for "heart unselected". + */ + public HeartRating(boolean hasHeart) { + isRated = true; + this.hasHeart = hasHeart; + } + + @Override + public boolean isRated() { + return isRated; + } + + @Override + public int hashCode() { + return Objects.hashCode(isRated, hasHeart); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof HeartRating)) { + return false; + } + HeartRating other = (HeartRating) obj; + return hasHeart == other.hasHeart && isRated == other.isRated; + } + + @Override + public String toString() { + return "HeartRating: " + (isRated ? "hasHeart=" + hasHeart : "unrated"); + } + + // Bundleable implementation. + @Documented + @Retention(RetentionPolicy.SOURCE) + @IntDef({FIELD_RATING_TYPE, FIELD_IS_RATED, FIELD_HAS_HEART}) + private @interface FieldNumber {} + + private static final int FIELD_IS_RATED = 1; + private static final int FIELD_HAS_HEART = 2; + + @Override + public Bundle toBundle() { + Bundle bundle = new Bundle(); + bundle.putInt(keyForField(FIELD_RATING_TYPE), TYPE); + bundle.putBoolean(keyForField(FIELD_IS_RATED), isRated); + bundle.putBoolean(keyForField(FIELD_HAS_HEART), hasHeart); + return bundle; + } + + public static final Creator CREATOR = HeartRating::fromBundle; + + private static HeartRating fromBundle(Bundle bundle) { + checkArgument( + bundle.getInt(keyForField(FIELD_RATING_TYPE), /* defaultValue= */ RATING_TYPE_DEFAULT) + == TYPE); + boolean isRated = bundle.getBoolean(keyForField(FIELD_IS_RATED), /* defaultValue= */ false); + return isRated + ? new HeartRating( + bundle.getBoolean(keyForField(FIELD_HAS_HEART), /* defaultValue= */ false)) + : new HeartRating(); + } + + private static String keyForField(@FieldNumber int field) { + return Integer.toString(field, Character.MAX_RADIX); + } +} diff --git a/library/common/src/main/java/com/google/android/exoplayer2/PercentageRating.java b/library/common/src/main/java/com/google/android/exoplayer2/PercentageRating.java new file mode 100644 index 0000000000..1af82a57c3 --- /dev/null +++ b/library/common/src/main/java/com/google/android/exoplayer2/PercentageRating.java @@ -0,0 +1,108 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2; + +import static com.google.android.exoplayer2.util.Assertions.checkArgument; + +import android.os.Bundle; +import androidx.annotation.FloatRange; +import androidx.annotation.IntDef; +import androidx.annotation.Nullable; +import com.google.common.base.Objects; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** A class for rating expressed as a percentage. */ +public final class PercentageRating extends Rating { + + @RatingType private static final int TYPE = RATING_TYPE_PERCENTAGE; + + /** + * The percent value of this rating. Will be greater or equal to 0.0f, or {@link #RATING_UNSET} if + * it is unrated. + */ + public final float percent; + + /** Creates a unrated PercentageRating instance. */ + public PercentageRating() { + percent = RATING_UNSET; + } + + /** + * Creates a PercentageRating instance with the given percentage. If {@code percent} is less than + * 0f or greater than 100f, it will throw IllegalArgumentException. + * + * @param percent the value of the rating + */ + public PercentageRating(@FloatRange(from = 0, to = 100) float percent) { + checkArgument(percent >= 0.0f && percent <= 100.0f, "percent must be in the rage of [0, 100]"); + this.percent = percent; + } + + @Override + public boolean isRated() { + return percent != RATING_UNSET; + } + + @Override + public int hashCode() { + return Objects.hashCode(percent); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof PercentageRating)) { + return false; + } + return percent == ((PercentageRating) obj).percent; + } + + @Override + public String toString() { + return "PercentageRating: " + (isRated() ? "percentage=" + percent : "unrated"); + } + + // Bundleable implementation. + @Documented + @Retention(RetentionPolicy.SOURCE) + @IntDef({FIELD_RATING_TYPE, FIELD_PERCENT}) + private @interface FieldNumber {} + + private static final int FIELD_PERCENT = 1; + + @Override + public Bundle toBundle() { + Bundle bundle = new Bundle(); + bundle.putInt(keyForField(FIELD_RATING_TYPE), TYPE); + bundle.putFloat(keyForField(FIELD_PERCENT), percent); + return bundle; + } + + public static final Creator CREATOR = PercentageRating::fromBundle; + + private static PercentageRating fromBundle(Bundle bundle) { + checkArgument( + bundle.getInt(keyForField(FIELD_RATING_TYPE), /* defaultValue= */ RATING_TYPE_DEFAULT) + == TYPE); + float percent = bundle.getFloat(keyForField(FIELD_PERCENT), /* defaultValue= */ RATING_UNSET); + return percent == RATING_UNSET ? new PercentageRating() : new PercentageRating(percent); + } + + private static String keyForField(@FieldNumber int field) { + return Integer.toString(field, Character.MAX_RADIX); + } +} diff --git a/library/common/src/main/java/com/google/android/exoplayer2/Rating.java b/library/common/src/main/java/com/google/android/exoplayer2/Rating.java new file mode 100644 index 0000000000..e80f033048 --- /dev/null +++ b/library/common/src/main/java/com/google/android/exoplayer2/Rating.java @@ -0,0 +1,83 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2; + +import android.os.Bundle; +import androidx.annotation.IntDef; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** An abstract class to encapsulate rating information used as content metadata. */ +public abstract class Rating implements Bundleable { + + public static final float RATING_UNSET = -1.0f; + + @Documented + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + RATING_TYPE_DEFAULT, + RATING_TYPE_HEART, + RATING_TYPE_PERCENTAGE, + RATING_TYPE_STAR, + RATING_TYPE_THUMB + }) + protected @interface RatingType {} + + protected static final int RATING_TYPE_DEFAULT = -1; + protected static final int RATING_TYPE_HEART = 0; + protected static final int RATING_TYPE_PERCENTAGE = 1; + protected static final int RATING_TYPE_STAR = 2; + protected static final int RATING_TYPE_THUMB = 3; + + // Default package-private constructor to prevent extending Rating class outside this package. + /* package */ Rating() {} + + /** Whether rating exists or not. */ + public abstract boolean isRated(); + + // Bundleable implementation. + @Documented + @Retention(RetentionPolicy.SOURCE) + @IntDef({FIELD_RATING_TYPE}) + private @interface FieldNumber {} + + protected static final int FIELD_RATING_TYPE = 0; + + public static final Creator CREATOR = Rating::fromBundle; + + private static Rating fromBundle(Bundle bundle) { + @RatingType + int ratingType = + bundle.getInt(keyForField(FIELD_RATING_TYPE), /* defaultValue= */ RATING_TYPE_DEFAULT); + switch (ratingType) { + case RATING_TYPE_HEART: + return HeartRating.CREATOR.fromBundle(bundle); + case RATING_TYPE_PERCENTAGE: + return PercentageRating.CREATOR.fromBundle(bundle); + case RATING_TYPE_STAR: + return StarRating.CREATOR.fromBundle(bundle); + case RATING_TYPE_THUMB: + return ThumbRating.CREATOR.fromBundle(bundle); + default: + throw new IllegalArgumentException("Encountered unknown rating type: " + ratingType); + } + } + + private static String keyForField(@FieldNumber int field) { + return Integer.toString(field, Character.MAX_RADIX); + } +} diff --git a/library/common/src/main/java/com/google/android/exoplayer2/StarRating.java b/library/common/src/main/java/com/google/android/exoplayer2/StarRating.java new file mode 100644 index 0000000000..af4b59ba1e --- /dev/null +++ b/library/common/src/main/java/com/google/android/exoplayer2/StarRating.java @@ -0,0 +1,138 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2; + +import static com.google.android.exoplayer2.util.Assertions.checkArgument; + +import android.os.Bundle; +import androidx.annotation.FloatRange; +import androidx.annotation.IntDef; +import androidx.annotation.IntRange; +import androidx.annotation.Nullable; +import com.google.common.base.Objects; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** A class for rating expressed as the number of stars. */ +public final class StarRating extends Rating { + + @RatingType private static final int TYPE = RATING_TYPE_STAR; + + private static final int MAX_STARS_DEFAULT = 5; + + /** The maximum number of stars for this rating. Must be a positive number. */ + @IntRange(from = 1) + public final int maxStars; + + /** + * The value of stars for this rating. Will range from 0.0f to {@link #maxStars}, or {@link + * #RATING_UNSET} if it is unrated. + */ + public final float starRating; + + /** + * Creates a unrated StarRating instance with {@code maxStars}. If {@code maxStars} is not a + * positive integer, it will throw IllegalArgumentException. + * + * @param maxStars a range of this star rating from 0.0f to {@code maxStars} + */ + public StarRating(@IntRange(from = 1) int maxStars) { + checkArgument(maxStars > 0, "maxStars must be a positive integer"); + this.maxStars = maxStars; + starRating = RATING_UNSET; + } + + /** + * Creates a StarRating instance with {@code maxStars} and the given integer or fractional number + * of stars. Non-integer values can for instance be used to represent an average rating value, + * which might not be an integer number of stars. If {@code maxStars} is not a positive integer or + * {@code starRating} has invalid value, it will throw IllegalArgumentException. + * + * @param maxStars the maximum number of stars which this rating can have. + * @param starRating a number ranging from 0.0f to {@code maxStars} + */ + public StarRating(@IntRange(from = 1) int maxStars, @FloatRange(from = 0.0) float starRating) { + checkArgument(maxStars > 0, "maxStars must be a positive integer"); + checkArgument( + starRating >= 0.0f && starRating <= maxStars, "starRating is out of range [0, maxStars]"); + this.maxStars = maxStars; + this.starRating = starRating; + } + + @Override + public boolean isRated() { + return starRating != RATING_UNSET; + } + + @Override + public int hashCode() { + return Objects.hashCode(maxStars, starRating); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof StarRating)) { + return false; + } + StarRating other = (StarRating) obj; + return maxStars == other.maxStars && starRating == other.starRating; + } + + @Override + public String toString() { + return "StarRating: maxStars=" + + maxStars + + (isRated() ? ", starRating=" + starRating : ", unrated"); + } + + // Bundleable implementation. + @Documented + @Retention(RetentionPolicy.SOURCE) + @IntDef({FIELD_RATING_TYPE, FIELD_MAX_STARS, FIELD_STAR_RATING}) + private @interface FieldNumber {} + + private static final int FIELD_MAX_STARS = 1; + private static final int FIELD_STAR_RATING = 2; + + @Override + public Bundle toBundle() { + Bundle bundle = new Bundle(); + bundle.putInt(keyForField(FIELD_RATING_TYPE), TYPE); + bundle.putInt(keyForField(FIELD_MAX_STARS), maxStars); + bundle.putFloat(keyForField(FIELD_STAR_RATING), starRating); + return bundle; + } + + public static final Creator CREATOR = StarRating::fromBundle; + + private static StarRating fromBundle(Bundle bundle) { + checkArgument( + bundle.getInt(keyForField(FIELD_RATING_TYPE), /* defaultValue= */ RATING_TYPE_DEFAULT) + == TYPE); + int maxStars = + bundle.getInt(keyForField(FIELD_MAX_STARS), /* defaultValue= */ MAX_STARS_DEFAULT); + float starRating = + bundle.getFloat(keyForField(FIELD_STAR_RATING), /* defaultValue= */ RATING_UNSET); + return starRating == RATING_UNSET + ? new StarRating(maxStars) + : new StarRating(maxStars, starRating); + } + + private static String keyForField(@FieldNumber int field) { + return Integer.toString(field, Character.MAX_RADIX); + } +} diff --git a/library/common/src/main/java/com/google/android/exoplayer2/ThumbRating.java b/library/common/src/main/java/com/google/android/exoplayer2/ThumbRating.java new file mode 100644 index 0000000000..dbb2bcb791 --- /dev/null +++ b/library/common/src/main/java/com/google/android/exoplayer2/ThumbRating.java @@ -0,0 +1,112 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2; + +import static com.google.android.exoplayer2.util.Assertions.checkArgument; + +import android.os.Bundle; +import androidx.annotation.IntDef; +import androidx.annotation.Nullable; +import com.google.common.base.Objects; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** A class for rating with a single degree of rating, "thumb up" vs "thumb down". */ +public final class ThumbRating extends Rating { + + @RatingType private static final int TYPE = RATING_TYPE_THUMB; + + private final boolean isRated; + + /** Whether the rating has a thumb up or thumb down rating. */ + public final boolean thumbUp; + + /** Creates a unrated ThumbRating instance. */ + public ThumbRating() { + isRated = false; + thumbUp = false; + } + + /** + * Creates a ThumbRating instance. + * + * @param thumbIsUp true for a "thumb up" rating, false for "thumb down". + */ + public ThumbRating(boolean thumbIsUp) { + isRated = true; + thumbUp = thumbIsUp; + } + + @Override + public boolean isRated() { + return isRated; + } + + @Override + public int hashCode() { + return Objects.hashCode(isRated, thumbUp); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof ThumbRating)) { + return false; + } + ThumbRating other = (ThumbRating) obj; + return thumbUp == other.thumbUp && isRated == other.isRated; + } + + @Override + public String toString() { + return "ThumbRating: " + (isRated ? "isThumbUp=" + thumbUp : "unrated"); + } + + // Bundleable implementation. + @Documented + @Retention(RetentionPolicy.SOURCE) + @IntDef({FIELD_RATING_TYPE, FIELD_IS_RATED, FIELD_IS_THUMB_UP}) + private @interface FieldNumber {} + + private static final int FIELD_IS_RATED = 1; + private static final int FIELD_IS_THUMB_UP = 2; + + @Override + public Bundle toBundle() { + Bundle bundle = new Bundle(); + bundle.putInt(keyForField(FIELD_RATING_TYPE), TYPE); + bundle.putBoolean(keyForField(FIELD_IS_RATED), isRated); + bundle.putBoolean(keyForField(FIELD_IS_THUMB_UP), thumbUp); + return bundle; + } + + public static final Creator CREATOR = ThumbRating::fromBundle; + + private static ThumbRating fromBundle(Bundle bundle) { + checkArgument( + bundle.getInt(keyForField(FIELD_RATING_TYPE), /* defaultValue= */ RATING_TYPE_DEFAULT) + == TYPE); + boolean isRated = bundle.getBoolean(keyForField(FIELD_IS_RATED), /* defaultValue= */ false); + return isRated + ? new ThumbRating( + bundle.getBoolean(keyForField(FIELD_IS_THUMB_UP), /* defaultValue= */ false)) + : new ThumbRating(); + } + + private static String keyForField(@FieldNumber int field) { + return Integer.toString(field, Character.MAX_RADIX); + } +} diff --git a/library/common/src/test/java/com/google/android/exoplayer2/RatingTest.java b/library/common/src/test/java/com/google/android/exoplayer2/RatingTest.java new file mode 100644 index 0000000000..3c50f1cdcf --- /dev/null +++ b/library/common/src/test/java/com/google/android/exoplayer2/RatingTest.java @@ -0,0 +1,101 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.internal.DoNotInstrument; + +/** Tests for {@link Rating} and its subclasses. */ +@RunWith(AndroidJUnit4.class) +@DoNotInstrument +public class RatingTest { + + @Test + public void unratedHeartRating() { + HeartRating rating = new HeartRating(); + assertThat(rating.isRated()).isFalse(); + assertThat(roundTripViaBundle(rating)).isEqualTo(rating); + } + + @Test + public void ratedHeartRating() { + boolean hasHeart = true; + HeartRating rating = new HeartRating(hasHeart); + assertThat(rating.isRated()).isTrue(); + assertThat(rating.hasHeart).isEqualTo(hasHeart); + assertThat(roundTripViaBundle(rating)).isEqualTo(rating); + } + + @Test + public void unratedPercentageRating() { + PercentageRating rating = new PercentageRating(); + assertThat(rating.isRated()).isFalse(); + assertThat(roundTripViaBundle(rating)).isEqualTo(rating); + } + + @Test + public void ratedPercentageRating() { + float percentage = 20.5f; + PercentageRating rating = new PercentageRating(percentage); + assertThat(rating.isRated()).isTrue(); + assertThat(rating.percent).isEqualTo(percentage); + assertThat(roundTripViaBundle(rating)).isEqualTo(rating); + } + + @Test + public void unratedThumbRating() { + ThumbRating rating = new ThumbRating(); + assertThat(rating.isRated()).isFalse(); + assertThat(roundTripViaBundle(rating)).isEqualTo(rating); + } + + @Test + public void ratedThumbRating() { + boolean isThumbUp = true; + ThumbRating rating = new ThumbRating(isThumbUp); + assertThat(rating.isRated()).isTrue(); + assertThat(rating.thumbUp).isEqualTo(isThumbUp); + assertThat(roundTripViaBundle(rating)).isEqualTo(rating); + } + + @Test + public void unratedStarRating() { + int maxStars = 5; + StarRating rating = new StarRating(maxStars); + assertThat(rating.isRated()).isFalse(); + assertThat(rating.maxStars).isEqualTo(maxStars); + assertThat(roundTripViaBundle(rating)).isEqualTo(rating); + } + + @Test + public void ratedStarRating() { + int maxStars = 5; + float starRating = 3.1f; + StarRating rating = new StarRating(maxStars, starRating); + assertThat(rating.isRated()).isTrue(); + assertThat(rating.maxStars).isEqualTo(maxStars); + assertThat(rating.starRating).isEqualTo(starRating); + assertThat(roundTripViaBundle(rating)).isEqualTo(rating); + } + + private static Rating roundTripViaBundle(Rating rating) { + return Rating.CREATOR.fromBundle(rating.toBundle()); + } +}