From d89ce3bfbd08dcd377a98994c3cd33f7f57a45ff Mon Sep 17 00:00:00 2001 From: Mariotaku Lee Date: Wed, 1 Mar 2017 14:13:10 +0800 Subject: [PATCH] updated media viewer library --- .travis.yml | 2 +- twidere/build.gradle | 4 +- .../util/media/TwidereMediaDownloader.java | 10 +-- .../twidere/activity/MediaViewerActivity.kt | 16 ++-- .../fragment/media/ExoPlayerPageFragment.kt | 86 ++++++++++++++++--- .../media/ExternalBrowserPageFragment.kt | 6 +- .../twidere/fragment/media/GifPageFragment.kt | 2 +- .../fragment/media/VideoPageFragment.kt | 3 +- .../task/twitter/message/SendMessageTask.kt | 36 ++++++-- .../twidere/util/dagger/ApplicationModule.kt | 8 ++ 10 files changed, 135 insertions(+), 38 deletions(-) diff --git a/.travis.yml b/.travis.yml index 23df9c016..1d5702ba5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -57,7 +57,7 @@ cache: - $HOME/.m2/ before_install: - - openssl aes-256-cbc -K $encrypted_9b8203f9524d_key -iv $encrypted_9b8203f9524d_iv -in twidere_private_config.tar.gz.enc -out travis/configs/twidere_private_config.tar.gz -d + - openssl aes-256-cbc -K $encrypted_9b8203f9524d_key -iv $encrypted_9b8203f9524d_iv -in travis/configs/twidere_private_config.tar.gz.enc -out travis/configs/twidere_private_config.tar.gz -d install: # Extracts build configs into source tree diff --git a/twidere/build.gradle b/twidere/build.gradle index c0abd08e9..5e8f10989 100644 --- a/twidere/build.gradle +++ b/twidere/build.gradle @@ -171,8 +171,8 @@ dependencies { compile "com.google.android.exoplayer:exoplayer:$exoplayer_version" compile "com.google.android.exoplayer:extension-okhttp:$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.MediaViewerLibrary:base:0.9.20' + compile 'com.github.mariotaku.MediaViewerLibrary:subsample-image-view:0.9.20' compile 'com.github.mariotaku:SQLiteQB:0.9.10' compile "com.github.mariotaku.ObjectCursor:core:$mariotaku_object_cursor_version" compile 'com.github.mariotaku:MultiValueSwitch:0.9.7' diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/media/TwidereMediaDownloader.java b/twidere/src/main/java/org/mariotaku/twidere/util/media/TwidereMediaDownloader.java index 9b77bafec..9df1942d7 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/media/TwidereMediaDownloader.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/media/TwidereMediaDownloader.java @@ -139,7 +139,7 @@ public class TwidereMediaDownloader implements MediaDownloader { additionalHeaders.add("User-Agent", userAgent); final String method = GET.METHOD; final String requestUri; - if (isAuthRequired(uri, account) && auth != null && auth.hasAuthorization()) { + if (isAuthRequired(account, uri) && auth != null && auth.hasAuthorization()) { final Endpoint endpoint; if (auth instanceof OAuthAuthorization) { endpoint = new OAuthEndpoint(getEndpoint(modifiedUri), getEndpoint(uri)); @@ -184,7 +184,7 @@ public class TwidereMediaDownloader implements MediaDownloader { return new TwidereDownloadResult(body, metadata); } - private String getEndpoint(Uri uri) { + public static String getEndpoint(Uri uri) { final StringBuilder sb = new StringBuilder(); sb.append(uri.getScheme()); sb.append("://"); @@ -197,7 +197,7 @@ public class TwidereMediaDownloader implements MediaDownloader { return sb.toString(); } - private boolean isAuthRequired(final Uri uri, @Nullable final AccountDetails details) { + public static boolean isAuthRequired(@Nullable final AccountDetails details, @NonNull final Uri uri) { if (details == null) return false; final String host = uri.getHost(); if (details.credentials.api_url_format != null && details.credentials.api_url_format.contains(host)) { @@ -206,11 +206,11 @@ public class TwidereMediaDownloader implements MediaDownloader { return "ton.twitter.com".equalsIgnoreCase(host); } - private boolean isTwitterUri(final Uri uri) { + private static boolean isTwitterUri(final Uri uri) { return uri != null && "ton.twitter.com".equalsIgnoreCase(uri.getHost()); } - private Uri getReplacedUri(@NonNull final Uri uri, final String apiUrlFormat) { + public static Uri getReplacedUri(@NonNull final Uri uri, final String apiUrlFormat) { if (apiUrlFormat == null) return uri; if (isTwitterUri(uri)) { final StringBuilder sb = new StringBuilder(); diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/MediaViewerActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/MediaViewerActivity.kt index 53c339896..907d98de2 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/MediaViewerActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/MediaViewerActivity.kt @@ -173,17 +173,11 @@ class MediaViewerActivity : BaseActivity(), IMediaViewerActivity, MediaSwipeClos if (currentItem < 0 || currentItem >= adapter.count) return false val obj = adapter.instantiateItem(viewPager, currentItem) as? MediaViewerFragment ?: return false if (obj.isDetached || obj.host == null) return false - if (obj is CacheDownloadMediaViewerFragment) { - val running = obj.loaderManager.hasRunningLoadersSafe() - val downloaded = obj.hasDownloadedData() - MenuUtils.setItemAvailability(menu, R.id.refresh, !running && !downloaded) - MenuUtils.setItemAvailability(menu, R.id.share, !running && downloaded) - MenuUtils.setItemAvailability(menu, R.id.save, !running && downloaded) - } else { - MenuUtils.setItemAvailability(menu, R.id.refresh, false) - MenuUtils.setItemAvailability(menu, R.id.share, true) - MenuUtils.setItemAvailability(menu, R.id.save, false) - } + val running = obj.isMediaLoading + val downloaded = obj.isMediaLoaded + MenuUtils.setItemAvailability(menu, R.id.refresh, !running && !downloaded) + MenuUtils.setItemAvailability(menu, R.id.share, !running && downloaded) + MenuUtils.setItemAvailability(menu, R.id.save, !running && downloaded) return true } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/media/ExoPlayerPageFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/media/ExoPlayerPageFragment.kt index df43fd51f..32a755bd2 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/media/ExoPlayerPageFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/media/ExoPlayerPageFragment.kt @@ -19,6 +19,7 @@ package org.mariotaku.twidere.fragment.media +import android.accounts.AccountManager import android.annotation.TargetApi import android.content.Context import android.graphics.Rect @@ -31,7 +32,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.google.android.exoplayer2.* -import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory +import com.google.android.exoplayer2.extractor.ExtractorsFactory import com.google.android.exoplayer2.source.ExtractorMediaSource import com.google.android.exoplayer2.source.LoopingMediaSource import com.google.android.exoplayer2.source.TrackGroupArray @@ -40,20 +41,33 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector import com.google.android.exoplayer2.trackselection.TrackSelectionArray import com.google.android.exoplayer2.upstream.DataSource import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter +import com.google.android.exoplayer2.upstream.HttpDataSource 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.restfu.RestRequest +import org.mariotaku.restfu.http.Endpoint +import org.mariotaku.restfu.http.MultiValueMap +import org.mariotaku.restfu.oauth.OAuthAuthorization +import org.mariotaku.restfu.oauth.OAuthEndpoint import org.mariotaku.twidere.R import org.mariotaku.twidere.constant.IntentConstants.EXTRA_POSITION +import org.mariotaku.twidere.extension.model.getAuthorization 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.SUPPORTED_VIDEO_TYPES +import org.mariotaku.twidere.fragment.media.VideoPageFragment.Companion.accountKey import org.mariotaku.twidere.fragment.media.VideoPageFragment.Companion.isControlDisabled import org.mariotaku.twidere.fragment.media.VideoPageFragment.Companion.isLoopEnabled import org.mariotaku.twidere.fragment.media.VideoPageFragment.Companion.isMutedByDefault import org.mariotaku.twidere.fragment.media.VideoPageFragment.Companion.media +import org.mariotaku.twidere.model.ParcelableMedia +import org.mariotaku.twidere.model.UserKey +import org.mariotaku.twidere.model.util.AccountUtils import org.mariotaku.twidere.util.dagger.GeneralComponentHelper +import org.mariotaku.twidere.util.media.TwidereMediaDownloader import javax.inject.Inject @@ -65,7 +79,10 @@ import javax.inject.Inject class ExoPlayerPageFragment : MediaViewerFragment(), IBaseFragment { @Inject - lateinit var dataSourceFactory: DataSource.Factory + internal lateinit var dataSourceFactory: DataSource.Factory + + @Inject + internal lateinit var extractorsFactory: ExtractorsFactory private lateinit var mainHandler: Handler @@ -80,6 +97,7 @@ class ExoPlayerPageFragment : MediaViewerFragment(), IBaseFragment Unit) { // No-op } + override fun isMediaLoaded(): Boolean { + val player = playerView.player ?: return false + return player.playbackState != ExoPlayer.STATE_IDLE + } + + override fun isMediaLoading(): Boolean { + val player = playerView.player ?: return false + return player.isLoading + } + private fun releasePlayer() { val player = playerView.player ?: return positionBackup = player.currentPosition @@ -243,9 +268,10 @@ class ExoPlayerPageFragment : MediaViewerFragment(), IBaseFragment(SubsampleImageViewerFragment.EXTRA_MEDIA_URI) } + + class AuthDelegatingDataSourceFactory( + val uri: Uri, + val accountKey: UserKey, + val am: AccountManager, + val delegate: DataSource.Factory + ) : DataSource.Factory { + override fun createDataSource(): DataSource { + val source = delegate.createDataSource() + if (source is HttpDataSource) { + setAuthorizationHeader(source) + } + return source + } + + private fun setAuthorizationHeader(dataSource: HttpDataSource) { + val account = AccountUtils.getAccountDetails(am, accountKey, true) ?: return + val modifiedUri = TwidereMediaDownloader.getReplacedUri(uri, account.credentials.api_url_format) ?: uri + if (TwidereMediaDownloader.isAuthRequired(account, uri)) { + val auth = account.credentials.getAuthorization() + val endpoint: Endpoint + if (auth is OAuthAuthorization) { + endpoint = OAuthEndpoint(TwidereMediaDownloader.getEndpoint(modifiedUri), + TwidereMediaDownloader.getEndpoint(uri)) + } else { + endpoint = Endpoint(TwidereMediaDownloader.getEndpoint(modifiedUri)) + } + val queries = MultiValueMap() + for (name in uri.queryParameterNames) { + for (value in uri.getQueryParameters(name)) { + queries.add(name, value) + } + } + val info = RestRequest("GET", false, uri.path, null, queries, null, null, null, null) + dataSource.setRequestProperty("Authorization", auth.getHeader(endpoint, info)) + } + + } + } + } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/media/ExternalBrowserPageFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/media/ExternalBrowserPageFragment.kt index be610d89a..d02cf7756 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/media/ExternalBrowserPageFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/media/ExternalBrowserPageFragment.kt @@ -67,8 +67,12 @@ class ExternalBrowserPageFragment : MediaViewerFragment() { super.onDestroy() } - override fun recycleMedia() { + override fun isMediaLoaded(): Boolean { + return true + } + override fun isMediaLoading(): Boolean { + return false } override fun setUserVisibleHint(isVisibleToUser: Boolean) { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/media/GifPageFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/media/GifPageFragment.kt index 60fb04995..2bfd6b8bc 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/media/GifPageFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/media/GifPageFragment.kt @@ -80,7 +80,7 @@ class GifPageFragment : CacheDownloadMediaViewerFragment() { return inflater.inflate(R.layout.layout_media_viewer_gif, parent, false) } - override fun recycleMedia() { + override fun releaseMediaResources() { gifView?.setInputSource(null) } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/media/VideoPageFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/media/VideoPageFragment.kt index 5289dfe44..b3cbd716e 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/media/VideoPageFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/media/VideoPageFragment.kt @@ -203,8 +203,7 @@ class VideoPageFragment : CacheDownloadMediaViewerFragment(), IBaseFragment(context) { +) : ExceptionHandlingAbstractTask(context) { override fun onExecute(params: ParcelableNewMessage): SendMessageResult { val account = params.account @@ -118,11 +119,11 @@ class SendMessageTask( e.deleteAlways?.forEach { it.delete(context) } - throw e + throw MicroBlogException(e) } finally { - deleteOnSuccess?.forEach { it.delete(context) } + deleteAlways?.forEach { it.delete(context) } } - deleteAlways?.forEach { it.delete(context) } + deleteOnSuccess?.forEach { it.delete(context) } val conversationId = sendResponse.entries?.firstOrNull { it.message != null }?.message?.conversationId @@ -137,8 +138,33 @@ class SendMessageTask( } private fun sendDefaultDM(microBlog: MicroBlog, account: AccountDetails, message: ParcelableNewMessage): GetMessagesTask.DatabaseUpdateData { + var deleteOnSuccess: List? = null + var deleteAlways: List? = null val recipientId = message.recipient_ids.singleOrNull() ?: throw MicroBlogException("No recipient") - val response = microBlog.sendDirectMessage(recipientId, message.text) + val response = try { + var mediaId: String? = null + if (message.media.isNotNullOrEmpty()) { + val upload = account.newMicroBlogInstance(context, cls = TwitterUpload::class.java) + val uploadResult = UpdateStatusTask.uploadAllMediaShared(context, + mediaLoader, upload, account, message.media, null, true, null) + mediaId = uploadResult.ids[0] + deleteAlways = uploadResult.deleteAlways + deleteOnSuccess = uploadResult.deleteOnSuccess + } + if (mediaId != null) { + microBlog.sendDirectMessage(recipientId, message.text, mediaId) + } else { + microBlog.sendDirectMessage(recipientId, message.text) + } + } catch (e: UpdateStatusTask.UploadException) { + e.deleteAlways?.forEach { + it.delete(context) + } + throw MicroBlogException(e) + } finally { + deleteAlways?.forEach { it.delete(context) } + } + deleteOnSuccess?.forEach { it.delete(context) } return createDatabaseUpdateData(account, response) } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/dagger/ApplicationModule.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/dagger/ApplicationModule.kt index 35c5d28e1..b050f09dc 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/dagger/ApplicationModule.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/dagger/ApplicationModule.kt @@ -28,6 +28,8 @@ import android.os.Looper import android.support.v4.net.ConnectivityManagerCompat import android.support.v4.text.BidiFormatter import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSourceFactory +import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory +import com.google.android.exoplayer2.extractor.ExtractorsFactory import com.google.android.exoplayer2.upstream.DataSource import com.nostra13.universalimageloader.cache.disc.DiskCache import com.nostra13.universalimageloader.cache.disc.impl.ext.LruDiskCache @@ -330,6 +332,12 @@ class ApplicationModule(private val application: Application) { return OkHttpDataSourceFactory(builder.build(), userAgent, null) } + @Provides + @Singleton + fun extractorsFactory(): ExtractorsFactory { + return DefaultExtractorsFactory() + } + private fun createDiskCache(dirName: String, preferences: SharedPreferencesWrapper): DiskCache { val cacheDir = Utils.getExternalCacheDir(application, dirName) val fallbackCacheDir = Utils.getInternalCacheDir(application, dirName)