From 58ac5720242e3893cecc66f476a862ba164da059 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 20 Mar 2017 07:56:36 -0700 Subject: [PATCH] Tell Moe about new UI module + update external settings.gradle ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=150625313 --- library/ui/src/main/AndroidManifest.xml | 17 + .../exoplayer2/ui/AspectRatioFrameLayout.java | 148 ++++ .../exoplayer2/ui/DebugTextViewHelper.java | 181 ++++ .../exoplayer2/ui/PlaybackControlView.java | 812 ++++++++++++++++++ .../exoplayer2/ui/SimpleExoPlayerView.java | 717 ++++++++++++++++ .../exoplayer2/ui/SubtitlePainter.java | 405 +++++++++ .../android/exoplayer2/ui/SubtitleView.java | 265 ++++++ .../exo_controls_fastforward.xml | 25 + .../drawable-anydpi-v21/exo_controls_next.xml | 25 + .../exo_controls_pause.xml | 25 + .../drawable-anydpi-v21/exo_controls_play.xml | 25 + .../exo_controls_previous.xml | 25 + .../exo_controls_rewind.xml | 25 + .../exo_controls_fastforward.png | Bin 0 -> 354 bytes .../res/drawable-hdpi/exo_controls_next.png | Bin 0 -> 323 bytes .../res/drawable-hdpi/exo_controls_pause.png | Bin 0 -> 108 bytes .../res/drawable-hdpi/exo_controls_play.png | Bin 0 -> 286 bytes .../drawable-hdpi/exo_controls_previous.png | Bin 0 -> 292 bytes .../res/drawable-hdpi/exo_controls_rewind.png | Bin 0 -> 347 bytes .../exo_controls_fastforward.png | Bin 0 -> 192 bytes .../res/drawable-ldpi/exo_controls_next.png | Bin 0 -> 167 bytes .../res/drawable-ldpi/exo_controls_pause.png | Bin 0 -> 91 bytes .../res/drawable-ldpi/exo_controls_play.png | Bin 0 -> 182 bytes .../drawable-ldpi/exo_controls_previous.png | Bin 0 -> 187 bytes .../res/drawable-ldpi/exo_controls_rewind.png | Bin 0 -> 214 bytes .../exo_controls_fastforward.png | Bin 0 -> 255 bytes .../res/drawable-mdpi/exo_controls_next.png | Bin 0 -> 276 bytes .../res/drawable-mdpi/exo_controls_pause.png | Bin 0 -> 153 bytes .../res/drawable-mdpi/exo_controls_play.png | Bin 0 -> 228 bytes .../drawable-mdpi/exo_controls_previous.png | Bin 0 -> 227 bytes .../res/drawable-mdpi/exo_controls_rewind.png | Bin 0 -> 273 bytes .../exo_controls_fastforward.png | Bin 0 -> 392 bytes .../res/drawable-xhdpi/exo_controls_next.png | Bin 0 -> 334 bytes .../res/drawable-xhdpi/exo_controls_pause.png | Bin 0 -> 164 bytes .../res/drawable-xhdpi/exo_controls_play.png | Bin 0 -> 343 bytes .../drawable-xhdpi/exo_controls_previous.png | Bin 0 -> 339 bytes .../drawable-xhdpi/exo_controls_rewind.png | Bin 0 -> 400 bytes .../exo_controls_fastforward.png | Bin 0 -> 584 bytes .../res/drawable-xxhdpi/exo_controls_next.png | Bin 0 -> 391 bytes .../drawable-xxhdpi/exo_controls_pause.png | Bin 0 -> 113 bytes .../res/drawable-xxhdpi/exo_controls_play.png | Bin 0 -> 384 bytes .../drawable-xxhdpi/exo_controls_previous.png | Bin 0 -> 464 bytes .../drawable-xxhdpi/exo_controls_rewind.png | Bin 0 -> 571 bytes .../res/layout/exo_playback_control_view.xml | 87 ++ .../res/layout/exo_simple_player_view.xml | 49 ++ library/ui/src/main/res/values-af/strings.xml | 25 + library/ui/src/main/res/values-am/strings.xml | 25 + library/ui/src/main/res/values-ar/strings.xml | 25 + .../ui/src/main/res/values-az-rAZ/strings.xml | 25 + .../src/main/res/values-b+sr+Latn/strings.xml | 25 + .../ui/src/main/res/values-be-rBY/strings.xml | 25 + library/ui/src/main/res/values-bg/strings.xml | 25 + .../ui/src/main/res/values-bn-rBD/strings.xml | 25 + .../ui/src/main/res/values-bs-rBA/strings.xml | 25 + library/ui/src/main/res/values-ca/strings.xml | 25 + library/ui/src/main/res/values-cs/strings.xml | 25 + library/ui/src/main/res/values-da/strings.xml | 25 + library/ui/src/main/res/values-de/strings.xml | 25 + library/ui/src/main/res/values-el/strings.xml | 25 + .../ui/src/main/res/values-en-rAU/strings.xml | 25 + .../ui/src/main/res/values-en-rGB/strings.xml | 25 + .../ui/src/main/res/values-en-rIN/strings.xml | 25 + .../ui/src/main/res/values-es-rUS/strings.xml | 25 + library/ui/src/main/res/values-es/strings.xml | 25 + .../ui/src/main/res/values-et-rEE/strings.xml | 25 + .../ui/src/main/res/values-eu-rES/strings.xml | 25 + library/ui/src/main/res/values-fa/strings.xml | 25 + library/ui/src/main/res/values-fi/strings.xml | 25 + .../ui/src/main/res/values-fr-rCA/strings.xml | 25 + library/ui/src/main/res/values-fr/strings.xml | 25 + .../ui/src/main/res/values-gl-rES/strings.xml | 25 + .../ui/src/main/res/values-gu-rIN/strings.xml | 25 + library/ui/src/main/res/values-hi/strings.xml | 25 + library/ui/src/main/res/values-hr/strings.xml | 25 + library/ui/src/main/res/values-hu/strings.xml | 25 + .../ui/src/main/res/values-hy-rAM/strings.xml | 25 + library/ui/src/main/res/values-in/strings.xml | 25 + .../ui/src/main/res/values-is-rIS/strings.xml | 25 + library/ui/src/main/res/values-it/strings.xml | 25 + library/ui/src/main/res/values-iw/strings.xml | 25 + library/ui/src/main/res/values-ja/strings.xml | 25 + .../ui/src/main/res/values-ka-rGE/strings.xml | 25 + .../ui/src/main/res/values-kk-rKZ/strings.xml | 25 + .../ui/src/main/res/values-km-rKH/strings.xml | 25 + .../ui/src/main/res/values-kn-rIN/strings.xml | 25 + library/ui/src/main/res/values-ko/strings.xml | 25 + .../ui/src/main/res/values-ky-rKG/strings.xml | 25 + .../ui/src/main/res/values-lo-rLA/strings.xml | 25 + library/ui/src/main/res/values-lt/strings.xml | 25 + library/ui/src/main/res/values-lv/strings.xml | 25 + .../ui/src/main/res/values-mk-rMK/strings.xml | 25 + .../ui/src/main/res/values-ml-rIN/strings.xml | 25 + .../ui/src/main/res/values-mn-rMN/strings.xml | 25 + .../ui/src/main/res/values-mr-rIN/strings.xml | 25 + .../ui/src/main/res/values-ms-rMY/strings.xml | 25 + .../ui/src/main/res/values-my-rMM/strings.xml | 25 + library/ui/src/main/res/values-nb/strings.xml | 25 + .../ui/src/main/res/values-ne-rNP/strings.xml | 25 + library/ui/src/main/res/values-nl/strings.xml | 25 + .../ui/src/main/res/values-pa-rIN/strings.xml | 25 + library/ui/src/main/res/values-pl/strings.xml | 25 + .../ui/src/main/res/values-pt-rBR/strings.xml | 25 + .../ui/src/main/res/values-pt-rPT/strings.xml | 25 + library/ui/src/main/res/values-pt/strings.xml | 25 + library/ui/src/main/res/values-ro/strings.xml | 25 + library/ui/src/main/res/values-ru/strings.xml | 25 + .../ui/src/main/res/values-si-rLK/strings.xml | 25 + library/ui/src/main/res/values-sk/strings.xml | 25 + library/ui/src/main/res/values-sl/strings.xml | 25 + .../ui/src/main/res/values-sq-rAL/strings.xml | 25 + library/ui/src/main/res/values-sr/strings.xml | 25 + library/ui/src/main/res/values-sv/strings.xml | 25 + library/ui/src/main/res/values-sw/strings.xml | 25 + .../ui/src/main/res/values-ta-rIN/strings.xml | 25 + .../ui/src/main/res/values-te-rIN/strings.xml | 25 + library/ui/src/main/res/values-th/strings.xml | 25 + library/ui/src/main/res/values-tl/strings.xml | 25 + library/ui/src/main/res/values-tr/strings.xml | 25 + library/ui/src/main/res/values-uk/strings.xml | 25 + .../ui/src/main/res/values-ur-rPK/strings.xml | 25 + .../ui/src/main/res/values-uz-rUZ/strings.xml | 25 + library/ui/src/main/res/values-v11/styles.xml | 24 + library/ui/src/main/res/values-vi/strings.xml | 25 + .../ui/src/main/res/values-zh-rCN/strings.xml | 25 + .../ui/src/main/res/values-zh-rHK/strings.xml | 25 + .../ui/src/main/res/values-zh-rTW/strings.xml | 25 + library/ui/src/main/res/values-zu/strings.xml | 25 + library/ui/src/main/res/values/attrs.xml | 62 ++ library/ui/src/main/res/values/constants.xml | 21 + library/ui/src/main/res/values/ids.xml | 34 + library/ui/src/main/res/values/strings.xml | 24 + library/ui/src/main/res/values/styles.xml | 54 ++ settings.gradle | 2 + 133 files changed, 5077 insertions(+) create mode 100644 library/ui/src/main/AndroidManifest.xml create mode 100644 library/ui/src/main/java/com/google/android/exoplayer2/ui/AspectRatioFrameLayout.java create mode 100644 library/ui/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java create mode 100644 library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java create mode 100644 library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java create mode 100644 library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitlePainter.java create mode 100644 library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleView.java create mode 100644 library/ui/src/main/res/drawable-anydpi-v21/exo_controls_fastforward.xml create mode 100644 library/ui/src/main/res/drawable-anydpi-v21/exo_controls_next.xml create mode 100644 library/ui/src/main/res/drawable-anydpi-v21/exo_controls_pause.xml create mode 100644 library/ui/src/main/res/drawable-anydpi-v21/exo_controls_play.xml create mode 100644 library/ui/src/main/res/drawable-anydpi-v21/exo_controls_previous.xml create mode 100644 library/ui/src/main/res/drawable-anydpi-v21/exo_controls_rewind.xml create mode 100644 library/ui/src/main/res/drawable-hdpi/exo_controls_fastforward.png create mode 100644 library/ui/src/main/res/drawable-hdpi/exo_controls_next.png create mode 100644 library/ui/src/main/res/drawable-hdpi/exo_controls_pause.png create mode 100644 library/ui/src/main/res/drawable-hdpi/exo_controls_play.png create mode 100644 library/ui/src/main/res/drawable-hdpi/exo_controls_previous.png create mode 100644 library/ui/src/main/res/drawable-hdpi/exo_controls_rewind.png create mode 100644 library/ui/src/main/res/drawable-ldpi/exo_controls_fastforward.png create mode 100644 library/ui/src/main/res/drawable-ldpi/exo_controls_next.png create mode 100644 library/ui/src/main/res/drawable-ldpi/exo_controls_pause.png create mode 100644 library/ui/src/main/res/drawable-ldpi/exo_controls_play.png create mode 100644 library/ui/src/main/res/drawable-ldpi/exo_controls_previous.png create mode 100644 library/ui/src/main/res/drawable-ldpi/exo_controls_rewind.png create mode 100644 library/ui/src/main/res/drawable-mdpi/exo_controls_fastforward.png create mode 100644 library/ui/src/main/res/drawable-mdpi/exo_controls_next.png create mode 100644 library/ui/src/main/res/drawable-mdpi/exo_controls_pause.png create mode 100644 library/ui/src/main/res/drawable-mdpi/exo_controls_play.png create mode 100644 library/ui/src/main/res/drawable-mdpi/exo_controls_previous.png create mode 100644 library/ui/src/main/res/drawable-mdpi/exo_controls_rewind.png create mode 100644 library/ui/src/main/res/drawable-xhdpi/exo_controls_fastforward.png create mode 100644 library/ui/src/main/res/drawable-xhdpi/exo_controls_next.png create mode 100644 library/ui/src/main/res/drawable-xhdpi/exo_controls_pause.png create mode 100644 library/ui/src/main/res/drawable-xhdpi/exo_controls_play.png create mode 100644 library/ui/src/main/res/drawable-xhdpi/exo_controls_previous.png create mode 100644 library/ui/src/main/res/drawable-xhdpi/exo_controls_rewind.png create mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_controls_fastforward.png create mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_controls_next.png create mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_controls_pause.png create mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_controls_play.png create mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_controls_previous.png create mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_controls_rewind.png create mode 100644 library/ui/src/main/res/layout/exo_playback_control_view.xml create mode 100644 library/ui/src/main/res/layout/exo_simple_player_view.xml create mode 100644 library/ui/src/main/res/values-af/strings.xml create mode 100644 library/ui/src/main/res/values-am/strings.xml create mode 100644 library/ui/src/main/res/values-ar/strings.xml create mode 100644 library/ui/src/main/res/values-az-rAZ/strings.xml create mode 100644 library/ui/src/main/res/values-b+sr+Latn/strings.xml create mode 100644 library/ui/src/main/res/values-be-rBY/strings.xml create mode 100644 library/ui/src/main/res/values-bg/strings.xml create mode 100644 library/ui/src/main/res/values-bn-rBD/strings.xml create mode 100644 library/ui/src/main/res/values-bs-rBA/strings.xml create mode 100644 library/ui/src/main/res/values-ca/strings.xml create mode 100644 library/ui/src/main/res/values-cs/strings.xml create mode 100644 library/ui/src/main/res/values-da/strings.xml create mode 100644 library/ui/src/main/res/values-de/strings.xml create mode 100644 library/ui/src/main/res/values-el/strings.xml create mode 100644 library/ui/src/main/res/values-en-rAU/strings.xml create mode 100644 library/ui/src/main/res/values-en-rGB/strings.xml create mode 100644 library/ui/src/main/res/values-en-rIN/strings.xml create mode 100644 library/ui/src/main/res/values-es-rUS/strings.xml create mode 100644 library/ui/src/main/res/values-es/strings.xml create mode 100644 library/ui/src/main/res/values-et-rEE/strings.xml create mode 100644 library/ui/src/main/res/values-eu-rES/strings.xml create mode 100644 library/ui/src/main/res/values-fa/strings.xml create mode 100644 library/ui/src/main/res/values-fi/strings.xml create mode 100644 library/ui/src/main/res/values-fr-rCA/strings.xml create mode 100644 library/ui/src/main/res/values-fr/strings.xml create mode 100644 library/ui/src/main/res/values-gl-rES/strings.xml create mode 100644 library/ui/src/main/res/values-gu-rIN/strings.xml create mode 100644 library/ui/src/main/res/values-hi/strings.xml create mode 100644 library/ui/src/main/res/values-hr/strings.xml create mode 100644 library/ui/src/main/res/values-hu/strings.xml create mode 100644 library/ui/src/main/res/values-hy-rAM/strings.xml create mode 100644 library/ui/src/main/res/values-in/strings.xml create mode 100644 library/ui/src/main/res/values-is-rIS/strings.xml create mode 100644 library/ui/src/main/res/values-it/strings.xml create mode 100644 library/ui/src/main/res/values-iw/strings.xml create mode 100644 library/ui/src/main/res/values-ja/strings.xml create mode 100644 library/ui/src/main/res/values-ka-rGE/strings.xml create mode 100644 library/ui/src/main/res/values-kk-rKZ/strings.xml create mode 100644 library/ui/src/main/res/values-km-rKH/strings.xml create mode 100644 library/ui/src/main/res/values-kn-rIN/strings.xml create mode 100644 library/ui/src/main/res/values-ko/strings.xml create mode 100644 library/ui/src/main/res/values-ky-rKG/strings.xml create mode 100644 library/ui/src/main/res/values-lo-rLA/strings.xml create mode 100644 library/ui/src/main/res/values-lt/strings.xml create mode 100644 library/ui/src/main/res/values-lv/strings.xml create mode 100644 library/ui/src/main/res/values-mk-rMK/strings.xml create mode 100644 library/ui/src/main/res/values-ml-rIN/strings.xml create mode 100644 library/ui/src/main/res/values-mn-rMN/strings.xml create mode 100644 library/ui/src/main/res/values-mr-rIN/strings.xml create mode 100644 library/ui/src/main/res/values-ms-rMY/strings.xml create mode 100644 library/ui/src/main/res/values-my-rMM/strings.xml create mode 100644 library/ui/src/main/res/values-nb/strings.xml create mode 100644 library/ui/src/main/res/values-ne-rNP/strings.xml create mode 100644 library/ui/src/main/res/values-nl/strings.xml create mode 100644 library/ui/src/main/res/values-pa-rIN/strings.xml create mode 100644 library/ui/src/main/res/values-pl/strings.xml create mode 100644 library/ui/src/main/res/values-pt-rBR/strings.xml create mode 100644 library/ui/src/main/res/values-pt-rPT/strings.xml create mode 100644 library/ui/src/main/res/values-pt/strings.xml create mode 100644 library/ui/src/main/res/values-ro/strings.xml create mode 100644 library/ui/src/main/res/values-ru/strings.xml create mode 100644 library/ui/src/main/res/values-si-rLK/strings.xml create mode 100644 library/ui/src/main/res/values-sk/strings.xml create mode 100644 library/ui/src/main/res/values-sl/strings.xml create mode 100644 library/ui/src/main/res/values-sq-rAL/strings.xml create mode 100644 library/ui/src/main/res/values-sr/strings.xml create mode 100644 library/ui/src/main/res/values-sv/strings.xml create mode 100644 library/ui/src/main/res/values-sw/strings.xml create mode 100644 library/ui/src/main/res/values-ta-rIN/strings.xml create mode 100644 library/ui/src/main/res/values-te-rIN/strings.xml create mode 100644 library/ui/src/main/res/values-th/strings.xml create mode 100644 library/ui/src/main/res/values-tl/strings.xml create mode 100644 library/ui/src/main/res/values-tr/strings.xml create mode 100644 library/ui/src/main/res/values-uk/strings.xml create mode 100644 library/ui/src/main/res/values-ur-rPK/strings.xml create mode 100644 library/ui/src/main/res/values-uz-rUZ/strings.xml create mode 100644 library/ui/src/main/res/values-v11/styles.xml create mode 100644 library/ui/src/main/res/values-vi/strings.xml create mode 100644 library/ui/src/main/res/values-zh-rCN/strings.xml create mode 100644 library/ui/src/main/res/values-zh-rHK/strings.xml create mode 100644 library/ui/src/main/res/values-zh-rTW/strings.xml create mode 100644 library/ui/src/main/res/values-zu/strings.xml create mode 100644 library/ui/src/main/res/values/attrs.xml create mode 100644 library/ui/src/main/res/values/constants.xml create mode 100644 library/ui/src/main/res/values/ids.xml create mode 100644 library/ui/src/main/res/values/strings.xml create mode 100644 library/ui/src/main/res/values/styles.xml diff --git a/library/ui/src/main/AndroidManifest.xml b/library/ui/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..72189e22a7 --- /dev/null +++ b/library/ui/src/main/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/AspectRatioFrameLayout.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/AspectRatioFrameLayout.java new file mode 100644 index 0000000000..38294f5208 --- /dev/null +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/AspectRatioFrameLayout.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2016 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.ui; + +import android.content.Context; +import android.content.res.TypedArray; +import android.support.annotation.IntDef; +import android.util.AttributeSet; +import android.widget.FrameLayout; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * A {@link FrameLayout} that resizes itself to match a specified aspect ratio. + */ +public final class AspectRatioFrameLayout extends FrameLayout { + + /** + * Resize modes for {@link AspectRatioFrameLayout}. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({RESIZE_MODE_FIT, RESIZE_MODE_FIXED_WIDTH, RESIZE_MODE_FIXED_HEIGHT, RESIZE_MODE_FILL}) + public @interface ResizeMode {} + + /** + * Either the width or height is decreased to obtain the desired aspect ratio. + */ + public static final int RESIZE_MODE_FIT = 0; + /** + * The width is fixed and the height is increased or decreased to obtain the desired aspect ratio. + */ + public static final int RESIZE_MODE_FIXED_WIDTH = 1; + /** + * The height is fixed and the width is increased or decreased to obtain the desired aspect ratio. + */ + public static final int RESIZE_MODE_FIXED_HEIGHT = 2; + /** + * The specified aspect ratio is ignored. + */ + public static final int RESIZE_MODE_FILL = 3; + + /** + * The {@link FrameLayout} will not resize itself if the fractional difference between its natural + * aspect ratio and the requested aspect ratio falls below this threshold. + *

+ * This tolerance allows the view to occupy the whole of the screen when the requested aspect + * ratio is very close, but not exactly equal to, the aspect ratio of the screen. This may reduce + * the number of view layers that need to be composited by the underlying system, which can help + * to reduce power consumption. + */ + private static final float MAX_ASPECT_RATIO_DEFORMATION_FRACTION = 0.01f; + + private float videoAspectRatio; + private int resizeMode; + + public AspectRatioFrameLayout(Context context) { + this(context, null); + } + + public AspectRatioFrameLayout(Context context, AttributeSet attrs) { + super(context, attrs); + resizeMode = RESIZE_MODE_FIT; + if (attrs != null) { + TypedArray a = context.getTheme().obtainStyledAttributes(attrs, + R.styleable.AspectRatioFrameLayout, 0, 0); + try { + resizeMode = a.getInt(R.styleable.AspectRatioFrameLayout_resize_mode, RESIZE_MODE_FIT); + } finally { + a.recycle(); + } + } + } + + /** + * Set the aspect ratio that this view should satisfy. + * + * @param widthHeightRatio The width to height ratio. + */ + public void setAspectRatio(float widthHeightRatio) { + if (this.videoAspectRatio != widthHeightRatio) { + this.videoAspectRatio = widthHeightRatio; + requestLayout(); + } + } + + /** + * Sets the resize mode. + * + * @param resizeMode The resize mode. + */ + public void setResizeMode(@ResizeMode int resizeMode) { + if (this.resizeMode != resizeMode) { + this.resizeMode = resizeMode; + requestLayout(); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (resizeMode == RESIZE_MODE_FILL || videoAspectRatio <= 0) { + // Aspect ratio not set. + return; + } + + int width = getMeasuredWidth(); + int height = getMeasuredHeight(); + float viewAspectRatio = (float) width / height; + float aspectDeformation = videoAspectRatio / viewAspectRatio - 1; + if (Math.abs(aspectDeformation) <= MAX_ASPECT_RATIO_DEFORMATION_FRACTION) { + // We're within the allowed tolerance. + return; + } + + switch (resizeMode) { + case RESIZE_MODE_FIXED_WIDTH: + height = (int) (width / videoAspectRatio); + break; + case RESIZE_MODE_FIXED_HEIGHT: + width = (int) (height * videoAspectRatio); + break; + default: + if (aspectDeformation > 0) { + height = (int) (width / videoAspectRatio); + } else { + width = (int) (height * videoAspectRatio); + } + break; + } + super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); + } + +} diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java new file mode 100644 index 0000000000..1bf5b59a4a --- /dev/null +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DebugTextViewHelper.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2016 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.ui; + +import android.widget.TextView; +import com.google.android.exoplayer2.ExoPlaybackException; +import com.google.android.exoplayer2.ExoPlayer; +import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.Timeline; +import com.google.android.exoplayer2.decoder.DecoderCounters; +import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; + +/** + * A helper class for periodically updating a {@link TextView} with debug information obtained from + * a {@link SimpleExoPlayer}. + */ +public final class DebugTextViewHelper implements Runnable, ExoPlayer.EventListener { + + private static final int REFRESH_INTERVAL_MS = 1000; + + private final SimpleExoPlayer player; + private final TextView textView; + + private boolean started; + + /** + * @param player The {@link SimpleExoPlayer} from which debug information should be obtained. + * @param textView The {@link TextView} that should be updated to display the information. + */ + public DebugTextViewHelper(SimpleExoPlayer player, TextView textView) { + this.player = player; + this.textView = textView; + } + + /** + * Starts periodic updates of the {@link TextView}. Must be called from the application's main + * thread. + */ + public void start() { + if (started) { + return; + } + started = true; + player.addListener(this); + updateAndPost(); + } + + /** + * Stops periodic updates of the {@link TextView}. Must be called from the application's main + * thread. + */ + public void stop() { + if (!started) { + return; + } + started = false; + player.removeListener(this); + textView.removeCallbacks(this); + } + + // ExoPlayer.EventListener implementation. + + @Override + public void onLoadingChanged(boolean isLoading) { + // Do nothing. + } + + @Override + public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { + updateAndPost(); + } + + @Override + public void onPositionDiscontinuity() { + updateAndPost(); + } + + @Override + public void onTimelineChanged(Timeline timeline, Object manifest) { + // Do nothing. + } + + @Override + public void onPlayerError(ExoPlaybackException error) { + // Do nothing. + } + + @Override + public void onTracksChanged(TrackGroupArray tracks, TrackSelectionArray selections) { + // Do nothing. + } + + // Runnable implementation. + + @Override + public void run() { + updateAndPost(); + } + + // Private methods. + + private void updateAndPost() { + textView.setText(getPlayerStateString() + getPlayerWindowIndexString() + getVideoString() + + getAudioString()); + textView.removeCallbacks(this); + textView.postDelayed(this, REFRESH_INTERVAL_MS); + } + + private String getPlayerStateString() { + String text = "playWhenReady:" + player.getPlayWhenReady() + " playbackState:"; + switch (player.getPlaybackState()) { + case ExoPlayer.STATE_BUFFERING: + text += "buffering"; + break; + case ExoPlayer.STATE_ENDED: + text += "ended"; + break; + case ExoPlayer.STATE_IDLE: + text += "idle"; + break; + case ExoPlayer.STATE_READY: + text += "ready"; + break; + default: + text += "unknown"; + break; + } + return text; + } + + private String getPlayerWindowIndexString() { + return " window:" + player.getCurrentWindowIndex(); + } + + private String getVideoString() { + Format format = player.getVideoFormat(); + if (format == null) { + return ""; + } + return "\n" + format.sampleMimeType + "(id:" + format.id + " r:" + format.width + "x" + + format.height + getDecoderCountersBufferCountString(player.getVideoDecoderCounters()) + + ")"; + } + + private String getAudioString() { + Format format = player.getAudioFormat(); + if (format == null) { + return ""; + } + return "\n" + format.sampleMimeType + "(id:" + format.id + " hz:" + format.sampleRate + " ch:" + + format.channelCount + + getDecoderCountersBufferCountString(player.getAudioDecoderCounters()) + ")"; + } + + private static String getDecoderCountersBufferCountString(DecoderCounters counters) { + if (counters == null) { + return ""; + } + counters.ensureUpdated(); + return " rb:" + counters.renderedOutputBufferCount + + " sb:" + counters.skippedOutputBufferCount + + " db:" + counters.droppedOutputBufferCount + + " mcdb:" + counters.maxConsecutiveDroppedOutputBufferCount; + } + +} diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java new file mode 100644 index 0000000000..88a65589fc --- /dev/null +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java @@ -0,0 +1,812 @@ +/* + * Copyright (C) 2016 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.ui; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.TypedArray; +import android.os.SystemClock; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.SeekBar; +import android.widget.TextView; +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.ExoPlaybackException; +import com.google.android.exoplayer2.ExoPlayer; +import com.google.android.exoplayer2.Timeline; +import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; +import com.google.android.exoplayer2.util.Util; +import java.util.Formatter; +import java.util.Locale; + +/** + * A view for controlling {@link ExoPlayer} instances. + *

+ * A PlaybackControlView can be customized by setting attributes (or calling corresponding methods), + * overriding the view's layout file or by specifying a custom view layout file, as outlined below. + * + *

Attributes

+ * The following attributes can be set on a PlaybackControlView when used in a layout XML file: + *

+ *

+ * + *

Overriding the layout file

+ * To customize the layout of PlaybackControlView throughout your app, or just for certain + * configurations, you can define {@code exo_playback_control_view.xml} layout files in your + * application {@code res/layout*} directories. These layouts will override the one provided by the + * ExoPlayer library, and will be inflated for use by PlaybackControlView. The view identifies and + * binds its children by looking for the following ids: + *

+ *

+ *

+ * All child views are optional and so can be omitted if not required, however where defined they + * must be of the expected type. + * + *

Specifying a custom layout file

+ * Defining your own {@code exo_playback_control_view.xml} is useful to customize the layout of + * PlaybackControlView throughout your application. It's also possible to customize the layout for a + * single instance in a layout file. This is achieved by setting the {@code controller_layout_id} + * attribute on a PlaybackControlView. This will cause the specified layout to be inflated instead + * of {@code exo_playback_control_view.xml} for only the instance on which the attribute is set. + */ +public class PlaybackControlView extends FrameLayout { + + /** + * Listener to be notified about changes of the visibility of the UI control. + */ + public interface VisibilityListener { + + /** + * Called when the visibility changes. + * + * @param visibility The new visibility. Either {@link View#VISIBLE} or {@link View#GONE}. + */ + void onVisibilityChange(int visibility); + + } + + /** + * Dispatches seek operations to the player. + */ + public interface SeekDispatcher { + + /** + * @param player The player to seek. + * @param windowIndex The index of the window. + * @param positionMs The seek position in the specified window, or {@link C#TIME_UNSET} to seek + * to the window's default position. + * @return True if the seek was dispatched. False otherwise. + */ + boolean dispatchSeek(ExoPlayer player, int windowIndex, long positionMs); + + } + + /** + * Default {@link SeekDispatcher} that dispatches seeks to the player without modification. + */ + public static final SeekDispatcher DEFAULT_SEEK_DISPATCHER = new SeekDispatcher() { + + @Override + public boolean dispatchSeek(ExoPlayer player, int windowIndex, long positionMs) { + player.seekTo(windowIndex, positionMs); + return true; + } + + }; + + public static final int DEFAULT_FAST_FORWARD_MS = 15000; + public static final int DEFAULT_REWIND_MS = 5000; + public static final int DEFAULT_SHOW_TIMEOUT_MS = 5000; + + private static final int PROGRESS_BAR_MAX = 1000; + private static final long MAX_POSITION_FOR_SEEK_TO_PREVIOUS = 3000; + + private final ComponentListener componentListener; + private final View previousButton; + private final View nextButton; + private final View playButton; + private final View pauseButton; + private final View fastForwardButton; + private final View rewindButton; + private final TextView durationView; + private final TextView positionView; + private final SeekBar progressBar; + private final StringBuilder formatBuilder; + private final Formatter formatter; + private final Timeline.Window currentWindow; + + private ExoPlayer player; + private SeekDispatcher seekDispatcher; + private VisibilityListener visibilityListener; + + private boolean isAttachedToWindow; + private boolean dragging; + private int rewindMs; + private int fastForwardMs; + private int showTimeoutMs; + private long hideAtMs; + + private final Runnable updateProgressAction = new Runnable() { + @Override + public void run() { + updateProgress(); + } + }; + + private final Runnable hideAction = new Runnable() { + @Override + public void run() { + hide(); + } + }; + + public PlaybackControlView(Context context) { + this(context, null); + } + + public PlaybackControlView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public PlaybackControlView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + int controllerLayoutId = R.layout.exo_playback_control_view; + rewindMs = DEFAULT_REWIND_MS; + fastForwardMs = DEFAULT_FAST_FORWARD_MS; + showTimeoutMs = DEFAULT_SHOW_TIMEOUT_MS; + if (attrs != null) { + TypedArray a = context.getTheme().obtainStyledAttributes(attrs, + R.styleable.PlaybackControlView, 0, 0); + try { + rewindMs = a.getInt(R.styleable.PlaybackControlView_rewind_increment, rewindMs); + fastForwardMs = a.getInt(R.styleable.PlaybackControlView_fastforward_increment, + fastForwardMs); + showTimeoutMs = a.getInt(R.styleable.PlaybackControlView_show_timeout, showTimeoutMs); + controllerLayoutId = a.getResourceId(R.styleable.PlaybackControlView_controller_layout_id, + controllerLayoutId); + } finally { + a.recycle(); + } + } + currentWindow = new Timeline.Window(); + formatBuilder = new StringBuilder(); + formatter = new Formatter(formatBuilder, Locale.getDefault()); + componentListener = new ComponentListener(); + seekDispatcher = DEFAULT_SEEK_DISPATCHER; + + LayoutInflater.from(context).inflate(controllerLayoutId, this); + setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); + + durationView = (TextView) findViewById(R.id.exo_duration); + positionView = (TextView) findViewById(R.id.exo_position); + progressBar = (SeekBar) findViewById(R.id.exo_progress); + if (progressBar != null) { + progressBar.setOnSeekBarChangeListener(componentListener); + progressBar.setMax(PROGRESS_BAR_MAX); + } + playButton = findViewById(R.id.exo_play); + if (playButton != null) { + playButton.setOnClickListener(componentListener); + } + pauseButton = findViewById(R.id.exo_pause); + if (pauseButton != null) { + pauseButton.setOnClickListener(componentListener); + } + previousButton = findViewById(R.id.exo_prev); + if (previousButton != null) { + previousButton.setOnClickListener(componentListener); + } + nextButton = findViewById(R.id.exo_next); + if (nextButton != null) { + nextButton.setOnClickListener(componentListener); + } + rewindButton = findViewById(R.id.exo_rew); + if (rewindButton != null) { + rewindButton.setOnClickListener(componentListener); + } + fastForwardButton = findViewById(R.id.exo_ffwd); + if (fastForwardButton != null) { + fastForwardButton.setOnClickListener(componentListener); + } + } + + /** + * Returns the player currently being controlled by this view, or null if no player is set. + */ + public ExoPlayer getPlayer() { + return player; + } + + /** + * Sets the {@link ExoPlayer} to control. + * + * @param player the {@code ExoPlayer} to control. + */ + public void setPlayer(ExoPlayer player) { + if (this.player == player) { + return; + } + if (this.player != null) { + this.player.removeListener(componentListener); + } + this.player = player; + if (player != null) { + player.addListener(componentListener); + } + updateAll(); + } + + /** + * Sets the {@link VisibilityListener}. + * + * @param listener The listener to be notified about visibility changes. + */ + public void setVisibilityListener(VisibilityListener listener) { + this.visibilityListener = listener; + } + + /** + * Sets the {@link SeekDispatcher}. + * + * @param seekDispatcher The {@link SeekDispatcher}, or null to use + * {@link #DEFAULT_SEEK_DISPATCHER}. + */ + public void setSeekDispatcher(SeekDispatcher seekDispatcher) { + this.seekDispatcher = seekDispatcher == null ? DEFAULT_SEEK_DISPATCHER : seekDispatcher; + } + + /** + * Sets the rewind increment in milliseconds. + * + * @param rewindMs The rewind increment in milliseconds. A non-positive value will cause the + * rewind button to be disabled. + */ + public void setRewindIncrementMs(int rewindMs) { + this.rewindMs = rewindMs; + updateNavigation(); + } + + /** + * Sets the fast forward increment in milliseconds. + * + * @param fastForwardMs The fast forward increment in milliseconds. A non-positive value will + * cause the fast forward button to be disabled. + */ + public void setFastForwardIncrementMs(int fastForwardMs) { + this.fastForwardMs = fastForwardMs; + updateNavigation(); + } + + /** + * Returns the playback controls timeout. The playback controls are automatically hidden after + * this duration of time has elapsed without user input. + * + * @return The duration in milliseconds. A non-positive value indicates that the controls will + * remain visible indefinitely. + */ + public int getShowTimeoutMs() { + return showTimeoutMs; + } + + /** + * Sets the playback controls timeout. The playback controls are automatically hidden after this + * duration of time has elapsed without user input. + * + * @param showTimeoutMs The duration in milliseconds. A non-positive value will cause the controls + * to remain visible indefinitely. + */ + public void setShowTimeoutMs(int showTimeoutMs) { + this.showTimeoutMs = showTimeoutMs; + } + + /** + * Shows the playback controls. If {@link #getShowTimeoutMs()} is positive then the controls will + * be automatically hidden after this duration of time has elapsed without user input. + */ + public void show() { + if (!isVisible()) { + setVisibility(VISIBLE); + if (visibilityListener != null) { + visibilityListener.onVisibilityChange(getVisibility()); + } + updateAll(); + requestPlayPauseFocus(); + } + // Call hideAfterTimeout even if already visible to reset the timeout. + hideAfterTimeout(); + } + + /** + * Hides the controller. + */ + public void hide() { + if (isVisible()) { + setVisibility(GONE); + if (visibilityListener != null) { + visibilityListener.onVisibilityChange(getVisibility()); + } + removeCallbacks(updateProgressAction); + removeCallbacks(hideAction); + hideAtMs = C.TIME_UNSET; + } + } + + /** + * Returns whether the controller is currently visible. + */ + public boolean isVisible() { + return getVisibility() == VISIBLE; + } + + private void hideAfterTimeout() { + removeCallbacks(hideAction); + if (showTimeoutMs > 0) { + hideAtMs = SystemClock.uptimeMillis() + showTimeoutMs; + if (isAttachedToWindow) { + postDelayed(hideAction, showTimeoutMs); + } + } else { + hideAtMs = C.TIME_UNSET; + } + } + + private void updateAll() { + updatePlayPauseButton(); + updateNavigation(); + updateProgress(); + } + + private void updatePlayPauseButton() { + if (!isVisible() || !isAttachedToWindow) { + return; + } + boolean requestPlayPauseFocus = false; + boolean playing = player != null && player.getPlayWhenReady(); + if (playButton != null) { + requestPlayPauseFocus |= playing && playButton.isFocused(); + playButton.setVisibility(playing ? View.GONE : View.VISIBLE); + } + if (pauseButton != null) { + requestPlayPauseFocus |= !playing && pauseButton.isFocused(); + pauseButton.setVisibility(!playing ? View.GONE : View.VISIBLE); + } + if (requestPlayPauseFocus) { + requestPlayPauseFocus(); + } + } + + private void updateNavigation() { + if (!isVisible() || !isAttachedToWindow) { + return; + } + Timeline currentTimeline = player != null ? player.getCurrentTimeline() : null; + boolean haveNonEmptyTimeline = currentTimeline != null && !currentTimeline.isEmpty(); + boolean isSeekable = false; + boolean enablePrevious = false; + boolean enableNext = false; + if (haveNonEmptyTimeline) { + int currentWindowIndex = player.getCurrentWindowIndex(); + currentTimeline.getWindow(currentWindowIndex, currentWindow); + isSeekable = currentWindow.isSeekable; + enablePrevious = currentWindowIndex > 0 || isSeekable || !currentWindow.isDynamic; + enableNext = (currentWindowIndex < currentTimeline.getWindowCount() - 1) + || currentWindow.isDynamic; + } + setButtonEnabled(enablePrevious , previousButton); + setButtonEnabled(enableNext, nextButton); + setButtonEnabled(fastForwardMs > 0 && isSeekable, fastForwardButton); + setButtonEnabled(rewindMs > 0 && isSeekable, rewindButton); + if (progressBar != null) { + progressBar.setEnabled(isSeekable); + } + } + + private void updateProgress() { + if (!isVisible() || !isAttachedToWindow) { + return; + } + long duration = player == null ? 0 : player.getDuration(); + long position = player == null ? 0 : player.getCurrentPosition(); + if (durationView != null) { + durationView.setText(stringForTime(duration)); + } + if (positionView != null && !dragging) { + positionView.setText(stringForTime(position)); + } + + if (progressBar != null) { + if (!dragging) { + progressBar.setProgress(progressBarValue(position)); + } + long bufferedPosition = player == null ? 0 : player.getBufferedPosition(); + progressBar.setSecondaryProgress(progressBarValue(bufferedPosition)); + // Remove scheduled updates. + } + removeCallbacks(updateProgressAction); + // Schedule an update if necessary. + int playbackState = player == null ? ExoPlayer.STATE_IDLE : player.getPlaybackState(); + if (playbackState != ExoPlayer.STATE_IDLE && playbackState != ExoPlayer.STATE_ENDED) { + long delayMs; + if (player.getPlayWhenReady() && playbackState == ExoPlayer.STATE_READY) { + delayMs = 1000 - (position % 1000); + if (delayMs < 200) { + delayMs += 1000; + } + } else { + delayMs = 1000; + } + postDelayed(updateProgressAction, delayMs); + } + } + + private void requestPlayPauseFocus() { + boolean playing = player != null && player.getPlayWhenReady(); + if (!playing && playButton != null) { + playButton.requestFocus(); + } else if (playing && pauseButton != null) { + pauseButton.requestFocus(); + } + } + + private void setButtonEnabled(boolean enabled, View view) { + if (view == null) { + return; + } + view.setEnabled(enabled); + if (Util.SDK_INT >= 11) { + setViewAlphaV11(view, enabled ? 1f : 0.3f); + view.setVisibility(VISIBLE); + } else { + view.setVisibility(enabled ? VISIBLE : INVISIBLE); + } + } + + @TargetApi(11) + private void setViewAlphaV11(View view, float alpha) { + view.setAlpha(alpha); + } + + private String stringForTime(long timeMs) { + if (timeMs == C.TIME_UNSET) { + timeMs = 0; + } + long totalSeconds = (timeMs + 500) / 1000; + long seconds = totalSeconds % 60; + long minutes = (totalSeconds / 60) % 60; + long hours = totalSeconds / 3600; + formatBuilder.setLength(0); + return hours > 0 ? formatter.format("%d:%02d:%02d", hours, minutes, seconds).toString() + : formatter.format("%02d:%02d", minutes, seconds).toString(); + } + + private int progressBarValue(long position) { + long duration = player == null ? C.TIME_UNSET : player.getDuration(); + return duration == C.TIME_UNSET || duration == 0 ? 0 + : (int) ((position * PROGRESS_BAR_MAX) / duration); + } + + private long positionValue(int progress) { + long duration = player == null ? C.TIME_UNSET : player.getDuration(); + return duration == C.TIME_UNSET ? 0 : ((duration * progress) / PROGRESS_BAR_MAX); + } + + private void previous() { + Timeline currentTimeline = player.getCurrentTimeline(); + if (currentTimeline.isEmpty()) { + return; + } + int currentWindowIndex = player.getCurrentWindowIndex(); + currentTimeline.getWindow(currentWindowIndex, currentWindow); + if (currentWindowIndex > 0 && (player.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS + || (currentWindow.isDynamic && !currentWindow.isSeekable))) { + seekTo(currentWindowIndex - 1, C.TIME_UNSET); + } else { + seekTo(0); + } + } + + private void next() { + Timeline currentTimeline = player.getCurrentTimeline(); + if (currentTimeline.isEmpty()) { + return; + } + int currentWindowIndex = player.getCurrentWindowIndex(); + if (currentWindowIndex < currentTimeline.getWindowCount() - 1) { + seekTo(currentWindowIndex + 1, C.TIME_UNSET); + } else if (currentTimeline.getWindow(currentWindowIndex, currentWindow, false).isDynamic) { + seekTo(currentWindowIndex, C.TIME_UNSET); + } + } + + private void rewind() { + if (rewindMs <= 0) { + return; + } + seekTo(Math.max(player.getCurrentPosition() - rewindMs, 0)); + } + + private void fastForward() { + if (fastForwardMs <= 0) { + return; + } + seekTo(Math.min(player.getCurrentPosition() + fastForwardMs, player.getDuration())); + } + + private void seekTo(long positionMs) { + seekTo(player.getCurrentWindowIndex(), positionMs); + } + + private void seekTo(int windowIndex, long positionMs) { + boolean dispatched = seekDispatcher.dispatchSeek(player, windowIndex, positionMs); + if (!dispatched) { + // The seek wasn't dispatched. If the progress bar was dragged by the user to perform the + // seek then it'll now be in the wrong position. Trigger a progress update to snap it back. + updateProgress(); + } + } + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + isAttachedToWindow = true; + if (hideAtMs != C.TIME_UNSET) { + long delayMs = hideAtMs - SystemClock.uptimeMillis(); + if (delayMs <= 0) { + hide(); + } else { + postDelayed(hideAction, delayMs); + } + } + updateAll(); + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + isAttachedToWindow = false; + removeCallbacks(updateProgressAction); + removeCallbacks(hideAction); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + boolean handled = dispatchMediaKeyEvent(event) || super.dispatchKeyEvent(event); + if (handled) { + show(); + } + return handled; + } + + /** + * Called to process media key events. Any {@link KeyEvent} can be passed but only media key + * events will be handled. + * + * @param event A key event. + * @return Whether the key event was handled. + */ + public boolean dispatchMediaKeyEvent(KeyEvent event) { + int keyCode = event.getKeyCode(); + if (player == null || !isHandledMediaKey(keyCode)) { + return false; + } + if (event.getAction() == KeyEvent.ACTION_DOWN) { + switch (keyCode) { + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: + fastForward(); + break; + case KeyEvent.KEYCODE_MEDIA_REWIND: + rewind(); + break; + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + player.setPlayWhenReady(!player.getPlayWhenReady()); + break; + case KeyEvent.KEYCODE_MEDIA_PLAY: + player.setPlayWhenReady(true); + break; + case KeyEvent.KEYCODE_MEDIA_PAUSE: + player.setPlayWhenReady(false); + break; + case KeyEvent.KEYCODE_MEDIA_NEXT: + next(); + break; + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + previous(); + break; + default: + break; + } + } + show(); + return true; + } + + private static boolean isHandledMediaKey(int keyCode) { + return keyCode == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD + || keyCode == KeyEvent.KEYCODE_MEDIA_REWIND + || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE + || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY + || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE + || keyCode == KeyEvent.KEYCODE_MEDIA_NEXT + || keyCode == KeyEvent.KEYCODE_MEDIA_PREVIOUS; + } + + private final class ComponentListener implements ExoPlayer.EventListener, + SeekBar.OnSeekBarChangeListener, OnClickListener { + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + removeCallbacks(hideAction); + dragging = true; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (fromUser) { + long position = positionValue(progress); + if (positionView != null) { + positionView.setText(stringForTime(position)); + } + if (player != null && !dragging) { + seekTo(position); + } + } + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + dragging = false; + if (player != null) { + seekTo(positionValue(seekBar.getProgress())); + } + hideAfterTimeout(); + } + + @Override + public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { + updatePlayPauseButton(); + updateProgress(); + } + + @Override + public void onPositionDiscontinuity() { + updateNavigation(); + updateProgress(); + } + + @Override + public void onTimelineChanged(Timeline timeline, Object manifest) { + updateNavigation(); + updateProgress(); + } + + @Override + public void onLoadingChanged(boolean isLoading) { + // Do nothing. + } + + @Override + public void onTracksChanged(TrackGroupArray tracks, TrackSelectionArray selections) { + // Do nothing. + } + + @Override + public void onPlayerError(ExoPlaybackException error) { + // Do nothing. + } + + @Override + public void onClick(View view) { + if (player != null) { + if (nextButton == view) { + next(); + } else if (previousButton == view) { + previous(); + } else if (fastForwardButton == view) { + fastForward(); + } else if (rewindButton == view) { + rewind(); + } else if (playButton == view) { + player.setPlayWhenReady(true); + } else if (pauseButton == view) { + player.setPlayWhenReady(false); + } + } + hideAfterTimeout(); + } + + } + +} diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java new file mode 100644 index 0000000000..08cb2536dc --- /dev/null +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java @@ -0,0 +1,717 @@ +/* + * Copyright (C) 2016 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.ui; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.SurfaceView; +import android.view.TextureView; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.ExoPlaybackException; +import com.google.android.exoplayer2.ExoPlayer; +import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.Timeline; +import com.google.android.exoplayer2.metadata.Metadata; +import com.google.android.exoplayer2.metadata.id3.ApicFrame; +import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.text.Cue; +import com.google.android.exoplayer2.text.TextRenderer; +import com.google.android.exoplayer2.trackselection.TrackSelection; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; +import com.google.android.exoplayer2.ui.AspectRatioFrameLayout.ResizeMode; +import com.google.android.exoplayer2.ui.PlaybackControlView.SeekDispatcher; +import com.google.android.exoplayer2.util.Assertions; +import java.util.List; + +/** + * A high level view for {@link SimpleExoPlayer} media playbacks. It displays video, subtitles and + * album art during playback, and displays playback controls using a {@link PlaybackControlView}. + *

+ * A SimpleExoPlayerView can be customized by setting attributes (or calling corresponding methods), + * overriding the view's layout file or by specifying a custom view layout file, as outlined below. + * + *

Attributes

+ * The following attributes can be set on a SimpleExoPlayerView when used in a layout XML file: + *

+ *

+ * + *

Overriding the layout file

+ * To customize the layout of SimpleExoPlayerView throughout your app, or just for certain + * configurations, you can define {@code exo_simple_player_view.xml} layout files in your + * application {@code res/layout*} directories. These layouts will override the one provided by the + * ExoPlayer library, and will be inflated for use by SimpleExoPlayerView. The view identifies and + * binds its children by looking for the following ids: + *

+ *

+ *

+ * All child views are optional and so can be omitted if not required, however where defined they + * must be of the expected type. + * + *

Specifying a custom layout file

+ * Defining your own {@code exo_simple_player_view.xml} is useful to customize the layout of + * SimpleExoPlayerView throughout your application. It's also possible to customize the layout for a + * single instance in a layout file. This is achieved by setting the {@code player_layout_id} + * attribute on a SimpleExoPlayerView. This will cause the specified layout to be inflated instead + * of {@code exo_simple_player_view.xml} for only the instance on which the attribute is set. + */ +@TargetApi(16) +public final class SimpleExoPlayerView extends FrameLayout { + + private static final int SURFACE_TYPE_NONE = 0; + private static final int SURFACE_TYPE_SURFACE_VIEW = 1; + private static final int SURFACE_TYPE_TEXTURE_VIEW = 2; + + private final AspectRatioFrameLayout contentFrame; + private final View shutterView; + private final View surfaceView; + private final ImageView artworkView; + private final SubtitleView subtitleView; + private final PlaybackControlView controller; + private final ComponentListener componentListener; + private final FrameLayout overlayFrameLayout; + + private SimpleExoPlayer player; + private boolean useController; + private boolean useArtwork; + private Bitmap defaultArtwork; + private int controllerShowTimeoutMs; + + public SimpleExoPlayerView(Context context) { + this(context, null); + } + + public SimpleExoPlayerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SimpleExoPlayerView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + int playerLayoutId = R.layout.exo_simple_player_view; + boolean useArtwork = true; + int defaultArtworkId = 0; + boolean useController = true; + int surfaceType = SURFACE_TYPE_SURFACE_VIEW; + int resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT; + int controllerShowTimeoutMs = PlaybackControlView.DEFAULT_SHOW_TIMEOUT_MS; + if (attrs != null) { + TypedArray a = context.getTheme().obtainStyledAttributes(attrs, + R.styleable.SimpleExoPlayerView, 0, 0); + try { + playerLayoutId = a.getResourceId(R.styleable.SimpleExoPlayerView_player_layout_id, + playerLayoutId); + useArtwork = a.getBoolean(R.styleable.SimpleExoPlayerView_use_artwork, useArtwork); + defaultArtworkId = a.getResourceId(R.styleable.SimpleExoPlayerView_default_artwork, + defaultArtworkId); + useController = a.getBoolean(R.styleable.SimpleExoPlayerView_use_controller, useController); + surfaceType = a.getInt(R.styleable.SimpleExoPlayerView_surface_type, surfaceType); + resizeMode = a.getInt(R.styleable.SimpleExoPlayerView_resize_mode, resizeMode); + controllerShowTimeoutMs = a.getInt(R.styleable.SimpleExoPlayerView_show_timeout, + controllerShowTimeoutMs); + } finally { + a.recycle(); + } + } + + LayoutInflater.from(context).inflate(playerLayoutId, this); + componentListener = new ComponentListener(); + setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); + + // Content frame. + contentFrame = (AspectRatioFrameLayout) findViewById(R.id.exo_content_frame); + if (contentFrame != null) { + setResizeModeRaw(contentFrame, resizeMode); + } + + // Shutter view. + shutterView = findViewById(R.id.exo_shutter); + + // Create a surface view and insert it into the content frame, if there is one. + if (contentFrame != null && surfaceType != SURFACE_TYPE_NONE) { + ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + surfaceView = surfaceType == SURFACE_TYPE_TEXTURE_VIEW ? new TextureView(context) + : new SurfaceView(context); + surfaceView.setLayoutParams(params); + contentFrame.addView(surfaceView, 0); + } else { + surfaceView = null; + } + + // Overlay frame layout. + overlayFrameLayout = (FrameLayout) findViewById(R.id.exo_overlay); + + // Artwork view. + artworkView = (ImageView) findViewById(R.id.exo_artwork); + this.useArtwork = useArtwork && artworkView != null; + if (defaultArtworkId != 0) { + defaultArtwork = BitmapFactory.decodeResource(context.getResources(), defaultArtworkId); + } + + // Subtitle view. + subtitleView = (SubtitleView) findViewById(R.id.exo_subtitles); + if (subtitleView != null) { + subtitleView.setUserDefaultStyle(); + subtitleView.setUserDefaultTextSize(); + } + + // Playback control view. + View controllerPlaceholder = findViewById(R.id.exo_controller_placeholder); + if (controllerPlaceholder != null) { + // Note: rewindMs and fastForwardMs are passed via attrs, so we don't need to make explicit + // calls to set them. + this.controller = new PlaybackControlView(context, attrs); + controller.setLayoutParams(controllerPlaceholder.getLayoutParams()); + ViewGroup parent = ((ViewGroup) controllerPlaceholder.getParent()); + int controllerIndex = parent.indexOfChild(controllerPlaceholder); + parent.removeView(controllerPlaceholder); + parent.addView(controller, controllerIndex); + } else { + this.controller = null; + } + this.controllerShowTimeoutMs = controller != null ? controllerShowTimeoutMs : 0; + this.useController = useController && controller != null; + hideController(); + } + + /** + * Returns the player currently set on this view, or null if no player is set. + */ + public SimpleExoPlayer getPlayer() { + return player; + } + + /** + * Set the {@link SimpleExoPlayer} to use. The {@link SimpleExoPlayer#setTextOutput} and + * {@link SimpleExoPlayer#setVideoListener} method of the player will be called and previous + * assignments are overridden. + * + * @param player The {@link SimpleExoPlayer} to use. + */ + public void setPlayer(SimpleExoPlayer player) { + if (this.player == player) { + return; + } + if (this.player != null) { + this.player.setTextOutput(null); + this.player.setVideoListener(null); + this.player.removeListener(componentListener); + this.player.setVideoSurface(null); + } + this.player = player; + if (useController) { + controller.setPlayer(player); + } + if (shutterView != null) { + shutterView.setVisibility(VISIBLE); + } + if (player != null) { + if (surfaceView instanceof TextureView) { + player.setVideoTextureView((TextureView) surfaceView); + } else if (surfaceView instanceof SurfaceView) { + player.setVideoSurfaceView((SurfaceView) surfaceView); + } + player.setVideoListener(componentListener); + player.addListener(componentListener); + player.setTextOutput(componentListener); + maybeShowController(false); + updateForCurrentTrackSelections(); + } else { + hideController(); + hideArtwork(); + } + } + + /** + * Sets the resize mode. + * + * @param resizeMode The resize mode. + */ + public void setResizeMode(@ResizeMode int resizeMode) { + Assertions.checkState(contentFrame != null); + contentFrame.setResizeMode(resizeMode); + } + + /** + * Returns whether artwork is displayed if present in the media. + */ + public boolean getUseArtwork() { + return useArtwork; + } + + /** + * Sets whether artwork is displayed if present in the media. + * + * @param useArtwork Whether artwork is displayed. + */ + public void setUseArtwork(boolean useArtwork) { + Assertions.checkState(!useArtwork || artworkView != null); + if (this.useArtwork != useArtwork) { + this.useArtwork = useArtwork; + updateForCurrentTrackSelections(); + } + } + + /** + * Returns the default artwork to display. + */ + public Bitmap getDefaultArtwork() { + return defaultArtwork; + } + + /** + * Sets the default artwork to display if {@code useArtwork} is {@code true} and no artwork is + * present in the media. + * + * @param defaultArtwork the default artwork to display. + */ + public void setDefaultArtwork(Bitmap defaultArtwork) { + if (this.defaultArtwork != defaultArtwork) { + this.defaultArtwork = defaultArtwork; + updateForCurrentTrackSelections(); + } + } + + /** + * Returns whether the playback controls are enabled. + */ + public boolean getUseController() { + return useController; + } + + /** + * Sets whether playback controls are enabled. If set to {@code false} the playback controls are + * never visible and are disconnected from the player. + * + * @param useController Whether playback controls should be enabled. + */ + public void setUseController(boolean useController) { + Assertions.checkState(!useController || controller != null); + if (this.useController == useController) { + return; + } + this.useController = useController; + if (useController) { + controller.setPlayer(player); + } else if (controller != null) { + controller.hide(); + controller.setPlayer(null); + } + } + + /** + * Called to process media key events. Any {@link KeyEvent} can be passed but only media key + * events will be handled. Does nothing if playback controls are disabled. + * + * @param event A key event. + * @return Whether the key event was handled. + */ + public boolean dispatchMediaKeyEvent(KeyEvent event) { + return useController && controller.dispatchMediaKeyEvent(event); + } + + /** + * Shows the playback controls. Does nothing if playback controls are disabled. + */ + public void showController() { + if (useController) { + maybeShowController(true); + } + } + + /** + * Hides the playback controls. Does nothing if playback controls are disabled. + */ + public void hideController() { + if (controller != null) { + controller.hide(); + } + } + + /** + * Returns the playback controls timeout. The playback controls are automatically hidden after + * this duration of time has elapsed without user input and with playback or buffering in + * progress. + * + * @return The timeout in milliseconds. A non-positive value will cause the controller to remain + * visible indefinitely. + */ + public int getControllerShowTimeoutMs() { + return controllerShowTimeoutMs; + } + + /** + * Sets the playback controls timeout. The playback controls are automatically hidden after this + * duration of time has elapsed without user input and with playback or buffering in progress. + * + * @param controllerShowTimeoutMs The timeout in milliseconds. A non-positive value will cause + * the controller to remain visible indefinitely. + */ + public void setControllerShowTimeoutMs(int controllerShowTimeoutMs) { + Assertions.checkState(controller != null); + this.controllerShowTimeoutMs = controllerShowTimeoutMs; + } + + /** + * Set the {@link PlaybackControlView.VisibilityListener}. + * + * @param listener The listener to be notified about visibility changes. + */ + public void setControllerVisibilityListener(PlaybackControlView.VisibilityListener listener) { + Assertions.checkState(controller != null); + controller.setVisibilityListener(listener); + } + + /** + * Sets the {@link SeekDispatcher}. + * + * @param seekDispatcher The {@link SeekDispatcher}, or null to use + * {@link PlaybackControlView#DEFAULT_SEEK_DISPATCHER}. + */ + public void setSeekDispatcher(SeekDispatcher seekDispatcher) { + Assertions.checkState(controller != null); + controller.setSeekDispatcher(seekDispatcher); + } + + /** + * Sets the rewind increment in milliseconds. + * + * @param rewindMs The rewind increment in milliseconds. + */ + public void setRewindIncrementMs(int rewindMs) { + Assertions.checkState(controller != null); + controller.setRewindIncrementMs(rewindMs); + } + + /** + * Sets the fast forward increment in milliseconds. + * + * @param fastForwardMs The fast forward increment in milliseconds. + */ + public void setFastForwardIncrementMs(int fastForwardMs) { + Assertions.checkState(controller != null); + controller.setFastForwardIncrementMs(fastForwardMs); + } + + /** + * Gets the view onto which video is rendered. This is either a {@link SurfaceView} (default) + * or a {@link TextureView} if the {@code use_texture_view} view attribute has been set to true. + * + * @return Either a {@link SurfaceView} or a {@link TextureView}. + */ + public View getVideoSurfaceView() { + return surfaceView; + } + + /** + * Gets the overlay {@link FrameLayout}, which can be populated with UI elements to show on top of + * the player. + * + * @return The overlay {@link FrameLayout}, or {@code null} if the layout has been customized and + * the overlay is not present. + */ + public FrameLayout getOverlayFrameLayout() { + return overlayFrameLayout; + } + + /** + * Gets the {@link SubtitleView}. + * + * @return The {@link SubtitleView}, or {@code null} if the layout has been customized and the + * subtitle view is not present. + */ + public SubtitleView getSubtitleView() { + return subtitleView; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (!useController || player == null || ev.getActionMasked() != MotionEvent.ACTION_DOWN) { + return false; + } + if (controller.isVisible()) { + controller.hide(); + } else { + maybeShowController(true); + } + return true; + } + + @Override + public boolean onTrackballEvent(MotionEvent ev) { + if (!useController || player == null) { + return false; + } + maybeShowController(true); + return true; + } + + private void maybeShowController(boolean isForced) { + if (!useController || player == null) { + return; + } + int playbackState = player.getPlaybackState(); + boolean showIndefinitely = playbackState == ExoPlayer.STATE_IDLE + || playbackState == ExoPlayer.STATE_ENDED || !player.getPlayWhenReady(); + boolean wasShowingIndefinitely = controller.isVisible() && controller.getShowTimeoutMs() <= 0; + controller.setShowTimeoutMs(showIndefinitely ? 0 : controllerShowTimeoutMs); + if (isForced || showIndefinitely || wasShowingIndefinitely) { + controller.show(); + } + } + + private void updateForCurrentTrackSelections() { + if (player == null) { + return; + } + TrackSelectionArray selections = player.getCurrentTrackSelections(); + for (int i = 0; i < selections.length; i++) { + if (player.getRendererType(i) == C.TRACK_TYPE_VIDEO && selections.get(i) != null) { + // Video enabled so artwork must be hidden. If the shutter is closed, it will be opened in + // onRenderedFirstFrame(). + hideArtwork(); + return; + } + } + // Video disabled so the shutter must be closed. + if (shutterView != null) { + shutterView.setVisibility(VISIBLE); + } + // Display artwork if enabled and available, else hide it. + if (useArtwork) { + for (int i = 0; i < selections.length; i++) { + TrackSelection selection = selections.get(i); + if (selection != null) { + for (int j = 0; j < selection.length(); j++) { + Metadata metadata = selection.getFormat(j).metadata; + if (metadata != null && setArtworkFromMetadata(metadata)) { + return; + } + } + } + } + if (setArtworkFromBitmap(defaultArtwork)) { + return; + } + } + // Artwork disabled or unavailable. + hideArtwork(); + } + + private boolean setArtworkFromMetadata(Metadata metadata) { + for (int i = 0; i < metadata.length(); i++) { + Metadata.Entry metadataEntry = metadata.get(i); + if (metadataEntry instanceof ApicFrame) { + byte[] bitmapData = ((ApicFrame) metadataEntry).pictureData; + Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length); + return setArtworkFromBitmap(bitmap); + } + } + return false; + } + + private boolean setArtworkFromBitmap(Bitmap bitmap) { + if (bitmap != null) { + int bitmapWidth = bitmap.getWidth(); + int bitmapHeight = bitmap.getHeight(); + if (bitmapWidth > 0 && bitmapHeight > 0) { + if (contentFrame != null) { + contentFrame.setAspectRatio((float) bitmapWidth / bitmapHeight); + } + artworkView.setImageBitmap(bitmap); + artworkView.setVisibility(VISIBLE); + return true; + } + } + return false; + } + + private void hideArtwork() { + if (artworkView != null) { + artworkView.setImageResource(android.R.color.transparent); // Clears any bitmap reference. + artworkView.setVisibility(INVISIBLE); + } + } + + @SuppressWarnings("ResourceType") + private static void setResizeModeRaw(AspectRatioFrameLayout aspectRatioFrame, int resizeMode) { + aspectRatioFrame.setResizeMode(resizeMode); + } + + private final class ComponentListener implements SimpleExoPlayer.VideoListener, + TextRenderer.Output, ExoPlayer.EventListener { + + // TextRenderer.Output implementation + + @Override + public void onCues(List cues) { + if (subtitleView != null) { + subtitleView.onCues(cues); + } + } + + // SimpleExoPlayer.VideoListener implementation + + @Override + public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, + float pixelWidthHeightRatio) { + if (contentFrame != null) { + float aspectRatio = height == 0 ? 1 : (width * pixelWidthHeightRatio) / height; + contentFrame.setAspectRatio(aspectRatio); + } + } + + @Override + public void onRenderedFirstFrame() { + if (shutterView != null) { + shutterView.setVisibility(INVISIBLE); + } + } + + @Override + public void onTracksChanged(TrackGroupArray tracks, TrackSelectionArray selections) { + updateForCurrentTrackSelections(); + } + + // ExoPlayer.EventListener implementation + + @Override + public void onLoadingChanged(boolean isLoading) { + // Do nothing. + } + + @Override + public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { + maybeShowController(false); + } + + @Override + public void onPlayerError(ExoPlaybackException e) { + // Do nothing. + } + + @Override + public void onPositionDiscontinuity() { + // Do nothing. + } + + @Override + public void onTimelineChanged(Timeline timeline, Object manifest) { + // Do nothing. + } + + } + +} diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitlePainter.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitlePainter.java new file mode 100644 index 0000000000..d4f09b1721 --- /dev/null +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitlePainter.java @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2016 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.ui; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Join; +import android.graphics.Paint.Style; +import android.graphics.Rect; +import android.graphics.RectF; +import android.text.Layout.Alignment; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.TextUtils; +import android.util.DisplayMetrics; +import android.util.Log; +import com.google.android.exoplayer2.text.CaptionStyleCompat; +import com.google.android.exoplayer2.text.Cue; +import com.google.android.exoplayer2.util.Util; + +/** + * Paints subtitle {@link Cue}s. + */ +/* package */ final class SubtitlePainter { + + private static final String TAG = "SubtitlePainter"; + + /** + * Ratio of inner padding to font size. + */ + private static final float INNER_PADDING_RATIO = 0.125f; + + /** + * Temporary rectangle used for computing line bounds. + */ + private final RectF lineBounds = new RectF(); + + // Styled dimensions. + private final float cornerRadius; + private final float outlineWidth; + private final float shadowRadius; + private final float shadowOffset; + private final float spacingMult; + private final float spacingAdd; + + private final TextPaint textPaint; + private final Paint paint; + + // Previous input variables. + private CharSequence cueText; + private Alignment cueTextAlignment; + private Bitmap cueBitmap; + private float cueLine; + @Cue.LineType + private int cueLineType; + @Cue.AnchorType + private int cueLineAnchor; + private float cuePosition; + @Cue.AnchorType + private int cuePositionAnchor; + private float cueSize; + private boolean applyEmbeddedStyles; + private int foregroundColor; + private int backgroundColor; + private int windowColor; + private int edgeColor; + @CaptionStyleCompat.EdgeType + private int edgeType; + private float textSizePx; + private float bottomPaddingFraction; + private int parentLeft; + private int parentTop; + private int parentRight; + private int parentBottom; + + // Derived drawing variables. + private StaticLayout textLayout; + private int textLeft; + private int textTop; + private int textPaddingX; + private Rect bitmapRect; + + @SuppressWarnings("ResourceType") + public SubtitlePainter(Context context) { + int[] viewAttr = {android.R.attr.lineSpacingExtra, android.R.attr.lineSpacingMultiplier}; + TypedArray styledAttributes = context.obtainStyledAttributes(null, viewAttr, 0, 0); + spacingAdd = styledAttributes.getDimensionPixelSize(0, 0); + spacingMult = styledAttributes.getFloat(1, 1); + styledAttributes.recycle(); + + Resources resources = context.getResources(); + DisplayMetrics displayMetrics = resources.getDisplayMetrics(); + int twoDpInPx = Math.round((2f * displayMetrics.densityDpi) / DisplayMetrics.DENSITY_DEFAULT); + cornerRadius = twoDpInPx; + outlineWidth = twoDpInPx; + shadowRadius = twoDpInPx; + shadowOffset = twoDpInPx; + + textPaint = new TextPaint(); + textPaint.setAntiAlias(true); + textPaint.setSubpixelText(true); + + paint = new Paint(); + paint.setAntiAlias(true); + paint.setStyle(Style.FILL); + } + + /** + * Draws the provided {@link Cue} into a canvas with the specified styling. + *

+ * A call to this method is able to use cached results of calculations made during the previous + * call, and so an instance of this class is able to optimize repeated calls to this method in + * which the same parameters are passed. + * + * @param cue The cue to draw. + * @param applyEmbeddedStyles Whether styling embedded within the cue should be applied. + * @param style The style to use when drawing the cue text. + * @param textSizePx The text size to use when drawing the cue text, in pixels. + * @param bottomPaddingFraction The bottom padding fraction to apply when {@link Cue#line} is + * {@link Cue#DIMEN_UNSET}, as a fraction of the viewport height + * @param canvas The canvas into which to draw. + * @param cueBoxLeft The left position of the enclosing cue box. + * @param cueBoxTop The top position of the enclosing cue box. + * @param cueBoxRight The right position of the enclosing cue box. + * @param cueBoxBottom The bottom position of the enclosing cue box. + */ + public void draw(Cue cue, boolean applyEmbeddedStyles, CaptionStyleCompat style, float textSizePx, + float bottomPaddingFraction, Canvas canvas, int cueBoxLeft, int cueBoxTop, int cueBoxRight, + int cueBoxBottom) { + boolean isTextCue = cue.bitmap == null; + CharSequence cueText = null; + Bitmap cueBitmap = null; + int windowColor = Color.BLACK; + if (isTextCue) { + cueText = cue.text; + if (TextUtils.isEmpty(cueText)) { + // Nothing to draw. + return; + } + windowColor = cue.windowColorSet ? cue.windowColor : style.windowColor; + if (!applyEmbeddedStyles) { + // Strip out any embedded styling. + cueText = cueText.toString(); + windowColor = style.windowColor; + } + } else { + cueBitmap = cue.bitmap; + } + if (areCharSequencesEqual(this.cueText, cueText) + && Util.areEqual(this.cueTextAlignment, cue.textAlignment) + && this.cueBitmap == cueBitmap + && this.cueLine == cue.line + && this.cueLineType == cue.lineType + && Util.areEqual(this.cueLineAnchor, cue.lineAnchor) + && this.cuePosition == cue.position + && Util.areEqual(this.cuePositionAnchor, cue.positionAnchor) + && this.cueSize == cue.size + && this.applyEmbeddedStyles == applyEmbeddedStyles + && this.foregroundColor == style.foregroundColor + && this.backgroundColor == style.backgroundColor + && this.windowColor == windowColor + && this.edgeType == style.edgeType + && this.edgeColor == style.edgeColor + && Util.areEqual(this.textPaint.getTypeface(), style.typeface) + && this.textSizePx == textSizePx + && this.bottomPaddingFraction == bottomPaddingFraction + && this.parentLeft == cueBoxLeft + && this.parentTop == cueBoxTop + && this.parentRight == cueBoxRight + && this.parentBottom == cueBoxBottom) { + // We can use the cached layout. + drawLayout(canvas, isTextCue); + return; + } + + this.cueText = cueText; + this.cueTextAlignment = cue.textAlignment; + this.cueBitmap = cueBitmap; + this.cueLine = cue.line; + this.cueLineType = cue.lineType; + this.cueLineAnchor = cue.lineAnchor; + this.cuePosition = cue.position; + this.cuePositionAnchor = cue.positionAnchor; + this.cueSize = cue.size; + this.applyEmbeddedStyles = applyEmbeddedStyles; + this.foregroundColor = style.foregroundColor; + this.backgroundColor = style.backgroundColor; + this.windowColor = windowColor; + this.edgeType = style.edgeType; + this.edgeColor = style.edgeColor; + this.textPaint.setTypeface(style.typeface); + this.textSizePx = textSizePx; + this.bottomPaddingFraction = bottomPaddingFraction; + this.parentLeft = cueBoxLeft; + this.parentTop = cueBoxTop; + this.parentRight = cueBoxRight; + this.parentBottom = cueBoxBottom; + + if (isTextCue) { + setupTextLayout(); + } else { + setupBitmapLayout(); + } + drawLayout(canvas, isTextCue); + } + + private void setupTextLayout() { + int parentWidth = parentRight - parentLeft; + int parentHeight = parentBottom - parentTop; + + textPaint.setTextSize(textSizePx); + int textPaddingX = (int) (textSizePx * INNER_PADDING_RATIO + 0.5f); + + int availableWidth = parentWidth - textPaddingX * 2; + if (cueSize != Cue.DIMEN_UNSET) { + availableWidth = (int) (availableWidth * cueSize); + } + if (availableWidth <= 0) { + Log.w(TAG, "Skipped drawing subtitle cue (insufficient space)"); + return; + } + + Alignment textAlignment = cueTextAlignment == null ? Alignment.ALIGN_CENTER : cueTextAlignment; + textLayout = new StaticLayout(cueText, textPaint, availableWidth, textAlignment, spacingMult, + spacingAdd, true); + int textHeight = textLayout.getHeight(); + int textWidth = 0; + int lineCount = textLayout.getLineCount(); + for (int i = 0; i < lineCount; i++) { + textWidth = Math.max((int) Math.ceil(textLayout.getLineWidth(i)), textWidth); + } + if (cueSize != Cue.DIMEN_UNSET && textWidth < availableWidth) { + textWidth = availableWidth; + } + textWidth += textPaddingX * 2; + + int textLeft; + int textRight; + if (cuePosition != Cue.DIMEN_UNSET) { + int anchorPosition = Math.round(parentWidth * cuePosition) + parentLeft; + textLeft = cuePositionAnchor == Cue.ANCHOR_TYPE_END ? anchorPosition - textWidth + : cuePositionAnchor == Cue.ANCHOR_TYPE_MIDDLE ? (anchorPosition * 2 - textWidth) / 2 + : anchorPosition; + textLeft = Math.max(textLeft, parentLeft); + textRight = Math.min(textLeft + textWidth, parentRight); + } else { + textLeft = (parentWidth - textWidth) / 2; + textRight = textLeft + textWidth; + } + + textWidth = textRight - textLeft; + if (textWidth <= 0) { + Log.w(TAG, "Skipped drawing subtitle cue (invalid horizontal positioning)"); + return; + } + + int textTop; + if (cueLine != Cue.DIMEN_UNSET) { + int anchorPosition; + if (cueLineType == Cue.LINE_TYPE_FRACTION) { + anchorPosition = Math.round(parentHeight * cueLine) + parentTop; + } else { + // cueLineType == Cue.LINE_TYPE_NUMBER + int firstLineHeight = textLayout.getLineBottom(0) - textLayout.getLineTop(0); + if (cueLine >= 0) { + anchorPosition = Math.round(cueLine * firstLineHeight) + parentTop; + } else { + anchorPosition = Math.round((cueLine + 1) * firstLineHeight) + parentBottom; + } + } + textTop = cueLineAnchor == Cue.ANCHOR_TYPE_END ? anchorPosition - textHeight + : cueLineAnchor == Cue.ANCHOR_TYPE_MIDDLE ? (anchorPosition * 2 - textHeight) / 2 + : anchorPosition; + if (textTop + textHeight > parentBottom) { + textTop = parentBottom - textHeight; + } else if (textTop < parentTop) { + textTop = parentTop; + } + } else { + textTop = parentBottom - textHeight - (int) (parentHeight * bottomPaddingFraction); + } + + // Update the derived drawing variables. + this.textLayout = new StaticLayout(cueText, textPaint, textWidth, textAlignment, spacingMult, + spacingAdd, true); + this.textLeft = textLeft; + this.textTop = textTop; + this.textPaddingX = textPaddingX; + } + + private void setupBitmapLayout() { + int parentWidth = parentRight - parentLeft; + int parentHeight = parentBottom - parentTop; + float anchorX = parentLeft + (parentWidth * cuePosition); + float anchorY = parentTop + (parentHeight * cueLine); + int width = Math.round(parentWidth * cueSize); + int height = Math.round(width * ((float) cueBitmap.getHeight() / cueBitmap.getWidth())); + int x = Math.round(cueLineAnchor == Cue.ANCHOR_TYPE_END ? (anchorX - width) + : cueLineAnchor == Cue.ANCHOR_TYPE_MIDDLE ? (anchorX - (width / 2)) : anchorX); + int y = Math.round(cuePositionAnchor == Cue.ANCHOR_TYPE_END ? (anchorY - height) + : cuePositionAnchor == Cue.ANCHOR_TYPE_MIDDLE ? (anchorY - (height / 2)) : anchorY); + bitmapRect = new Rect(x, y, x + width, y + height); + } + + private void drawLayout(Canvas canvas, boolean isTextCue) { + if (isTextCue) { + drawTextLayout(canvas); + } else { + drawBitmapLayout(canvas); + } + } + + private void drawTextLayout(Canvas canvas) { + StaticLayout layout = textLayout; + if (layout == null) { + // Nothing to draw. + return; + } + + int saveCount = canvas.save(); + canvas.translate(textLeft, textTop); + + if (Color.alpha(windowColor) > 0) { + paint.setColor(windowColor); + canvas.drawRect(-textPaddingX, 0, layout.getWidth() + textPaddingX, layout.getHeight(), + paint); + } + + if (Color.alpha(backgroundColor) > 0) { + paint.setColor(backgroundColor); + float previousBottom = layout.getLineTop(0); + int lineCount = layout.getLineCount(); + for (int i = 0; i < lineCount; i++) { + lineBounds.left = layout.getLineLeft(i) - textPaddingX; + lineBounds.right = layout.getLineRight(i) + textPaddingX; + lineBounds.top = previousBottom; + lineBounds.bottom = layout.getLineBottom(i); + previousBottom = lineBounds.bottom; + canvas.drawRoundRect(lineBounds, cornerRadius, cornerRadius, paint); + } + } + + if (edgeType == CaptionStyleCompat.EDGE_TYPE_OUTLINE) { + textPaint.setStrokeJoin(Join.ROUND); + textPaint.setStrokeWidth(outlineWidth); + textPaint.setColor(edgeColor); + textPaint.setStyle(Style.FILL_AND_STROKE); + layout.draw(canvas); + } else if (edgeType == CaptionStyleCompat.EDGE_TYPE_DROP_SHADOW) { + textPaint.setShadowLayer(shadowRadius, shadowOffset, shadowOffset, edgeColor); + } else if (edgeType == CaptionStyleCompat.EDGE_TYPE_RAISED + || edgeType == CaptionStyleCompat.EDGE_TYPE_DEPRESSED) { + boolean raised = edgeType == CaptionStyleCompat.EDGE_TYPE_RAISED; + int colorUp = raised ? Color.WHITE : edgeColor; + int colorDown = raised ? edgeColor : Color.WHITE; + float offset = shadowRadius / 2f; + textPaint.setColor(foregroundColor); + textPaint.setStyle(Style.FILL); + textPaint.setShadowLayer(shadowRadius, -offset, -offset, colorUp); + layout.draw(canvas); + textPaint.setShadowLayer(shadowRadius, offset, offset, colorDown); + } + + textPaint.setColor(foregroundColor); + textPaint.setStyle(Style.FILL); + layout.draw(canvas); + textPaint.setShadowLayer(0, 0, 0, 0); + + canvas.restoreToCount(saveCount); + } + + private void drawBitmapLayout(Canvas canvas) { + canvas.drawBitmap(cueBitmap, null, bitmapRect, null); + } + + /** + * This method is used instead of {@link TextUtils#equals(CharSequence, CharSequence)} because the + * latter only checks the text of each sequence, and does not check for equality of styling that + * may be embedded within the {@link CharSequence}s. + */ + private static boolean areCharSequencesEqual(CharSequence first, CharSequence second) { + // Some CharSequence implementations don't perform a cheap referential equality check in their + // equals methods, so we perform one explicitly here. + return first == second || (first != null && first.equals(second)); + } + +} diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleView.java new file mode 100644 index 0000000000..49516ab6f4 --- /dev/null +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleView.java @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2016 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.ui; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; +import android.view.accessibility.CaptioningManager; +import com.google.android.exoplayer2.text.CaptionStyleCompat; +import com.google.android.exoplayer2.text.Cue; +import com.google.android.exoplayer2.text.TextRenderer; +import com.google.android.exoplayer2.util.Util; +import java.util.ArrayList; +import java.util.List; + +/** + * A view for displaying subtitle {@link Cue}s. + */ +public final class SubtitleView extends View implements TextRenderer.Output { + + /** + * The default fractional text size. + * + * @see #setFractionalTextSize(float, boolean) + */ + public static final float DEFAULT_TEXT_SIZE_FRACTION = 0.0533f; + + /** + * The default bottom padding to apply when {@link Cue#line} is {@link Cue#DIMEN_UNSET}, as a + * fraction of the viewport height. + * + * @see #setBottomPaddingFraction(float) + */ + public static final float DEFAULT_BOTTOM_PADDING_FRACTION = 0.08f; + + private static final int FRACTIONAL = 0; + private static final int FRACTIONAL_IGNORE_PADDING = 1; + private static final int ABSOLUTE = 2; + + private final List painters; + + private List cues; + private int textSizeType; + private float textSize; + private boolean applyEmbeddedStyles; + private CaptionStyleCompat style; + private float bottomPaddingFraction; + + public SubtitleView(Context context) { + this(context, null); + } + + public SubtitleView(Context context, AttributeSet attrs) { + super(context, attrs); + painters = new ArrayList<>(); + textSizeType = FRACTIONAL; + textSize = DEFAULT_TEXT_SIZE_FRACTION; + applyEmbeddedStyles = true; + style = CaptionStyleCompat.DEFAULT; + bottomPaddingFraction = DEFAULT_BOTTOM_PADDING_FRACTION; + } + + @Override + public void onCues(List cues) { + setCues(cues); + } + + /** + * Sets the cues to be displayed by the view. + * + * @param cues The cues to display. + */ + public void setCues(List cues) { + if (this.cues == cues) { + return; + } + this.cues = cues; + // Ensure we have sufficient painters. + int cueCount = (cues == null) ? 0 : cues.size(); + while (painters.size() < cueCount) { + painters.add(new SubtitlePainter(getContext())); + } + // Invalidate to trigger drawing. + invalidate(); + } + + /** + * Set the text size to a given unit and value. + *

+ * See {@link TypedValue} for the possible dimension units. + * + * @param unit The desired dimension unit. + * @param size The desired size in the given units. + */ + public void setFixedTextSize(int unit, float size) { + Context context = getContext(); + Resources resources; + if (context == null) { + resources = Resources.getSystem(); + } else { + resources = context.getResources(); + } + setTextSize(ABSOLUTE, TypedValue.applyDimension(unit, size, resources.getDisplayMetrics())); + } + + /** + * Sets the text size to one derived from {@link CaptioningManager#getFontScale()}, or to a + * default size before API level 19. + */ + public void setUserDefaultTextSize() { + float fontScale = Util.SDK_INT >= 19 && !isInEditMode() ? getUserCaptionFontScaleV19() : 1f; + setFractionalTextSize(DEFAULT_TEXT_SIZE_FRACTION * fontScale); + } + + /** + * Sets the text size to be a fraction of the view's remaining height after its top and bottom + * padding have been subtracted. + *

+ * Equivalent to {@code #setFractionalTextSize(fractionOfHeight, false)}. + * + * @param fractionOfHeight A fraction between 0 and 1. + */ + public void setFractionalTextSize(float fractionOfHeight) { + setFractionalTextSize(fractionOfHeight, false); + } + + /** + * Sets the text size to be a fraction of the height of this view. + * + * @param fractionOfHeight A fraction between 0 and 1. + * @param ignorePadding Set to true if {@code fractionOfHeight} should be interpreted as a + * fraction of this view's height ignoring any top and bottom padding. Set to false if + * {@code fractionOfHeight} should be interpreted as a fraction of this view's remaining + * height after the top and bottom padding has been subtracted. + */ + public void setFractionalTextSize(float fractionOfHeight, boolean ignorePadding) { + setTextSize(ignorePadding ? FRACTIONAL_IGNORE_PADDING : FRACTIONAL, fractionOfHeight); + } + + private void setTextSize(int textSizeType, float textSize) { + if (this.textSizeType == textSizeType && this.textSize == textSize) { + return; + } + this.textSizeType = textSizeType; + this.textSize = textSize; + // Invalidate to trigger drawing. + invalidate(); + } + + /** + * Sets whether styling embedded within the cues should be applied. Enabled by default. + * + * @param applyEmbeddedStyles Whether styling embedded within the cues should be applied. + */ + public void setApplyEmbeddedStyles(boolean applyEmbeddedStyles) { + if (this.applyEmbeddedStyles == applyEmbeddedStyles) { + return; + } + this.applyEmbeddedStyles = applyEmbeddedStyles; + // Invalidate to trigger drawing. + invalidate(); + } + + /** + * Sets the caption style to be equivalent to the one returned by + * {@link CaptioningManager#getUserStyle()}, or to a default style before API level 19. + */ + public void setUserDefaultStyle() { + setStyle(Util.SDK_INT >= 19 && !isInEditMode() + ? getUserCaptionStyleV19() : CaptionStyleCompat.DEFAULT); + } + + /** + * Sets the caption style. + * + * @param style A style for the view. + */ + public void setStyle(CaptionStyleCompat style) { + if (this.style == style) { + return; + } + this.style = style; + // Invalidate to trigger drawing. + invalidate(); + } + + /** + * Sets the bottom padding fraction to apply when {@link Cue#line} is {@link Cue#DIMEN_UNSET}, + * as a fraction of the view's remaining height after its top and bottom padding have been + * subtracted. + *

+ * Note that this padding is applied in addition to any standard view padding. + * + * @param bottomPaddingFraction The bottom padding fraction. + */ + public void setBottomPaddingFraction(float bottomPaddingFraction) { + if (this.bottomPaddingFraction == bottomPaddingFraction) { + return; + } + this.bottomPaddingFraction = bottomPaddingFraction; + // Invalidate to trigger drawing. + invalidate(); + } + + @Override + public void dispatchDraw(Canvas canvas) { + int cueCount = (cues == null) ? 0 : cues.size(); + int rawTop = getTop(); + int rawBottom = getBottom(); + + // Calculate the bounds after padding is taken into account. + int left = getLeft() + getPaddingLeft(); + int top = rawTop + getPaddingTop(); + int right = getRight() + getPaddingRight(); + int bottom = rawBottom - getPaddingBottom(); + if (bottom <= top || right <= left) { + // No space to draw subtitles. + return; + } + + float textSizePx = textSizeType == ABSOLUTE ? textSize + : textSize * (textSizeType == FRACTIONAL ? (bottom - top) : (rawBottom - rawTop)); + if (textSizePx <= 0) { + // Text has no height. + return; + } + + for (int i = 0; i < cueCount; i++) { + painters.get(i).draw(cues.get(i), applyEmbeddedStyles, style, textSizePx, + bottomPaddingFraction, canvas, left, top, right, bottom); + } + } + + @TargetApi(19) + private float getUserCaptionFontScaleV19() { + CaptioningManager captioningManager = + (CaptioningManager) getContext().getSystemService(Context.CAPTIONING_SERVICE); + return captioningManager.getFontScale(); + } + + @TargetApi(19) + private CaptionStyleCompat getUserCaptionStyleV19() { + CaptioningManager captioningManager = + (CaptioningManager) getContext().getSystemService(Context.CAPTIONING_SERVICE); + return CaptionStyleCompat.createFromCaptionStyle(captioningManager.getUserStyle()); + } + +} diff --git a/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_fastforward.xml b/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_fastforward.xml new file mode 100644 index 0000000000..4b86e109e9 --- /dev/null +++ b/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_fastforward.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_next.xml b/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_next.xml new file mode 100644 index 0000000000..6305bcbc90 --- /dev/null +++ b/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_next.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_pause.xml b/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_pause.xml new file mode 100644 index 0000000000..45cd68bed6 --- /dev/null +++ b/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_pause.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_play.xml b/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_play.xml new file mode 100644 index 0000000000..c8c4cdb127 --- /dev/null +++ b/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_play.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_previous.xml b/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_previous.xml new file mode 100644 index 0000000000..9564a2a350 --- /dev/null +++ b/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_previous.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_rewind.xml b/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_rewind.xml new file mode 100644 index 0000000000..976b706170 --- /dev/null +++ b/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_rewind.xml @@ -0,0 +1,25 @@ + + + + + + diff --git a/library/ui/src/main/res/drawable-hdpi/exo_controls_fastforward.png b/library/ui/src/main/res/drawable-hdpi/exo_controls_fastforward.png new file mode 100644 index 0000000000000000000000000000000000000000..843df84091bb7e43f85088e2985cb17712eb4b12 GIT binary patch literal 354 zcmV-o0iFJdP)~5QPzl6$m6^1rmWoAW4u24uIeQ2qc0dZ~z1cKp+tuU<=Tl1wDU|KjmESlLKwAWuDy0?Ju3$lDWgaLQ6r z0eDtE9z+44UOeG~t54wP|l{5gJor@<%0p%hYWnGgfPpkqdf0AlUPwWE9 zNzwy&j_*7c1yUB0GQcyx_gEHC_L3ITV_iT$r#{kSS>WME4DhUjerS&a^(q`cUxg@7 z>;fMjBM(p0`Wnp~2fpykpksx@fldP%%7ZT8JQ6#2el3T&ua}Yx&(M0D_F4;3p3nl` zi}@SRkL60~|CGOa+SW_`B50Tbdk7(f5JJd}D`jHlVpJacasU7T07*qoM6N<$f{G`F Aj{pDw literal 0 HcmV?d00001 diff --git a/library/ui/src/main/res/drawable-hdpi/exo_controls_next.png b/library/ui/src/main/res/drawable-hdpi/exo_controls_next.png new file mode 100644 index 0000000000000000000000000000000000000000..c37541472ebb20e052a5c4e68f82852d4a489ec0 GIT binary patch literal 323 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-s;Q>A&uI>ds|7if^nwEqEt&k}R z@(X5g;PIDueVZlw_jHBT=RXUWJnsHpeOP02w9fW;RW^Pzpt^cb7srr_xVM)qg$@}A zuwG>3G3w^Ik@o)g{K>04IW(I1PF(rP?e^C6Z?e~t}8mc^KVEOXY<)RyS%oA nDVdY8h==XT9hM(YvbWwlo}!Q;Ydn(!=oAJ|S3j3^P6E)*( literal 0 HcmV?d00001 diff --git a/library/ui/src/main/res/drawable-hdpi/exo_controls_pause.png b/library/ui/src/main/res/drawable-hdpi/exo_controls_pause.png new file mode 100644 index 0000000000000000000000000000000000000000..0a23452746c0f33622ff4d909e2213b98ce990b9 GIT binary patch literal 108 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDH3?y^UWFG-iYymzYu0Z<#|Nl#G&c6#}F_r}R z1v5B2yO9RuhnjGkCiCxvXAbpmr5JpHO}HTOB5@xyCL>^BiEHX8zeV3eCC}QlkKsiLyr`#VO({s-JnDN;}FLM6npO-wE_EgQu&X%Q~loCID~aZ8HD> literal 0 HcmV?d00001 diff --git a/library/ui/src/main/res/drawable-hdpi/exo_controls_previous.png b/library/ui/src/main/res/drawable-hdpi/exo_controls_previous.png new file mode 100644 index 0000000000000000000000000000000000000000..3eae5c883bf2c1d8b950e35307ca7e0bf0fc664b GIT binary patch literal 292 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-st^qzFuI>ds|0xFHS4_484HPR0 z@(X4#xE%a&;{M-UA61GMy32^HPd(Xe_1wSeC_hj|g{O;SNJZS+OB=}UWAyQc>(V?C+-zHvv;vr9<^9_ b@df)-Q^{poR@z^IZeZ|q^>bP0l+XkKj~kZ6 literal 0 HcmV?d00001 diff --git a/library/ui/src/main/res/drawable-hdpi/exo_controls_rewind.png b/library/ui/src/main/res/drawable-hdpi/exo_controls_rewind.png new file mode 100644 index 0000000000000000000000000000000000000000..36537d3b7320d0e2b352527ae374bc8c6f3a1d64 GIT binary patch literal 347 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sfdM`tuI>ds|EUU+r44zwK8TlpZ=V`AX~Kb@xBLUpBqutG}z@yPb#Hr0Npiu4KG(s4v}T&!I9w zksXaJ*N)%W2b^$lV+00^T7r*g}8FYn}2J|fI5tI_Y)qkwje@(&0_aOxv z<9#Jszxs2`G_3UgsrOxvSk=Qd V9_(4)c?IZH22WQ%mvv4FO#p)T!5#nr literal 0 HcmV?d00001 diff --git a/library/ui/src/main/res/drawable-ldpi/exo_controls_fastforward.png b/library/ui/src/main/res/drawable-ldpi/exo_controls_fastforward.png new file mode 100644 index 0000000000000000000000000000000000000000..19b9e6015c7cfe517037032e34ef1fd2de721475 GIT binary patch literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gj?Vc`@3FFZCs1}JDH z@L6DS8&IL6qH~W_kS>F3nw?5rI|NsA8 zhciwsRt5^knFK8Ok63Nl!oaeRxzJ2kx1sT@gN~y&x5EQdf$Gbbm?UQK9kJQ6CBa(Y z^o2uA0WHrjYjtc;i_D5-I8`FE#ba524kRmlTeg(dV1rbLYDOj_#~$`aDN#`j%)c8WnnBX#3hUUC#G)b@n5Q@1XqqOr zhwV{8RHT7?2Wy6)%l5__jbao3NO#hahox3Je0A~ do~wX~q2W~A`8Aye6Mzn3@O1TaS?83{1OTu`L7V^p literal 0 HcmV?d00001 diff --git a/library/ui/src/main/res/drawable-ldpi/exo_controls_previous.png b/library/ui/src/main/res/drawable-ldpi/exo_controls_previous.png new file mode 100644 index 0000000000000000000000000000000000000000..930534d3127ceef161e086437aab19a725009e09 GIT binary patch literal 187 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjO`a}}Ar-fh6C_v{H>5qV7ZF(4 z$KLV$@)4#5TX+TTs~=NTiDVE-V|%Z zT*VkP1r_4{%kwX|!nEd~(^Z8gw;b!DBSE%&7bdIu>NK308E)g3vbV?ft1c)I$ztaD0e0sshPLKpx5 literal 0 HcmV?d00001 diff --git a/library/ui/src/main/res/drawable-ldpi/exo_controls_rewind.png b/library/ui/src/main/res/drawable-ldpi/exo_controls_rewind.png new file mode 100644 index 0000000000000000000000000000000000000000..83d71782f65462fbc78127f99fa99dd10cbee9ef GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gjb3I)gLn>}1CrGd^ZqR%1|Nnm* z2b=jpraMfc7(Ui2{Ofn`o9owS^-t^h%r9;`u`(yQx*s7=}aaufe`1SBa5=zR%ISv-ZrD6LEYMBqE7&? z;mmA9yR#08j80h{8#N?4?hBvDc-V49V3P}5$M@h>X(ByLpBNY{e!iaao+04@(4h>T Lu6{1-oD!MFWREOXtO~A2?8{a7`g_FQ2I_1JnIRi`g+>uM{xwds|EUK$mVS={nk-oo zMzKY@y3JY5_^D&pQ=bmwa@5MXu) z@^pItmdKI;Vst0BZD- A)c^nh literal 0 HcmV?d00001 diff --git a/library/ui/src/main/res/drawable-mdpi/exo_controls_pause.png b/library/ui/src/main/res/drawable-mdpi/exo_controls_pause.png new file mode 100644 index 0000000000000000000000000000000000000000..f54c942201a83c6b866d651cb6dda51ab06d5cc9 GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv(Ey(iSN8&+{}^EVDaKns1#Bfj ze!&d!4iW+?+N{j~fFin{E{-7@=aUl_NCyN3B_#` zyzvFT4)#Z99dwK*@G&%=cbLO_M2Gn)gMTnjll#U zebYe)NHa7(caX6G@)=*HMHgTe~DWM4f@A*$c literal 0 HcmV?d00001 diff --git a/library/ui/src/main/res/drawable-mdpi/exo_controls_previous.png b/library/ui/src/main/res/drawable-mdpi/exo_controls_previous.png new file mode 100644 index 0000000000000000000000000000000000000000..950e213d2f550cf8022cc7ee063e509e6903d393 GIT binary patch literal 227 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJRh}-6Ar-fh6C_v{Cy4YgF+H$n zI-?#a&%d&2#WRKlQPK+g_%FGHvol=Xz))x`@V_l{(QM|1Pg)K&zwD(JbTc;;y>$5V z(_YLWn4RIMOGCxqdYgtcK88gMUIs4~z z1~5loTbylnB<#n9y)q8^b?iqNJwz`&0!mMsm!ME*pTO7fYBJ-azl(Wi1sZUmdKI;Vst07C3gL;wH) literal 0 HcmV?d00001 diff --git a/library/ui/src/main/res/drawable-mdpi/exo_controls_rewind.png b/library/ui/src/main/res/drawable-mdpi/exo_controls_rewind.png new file mode 100644 index 0000000000000000000000000000000000000000..e75efae1893e7c53fce2d4c8809bbbe12e577923 GIT binary patch literal 273 zcmV+s0q*{ZP)`R z%w)4WEs7urf)7yDIa2=0w~Cd>9TCEByne3G6glVI|tLyqc0l7I60o&l_5><&Qr%*Cs`1zG>w zrB)~q1X z@^Hn92fydexcT4i@{=WsXErogKMntLwbk8b_SrOP_g7WAYW}h80aLG(GCX`b<2Azu zT_CF(DAcfg`8sBc@K;t0GVj*6uYb+(ta}%q!WE!whSe*ph9r+4Gb9<*{B6`5R{2z> zF+42WIN4qJ{oCMqq8e>mp0mvA+rif|YbW0s@nTsEnL=5M@Ml&Zz8on{crSeKS*^xx zrx^A_Z#!N)gc@CMJbC<|b*-!h+cT>RB}cygnzduj*|g2I4C0r(-V6Fk^8x+D;OXk; Jvd$@?2>^VR(}n;5 literal 0 HcmV?d00001 diff --git a/library/ui/src/main/res/drawable-xhdpi/exo_controls_next.png b/library/ui/src/main/res/drawable-xhdpi/exo_controls_next.png new file mode 100644 index 0000000000000000000000000000000000000000..bc1ebf83c58c0f6b983817f62a24213cf25c2c27 GIT binary patch literal 334 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU=;OqaSW-r_4cNskYk`od!eHM zOQ3*`gNTcyt{r}#)c+<@MXM4-f zhpq$~2m~TiQg7Z__N)26WP14@XNK*YUuv)yq|fdwYDmbv9K!dY&&>HL!@)z#*2x@r zo4e~2SL}&66gRYYcTuay8?+egzqa?iKrg>VC>bmL1i7;T#`6T@CAEmyH<6wnL?Pgg&ebxsLQ05&!_aR2}S literal 0 HcmV?d00001 diff --git a/library/ui/src/main/res/drawable-xhdpi/exo_controls_play.png b/library/ui/src/main/res/drawable-xhdpi/exo_controls_play.png new file mode 100644 index 0000000000000000000000000000000000000000..f2f934413e8580fe363938e427fbc11905441893 GIT binary patch literal 343 zcmV-d0jU0oP)G|d_tc@}WR?%2|^fWQwsU}LWWBG2$FJPU}Nu&HMOfj3rr z77)2$bI$?-UuZ~Y$daY09(oIDZoxL2MW-i%tQk8CNrS`oyp7~Kvyy|3DA?w z%mU;mvw#5E$t);9W-|XE;D#+c3ix1KuTS8N%{+btU)aWL29Map<016GRvu5`aQ^Pq z{V_@;Kvvoc3Xqw$LIUKbEwcdmY0D@;N7^z7(3Q4?0(7P=kpR7E8z?}3+6D-)k+vQJ pY^ANEfZANz2qA?qY_z{C1* z%d}ffN-Gw;|0n*{`1iWB*_H?Pd(P|aTBLDl|7zYWcIF^a#%HNQjC$ss4B6t0r}P99 zes?jvRTO;iQ;aEYGDpBBmxgL_##@Sl3ZaY#;^i6s{o?QtWxD3a_Jdb3LDl%ce+kBY zDl9*yaX!eE=ZLt;`CzRq#uN5$XW0pK OCWEJ|pUXO@geCy!sjGbe literal 0 HcmV?d00001 diff --git a/library/ui/src/main/res/drawable-xhdpi/exo_controls_rewind.png b/library/ui/src/main/res/drawable-xhdpi/exo_controls_rewind.png new file mode 100644 index 0000000000000000000000000000000000000000..3340ef9bd227be28c3641fc1a111522d9d0af362 GIT binary patch literal 400 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!LIQk3T-^(N{!<$?d%wI4G+(+T z$S;_|kmEGV1`&r(zgw*zPI6BVmD#t5ulUMy6^-P8+MT9AWfweM978JN-cGUPYjzM| zsZHRQ;Mw=V@&Et%eCjWBGvzmkPSbM8s46!#k?K9RN&olRnu(u18}~K+T%2U#?{!{g zT~OPTnSY}t7o}fwl$@0Qj$hOH)0ws5K*8(5o(tTMd5SG6-*sAn`=a&z#VLwa=`TZh z%s-bra+}b8S-^DMt#f7jXWpDl6QVfyONCe??d>Y3Y7#J8s zJY5_^D&pQwKb?2TK*U9MQUaF=r?1atr}!^V|NlRvw`Yd$(w$QI?<3`3>g4pk`C#Ao z*aC?Bq<#DR?#c0-D|c0R&pvrEdy!q=ou880Oh4b5k76Y3*kztw*42IAQQ~^>bP0 Hl+XkKRE;tn literal 0 HcmV?d00001 diff --git a/library/ui/src/main/res/drawable-xxhdpi/exo_controls_next.png b/library/ui/src/main/res/drawable-xxhdpi/exo_controls_next.png new file mode 100644 index 0000000000000000000000000000000000000000..232f09e910954a5a56b32d2cb807f2e68f8de781 GIT binary patch literal 391 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy#_0(?ST-3xsFQwF}~PUr`kCR!5Y z7tG*y)Qt7M!Y{seD`%c}?5@(zm5SXa?clKjsNlY*i(^Pd+}o>XxeggHuq6JTyzJt( zZP#l5hksRi<0~@f_BqQBjvTX3KM(TKTsq~FSzWo``#QEQZwwYx9ccJcbD$xLo%vF3 z8{`>6vf8#!6LBX zI49c`KOpO1!znK2w<(QvP3unRPh~m9U>m%imAT@*z=3XGrmqdpZtYj&`f;C~p`V$d z;vUC=%gqe)FVdQ&MBb@0Q^V4E&u=k literal 0 HcmV?d00001 diff --git a/library/ui/src/main/res/drawable-xxhdpi/exo_controls_pause.png b/library/ui/src/main/res/drawable-xxhdpi/exo_controls_pause.png new file mode 100644 index 0000000000000000000000000000000000000000..50a545db4d67f51ccc127aa98582a2d5236e127f GIT binary patch literal 113 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeK3?y%aJ*@^(YymzYuI>ds|NsA=KV^|KP=v80 z$S;_|;n|HeAV<>E#WBR_ALEHZkC5TjW01WFdewB_m zpN&S#A|!*?^k{}16VXoYnre9T)QqH5!op(Rt?bj}tv}c;oS*SOslKRi@&Y&Ci%$dR zhP?2L$lEM(JEzsGdg0l)jLqV=-e6}st}o!u{dPy~NUjrvcs8ro@R-D7ev`ELYA)%6 Rr9gi&c)I$ztaD0e0ssTizK8$- literal 0 HcmV?d00001 diff --git a/library/ui/src/main/res/drawable-xxhdpi/exo_controls_previous.png b/library/ui/src/main/res/drawable-xxhdpi/exo_controls_previous.png new file mode 100644 index 0000000000000000000000000000000000000000..f71acc4875e1ee7375b4023d800abd6b1292e717 GIT binary patch literal 464 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy#z0(?ST-3xsF(-#!%=eQ5FU9}|0 zFPP!IK{1ES>EA3i_dlnLJYPI{zq`if>)y>g?E=9n>o14*?|%G!e#s*KRX`0dJY5_^ zD&pQ=wJc%^6mh=D?)&*RbHay~vpgC9qg57qNxBvp-{qa-v+DTLDM4PEs&`i}kBX0c z@HAA#;E&nF17%th5B#3(k+5yDM}nz1r;nYAki`2|rdVOlFW#LjX37%}R4FPpEN^GD zvE_O2(viWRpShySBcW7`)2B~}siG=i!R`*0Uz3k8%##y&A;oj$$84sWeuIFo#_f%> zY7-l#^Rc}u=3+MeW6Jd*{*c38eT4(EZUzB=h1jxQNU>GSQ8@6pg;7qO>w`sN!|_fg z8$XE$g^L*ag;^`+7#w))!YH@AD#A+>=<;2Rb!XSeIO>$-0R7D1>FVdQ&MBb@0G@yI AVgLXD literal 0 HcmV?d00001 diff --git a/library/ui/src/main/res/drawable-xxhdpi/exo_controls_rewind.png b/library/ui/src/main/res/drawable-xxhdpi/exo_controls_rewind.png new file mode 100644 index 0000000000000000000000000000000000000000..db0555f9e5c5f89cf29edafc7c5f277556f3cb74 GIT binary patch literal 571 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy$81AIbU-3xsF4-n8x+3yN;hDJ${ zUob=Q{m(M*4Sut5h%`U9nS7cjcz?S4VwLrmH8x-Op5K3bce23a@8K5hd){5)sP1bJzu zex8u|$?iP!?4_9w#fJS7cQ&kE{)6%CEt3bSG8KD_c_U(uuXp5qeV?iS_c@ce-x7|; z-<~o_C=+&n+17iHx!=9`a>eB599HvUX0hcgXFPu4!kQ!Tx8C?3zTAA=J0l@1WYxo$ zfmXI!hPGbI-Bp*lFO`}nxAgL2q05U)szm(z7xo@{SqNnL^e?}>_|KPy%d?c{#ufx6 zpLzFlhV=5-hlb|&7B(m69eBjox6yI8p^Wq4yz2#Lc=wxC+^KPNzF%O`)|UIN<_{O| z4Zoj14x9L->#~^|_!Zo7m|RnLreUU x-~XHn5bxna#>s3aK743!bbf5IM-v<<_wCJor+pLc2m?j~gQu&X%Q~loCIETRKIZ@c literal 0 HcmV?d00001 diff --git a/library/ui/src/main/res/layout/exo_playback_control_view.xml b/library/ui/src/main/res/layout/exo_playback_control_view.xml new file mode 100644 index 0000000000..f8ef5a6fdd --- /dev/null +++ b/library/ui/src/main/res/layout/exo_playback_control_view.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/library/ui/src/main/res/layout/exo_simple_player_view.xml b/library/ui/src/main/res/layout/exo_simple_player_view.xml new file mode 100644 index 0000000000..1f59b7796d --- /dev/null +++ b/library/ui/src/main/res/layout/exo_simple_player_view.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/library/ui/src/main/res/values-af/strings.xml b/library/ui/src/main/res/values-af/strings.xml new file mode 100644 index 0000000000..9f1bce53d9 --- /dev/null +++ b/library/ui/src/main/res/values-af/strings.xml @@ -0,0 +1,25 @@ + + + + "Vorige snit" + "Volgende snit" + "Wag" + "Speel" + "Stop" + "Spoel terug" + "Vinnig vorentoe" + diff --git a/library/ui/src/main/res/values-am/strings.xml b/library/ui/src/main/res/values-am/strings.xml new file mode 100644 index 0000000000..f06c2a664e --- /dev/null +++ b/library/ui/src/main/res/values-am/strings.xml @@ -0,0 +1,25 @@ + + + + "ቀዳሚ ትራክ" + "ቀጣይ ትራክ" + "ለአፍታ አቁም" + "አጫውት" + "አቁም" + "ወደኋላ አጠንጥን" + "በፍጥነት አሳልፍ" + diff --git a/library/ui/src/main/res/values-ar/strings.xml b/library/ui/src/main/res/values-ar/strings.xml new file mode 100644 index 0000000000..a40c961bf7 --- /dev/null +++ b/library/ui/src/main/res/values-ar/strings.xml @@ -0,0 +1,25 @@ + + + + "المقطع الصوتي السابق" + "المقطع الصوتي التالي" + "إيقاف مؤقت" + "تشغيل" + "إيقاف" + "إرجاع" + "تقديم سريع" + diff --git a/library/ui/src/main/res/values-az-rAZ/strings.xml b/library/ui/src/main/res/values-az-rAZ/strings.xml new file mode 100644 index 0000000000..7b3b9366b5 --- /dev/null +++ b/library/ui/src/main/res/values-az-rAZ/strings.xml @@ -0,0 +1,25 @@ + + + + "Öncəki trek" + "Növbəti trek" + "Pauza" + "Oyun" + "Dayandır" + "Geri sarıma" + "Sürətlə irəli" + diff --git a/library/ui/src/main/res/values-b+sr+Latn/strings.xml b/library/ui/src/main/res/values-b+sr+Latn/strings.xml new file mode 100644 index 0000000000..b5fdd74402 --- /dev/null +++ b/library/ui/src/main/res/values-b+sr+Latn/strings.xml @@ -0,0 +1,25 @@ + + + + "Prethodna pesma" + "Sledeća pesma" + "Pauza" + "Pusti" + "Zaustavi" + "Premotaj unazad" + "Premotaj unapred" + diff --git a/library/ui/src/main/res/values-be-rBY/strings.xml b/library/ui/src/main/res/values-be-rBY/strings.xml new file mode 100644 index 0000000000..890c23ebd5 --- /dev/null +++ b/library/ui/src/main/res/values-be-rBY/strings.xml @@ -0,0 +1,25 @@ + + + + "Папярэдні трэк" + "Наступны трэк" + "Прыпыніць" + "Прайграць" + "Спыніць" + "Перамотка назад" + "Перамотка ўперад" + diff --git a/library/ui/src/main/res/values-bg/strings.xml b/library/ui/src/main/res/values-bg/strings.xml new file mode 100644 index 0000000000..30b905fb8e --- /dev/null +++ b/library/ui/src/main/res/values-bg/strings.xml @@ -0,0 +1,25 @@ + + + + "Предишен запис" + "Следващ запис" + "Пауза" + "Пускане" + "Спиране" + "Превъртане назад" + "Превъртане напред" + diff --git a/library/ui/src/main/res/values-bn-rBD/strings.xml b/library/ui/src/main/res/values-bn-rBD/strings.xml new file mode 100644 index 0000000000..ca5d9461d3 --- /dev/null +++ b/library/ui/src/main/res/values-bn-rBD/strings.xml @@ -0,0 +1,25 @@ + + + + "পূর্ববর্তী ট্র্যাক" + "পরবর্তী ট্র্যাক" + "বিরাম দিন" + "প্লে করুন" + "থামান" + "গুটিয়ে নিন" + "দ্রুত সামনে এগোন" + diff --git a/library/ui/src/main/res/values-bs-rBA/strings.xml b/library/ui/src/main/res/values-bs-rBA/strings.xml new file mode 100644 index 0000000000..9cb0ca4d76 --- /dev/null +++ b/library/ui/src/main/res/values-bs-rBA/strings.xml @@ -0,0 +1,25 @@ + + + + "Prethodna numera" + "Sljedeća numera" + "Pauziraj" + "Reproduciraj" + "Zaustavi" + "Premotaj" + "Ubrzaj" + diff --git a/library/ui/src/main/res/values-ca/strings.xml b/library/ui/src/main/res/values-ca/strings.xml new file mode 100644 index 0000000000..0816c76b12 --- /dev/null +++ b/library/ui/src/main/res/values-ca/strings.xml @@ -0,0 +1,25 @@ + + + + "Ruta anterior" + "Ruta següent" + "Posa en pausa" + "Reprodueix" + "Atura" + "Rebobina" + "Avança ràpidament" + diff --git a/library/ui/src/main/res/values-cs/strings.xml b/library/ui/src/main/res/values-cs/strings.xml new file mode 100644 index 0000000000..22cff4041e --- /dev/null +++ b/library/ui/src/main/res/values-cs/strings.xml @@ -0,0 +1,25 @@ + + + + "Předchozí skladba" + "Další skladba" + "Pozastavit" + "Přehrát" + "Zastavit" + "Přetočit zpět" + "Přetočit vpřed" + diff --git a/library/ui/src/main/res/values-da/strings.xml b/library/ui/src/main/res/values-da/strings.xml new file mode 100644 index 0000000000..a6710bea50 --- /dev/null +++ b/library/ui/src/main/res/values-da/strings.xml @@ -0,0 +1,25 @@ + + + + "Forrige nummer" + "Næste nummer" + "Pause" + "Afspil" + "Stop" + "Spol tilbage" + "Spol frem" + diff --git a/library/ui/src/main/res/values-de/strings.xml b/library/ui/src/main/res/values-de/strings.xml new file mode 100644 index 0000000000..cdfd2d4baf --- /dev/null +++ b/library/ui/src/main/res/values-de/strings.xml @@ -0,0 +1,25 @@ + + + + "Vorheriger Titel" + "Nächster Titel" + "Pausieren" + "Wiedergabe" + "Beenden" + "Zurückspulen" + "Vorspulen" + diff --git a/library/ui/src/main/res/values-el/strings.xml b/library/ui/src/main/res/values-el/strings.xml new file mode 100644 index 0000000000..1e11df3b14 --- /dev/null +++ b/library/ui/src/main/res/values-el/strings.xml @@ -0,0 +1,25 @@ + + + + "Προηγούμενο κομμάτι" + "Επόμενο κομμάτι" + "Παύση" + "Αναπαραγωγή" + "Διακοπή" + "Επαναφορά" + "Γρήγορη προώθηση" + diff --git a/library/ui/src/main/res/values-en-rAU/strings.xml b/library/ui/src/main/res/values-en-rAU/strings.xml new file mode 100644 index 0000000000..5077cf2b94 --- /dev/null +++ b/library/ui/src/main/res/values-en-rAU/strings.xml @@ -0,0 +1,25 @@ + + + + "Previous track" + "Next track" + "Pause" + "Play" + "Stop" + "Rewind" + "Fast-forward" + diff --git a/library/ui/src/main/res/values-en-rGB/strings.xml b/library/ui/src/main/res/values-en-rGB/strings.xml new file mode 100644 index 0000000000..5077cf2b94 --- /dev/null +++ b/library/ui/src/main/res/values-en-rGB/strings.xml @@ -0,0 +1,25 @@ + + + + "Previous track" + "Next track" + "Pause" + "Play" + "Stop" + "Rewind" + "Fast-forward" + diff --git a/library/ui/src/main/res/values-en-rIN/strings.xml b/library/ui/src/main/res/values-en-rIN/strings.xml new file mode 100644 index 0000000000..5077cf2b94 --- /dev/null +++ b/library/ui/src/main/res/values-en-rIN/strings.xml @@ -0,0 +1,25 @@ + + + + "Previous track" + "Next track" + "Pause" + "Play" + "Stop" + "Rewind" + "Fast-forward" + diff --git a/library/ui/src/main/res/values-es-rUS/strings.xml b/library/ui/src/main/res/values-es-rUS/strings.xml new file mode 100644 index 0000000000..72b176e538 --- /dev/null +++ b/library/ui/src/main/res/values-es-rUS/strings.xml @@ -0,0 +1,25 @@ + + + + "Pista anterior" + "Siguiente pista" + "Pausar" + "Reproducir" + "Detener" + "Retroceder" + "Avanzar" + diff --git a/library/ui/src/main/res/values-es/strings.xml b/library/ui/src/main/res/values-es/strings.xml new file mode 100644 index 0000000000..3b188d266d --- /dev/null +++ b/library/ui/src/main/res/values-es/strings.xml @@ -0,0 +1,25 @@ + + + + "Canción anterior" + "Siguiente canción" + "Pausar" + "Reproducir" + "Detener" + "Rebobinar" + "Avance rápido" + diff --git a/library/ui/src/main/res/values-et-rEE/strings.xml b/library/ui/src/main/res/values-et-rEE/strings.xml new file mode 100644 index 0000000000..7a01bd9d5a --- /dev/null +++ b/library/ui/src/main/res/values-et-rEE/strings.xml @@ -0,0 +1,25 @@ + + + + "Eelmine lugu" + "Järgmine lugu" + "Peata" + "Esita" + "Peata" + "Keri tagasi" + "Keri edasi" + diff --git a/library/ui/src/main/res/values-eu-rES/strings.xml b/library/ui/src/main/res/values-eu-rES/strings.xml new file mode 100644 index 0000000000..3dd51d2138 --- /dev/null +++ b/library/ui/src/main/res/values-eu-rES/strings.xml @@ -0,0 +1,25 @@ + + + + "Aurreko pista" + "Hurrengo pista" + "Pausatu" + "Erreproduzitu" + "Gelditu" + "Atzeratu" + "Aurreratu" + diff --git a/library/ui/src/main/res/values-fa/strings.xml b/library/ui/src/main/res/values-fa/strings.xml new file mode 100644 index 0000000000..a8955ca2f3 --- /dev/null +++ b/library/ui/src/main/res/values-fa/strings.xml @@ -0,0 +1,25 @@ + + + + "آهنگ قبلی" + "آهنگ بعدی" + "مکث" + "پخش" + "توقف" + "عقب بردن" + "جلو بردن سریع" + diff --git a/library/ui/src/main/res/values-fi/strings.xml b/library/ui/src/main/res/values-fi/strings.xml new file mode 100644 index 0000000000..5f1352d1af --- /dev/null +++ b/library/ui/src/main/res/values-fi/strings.xml @@ -0,0 +1,25 @@ + + + + "Edellinen raita" + "Seuraava raita" + "Tauko" + "Toista" + "Seis" + "Kelaa taakse" + "Kelaa eteen" + diff --git a/library/ui/src/main/res/values-fr-rCA/strings.xml b/library/ui/src/main/res/values-fr-rCA/strings.xml new file mode 100644 index 0000000000..51ba11e0c0 --- /dev/null +++ b/library/ui/src/main/res/values-fr-rCA/strings.xml @@ -0,0 +1,25 @@ + + + + "Chanson précédente" + "Chanson suivante" + "Pause" + "Lecture" + "Arrêter" + "Reculer" + "Avance rapide" + diff --git a/library/ui/src/main/res/values-fr/strings.xml b/library/ui/src/main/res/values-fr/strings.xml new file mode 100644 index 0000000000..d55b32b6f7 --- /dev/null +++ b/library/ui/src/main/res/values-fr/strings.xml @@ -0,0 +1,25 @@ + + + + "Piste précédente" + "Piste suivante" + "Interrompre" + "Lire" + "Arrêter" + "Retour arrière" + "Avance rapide" + diff --git a/library/ui/src/main/res/values-gl-rES/strings.xml b/library/ui/src/main/res/values-gl-rES/strings.xml new file mode 100644 index 0000000000..99ae59c7f9 --- /dev/null +++ b/library/ui/src/main/res/values-gl-rES/strings.xml @@ -0,0 +1,25 @@ + + + + "Pista anterior" + "Seguinte pista" + "Pausar" + "Reproducir" + "Deter" + "Rebobinar" + "Avance rápido" + diff --git a/library/ui/src/main/res/values-gu-rIN/strings.xml b/library/ui/src/main/res/values-gu-rIN/strings.xml new file mode 100644 index 0000000000..6feab0a3a6 --- /dev/null +++ b/library/ui/src/main/res/values-gu-rIN/strings.xml @@ -0,0 +1,25 @@ + + + + "પહેલાનો ટ્રૅક" + "આગલો ટ્રૅક" + "થોભો" + "ચલાવો" + "રોકો" + "રીવાઇન્ડ કરો" + "ઝડપી ફોરવર્ડ કરો" + diff --git a/library/ui/src/main/res/values-hi/strings.xml b/library/ui/src/main/res/values-hi/strings.xml new file mode 100644 index 0000000000..5229b67d0e --- /dev/null +++ b/library/ui/src/main/res/values-hi/strings.xml @@ -0,0 +1,25 @@ + + + + "पिछला ट्रैक" + "अगला ट्रैक" + "रोकें" + "चलाएं" + "बंद करें" + "रिवाइंड करें" + "फ़ास्ट फ़ॉरवर्ड" + diff --git a/library/ui/src/main/res/values-hr/strings.xml b/library/ui/src/main/res/values-hr/strings.xml new file mode 100644 index 0000000000..c0b075edde --- /dev/null +++ b/library/ui/src/main/res/values-hr/strings.xml @@ -0,0 +1,25 @@ + + + + "Prethodna pjesma" + "Sljedeća pjesma" + "Pauziraj" + "Reproduciraj" + "Zaustavi" + "Unatrag" + "Brzo unaprijed" + diff --git a/library/ui/src/main/res/values-hu/strings.xml b/library/ui/src/main/res/values-hu/strings.xml new file mode 100644 index 0000000000..2a34684edb --- /dev/null +++ b/library/ui/src/main/res/values-hu/strings.xml @@ -0,0 +1,25 @@ + + + + "Előző szám" + "Következő szám" + "Szünet" + "Lejátszás" + "Leállítás" + "Visszatekerés" + "Előretekerés" + diff --git a/library/ui/src/main/res/values-hy-rAM/strings.xml b/library/ui/src/main/res/values-hy-rAM/strings.xml new file mode 100644 index 0000000000..05f9d04ab7 --- /dev/null +++ b/library/ui/src/main/res/values-hy-rAM/strings.xml @@ -0,0 +1,25 @@ + + + + "Նախորդը" + "Հաջորդը" + "Դադարեցնել" + "Նվագարկել" + "Դադարեցնել" + "Հետ փաթաթել" + "Արագ առաջ անցնել" + diff --git a/library/ui/src/main/res/values-in/strings.xml b/library/ui/src/main/res/values-in/strings.xml new file mode 100644 index 0000000000..062933a0a8 --- /dev/null +++ b/library/ui/src/main/res/values-in/strings.xml @@ -0,0 +1,25 @@ + + + + "Lagu sebelumnya" + "Lagu berikutnya" + "Jeda" + "Putar" + "Berhenti" + "Putar Ulang" + "Maju cepat" + diff --git a/library/ui/src/main/res/values-is-rIS/strings.xml b/library/ui/src/main/res/values-is-rIS/strings.xml new file mode 100644 index 0000000000..9c4421a272 --- /dev/null +++ b/library/ui/src/main/res/values-is-rIS/strings.xml @@ -0,0 +1,25 @@ + + + + "Fyrra lag" + "Næsta lag" + "Hlé" + "Spila" + "Stöðva" + "Spóla til baka" + "Spóla áfram" + diff --git a/library/ui/src/main/res/values-it/strings.xml b/library/ui/src/main/res/values-it/strings.xml new file mode 100644 index 0000000000..71525a2b3e --- /dev/null +++ b/library/ui/src/main/res/values-it/strings.xml @@ -0,0 +1,25 @@ + + + + "Traccia precedente" + "Traccia successiva" + "Metti in pausa" + "Riproduci" + "Interrompi" + "Riavvolgi" + "Avanti veloce" + diff --git a/library/ui/src/main/res/values-iw/strings.xml b/library/ui/src/main/res/values-iw/strings.xml new file mode 100644 index 0000000000..f33cc2adb0 --- /dev/null +++ b/library/ui/src/main/res/values-iw/strings.xml @@ -0,0 +1,25 @@ + + + + "הרצועה הקודמת" + "הרצועה הבאה" + "השהה" + "הפעל" + "הפסק" + "הרץ אחורה" + "הרץ קדימה" + diff --git a/library/ui/src/main/res/values-ja/strings.xml b/library/ui/src/main/res/values-ja/strings.xml new file mode 100644 index 0000000000..baa459aeca --- /dev/null +++ b/library/ui/src/main/res/values-ja/strings.xml @@ -0,0 +1,25 @@ + + + + "前のトラック" + "次のトラック" + "一時停止" + "再生" + "停止" + "巻き戻し" + "早送り" + diff --git a/library/ui/src/main/res/values-ka-rGE/strings.xml b/library/ui/src/main/res/values-ka-rGE/strings.xml new file mode 100644 index 0000000000..5b87f86c34 --- /dev/null +++ b/library/ui/src/main/res/values-ka-rGE/strings.xml @@ -0,0 +1,25 @@ + + + + "წინა ჩანაწერი" + "შემდეგი ჩანაწერი" + "პაუზა" + "დაკვრა" + "შეწყვეტა" + "უკან გადახვევა" + "წინ გადახვევა" + diff --git a/library/ui/src/main/res/values-kk-rKZ/strings.xml b/library/ui/src/main/res/values-kk-rKZ/strings.xml new file mode 100644 index 0000000000..c1bf5c8b4b --- /dev/null +++ b/library/ui/src/main/res/values-kk-rKZ/strings.xml @@ -0,0 +1,25 @@ + + + + "Алдыңғы трек" + "Келесі трек" + "Кідірту" + "Ойнату" + "Тоқтату" + "Кері айналдыру" + "Жылдам алға айналдыру" + diff --git a/library/ui/src/main/res/values-km-rKH/strings.xml b/library/ui/src/main/res/values-km-rKH/strings.xml new file mode 100644 index 0000000000..dbeeab60a6 --- /dev/null +++ b/library/ui/src/main/res/values-km-rKH/strings.xml @@ -0,0 +1,25 @@ + + + + "បទ​មុន" + "បទ​បន្ទាប់" + "ផ្អាក" + "ចាក់" + "បញ្ឈប់" + "ខា​ថយក្រោយ" + "ទៅ​មុខ​​​រហ័ស" + diff --git a/library/ui/src/main/res/values-kn-rIN/strings.xml b/library/ui/src/main/res/values-kn-rIN/strings.xml new file mode 100644 index 0000000000..b73cf0fdb0 --- /dev/null +++ b/library/ui/src/main/res/values-kn-rIN/strings.xml @@ -0,0 +1,25 @@ + + + + "ಹಿಂದಿನ ಟ್ರ್ಯಾಕ್" + "ಮುಂದಿನ ಟ್ರ್ಯಾಕ್" + "ವಿರಾಮಗೊಳಿಸು" + "ಪ್ಲೇ ಮಾಡು" + "ನಿಲ್ಲಿಸು" + "ರಿವೈಂಡ್ ಮಾಡು" + "ವೇಗವಾಗಿ ಮುಂದಕ್ಕೆ" + diff --git a/library/ui/src/main/res/values-ko/strings.xml b/library/ui/src/main/res/values-ko/strings.xml new file mode 100644 index 0000000000..7097e2d9f7 --- /dev/null +++ b/library/ui/src/main/res/values-ko/strings.xml @@ -0,0 +1,25 @@ + + + + "이전 트랙" + "다음 트랙" + "일시중지" + "재생" + "중지" + "되감기" + "빨리 감기" + diff --git a/library/ui/src/main/res/values-ky-rKG/strings.xml b/library/ui/src/main/res/values-ky-rKG/strings.xml new file mode 100644 index 0000000000..7090c178c3 --- /dev/null +++ b/library/ui/src/main/res/values-ky-rKG/strings.xml @@ -0,0 +1,25 @@ + + + + "Мурунку трек" + "Кийинки трек" + "Тындыруу" + "Ойнотуу" + "Токтотуу" + "Артка түрүү" + "Алдыга түрүү" + diff --git a/library/ui/src/main/res/values-lo-rLA/strings.xml b/library/ui/src/main/res/values-lo-rLA/strings.xml new file mode 100644 index 0000000000..44095e4323 --- /dev/null +++ b/library/ui/src/main/res/values-lo-rLA/strings.xml @@ -0,0 +1,25 @@ + + + + "​ເພງ​ກ່ອນ​ໜ້າ" + "​ເພງ​ຕໍ່​ໄປ" + "ຢຸດຊົ່ວຄາວ" + "ຫຼິ້ນ" + "ຢຸດ" + "​ຣີ​​ວາຍກັບ" + "ເລື່ອນ​ໄປ​ໜ້າ" + diff --git a/library/ui/src/main/res/values-lt/strings.xml b/library/ui/src/main/res/values-lt/strings.xml new file mode 100644 index 0000000000..138caec322 --- /dev/null +++ b/library/ui/src/main/res/values-lt/strings.xml @@ -0,0 +1,25 @@ + + + + "Ankstesnis takelis" + "Kitas takelis" + "Pristabdyti" + "Leisti" + "Stabdyti" + "Sukti atgal" + "Sukti pirmyn" + diff --git a/library/ui/src/main/res/values-lv/strings.xml b/library/ui/src/main/res/values-lv/strings.xml new file mode 100644 index 0000000000..4c91da86cc --- /dev/null +++ b/library/ui/src/main/res/values-lv/strings.xml @@ -0,0 +1,25 @@ + + + + "Iepriekšējais ieraksts" + "Nākamais ieraksts" + "Pārtraukt" + "Atskaņot" + "Apturēt" + "Attīt atpakaļ" + "Ātri patīt" + diff --git a/library/ui/src/main/res/values-mk-rMK/strings.xml b/library/ui/src/main/res/values-mk-rMK/strings.xml new file mode 100644 index 0000000000..e9fedf689f --- /dev/null +++ b/library/ui/src/main/res/values-mk-rMK/strings.xml @@ -0,0 +1,25 @@ + + + + "Претходна песна" + "Следна песна" + "Пауза" + "Пушти" + "Запри" + "Премотај назад" + "Брзо премотај напред" + diff --git a/library/ui/src/main/res/values-ml-rIN/strings.xml b/library/ui/src/main/res/values-ml-rIN/strings.xml new file mode 100644 index 0000000000..acc33934fb --- /dev/null +++ b/library/ui/src/main/res/values-ml-rIN/strings.xml @@ -0,0 +1,25 @@ + + + + "മുമ്പത്തെ ട്രാക്ക്" + "അടുത്ത ട്രാക്ക്" + "താൽക്കാലികമായി നിർത്തുക" + "പ്ലേ ചെയ്യുക" + "നിര്‍ത്തുക" + "റിവൈൻഡുചെയ്യുക" + "വേഗത്തിലുള്ള കൈമാറൽ" + diff --git a/library/ui/src/main/res/values-mn-rMN/strings.xml b/library/ui/src/main/res/values-mn-rMN/strings.xml new file mode 100644 index 0000000000..6434e9ea16 --- /dev/null +++ b/library/ui/src/main/res/values-mn-rMN/strings.xml @@ -0,0 +1,25 @@ + + + + "Өмнөх трек" + "Дараагийн трек" + "Түр зогсоох" + "Тоглуулах" + "Зогсоох" + "Буцааж хураах" + "Хурдан урагшлуулах" + diff --git a/library/ui/src/main/res/values-mr-rIN/strings.xml b/library/ui/src/main/res/values-mr-rIN/strings.xml new file mode 100644 index 0000000000..8f4d0d75b1 --- /dev/null +++ b/library/ui/src/main/res/values-mr-rIN/strings.xml @@ -0,0 +1,25 @@ + + + + "मागील ट्रॅक" + "पुढील ट्रॅक" + "विराम द्या" + "प्ले करा" + "थांबा" + "रिवाईँड करा" + "फास्ट फॉरवर्ड करा" + diff --git a/library/ui/src/main/res/values-ms-rMY/strings.xml b/library/ui/src/main/res/values-ms-rMY/strings.xml new file mode 100644 index 0000000000..91f74bbc1c --- /dev/null +++ b/library/ui/src/main/res/values-ms-rMY/strings.xml @@ -0,0 +1,25 @@ + + + + "Lagu sebelumnya" + "Lagu seterusnya" + "Jeda" + "Main" + "Berhenti" + "Gulung semula" + "Mara laju" + diff --git a/library/ui/src/main/res/values-my-rMM/strings.xml b/library/ui/src/main/res/values-my-rMM/strings.xml new file mode 100644 index 0000000000..4b68e6e950 --- /dev/null +++ b/library/ui/src/main/res/values-my-rMM/strings.xml @@ -0,0 +1,25 @@ + + + + "ယခင် တစ်ပုဒ်" + "နောက် တစ်ပုဒ်" + "ခဏရပ်ရန်" + "ဖွင့်ရန်" + "ရပ်ရန်" + "ပြန်ရစ်ရန်" + "ရှေ့သို့ သွားရန်" + diff --git a/library/ui/src/main/res/values-nb/strings.xml b/library/ui/src/main/res/values-nb/strings.xml new file mode 100644 index 0000000000..37454235ad --- /dev/null +++ b/library/ui/src/main/res/values-nb/strings.xml @@ -0,0 +1,25 @@ + + + + "Forrige spor" + "Neste spor" + "Sett på pause" + "Spill av" + "Stopp" + "Tilbakespoling" + "Fremoverspoling" + diff --git a/library/ui/src/main/res/values-ne-rNP/strings.xml b/library/ui/src/main/res/values-ne-rNP/strings.xml new file mode 100644 index 0000000000..375e44afce --- /dev/null +++ b/library/ui/src/main/res/values-ne-rNP/strings.xml @@ -0,0 +1,25 @@ + + + + "अघिल्लो ट्रयाक" + "अर्को ट्रयाक" + "रोक्नुहोस्" + "चलाउनुहोस्" + "रोक्नुहोस्" + "दोहोर्याउनुहोस्" + "फास्ट फर्वार्ड" + diff --git a/library/ui/src/main/res/values-nl/strings.xml b/library/ui/src/main/res/values-nl/strings.xml new file mode 100644 index 0000000000..2bdbf0bdae --- /dev/null +++ b/library/ui/src/main/res/values-nl/strings.xml @@ -0,0 +1,25 @@ + + + + "Vorig nummer" + "Volgend nummer" + "Onderbreken" + "Afspelen" + "Stoppen" + "Terugspoelen" + "Vooruitspoelen" + diff --git a/library/ui/src/main/res/values-pa-rIN/strings.xml b/library/ui/src/main/res/values-pa-rIN/strings.xml new file mode 100644 index 0000000000..143508e071 --- /dev/null +++ b/library/ui/src/main/res/values-pa-rIN/strings.xml @@ -0,0 +1,25 @@ + + + + "ਪਿਛਲਾ ਟਰੈਕ" + "ਅਗਲਾ ਟਰੈਕ" + "ਰੋਕੋ" + "ਪਲੇ ਕਰੋ" + "ਰੋਕੋ" + "ਰੀਵਾਈਂਡ ਕਰੋ" + "ਅੱਗੇ ਭੇਜੋ" + diff --git a/library/ui/src/main/res/values-pl/strings.xml b/library/ui/src/main/res/values-pl/strings.xml new file mode 100644 index 0000000000..64f52d5d09 --- /dev/null +++ b/library/ui/src/main/res/values-pl/strings.xml @@ -0,0 +1,25 @@ + + + + "Poprzedni utwór" + "Następny utwór" + "Wstrzymaj" + "Odtwórz" + "Zatrzymaj" + "Przewiń do tyłu" + "Przewiń do przodu" + diff --git a/library/ui/src/main/res/values-pt-rBR/strings.xml b/library/ui/src/main/res/values-pt-rBR/strings.xml new file mode 100644 index 0000000000..51bcf4d723 --- /dev/null +++ b/library/ui/src/main/res/values-pt-rBR/strings.xml @@ -0,0 +1,25 @@ + + + + "Faixa anterior" + "Próxima faixa" + "Pausar" + "Reproduzir" + "Parar" + "Retroceder" + "Avançar" + diff --git a/library/ui/src/main/res/values-pt-rPT/strings.xml b/library/ui/src/main/res/values-pt-rPT/strings.xml new file mode 100644 index 0000000000..5b3c9131d0 --- /dev/null +++ b/library/ui/src/main/res/values-pt-rPT/strings.xml @@ -0,0 +1,25 @@ + + + + "Faixa anterior" + "Faixa seguinte" + "Interromper" + "Reproduzir" + "Parar" + "Rebobinar" + "Avançar" + diff --git a/library/ui/src/main/res/values-pt/strings.xml b/library/ui/src/main/res/values-pt/strings.xml new file mode 100644 index 0000000000..51bcf4d723 --- /dev/null +++ b/library/ui/src/main/res/values-pt/strings.xml @@ -0,0 +1,25 @@ + + + + "Faixa anterior" + "Próxima faixa" + "Pausar" + "Reproduzir" + "Parar" + "Retroceder" + "Avançar" + diff --git a/library/ui/src/main/res/values-ro/strings.xml b/library/ui/src/main/res/values-ro/strings.xml new file mode 100644 index 0000000000..5a7feda78c --- /dev/null +++ b/library/ui/src/main/res/values-ro/strings.xml @@ -0,0 +1,25 @@ + + + + "Melodia anterioară" + "Melodia următoare" + "Pauză" + "Redați" + "Opriți" + "Derulați" + "Derulați rapid înainte" + diff --git a/library/ui/src/main/res/values-ru/strings.xml b/library/ui/src/main/res/values-ru/strings.xml new file mode 100644 index 0000000000..da47546a8b --- /dev/null +++ b/library/ui/src/main/res/values-ru/strings.xml @@ -0,0 +1,25 @@ + + + + "Предыдущий трек" + "Следующий трек" + "Приостановить" + "Воспроизвести" + "Остановить" + "Перемотать назад" + "Перемотать вперед" + diff --git a/library/ui/src/main/res/values-si-rLK/strings.xml b/library/ui/src/main/res/values-si-rLK/strings.xml new file mode 100644 index 0000000000..0b579240e8 --- /dev/null +++ b/library/ui/src/main/res/values-si-rLK/strings.xml @@ -0,0 +1,25 @@ + + + + "පෙර ගීතය" + "ඊළඟ ගීතය" + "විරාමය" + "ධාවනය කරන්න" + "නතර කරන්න" + "නැවත ඔතන්න" + "වේගයෙන් ඉදිරියට යන" + diff --git a/library/ui/src/main/res/values-sk/strings.xml b/library/ui/src/main/res/values-sk/strings.xml new file mode 100644 index 0000000000..7596497e06 --- /dev/null +++ b/library/ui/src/main/res/values-sk/strings.xml @@ -0,0 +1,25 @@ + + + + "Predchádzajúca stopa" + "Ďalšia stopa" + "Pozastaviť" + "Prehrať" + "Zastaviť" + "Pretočiť späť" + "Pretočiť dopredu" + diff --git a/library/ui/src/main/res/values-sl/strings.xml b/library/ui/src/main/res/values-sl/strings.xml new file mode 100644 index 0000000000..a77586b50c --- /dev/null +++ b/library/ui/src/main/res/values-sl/strings.xml @@ -0,0 +1,25 @@ + + + + "Prejšnja skladba" + "Naslednja skladba" + "Zaustavi" + "Predvajaj" + "Ustavi" + "Previj nazaj" + "Previj naprej" + diff --git a/library/ui/src/main/res/values-sq-rAL/strings.xml b/library/ui/src/main/res/values-sq-rAL/strings.xml new file mode 100644 index 0000000000..1fb824366d --- /dev/null +++ b/library/ui/src/main/res/values-sq-rAL/strings.xml @@ -0,0 +1,25 @@ + + + + "Kënga e mëparshme" + "Kënga tjetër" + "Pauzë" + "Luaj" + "Ndalo" + "Kthehu pas" + "Përparo me shpejtësi" + diff --git a/library/ui/src/main/res/values-sr/strings.xml b/library/ui/src/main/res/values-sr/strings.xml new file mode 100644 index 0000000000..175ad4fe7f --- /dev/null +++ b/library/ui/src/main/res/values-sr/strings.xml @@ -0,0 +1,25 @@ + + + + "Претходна песма" + "Следећа песма" + "Пауза" + "Пусти" + "Заустави" + "Премотај уназад" + "Премотај унапред" + diff --git a/library/ui/src/main/res/values-sv/strings.xml b/library/ui/src/main/res/values-sv/strings.xml new file mode 100644 index 0000000000..e6a8960458 --- /dev/null +++ b/library/ui/src/main/res/values-sv/strings.xml @@ -0,0 +1,25 @@ + + + + "Föregående spår" + "Nästa spår" + "Pausa" + "Spela upp" + "Avbryt" + "Spola tillbaka" + "Snabbspola framåt" + diff --git a/library/ui/src/main/res/values-sw/strings.xml b/library/ui/src/main/res/values-sw/strings.xml new file mode 100644 index 0000000000..8055b7daff --- /dev/null +++ b/library/ui/src/main/res/values-sw/strings.xml @@ -0,0 +1,25 @@ + + + + "Wimbo uliotangulia" + "Wimbo unaofuata" + "Sitisha" + "Cheza" + "Simamisha" + "Rudisha nyuma" + "Peleka mbele kwa kasi" + diff --git a/library/ui/src/main/res/values-ta-rIN/strings.xml b/library/ui/src/main/res/values-ta-rIN/strings.xml new file mode 100644 index 0000000000..3eb995d467 --- /dev/null +++ b/library/ui/src/main/res/values-ta-rIN/strings.xml @@ -0,0 +1,25 @@ + + + + "முந்தைய ட்ராக்" + "அடுத்த ட்ராக்" + "இடைநிறுத்து" + "இயக்கு" + "நிறுத்து" + "மீண்டும் காட்டு" + "வேகமாக முன்செல்" + diff --git a/library/ui/src/main/res/values-te-rIN/strings.xml b/library/ui/src/main/res/values-te-rIN/strings.xml new file mode 100644 index 0000000000..fe7930455a --- /dev/null +++ b/library/ui/src/main/res/values-te-rIN/strings.xml @@ -0,0 +1,25 @@ + + + + "మునుపటి ట్రాక్" + "తదుపరి ట్రాక్" + "పాజ్ చేయి" + "ప్లే చేయి" + "ఆపివేయి" + "రివైండ్ చేయి" + "వేగంగా ఫార్వార్డ్ చేయి" + diff --git a/library/ui/src/main/res/values-th/strings.xml b/library/ui/src/main/res/values-th/strings.xml new file mode 100644 index 0000000000..deb2aac87d --- /dev/null +++ b/library/ui/src/main/res/values-th/strings.xml @@ -0,0 +1,25 @@ + + + + "แทร็กก่อนหน้า" + "แทร็กถัดไป" + "หยุดชั่วคราว" + "เล่น" + "หยุด" + "กรอกลับ" + "กรอไปข้างหน้า" + diff --git a/library/ui/src/main/res/values-tl/strings.xml b/library/ui/src/main/res/values-tl/strings.xml new file mode 100644 index 0000000000..28dcb3267e --- /dev/null +++ b/library/ui/src/main/res/values-tl/strings.xml @@ -0,0 +1,25 @@ + + + + "Nakaraang track" + "Susunod na track" + "I-pause" + "I-play" + "Ihinto" + "I-rewind" + "I-fast forward" + diff --git a/library/ui/src/main/res/values-tr/strings.xml b/library/ui/src/main/res/values-tr/strings.xml new file mode 100644 index 0000000000..4265d796fe --- /dev/null +++ b/library/ui/src/main/res/values-tr/strings.xml @@ -0,0 +1,25 @@ + + + + "Önceki parça" + "Sonraki parça" + "Duraklat" + "Çal" + "Durdur" + "Geri sar" + "İleri sar" + diff --git a/library/ui/src/main/res/values-uk/strings.xml b/library/ui/src/main/res/values-uk/strings.xml new file mode 100644 index 0000000000..487ca07556 --- /dev/null +++ b/library/ui/src/main/res/values-uk/strings.xml @@ -0,0 +1,25 @@ + + + + "Попередня композиція" + "Наступна композиція" + "Пауза" + "Відтворити" + "Зупинити" + "Перемотати назад" + "Перемотати вперед" + diff --git a/library/ui/src/main/res/values-ur-rPK/strings.xml b/library/ui/src/main/res/values-ur-rPK/strings.xml new file mode 100644 index 0000000000..55fa908bcd --- /dev/null +++ b/library/ui/src/main/res/values-ur-rPK/strings.xml @@ -0,0 +1,25 @@ + + + + "پچھلا ٹریک" + "اگلا ٹریک" + "موقوف کریں" + "چلائیں" + "روکیں" + "ریوائینڈ کریں" + "تیزی سے فارورڈ کریں" + diff --git a/library/ui/src/main/res/values-uz-rUZ/strings.xml b/library/ui/src/main/res/values-uz-rUZ/strings.xml new file mode 100644 index 0000000000..9cee926844 --- /dev/null +++ b/library/ui/src/main/res/values-uz-rUZ/strings.xml @@ -0,0 +1,25 @@ + + + + "Avvalgi musiqa" + "Keyingi musiqa" + "To‘xtatib turish" + "Ijro qilish" + "To‘xtatish" + "Orqaga o‘tkazish" + "Oldinga o‘tkazish" + diff --git a/library/ui/src/main/res/values-v11/styles.xml b/library/ui/src/main/res/values-v11/styles.xml new file mode 100644 index 0000000000..6f77440287 --- /dev/null +++ b/library/ui/src/main/res/values-v11/styles.xml @@ -0,0 +1,24 @@ + + + + + + + diff --git a/library/ui/src/main/res/values-vi/strings.xml b/library/ui/src/main/res/values-vi/strings.xml new file mode 100644 index 0000000000..917ec8e95c --- /dev/null +++ b/library/ui/src/main/res/values-vi/strings.xml @@ -0,0 +1,25 @@ + + + + "Bản nhạc trước" + "Bản nhạc tiếp theo" + "Tạm dừng" + "Phát" + "Ngừng" + "Tua lại" + "Tua đi" + diff --git a/library/ui/src/main/res/values-zh-rCN/strings.xml b/library/ui/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000000..41e02409e2 --- /dev/null +++ b/library/ui/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,25 @@ + + + + "上一曲" + "下一曲" + "暂停" + "播放" + "停止" + "快退" + "快进" + diff --git a/library/ui/src/main/res/values-zh-rHK/strings.xml b/library/ui/src/main/res/values-zh-rHK/strings.xml new file mode 100644 index 0000000000..a3244bcd70 --- /dev/null +++ b/library/ui/src/main/res/values-zh-rHK/strings.xml @@ -0,0 +1,25 @@ + + + + "上一首曲目" + "下一首曲目" + "暫停" + "播放" + "停止" + "倒帶" + "向前快轉" + diff --git a/library/ui/src/main/res/values-zh-rTW/strings.xml b/library/ui/src/main/res/values-zh-rTW/strings.xml new file mode 100644 index 0000000000..ee915c5d9d --- /dev/null +++ b/library/ui/src/main/res/values-zh-rTW/strings.xml @@ -0,0 +1,25 @@ + + + + "上一首曲目" + "下一首曲目" + "暫停" + "播放" + "停止" + "倒轉" + "快轉" + diff --git a/library/ui/src/main/res/values-zu/strings.xml b/library/ui/src/main/res/values-zu/strings.xml new file mode 100644 index 0000000000..e998846454 --- /dev/null +++ b/library/ui/src/main/res/values-zu/strings.xml @@ -0,0 +1,25 @@ + + + + "Ithrekhi yangaphambilini" + "Ithrekhi elandelayo" + "Misa isikhashana" + "Dlala" + "Misa" + "Buyisela emumva" + "Ukudlulisa ngokushesha" + diff --git a/library/ui/src/main/res/values/attrs.xml b/library/ui/src/main/res/values/attrs.xml new file mode 100644 index 0000000000..c73bfb0a3c --- /dev/null +++ b/library/ui/src/main/res/values/attrs.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/library/ui/src/main/res/values/constants.xml b/library/ui/src/main/res/values/constants.xml new file mode 100644 index 0000000000..5c86696ea0 --- /dev/null +++ b/library/ui/src/main/res/values/constants.xml @@ -0,0 +1,21 @@ + + + + + 71dp + 52dp + + diff --git a/library/ui/src/main/res/values/ids.xml b/library/ui/src/main/res/values/ids.xml new file mode 100644 index 0000000000..61db83825e --- /dev/null +++ b/library/ui/src/main/res/values/ids.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/library/ui/src/main/res/values/strings.xml b/library/ui/src/main/res/values/strings.xml new file mode 100644 index 0000000000..1e652dddb3 --- /dev/null +++ b/library/ui/src/main/res/values/strings.xml @@ -0,0 +1,24 @@ + + + + Previous track + Next track + Pause + Play + Stop + Rewind + Fast forward + diff --git a/library/ui/src/main/res/values/styles.xml b/library/ui/src/main/res/values/styles.xml new file mode 100644 index 0000000000..a67cffe420 --- /dev/null +++ b/library/ui/src/main/res/values/styles.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + diff --git a/settings.gradle b/settings.gradle index 9578420959..4961921068 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,6 +13,7 @@ // limitations under the License. include ':library' include ':library-core' +include ':library-ui' include ':testutils' include ':demo' include ':playbacktests' @@ -27,6 +28,7 @@ include ':extension-vp9' project(':library').projectDir = new File(settingsDir, 'library/all') project(':library-core').projectDir = new File(settingsDir, 'library/core') +project(':library-ui').projectDir = new File(settingsDir, 'library/ui') project(':extension-ffmpeg').projectDir = new File(settingsDir, 'extensions/ffmpeg') project(':extension-flac').projectDir = new File(settingsDir, 'extensions/flac') project(':extension-gvr').projectDir = new File(settingsDir, 'extensions/gvr')