Commit 072f376b by bachinger Committed by Marc Baechinger

Add withAvailableAd for server side inserted ad groups

#minor-release

PiperOrigin-RevId: 472714732
parent 125646e4
...@@ -965,7 +965,7 @@ import java.util.Map; ...@@ -965,7 +965,7 @@ import java.util.Map;
Uri adUri = Uri.parse(adMediaInfo.getUrl()); Uri adUri = Uri.parse(adMediaInfo.getUrl());
adPlaybackState = adPlaybackState =
adPlaybackState.withAdUri(adInfo.adGroupIndex, adInfo.adIndexInAdGroup, adUri); adPlaybackState.withAvailableAdUri(adInfo.adGroupIndex, adInfo.adIndexInAdGroup, adUri);
updateAdPlaybackState(); updateAdPlaybackState();
} }
......
...@@ -315,7 +315,7 @@ public final class ImaAdsLoaderTest { ...@@ -315,7 +315,7 @@ public final class ImaAdsLoaderTest {
new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 0) new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 0)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US) .withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI) .withAvailableAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI)
.withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}}) .withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}})
.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0) .withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0)
.withAdResumePositionUs(/* adResumePositionUs= */ 0)); .withAdResumePositionUs(/* adResumePositionUs= */ 0));
...@@ -1063,7 +1063,7 @@ public final class ImaAdsLoaderTest { ...@@ -1063,7 +1063,7 @@ public final class ImaAdsLoaderTest {
new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints)) new AdPlaybackState(TEST_ADS_ID, getAdGroupTimesUsForCuePoints(cuePoints))
.withContentDurationUs(CONTENT_PERIOD_DURATION_US) .withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI) .withAvailableAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI)
.withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}})); .withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}}));
} }
...@@ -1117,7 +1117,7 @@ public final class ImaAdsLoaderTest { ...@@ -1117,7 +1117,7 @@ public final class ImaAdsLoaderTest {
new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 0) new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 0)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US) .withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI) .withAvailableAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI)
.withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}}) .withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}})
.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0) .withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0)
.withAdResumePositionUs(/* adResumePositionUs= */ 0)); .withAdResumePositionUs(/* adResumePositionUs= */ 0));
...@@ -1184,7 +1184,7 @@ public final class ImaAdsLoaderTest { ...@@ -1184,7 +1184,7 @@ public final class ImaAdsLoaderTest {
new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 0) new AdPlaybackState(TEST_ADS_ID, /* adGroupTimesUs...= */ 0)
.withContentDurationUs(CONTENT_PERIOD_DURATION_US) .withContentDurationUs(CONTENT_PERIOD_DURATION_US)
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI) .withAvailableAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, TEST_URI)
.withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}}) .withAdDurationsUs(new long[][] {{TEST_AD_DURATION_US}})
.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0) .withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0)
.withAdResumePositionUs(/* adResumePositionUs= */ 0)); .withAdResumePositionUs(/* adResumePositionUs= */ 0));
......
...@@ -126,6 +126,9 @@ public final class AdPlaybackState implements Bundleable { ...@@ -126,6 +126,9 @@ public final class AdPlaybackState implements Bundleable {
* Returns the index of the next ad in the ad group that should be played after playing {@code * Returns the index of the next ad in the ad group that should be played after playing {@code
* lastPlayedAdIndex}, or {@link #count} if no later ads should be played. If no ads have been * lastPlayedAdIndex}, or {@link #count} if no later ads should be played. If no ads have been
* played, pass -1 to get the index of the first ad to play. * played, pass -1 to get the index of the first ad to play.
*
* <p>Note: {@linkplain #isServerSideInserted Server side inserted ads} are always considered
* playable.
*/ */
public int getNextAdIndexToPlay(@IntRange(from = -1) int lastPlayedAdIndex) { public int getNextAdIndexToPlay(@IntRange(from = -1) int lastPlayedAdIndex) {
int nextAdIndexToPlay = lastPlayedAdIndex + 1; int nextAdIndexToPlay = lastPlayedAdIndex + 1;
...@@ -236,7 +239,7 @@ public final class AdPlaybackState implements Bundleable { ...@@ -236,7 +239,7 @@ public final class AdPlaybackState implements Bundleable {
@CheckResult @CheckResult
public AdGroup withAdState(@AdState int state, @IntRange(from = 0) int index) { public AdGroup withAdState(@AdState int state, @IntRange(from = 0) int index) {
checkArgument(count == C.LENGTH_UNSET || index < count); checkArgument(count == C.LENGTH_UNSET || index < count);
@AdState int[] states = copyStatesWithSpaceForAdCount(this.states, index + 1); @AdState int[] states = copyStatesWithSpaceForAdCount(this.states, /* count= */ index + 1);
checkArgument( checkArgument(
states[index] == AD_STATE_UNAVAILABLE states[index] == AD_STATE_UNAVAILABLE
|| states[index] == AD_STATE_AVAILABLE || states[index] == AD_STATE_AVAILABLE
...@@ -471,7 +474,7 @@ public final class AdPlaybackState implements Bundleable { ...@@ -471,7 +474,7 @@ public final class AdPlaybackState implements Bundleable {
*/ */
public final long contentDurationUs; public final long contentDurationUs;
/** /**
* The number of ad groups the have been removed. Ad groups with indices between {@code 0} * The number of ad groups that have been removed. Ad groups with indices between {@code 0}
* (inclusive) and {@code removedAdGroupCount} (exclusive) will be empty and must not be modified * (inclusive) and {@code removedAdGroupCount} (exclusive) will be empty and must not be modified
* by any of the {@code with*} methods. * by any of the {@code with*} methods.
*/ */
...@@ -640,18 +643,40 @@ public final class AdPlaybackState implements Bundleable { ...@@ -640,18 +643,40 @@ public final class AdPlaybackState implements Bundleable {
adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount);
} }
/** Returns an instance with the specified ad URI. */ /**
* Returns an instance with the specified ad URI and the ad marked as {@linkplain
* #AD_STATE_AVAILABLE available}.
*
* @throws IllegalStateException If {@link Uri#EMPTY} is passed as argument for a client-side
* inserted ad group.
*/
@CheckResult @CheckResult
public AdPlaybackState withAdUri( public AdPlaybackState withAvailableAdUri(
@IntRange(from = 0) int adGroupIndex, @IntRange(from = 0) int adIndexInAdGroup, Uri uri) { @IntRange(from = 0) int adGroupIndex, @IntRange(from = 0) int adIndexInAdGroup, Uri uri) {
int adjustedIndex = adGroupIndex - removedAdGroupCount; int adjustedIndex = adGroupIndex - removedAdGroupCount;
AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length); AdGroup[] adGroups = Util.nullSafeArrayCopy(this.adGroups, this.adGroups.length);
checkState(!Uri.EMPTY.equals(uri) || adGroups[adjustedIndex].isServerSideInserted);
adGroups[adjustedIndex] = adGroups[adjustedIndex].withAdUri(uri, adIndexInAdGroup); adGroups[adjustedIndex] = adGroups[adjustedIndex].withAdUri(uri, adIndexInAdGroup);
return new AdPlaybackState( return new AdPlaybackState(
adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount);
} }
/** Returns an instance with the specified ad marked as played. */ /**
* Returns an instance with the specified ad marked as {@linkplain #AD_STATE_AVAILABLE available}.
*
* <p>Must not be called with client side inserted ad groups. Client side inserted ads should use
* {@link #withAvailableAdUri}.
*
* @throws IllegalStateException in case this methods is called on an ad group that {@linkplain
* AdGroup#isServerSideInserted is not server side inserted}.
*/
@CheckResult
public AdPlaybackState withAvailableAd(
@IntRange(from = 0) int adGroupIndex, @IntRange(from = 0) int adIndexInAdGroup) {
return withAvailableAdUri(adGroupIndex, adIndexInAdGroup, Uri.EMPTY);
}
/** Returns an instance with the specified ad marked as {@linkplain #AD_STATE_PLAYED played}. */
@CheckResult @CheckResult
public AdPlaybackState withPlayedAd( public AdPlaybackState withPlayedAd(
@IntRange(from = 0) int adGroupIndex, @IntRange(from = 0) int adIndexInAdGroup) { @IntRange(from = 0) int adGroupIndex, @IntRange(from = 0) int adIndexInAdGroup) {
...@@ -663,7 +688,7 @@ public final class AdPlaybackState implements Bundleable { ...@@ -663,7 +688,7 @@ public final class AdPlaybackState implements Bundleable {
adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount);
} }
/** Returns an instance with the specified ad marked as skipped. */ /** Returns an instance with the specified ad marked as {@linkplain #AD_STATE_SKIPPED skipped}. */
@CheckResult @CheckResult
public AdPlaybackState withSkippedAd( public AdPlaybackState withSkippedAd(
@IntRange(from = 0) int adGroupIndex, @IntRange(from = 0) int adIndexInAdGroup) { @IntRange(from = 0) int adGroupIndex, @IntRange(from = 0) int adIndexInAdGroup) {
...@@ -675,7 +700,10 @@ public final class AdPlaybackState implements Bundleable { ...@@ -675,7 +700,10 @@ public final class AdPlaybackState implements Bundleable {
adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount); adsId, adGroups, adResumePositionUs, contentDurationUs, removedAdGroupCount);
} }
/** Returns an instance with the specified ad marked as having a load error. */ /**
* Returns an instance with the specified ad marked {@linkplain #AD_STATE_ERROR as having a load
* error}.
*/
@CheckResult @CheckResult
public AdPlaybackState withAdLoadError( public AdPlaybackState withAdLoadError(
@IntRange(from = 0) int adGroupIndex, @IntRange(from = 0) int adIndexInAdGroup) { @IntRange(from = 0) int adGroupIndex, @IntRange(from = 0) int adIndexInAdGroup) {
......
...@@ -4748,7 +4748,8 @@ public final class ExoPlayerTest { ...@@ -4748,7 +4748,8 @@ public final class ExoPlayerTest {
new AdPlaybackState(/* adsId= */ new Object(), /* adGroupTimesUs...= */ 0); new AdPlaybackState(/* adsId= */ new Object(), /* adGroupTimesUs...= */ 0);
adPlaybackState = adPlaybackState.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1); adPlaybackState = adPlaybackState.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1);
adPlaybackState = adPlaybackState =
adPlaybackState.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, Uri.EMPTY); adPlaybackState.withAvailableAdUri(
/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, Uri.parse("https://google.com/ad"));
long[][] durationsUs = new long[1][]; long[][] durationsUs = new long[1][];
durationsUs[0] = new long[] {Util.msToUs(adDurationMs)}; durationsUs[0] = new long[] {Util.msToUs(adDurationMs)};
adPlaybackState = adPlaybackState.withAdDurationsUs(durationsUs); adPlaybackState = adPlaybackState.withAdDurationsUs(durationsUs);
...@@ -4849,7 +4850,8 @@ public final class ExoPlayerTest { ...@@ -4849,7 +4850,8 @@ public final class ExoPlayerTest {
new AdPlaybackState(/* adsId= */ new Object(), /* adGroupTimesUs...= */ 0); new AdPlaybackState(/* adsId= */ new Object(), /* adGroupTimesUs...= */ 0);
adPlaybackState = adPlaybackState.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1); adPlaybackState = adPlaybackState.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1);
adPlaybackState = adPlaybackState =
adPlaybackState.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, Uri.EMPTY); adPlaybackState.withAvailableAdUri(
/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, Uri.parse("https://google.com/ad"));
long[][] durationsUs = new long[1][]; long[][] durationsUs = new long[1][];
durationsUs[0] = new long[] {Util.msToUs(adDurationMs)}; durationsUs[0] = new long[] {Util.msToUs(adDurationMs)};
adPlaybackState = adPlaybackState.withAdDurationsUs(durationsUs); adPlaybackState = adPlaybackState.withAdDurationsUs(durationsUs);
...@@ -4930,7 +4932,10 @@ public final class ExoPlayerTest { ...@@ -4930,7 +4932,10 @@ public final class ExoPlayerTest {
AdPlaybackState adPlaybackState = AdPlaybackState adPlaybackState =
new AdPlaybackState(/* adsId= */ new Object(), /* adGroupTimesUs...= */ 0) new AdPlaybackState(/* adsId= */ new Object(), /* adGroupTimesUs...= */ 0)
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, Uri.EMPTY); .withAvailableAdUri(
/* adGroupIndex= */ 0,
/* adIndexInAdGroup= */ 0,
Uri.parse("https://google.com/ad"));
long[][] durationsUs = new long[1][]; long[][] durationsUs = new long[1][];
durationsUs[0] = new long[] {Util.msToUs(adDurationMs)}; durationsUs[0] = new long[] {Util.msToUs(adDurationMs)};
adPlaybackState = adPlaybackState.withAdDurationsUs(durationsUs); adPlaybackState = adPlaybackState.withAdDurationsUs(durationsUs);
...@@ -9042,7 +9047,10 @@ public final class ExoPlayerTest { ...@@ -9042,7 +9047,10 @@ public final class ExoPlayerTest {
AdPlaybackState adPlaybackState = AdPlaybackState adPlaybackState =
new AdPlaybackState(/* adsId= */ new Object(), /* adGroupTimesUs...= */ 0) new AdPlaybackState(/* adsId= */ new Object(), /* adGroupTimesUs...= */ 0)
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, Uri.EMPTY) .withAvailableAdUri(
/* adGroupIndex= */ 0,
/* adIndexInAdGroup= */ 0,
Uri.parse("https://google.com/ad"))
.withAdDurationsUs(/* adDurationUs= */ new long[][] {{Util.msToUs(4_000)}}); .withAdDurationsUs(/* adDurationUs= */ new long[][] {{Util.msToUs(4_000)}});
Timeline adTimeline = Timeline adTimeline =
new FakeTimeline( new FakeTimeline(
......
...@@ -64,6 +64,7 @@ public final class MediaPeriodQueueTest { ...@@ -64,6 +64,7 @@ public final class MediaPeriodQueueTest {
private static final long FIRST_AD_START_TIME_US = 10 * C.MICROS_PER_SECOND; private static final long FIRST_AD_START_TIME_US = 10 * C.MICROS_PER_SECOND;
private static final long SECOND_AD_START_TIME_US = 20 * C.MICROS_PER_SECOND; private static final long SECOND_AD_START_TIME_US = 20 * C.MICROS_PER_SECOND;
private static final Uri AD_URI = Uri.parse("https://google.com/empty");
private static final Timeline CONTENT_TIMELINE = private static final Timeline CONTENT_TIMELINE =
new SinglePeriodTimeline( new SinglePeriodTimeline(
CONTENT_DURATION_US, CONTENT_DURATION_US,
...@@ -71,8 +72,7 @@ public final class MediaPeriodQueueTest { ...@@ -71,8 +72,7 @@ public final class MediaPeriodQueueTest {
/* isDynamic= */ false, /* isDynamic= */ false,
/* useLiveConfiguration= */ false, /* useLiveConfiguration= */ false,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(AD_URI));
private static final Uri AD_URI = Uri.EMPTY;
private MediaPeriodQueue mediaPeriodQueue; private MediaPeriodQueue mediaPeriodQueue;
private AdPlaybackState adPlaybackState; private AdPlaybackState adPlaybackState;
...@@ -1168,7 +1168,7 @@ public final class MediaPeriodQueueTest { ...@@ -1168,7 +1168,7 @@ public final class MediaPeriodQueueTest {
adPlaybackState = adPlaybackState =
adPlaybackState adPlaybackState
.withAdCount(adGroupIndex, /* adCount= */ 1) .withAdCount(adGroupIndex, /* adCount= */ 1)
.withAdUri(adGroupIndex, /* adIndexInAdGroup= */ 0, AD_URI) .withAvailableAdUri(adGroupIndex, /* adIndexInAdGroup= */ 0, AD_URI)
.withAdDurationsUs(newDurations); .withAdDurationsUs(newDurations);
updateTimeline(); updateTimeline();
} }
......
...@@ -61,7 +61,7 @@ public final class AdsMediaSourceTest { ...@@ -61,7 +61,7 @@ public final class AdsMediaSourceTest {
/* isDynamic= */ false, /* isDynamic= */ false,
/* useLiveConfiguration= */ false, /* useLiveConfiguration= */ false,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.parse("https://google.com/empty")));
private static final Object PREROLL_AD_PERIOD_UID = private static final Object PREROLL_AD_PERIOD_UID =
PREROLL_AD_TIMELINE.getUidOfPeriod(/* periodIndex= */ 0); PREROLL_AD_TIMELINE.getUidOfPeriod(/* periodIndex= */ 0);
...@@ -73,7 +73,7 @@ public final class AdsMediaSourceTest { ...@@ -73,7 +73,7 @@ public final class AdsMediaSourceTest {
/* isDynamic= */ false, /* isDynamic= */ false,
/* useLiveConfiguration= */ false, /* useLiveConfiguration= */ false,
/* manifest= */ null, /* manifest= */ null,
MediaItem.fromUri(Uri.EMPTY)); MediaItem.fromUri(Uri.parse("https://google.com/empty")));
private static final Object CONTENT_PERIOD_UID = private static final Object CONTENT_PERIOD_UID =
CONTENT_TIMELINE.getUidOfPeriod(/* periodIndex= */ 0); CONTENT_TIMELINE.getUidOfPeriod(/* periodIndex= */ 0);
...@@ -81,7 +81,8 @@ public final class AdsMediaSourceTest { ...@@ -81,7 +81,8 @@ public final class AdsMediaSourceTest {
new AdPlaybackState(/* adsId= */ new Object(), /* adGroupTimesUs...= */ 0) new AdPlaybackState(/* adsId= */ new Object(), /* adGroupTimesUs...= */ 0)
.withContentDurationUs(CONTENT_DURATION_US) .withContentDurationUs(CONTENT_DURATION_US)
.withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1)
.withAdUri(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, Uri.EMPTY) .withAvailableAdUri(
/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0, Uri.parse("https://google.com/ad"))
.withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0) .withPlayedAd(/* adGroupIndex= */ 0, /* adIndexInAdGroup= */ 0)
.withAdResumePositionUs(/* adResumePositionUs= */ 0); .withAdResumePositionUs(/* adResumePositionUs= */ 0);
......
...@@ -293,7 +293,7 @@ public final class FakeTimeline extends Timeline { ...@@ -293,7 +293,7 @@ public final class FakeTimeline extends Timeline {
adPlaybackState = adPlaybackState.withAdCount(/* adGroupIndex= */ i, adsPerAdGroup); adPlaybackState = adPlaybackState.withAdCount(/* adGroupIndex= */ i, adsPerAdGroup);
for (int j = 0; j < adsPerAdGroup; j++) { for (int j = 0; j < adsPerAdGroup; j++) {
adPlaybackState = adPlaybackState =
adPlaybackState.withAdUri( adPlaybackState.withAvailableAdUri(
/* adGroupIndex= */ i, /* adGroupIndex= */ i,
/* adIndexInAdGroup= */ j, /* adIndexInAdGroup= */ j,
Uri.parse("https://example.com/ad/" + i + "/" + j)); Uri.parse("https://example.com/ad/" + i + "/" + j));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment