updated media viewer library

This commit is contained in:
Mariotaku Lee 2017-03-01 14:13:10 +08:00
parent b10a5019cc
commit d89ce3bfbd
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
10 changed files with 135 additions and 38 deletions

View File

@ -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

View File

@ -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'

View File

@ -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();

View File

@ -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
}

View File

@ -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<ExoPlayerPageFragment> {
@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<ExoPlayerPage
}
override fun onPlayerError(error: ExoPlaybackException) {
}
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
@ -212,13 +230,20 @@ class ExoPlayerPageFragment : MediaViewerFragment(), IBaseFragment<ExoPlayerPage
requestFitSystemWindows()
}
override fun recycleMedia() {
}
override fun executeAfterFragmentResumed(useHandler: Boolean, action: (ExoPlayerPageFragment) -> 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<ExoPlayerPage
return@run player
}
val uri = getDownloadUri() ?: return
val uriSource = ExtractorMediaSource(uri, dataSourceFactory, DefaultExtractorsFactory(),
null, null)
val uri = media?.getDownloadUri() ?: return
val am = AccountManager.get(context)
val factory = AuthDelegatingDataSourceFactory(uri, accountKey, am, dataSourceFactory)
val uriSource = ExtractorMediaSource(uri, factory, extractorsFactory, null, null)
if (isLoopEnabled) {
playerView.player.prepare(LoopingMediaSource(uriSource))
} else {
@ -264,11 +290,51 @@ class ExoPlayerPageFragment : MediaViewerFragment(), IBaseFragment<ExoPlayerPage
}
}
fun getDownloadUri(): Uri? {
val bestVideoUrlAndType = VideoPageFragment.getBestVideoUrlAndType(media, VideoPageFragment.SUPPORTED_VIDEO_TYPES)
fun ParcelableMedia.getDownloadUri(): Uri? {
val bestVideoUrlAndType = VideoPageFragment.getBestVideoUrlAndType(this, SUPPORTED_VIDEO_TYPES)
if (bestVideoUrlAndType != null && bestVideoUrlAndType.first != null) {
return Uri.parse(bestVideoUrlAndType.first)
}
return arguments.getParcelable<Uri>(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<String>()
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))
}
}
}
}

View File

@ -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) {

View File

@ -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)
}

View File

@ -203,8 +203,7 @@ class VideoPageFragment : CacheDownloadMediaViewerFragment(), IBaseFragment<Vide
activity.supportInvalidateOptionsMenu()
}
override fun recycleMedia() {
override fun releaseMediaResources() {
}
override fun onCompletion(mp: MediaPlayer) {

View File

@ -48,7 +48,8 @@ import org.mariotaku.twidere.task.twitter.message.GetMessagesTask.Companion.addL
*/
class SendMessageTask(
context: Context
) : ExceptionHandlingAbstractTask<ParcelableNewMessage, SendMessageTask.SendMessageResult, MicroBlogException, Unit>(context) {
) : ExceptionHandlingAbstractTask<ParcelableNewMessage, SendMessageTask.SendMessageResult,
MicroBlogException, Unit>(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<UpdateStatusTask.MediaDeletionItem>? = null
var deleteAlways: List<UpdateStatusTask.MediaDeletionItem>? = 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)
}

View File

@ -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)