started to use ExoPlayer for jellybean+

This commit is contained in:
Mariotaku Lee 2017-02-28 21:30:18 +08:00
parent d42165c53b
commit 4dc08599fa
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
13 changed files with 592 additions and 133 deletions

View File

@ -31,16 +31,20 @@ allprojects {
subprojects {
buildscript {
ext.kotlin_version = '1.0.6'
ext.android_support_lib_version = '25.2.0'
ext.mariotaku_commons_library_version = '0.9.11'
ext.mariotaku_restfu_version = '0.9.35'
ext.mariotaku_object_cursor_version = '0.9.13'
ext.play_services_version = '10.2.0'
ext.crashlyrics_version = '2.6.6'
ext.fabric_plugin_version = '1.22.0'
ext.dropbox_core_sdk_version = '2.1.2'
ext.google_api_drive_version = 'v3-rev55-1.22.0'
ext {
kotlin_version = '1.0.6'
android_support_lib_version = '25.2.0'
mariotaku_commons_library_version = '0.9.11'
mariotaku_restfu_version = '0.9.35'
mariotaku_object_cursor_version = '0.9.13'
play_services_version = '10.2.0'
crashlyrics_version = '2.6.6'
fabric_plugin_version = '1.22.0'
dropbox_core_sdk_version = '2.1.2'
google_api_drive_version = 'v3-rev55-1.22.0'
exoplayer_version = 'r2.2.0'
toro_version = '2.1.0'
}
}
afterEvaluate { project ->

View File

@ -168,6 +168,8 @@ dependencies {
compile 'org.attoparser:attoparser:2.0.1.RELEASE'
compile 'com.getkeepsafe.taptargetview:taptargetview:1.6.0'
compile 'net.ypresto.androidtranscoder:android-transcoder:0.2.0'
compile "com.google.android.exoplayer:exoplayer:$exoplayer_version"
compile 'com.github.mariotaku.MediaViewerLibrary:base:0.9.17'
compile 'com.github.mariotaku.MediaViewerLibrary:subsample-image-view:0.9.17'
compile 'com.github.mariotaku:SQLiteQB:0.9.10'
@ -180,6 +182,7 @@ dependencies {
compile "com.github.mariotaku.CommonsLibrary:text-kotlin:$mariotaku_commons_library_version"
compile 'com.github.mariotaku:KPreferences:0.9.5'
compile 'com.github.mariotaku:Chameleon:0.9.14'
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile 'nl.komponents.kovenant:kovenant:3.3.0'
compile 'nl.komponents.kovenant:kovenant-android:3.3.0'

View File

@ -25,7 +25,6 @@ import org.mariotaku.restfu.http.RestHttpClient;
import org.mariotaku.restfu.http.mime.Body;
import org.mariotaku.restfu.oauth.OAuthAuthorization;
import org.mariotaku.restfu.oauth.OAuthEndpoint;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.extension.model.CredentialsExtensionsKt;
import org.mariotaku.twidere.model.AccountDetails;
import org.mariotaku.twidere.model.CacheMetadata;
@ -44,10 +43,14 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import static org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_THUMBOR_ADDRESS;
import static org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_THUMBOR_ENABLED;
import static org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_THUMBOR_SECURITY_KEY;
/**
* Created by mariotaku on 16/1/28.
*/
public class TwidereMediaDownloader implements MediaDownloader, Constants {
public class TwidereMediaDownloader implements MediaDownloader {
private final Context context;
private final SharedPreferencesWrapper preferences;
@ -57,7 +60,7 @@ public class TwidereMediaDownloader implements MediaDownloader, Constants {
private Thumbor thumbor;
public TwidereMediaDownloader(final Context context, SharedPreferencesWrapper preferences,
RestHttpClient client) {
RestHttpClient client) {
this.context = context;
this.preferences = preferences;
this.client = client;
@ -116,7 +119,7 @@ public class TwidereMediaDownloader implements MediaDownloader, Constants {
}
protected CacheDownloadLoader.DownloadResult getInternal(@NonNull String url,
@Nullable Object extra) throws IOException {
@Nullable Object extra) throws IOException {
final Uri uri = Uri.parse(url);
Authorization auth = null;
AccountDetails account = null;

View File

@ -49,8 +49,10 @@ import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.activity.iface.IControlBarActivity.ControlBarShowHideHelper
import org.mariotaku.twidere.activity.iface.IExtendedActivity
import org.mariotaku.twidere.annotation.CacheFileType
import org.mariotaku.twidere.fragment.*
import org.mariotaku.twidere.fragment.PermissionRequestDialog
import org.mariotaku.twidere.fragment.ProgressDialogFragment
import org.mariotaku.twidere.fragment.iface.IBaseFragment
import org.mariotaku.twidere.fragment.media.*
import org.mariotaku.twidere.model.ParcelableMedia
import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.provider.CacheProvider
@ -314,11 +316,18 @@ class MediaViewerActivity : BaseActivity(), IMediaViewerActivity, MediaSwipeClos
args.putBoolean(VideoPageFragment.EXTRA_LOOP, true)
args.putBoolean(VideoPageFragment.EXTRA_DISABLE_CONTROL, true)
args.putBoolean(VideoPageFragment.EXTRA_DEFAULT_MUTE, true)
return Fragment.instantiate(this, VideoPageFragment::class.java.name, args) as MediaViewerFragment
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
return Fragment.instantiate(this, VideoPageFragment::class.java.name, args) as MediaViewerFragment
} else {
return Fragment.instantiate(this, ExoPlayerPageFragment::class.java.name, args) as MediaViewerFragment
}
}
ParcelableMedia.Type.VIDEO -> {
return Fragment.instantiate(this,
VideoPageFragment::class.java.name, args) as MediaViewerFragment
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
return Fragment.instantiate(this, VideoPageFragment::class.java.name, args) as MediaViewerFragment
} else {
return Fragment.instantiate(this, ExoPlayerPageFragment::class.java.name, args) as MediaViewerFragment
}
}
ParcelableMedia.Type.EXTERNAL_PLAYER -> {
return Fragment.instantiate(this, ExternalBrowserPageFragment::class.java.name, args) as MediaViewerFragment

View File

@ -0,0 +1,194 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.fragment.media
import android.annotation.TargetApi
import android.content.Context
import android.graphics.Rect
import android.media.AudioManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.exoplayer2.DefaultLoadControl
import com.google.android.exoplayer2.ExoPlayerFactory
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory
import com.google.android.exoplayer2.source.ExtractorMediaSource
import com.google.android.exoplayer2.trackselection.AdaptiveVideoTrackSelection
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.upstream.DataSource
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory
import kotlinx.android.synthetic.main.layout_media_viewer_exo_player_view.*
import kotlinx.android.synthetic.main.layout_media_viewer_video_overlay.*
import org.mariotaku.mediaviewer.library.MediaViewerFragment
import org.mariotaku.mediaviewer.library.subsampleimageview.SubsampleImageViewerFragment
import org.mariotaku.twidere.R
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_POSITION
import org.mariotaku.twidere.fragment.iface.IBaseFragment
import org.mariotaku.twidere.fragment.media.VideoPageFragment.Companion.EXTRA_PAUSED_BY_USER
import org.mariotaku.twidere.fragment.media.VideoPageFragment.Companion.EXTRA_PLAY_AUDIO
import org.mariotaku.twidere.fragment.media.VideoPageFragment.Companion.isMutedByDefault
import org.mariotaku.twidere.fragment.media.VideoPageFragment.Companion.media
import org.mariotaku.twidere.util.UserAgentUtils
/**
* Successor of `VideoPageFragment`, backed by `ExoPlayer`
* Created by mariotaku on 2017/2/28.
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
class ExoPlayerPageFragment : MediaViewerFragment(), IBaseFragment<ExoPlayerPageFragment> {
private lateinit var mediaDataSourceFactory: DataSource.Factory
private lateinit var mainHandler: Handler
private var playAudio: Boolean = false
private var pausedByUser: Boolean = false
private var positionBackup: Int = -1
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
mediaDataSourceFactory = DefaultHttpDataSourceFactory(UserAgentUtils.getDefaultUserAgentString(context))
mainHandler = Handler()
if (savedInstanceState != null) {
positionBackup = savedInstanceState.getInt(EXTRA_POSITION)
pausedByUser = savedInstanceState.getBoolean(EXTRA_PAUSED_BY_USER)
playAudio = savedInstanceState.getBoolean(EXTRA_PLAY_AUDIO)
} else {
val am = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
// Play audio by default if ringer mode on
playAudio = !isMutedByDefault && am.ringerMode == AudioManager.RINGER_MODE_NORMAL
}
volumeButton.setOnClickListener {
this.playAudio = !this.playAudio
updateVolume()
}
updateVolume()
}
override fun onStart() {
super.onStart()
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
initializePlayer()
}
}
override fun onResume() {
super.onResume()
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
initializePlayer()
}
}
override fun onPause() {
super.onPause()
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
releasePlayer()
}
}
override fun onStop() {
super.onStop()
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
releasePlayer()
}
}
override fun onCreateMediaView(inflater: LayoutInflater, parent: ViewGroup?, savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.layout_media_viewer_exo_player_view, parent, false)
}
override fun fitSystemWindows(insets: Rect) {
val lp = videoControl.layoutParams
if (lp is ViewGroup.MarginLayoutParams) {
lp.bottomMargin = insets.bottom
lp.leftMargin = insets.left
lp.rightMargin = insets.right
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt(EXTRA_POSITION, positionBackup)
outState.putBoolean(EXTRA_PAUSED_BY_USER, pausedByUser)
outState.putBoolean(EXTRA_PLAY_AUDIO, playAudio)
}
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
requestFitSystemWindows()
}
override fun recycleMedia() {
}
override fun executeAfterFragmentResumed(useHandler: Boolean, action: (ExoPlayerPageFragment) -> Unit) {
// No-op
}
private fun releasePlayer() {
if (playerView.player == null) return
playerView.player.release();
playerView.player = null
}
private fun initializePlayer() {
if (playerView.player != null) return
playerView.player = run {
val bandwidthMeter = DefaultBandwidthMeter()
val videoTrackSelectionFactory = AdaptiveVideoTrackSelection.Factory(bandwidthMeter)
val trackSelector = DefaultTrackSelector(videoTrackSelectionFactory)
val player = ExoPlayerFactory.newSimpleInstance(context, trackSelector, DefaultLoadControl())
player.playWhenReady = true
return@run player
}
val uri = getDownloadUri() ?: return
val mediaSource = ExtractorMediaSource(uri, mediaDataSourceFactory, DefaultExtractorsFactory(),
null, null)
playerView.player.prepare(mediaSource)
updateVolume()
}
private fun updateVolume() {
volumeButton.setImageResource(if (playAudio) R.drawable.ic_action_speaker_max else R.drawable.ic_action_speaker_muted)
val player = playerView.player ?: return
if (playAudio) {
player.volume = 1f
} else {
player.volume = 0f
}
}
fun getDownloadUri(): Uri? {
val bestVideoUrlAndType = VideoPageFragment.getBestVideoUrlAndType(media, VideoPageFragment.SUPPORTED_VIDEO_TYPES)
if (bestVideoUrlAndType != null && bestVideoUrlAndType.first != null) {
return Uri.parse(bestVideoUrlAndType.first)
}
return arguments.getParcelable<Uri>(SubsampleImageViewerFragment.EXTRA_MEDIA_URI)
}
}

View File

@ -1,4 +1,23 @@
package org.mariotaku.twidere.fragment
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.fragment.media
import android.annotation.SuppressLint
import android.os.Bundle
@ -11,6 +30,7 @@ import kotlinx.android.synthetic.main.layout_media_viewer_texture_video_view.*
import org.mariotaku.mediaviewer.library.MediaViewerFragment
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.EXTRA_MEDIA
import org.mariotaku.twidere.fragment.media.VideoPageFragment
import org.mariotaku.twidere.model.ParcelableMedia
class ExternalBrowserPageFragment : MediaViewerFragment() {

View File

@ -1,4 +1,23 @@
package org.mariotaku.twidere.fragment
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.fragment.media
import android.net.Uri
import android.os.Bundle

View File

@ -1,4 +1,23 @@
package org.mariotaku.twidere.fragment
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.fragment.media
import android.content.ContentResolver
import android.content.Context

View File

@ -1,4 +1,23 @@
package org.mariotaku.twidere.fragment
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.fragment.media
import android.annotation.SuppressLint
import android.content.Context
@ -24,8 +43,10 @@ import com.commonsware.cwac.layouts.AspectLockedFrameLayout.AspectRatioSource
import edu.tsinghua.hotmobi.HotMobiLogger
import edu.tsinghua.hotmobi.model.MediaDownloadEvent
import kotlinx.android.synthetic.main.layout_media_viewer_texture_video_view.*
import kotlinx.android.synthetic.main.layout_media_viewer_video_overlay.*
import org.mariotaku.mediaviewer.library.CacheDownloadLoader
import org.mariotaku.mediaviewer.library.CacheDownloadMediaViewerFragment
import org.mariotaku.mediaviewer.library.MediaViewerFragment
import org.mariotaku.mediaviewer.library.subsampleimageview.SubsampleImageViewerFragment
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.EXTRA_ACCOUNT_KEY
@ -44,18 +65,13 @@ class VideoPageFragment : CacheDownloadMediaViewerFragment(), IBaseFragment<Vide
MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener,
View.OnClickListener, IControlBarActivity.ControlBarOffsetListener {
private val isLoopEnabled: Boolean get() = arguments.getBoolean(EXTRA_LOOP, false)
private val isControlDisabled: Boolean get() = arguments.getBoolean(EXTRA_DISABLE_CONTROL, false)
private val isMutedByDefault: Boolean get() = arguments.getBoolean(EXTRA_DEFAULT_MUTE, false)
private val media: ParcelableMedia? get() = arguments.getParcelable<ParcelableMedia>(EXTRA_MEDIA)
private val accountKey: UserKey get() = arguments.getParcelable<UserKey>(EXTRA_ACCOUNT_KEY)
private var mediaPlayer: MediaPlayer? = null
private var mediaPlayerError: Int = 0
private var playAudio: Boolean = false
private var pausedByUser: Boolean = false
private var positionBackup: Int = -1
private var videoProgressRunnable: VideoPlayProgressRunnable? = null
private var mediaDownloadEvent: MediaDownloadEvent? = null
@ -69,8 +85,6 @@ class VideoPageFragment : CacheDownloadMediaViewerFragment(), IBaseFragment<Vide
handler = Handler(activity.mainLooper)
}
val am = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
videoProgressRunnable = VideoPlayProgressRunnable(handler, videoViewProgress,
durationLabel, positionLabel, videoView)
@ -80,6 +94,7 @@ class VideoPageFragment : CacheDownloadMediaViewerFragment(), IBaseFragment<Vide
pausedByUser = savedInstanceState.getBoolean(EXTRA_PAUSED_BY_USER)
playAudio = savedInstanceState.getBoolean(EXTRA_PLAY_AUDIO)
} else {
val am = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
// Play audio by default if ringer mode on
playAudio = !isMutedByDefault && am.ringerMode == AudioManager.RINGER_MODE_NORMAL
}
@ -159,7 +174,6 @@ class VideoPageFragment : CacheDownloadMediaViewerFragment(), IBaseFragment<Vide
requestFitSystemWindows()
}
override fun getDownloadExtra(): Any? {
val extra = MediaExtra()
extra.isUseThumbor = false
@ -328,27 +342,6 @@ class VideoPageFragment : CacheDownloadMediaViewerFragment(), IBaseFragment<Vide
// No-op
}
@SuppressLint("SwitchIntDef")
private fun getBestVideoUrlAndType(media: ParcelableMedia?, supportedTypes: Array<String>): Pair<String, String>? {
if (media == null) return null
when (media.type) {
ParcelableMedia.Type.VIDEO, ParcelableMedia.Type.ANIMATED_GIF -> {
if (media.video_info == null) {
return Pair.create<String, String>(media.media_url, null)
}
val firstMatch = media.video_info.variants.filter { variant ->
supportedTypes.any { it.equals(variant.content_type, ignoreCase = true) }
}.sortedByDescending(ParcelableMedia.VideoInfo.Variant::bitrate).firstOrNull() ?: return null
return Pair.create(firstMatch.url, firstMatch.content_type)
}
ParcelableMedia.Type.CARD_ANIMATED_GIF -> {
return Pair.create<String, String>(media.media_url, "video/mp4")
}
else -> {
return null
}
}
}
private fun updatePlayerState() {
val playing = videoView.isPlaying
@ -426,10 +419,21 @@ class VideoPageFragment : CacheDownloadMediaViewerFragment(), IBaseFragment<Vide
const val EXTRA_LOOP = "loop"
const val EXTRA_DISABLE_CONTROL = "disable_control"
const val EXTRA_DEFAULT_MUTE = "default_mute"
private const val EXTRA_PAUSED_BY_USER = "paused_by_user"
private const val EXTRA_PLAY_AUDIO = "play_audio"
private val SUPPORTED_VIDEO_TYPES: Array<String>
private val FALLBACK_VIDEO_TYPES: Array<String> = arrayOf("video/mp4")
internal const val EXTRA_PAUSED_BY_USER = "paused_by_user"
internal const val EXTRA_PLAY_AUDIO = "play_audio"
internal val SUPPORTED_VIDEO_TYPES: Array<String>
internal val FALLBACK_VIDEO_TYPES: Array<String> = arrayOf("video/mp4")
internal val MediaViewerFragment.isLoopEnabled: Boolean
get() = arguments.getBoolean(EXTRA_LOOP, false)
internal val MediaViewerFragment.isControlDisabled: Boolean
get() = arguments.getBoolean(EXTRA_DISABLE_CONTROL, false)
internal val MediaViewerFragment.isMutedByDefault: Boolean
get() = arguments.getBoolean(EXTRA_DEFAULT_MUTE, false)
internal val MediaViewerFragment.media: ParcelableMedia?
get() = arguments.getParcelable<ParcelableMedia>(EXTRA_MEDIA)
internal val MediaViewerFragment.accountKey: UserKey
get() = arguments.getParcelable<UserKey>(EXTRA_ACCOUNT_KEY)
init {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
@ -438,5 +442,28 @@ class VideoPageFragment : CacheDownloadMediaViewerFragment(), IBaseFragment<Vide
SUPPORTED_VIDEO_TYPES = arrayOf("video/webm", "video/mp4")
}
}
@SuppressLint("SwitchIntDef")
internal fun getBestVideoUrlAndType(media: ParcelableMedia?, supportedTypes: Array<String>): Pair<String, String>? {
if (media == null) return null
when (media.type) {
ParcelableMedia.Type.VIDEO, ParcelableMedia.Type.ANIMATED_GIF -> {
if (media.video_info == null) {
return Pair.create<String, String>(media.media_url, null)
}
val firstMatch = media.video_info.variants.filter { variant ->
supportedTypes.any { it.equals(variant.content_type, ignoreCase = true) }
}.sortedByDescending(ParcelableMedia.VideoInfo.Variant::bitrate).firstOrNull() ?: return null
return Pair.create(firstMatch.url, firstMatch.content_type)
}
ParcelableMedia.Type.CARD_ANIMATED_GIF -> {
return Pair.create<String, String>(media.media_url, "video/mp4")
}
else -> {
return null
}
}
}
}
}

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Twidere - Twitter client for Android
~
~ Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<com.google.android.exoplayer2.ui.SimpleExoPlayerView
android:id="@+id/playerView"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
app:controller_layout_id="@layout/layout_media_viewer_video_controller_exo"
app:resize_mode="fit"
app:surface_type="texture_view"/>

View File

@ -40,81 +40,5 @@
</com.commonsware.cwac.layouts.AspectLockedFrameLayout>
<FrameLayout
android:id="@+id/videoViewOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true">
<RelativeLayout
android:id="@+id/videoControl"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:layout_gravity="bottom"
android:clickable="true"
android:gravity="center_vertical"
android:orientation="horizontal"
tools:ignore="UselessParent">
<ImageButton
android:id="@+id/playPauseButton"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/play"
android:minWidth="@dimen/element_size_normal"
android:src="@drawable/ic_action_play_arrow"/>
<org.mariotaku.twidere.view.FixedTextView
android:id="@+id/positionLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/videoViewProgress"
android:layout_alignStart="@+id/videoViewProgress"
android:layout_below="@+id/videoViewProgress"
android:gravity="center"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/text_size_extra_small"
tools:text="--:--"/>
<SeekBar
android:id="@+id/videoViewProgress"
style="?android:progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/playPauseButton"
android:layout_toLeftOf="@+id/volumeButton"
android:layout_toRightOf="@+id/playPauseButton"
android:layout_toStartOf="@+id/volumeButton"/>
<org.mariotaku.twidere.view.FixedTextView
android:id="@+id/durationLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignEnd="@+id/videoViewProgress"
android:layout_alignRight="@+id/videoViewProgress"
android:layout_below="@+id/videoViewProgress"
android:gravity="center"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/text_size_extra_small"
tools:text="--:--"/>
<ImageButton
android:id="@+id/volumeButton"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/action_mute"
android:minWidth="@dimen/element_size_normal"
android:src="@drawable/ic_action_speaker_max"/>
</RelativeLayout>
</FrameLayout>
<include layout="@layout/layout_media_viewer_video_overlay"/>
</RelativeLayout>

View File

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Twidere - Twitter client for Android
~
~ Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<RelativeLayout
android:id="@+id/videoControl"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:layout_gravity="bottom"
android:clickable="true"
android:orientation="horizontal">
<FrameLayout
android:id="@+id/playPauseContainer"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true">
<ImageButton
android:id="@id/exo_play"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/exo_controls_play_description"
android:minWidth="@dimen/element_size_normal"
android:src="@drawable/ic_action_play_arrow"
tools:visibility="visible"/>
<ImageButton
android:id="@id/exo_pause"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/exo_controls_pause_description"
android:minWidth="@dimen/element_size_normal"
android:src="@drawable/ic_action_pause"
tools:visibility="gone"/>
</FrameLayout>
<org.mariotaku.twidere.view.FixedTextView
android:id="@id/exo_position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/exo_progress"
android:layout_alignStart="@id/exo_progress"
android:layout_below="@id/exo_progress"
android:gravity="center"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/text_size_extra_small"
tools:text="--:--"/>
<SeekBar
android:id="@id/exo_progress"
style="?android:progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/playPauseContainer"
android:layout_toLeftOf="@+id/volumeButton"
android:layout_toRightOf="@+id/playPauseContainer"
android:layout_toStartOf="@+id/volumeButton"/>
<org.mariotaku.twidere.view.FixedTextView
android:id="@id/exo_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignEnd="@id/exo_progress"
android:layout_alignRight="@id/exo_progress"
android:layout_below="@id/exo_progress"
android:gravity="center"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/text_size_extra_small"
tools:text="--:--"/>
<ImageButton
android:id="@+id/volumeButton"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/action_mute"
android:minWidth="@dimen/element_size_normal"
android:src="@drawable/ic_action_speaker_max"/>
</RelativeLayout>

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Twidere - Twitter client for Android
~
~ Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/videoViewOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
tools:showIn="@layout/layout_media_viewer_texture_video_view">
<RelativeLayout
android:id="@+id/videoControl"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:layout_gravity="bottom"
android:clickable="true"
android:gravity="center_vertical"
android:orientation="horizontal"
tools:ignore="UselessParent">
<ImageButton
android:id="@+id/playPauseButton"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/play"
android:minWidth="@dimen/element_size_normal"
android:src="@drawable/ic_action_play_arrow"/>
<org.mariotaku.twidere.view.FixedTextView
android:id="@+id/positionLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/videoViewProgress"
android:layout_alignStart="@+id/videoViewProgress"
android:layout_below="@+id/videoViewProgress"
android:gravity="center"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/text_size_extra_small"
tools:text="--:--"/>
<SeekBar
android:id="@+id/videoViewProgress"
style="?android:progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/playPauseButton"
android:layout_toLeftOf="@+id/volumeButton"
android:layout_toRightOf="@+id/playPauseButton"
android:layout_toStartOf="@+id/volumeButton"/>
<org.mariotaku.twidere.view.FixedTextView
android:id="@+id/durationLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignEnd="@+id/videoViewProgress"
android:layout_alignRight="@+id/videoViewProgress"
android:layout_below="@+id/videoViewProgress"
android:gravity="center"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/text_size_extra_small"
tools:text="--:--"/>
<ImageButton
android:id="@+id/volumeButton"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/action_mute"
android:minWidth="@dimen/element_size_normal"
android:src="@drawable/ic_action_speaker_max"/>
</RelativeLayout>
</FrameLayout>