From e7e37caff84f24b14d67d8dfd690d9d55dce9bd9 Mon Sep 17 00:00:00 2001 From: Mariotaku Lee Date: Wed, 1 Feb 2017 22:00:55 +0800 Subject: [PATCH] fixed #429 close #691 trying to improve #692 tweaked media viewer swipe --- .../twidere/util/media/MediaExtra.java | 8 +- .../util/media/TwidereMediaDownloader.java | 46 +-- .../twidere/activity/SignInActivity.kt | 1 + .../activity/iface/IControlBarActivity.kt | 2 +- .../model/ParcelableMediaExtensions.kt | 4 +- .../fragment/AccountsDashboardFragment.kt | 2 + .../fragment/AccountsManagerFragment.kt | 19 +- .../twidere/fragment/VideoPageFragment.kt | 282 +++++++++++------- .../twidere/util/DataStoreFunctions.kt | 13 + .../twidere/util/ReadStateManager.kt | 2 +- .../view/viewer/MediaSwipeCloseContainer.kt | 23 +- ...layout_media_viewer_texture_video_view.xml | 20 +- 12 files changed, 253 insertions(+), 169 deletions(-) diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/media/MediaExtra.java b/twidere/src/main/java/org/mariotaku/twidere/util/media/MediaExtra.java index fee3eb027..678998de8 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/media/MediaExtra.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/media/MediaExtra.java @@ -6,10 +6,10 @@ import org.mariotaku.twidere.model.UserKey; * Created by mariotaku on 16/1/28. */ public class MediaExtra { - UserKey accountKey; - boolean useThumbor = true; - String fallbackUrl; - boolean skipUrlReplacing; + private UserKey accountKey; + private boolean useThumbor = true; + private String fallbackUrl; + private boolean skipUrlReplacing; public UserKey getAccountKey() { return accountKey; 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 9f3152154..1c1d4a612 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 @@ -48,37 +48,37 @@ import java.io.InputStream; */ public class TwidereMediaDownloader implements MediaDownloader, Constants { - private final Context mContext; - private final SharedPreferencesWrapper mPreferences; - private final RestHttpClient mClient; - private final String mUserAgent; + private final Context context; + private final SharedPreferencesWrapper preferences; + private final RestHttpClient client; + private final String userAgent; - private Thumbor mThumbor; + private Thumbor thumbor; public TwidereMediaDownloader(final Context context, SharedPreferencesWrapper preferences, RestHttpClient client) { - mContext = context; - mPreferences = preferences; - mClient = client; - mUserAgent = UserAgentUtils.getDefaultUserAgentStringSafe(context); + this.context = context; + this.preferences = preferences; + this.client = client; + userAgent = UserAgentUtils.getDefaultUserAgentStringSafe(context); reloadConnectivitySettings(); } public void reloadConnectivitySettings() { - if (mPreferences.getBoolean(KEY_THUMBOR_ENABLED)) { - final String address = mPreferences.getString(KEY_THUMBOR_ADDRESS, null); - final String securityKey = mPreferences.getString(KEY_THUMBOR_SECURITY_KEY, null); + if (preferences.getBoolean(KEY_THUMBOR_ENABLED)) { + final String address = preferences.getString(KEY_THUMBOR_ADDRESS, null); + final String securityKey = preferences.getString(KEY_THUMBOR_SECURITY_KEY, null); if (address != null && URLUtil.isValidUrl(address)) { if (TextUtils.isEmpty(securityKey)) { - mThumbor = Thumbor.create(address); + thumbor = Thumbor.create(address); } else { - mThumbor = Thumbor.create(address, securityKey); + thumbor = Thumbor.create(address, securityKey); } } else { - mThumbor = null; + thumbor = null; } } else { - mThumbor = null; + thumbor = null; } } @@ -91,7 +91,7 @@ public class TwidereMediaDownloader implements MediaDownloader, Constants { skipUrlReplacing = ((MediaExtra) extra).isSkipUrlReplacing(); } if (!skipUrlReplacing) { - final ParcelableMedia media = PreviewMediaExtractor.fromLink(url, mClient, extra); + final ParcelableMedia media = PreviewMediaExtractor.fromLink(url, client, extra); if (media != null && media.media_url != null) { return getInternal(media.media_url, extra); } @@ -102,7 +102,7 @@ public class TwidereMediaDownloader implements MediaDownloader, Constants { final String fallbackUrl = ((MediaExtra) extra).getFallbackUrl(); if (fallbackUrl != null) { final ParcelableMedia media = PreviewMediaExtractor.fromLink(fallbackUrl, - mClient, extra); + client, extra); if (media != null && media.media_url != null) { return getInternal(media.media_url, extra); } else { @@ -124,7 +124,7 @@ public class TwidereMediaDownloader implements MediaDownloader, Constants { useThumbor = ((MediaExtra) extra).isUseThumbor(); UserKey accountKey = ((MediaExtra) extra).getAccountKey(); if (accountKey != null) { - account = AccountUtils.getAccountDetails(AccountManager.get(mContext), accountKey, true); + account = AccountUtils.getAccountDetails(AccountManager.get(context), accountKey, true); if (account != null) { auth = CredentialsExtensionsKt.getAuthorization(account.credentials); } @@ -132,7 +132,7 @@ public class TwidereMediaDownloader implements MediaDownloader, Constants { } final Uri modifiedUri = getReplacedUri(uri, account != null ? account.credentials.api_url_format : null); final MultiValueMap additionalHeaders = new MultiValueMap<>(); - additionalHeaders.add("User-Agent", mUserAgent); + additionalHeaders.add("User-Agent", userAgent); final String method = GET.METHOD; final String requestUri; if (isAuthRequired(uri, account) && auth != null && auth.hasAuthorization()) { @@ -152,8 +152,8 @@ public class TwidereMediaDownloader implements MediaDownloader, Constants { queries, null, null, null, null); additionalHeaders.add("Authorization", auth.getHeader(endpoint, info)); requestUri = modifiedUri.toString(); - } else if (mThumbor != null && useThumbor) { - requestUri = mThumbor.buildImage(modifiedUri.toString()).filter(ThumborUrlBuilder.quality(85)).toUrl(); + } else if (thumbor != null && useThumbor) { + requestUri = thumbor.buildImage(Uri.encode(modifiedUri.toString())).filter(ThumborUrlBuilder.quality(85)).toUrl(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { additionalHeaders.add("Accept", "image/webp, */*"); } @@ -165,7 +165,7 @@ public class TwidereMediaDownloader implements MediaDownloader, Constants { builder.url(requestUri); builder.headers(additionalHeaders); builder.tag(NoIntercept.INSTANCE); - final HttpResponse resp = mClient.newCall(builder.build()).execute(); + final HttpResponse resp = client.newCall(builder.build()).execute(); if (!resp.isSuccessful()) { final String detailMessage = "Unable to get " + requestUri + ", response code: " + resp.getStatus(); diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SignInActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SignInActivity.kt index 575017316..95dd9b2cd 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SignInActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SignInActivity.kt @@ -338,6 +338,7 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher, APIEditorDi setSignInButton() if (result.alreadyLoggedIn) { result.updateAccount(am) + deleteAccountData(contentResolver, result.user.key) Toast.makeText(this, R.string.message_toast_already_logged_in, Toast.LENGTH_SHORT).show() } else { result.addAccount(am, preferences[randomizeAccountNameKey]) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/iface/IControlBarActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/iface/IControlBarActivity.kt index 7976d95d6..cc99fe134 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/iface/IControlBarActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/iface/IControlBarActivity.kt @@ -29,7 +29,7 @@ interface IControlBarActivity { fun notifyControlBarOffsetChanged() {} interface ControlBarOffsetListener { - fun onControlBarOffsetChanged(activity: IControlBarActivity, offset: Float) {} + fun onControlBarOffsetChanged(activity: IControlBarActivity, offset: Float) } class ControlBarShowHideHelper(private val activity: IControlBarActivity) { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/ParcelableMediaExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/ParcelableMediaExtensions.kt index 7c66c9e4c..b229126f8 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/ParcelableMediaExtensions.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/ParcelableMediaExtensions.kt @@ -16,4 +16,6 @@ fun parcelableMediaTypeString(@ParcelableMedia.Type type: Int): String? { ParcelableMedia.Type.VARIABLE_TYPE -> "variable" else -> null } -} \ No newline at end of file +} + +val ParcelableMedia.aspect_ratio: Double get() = this.width / this.height.toDouble() \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AccountsDashboardFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AccountsDashboardFragment.kt index d366c9002..d55d21e4b 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AccountsDashboardFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AccountsDashboardFragment.kt @@ -350,6 +350,8 @@ class AccountsDashboardFragment : BaseFragment(), LoaderCallbacks, val color = ContextCompat.getColor(context, R.color.material_red) val size = resources.getDimensionPixelSize(R.dimen.element_spacing_msmall) menu.setMenuItemIcon(R.id.premium_features, BadgeDrawable(icon, color, size)) + } else { + menu.setMenuItemIcon(R.id.premium_features, R.drawable.ic_action_infinity) } var hasLists = false var hasGroups = false diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AccountsManagerFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AccountsManagerFragment.kt index d45d0e2b5..4756c2628 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AccountsManagerFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AccountsManagerFragment.kt @@ -38,11 +38,11 @@ import org.mariotaku.twidere.extension.model.setPosition import org.mariotaku.twidere.loader.AccountDetailsLoader import org.mariotaku.twidere.model.AccountDetails import org.mariotaku.twidere.model.UserKey -import org.mariotaku.twidere.provider.TwidereDataStore.* -import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.Inbox -import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.Outbox +import org.mariotaku.twidere.provider.TwidereDataStore.Activities +import org.mariotaku.twidere.provider.TwidereDataStore.Statuses import org.mariotaku.twidere.util.DataStoreUtils import org.mariotaku.twidere.util.IntentUtils +import org.mariotaku.twidere.util.deleteAccountData import org.mariotaku.twidere.util.support.removeAccountSupport /** @@ -206,6 +206,9 @@ class AccountsManagerFragment : BaseFragment(), LoaderManager.LoaderCallbacks
  • { val accountKey = account.getAccountKey(am) + deleteAccountData(resolver, accountKey) am.removeAccountSupport(account) - val where = Expression.equalsArgs(AccountSupportColumns.ACCOUNT_KEY).sql - val whereArgs = arrayOf(accountKey.toString()) - // Also delete tweets related to the account we previously - // deleted. - resolver.delete(Statuses.CONTENT_URI, where, whereArgs) - resolver.delete(Mentions.CONTENT_URI, where, whereArgs) - resolver.delete(Inbox.CONTENT_URI, where, whereArgs) - resolver.delete(Outbox.CONTENT_URI, where, whereArgs) } } } + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val context = context val builder = AlertDialog.Builder(context) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/VideoPageFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/VideoPageFragment.kt index 5cb734267..58fadc222 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/VideoPageFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/VideoPageFragment.kt @@ -14,7 +14,9 @@ import android.view.View import android.view.ViewGroup import android.widget.MediaController import android.widget.ProgressBar +import android.widget.SeekBar import android.widget.TextView +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.* @@ -25,6 +27,7 @@ import org.mariotaku.twidere.R import org.mariotaku.twidere.TwidereConstants.EXTRA_ACCOUNT_KEY import org.mariotaku.twidere.TwidereConstants.EXTRA_MEDIA import org.mariotaku.twidere.activity.MediaViewerActivity +import org.mariotaku.twidere.activity.iface.IControlBarActivity import org.mariotaku.twidere.model.ParcelableMedia import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.util.media.MediaExtra @@ -32,14 +35,105 @@ import java.util.* import java.util.concurrent.TimeUnit class VideoPageFragment : CacheDownloadMediaViewerFragment(), MediaPlayer.OnPreparedListener, - MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener, View.OnClickListener { + MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener, View.OnClickListener, IControlBarActivity.ControlBarOffsetListener { - private var mPlayAudio: Boolean = false - private var mVideoProgressRunnable: VideoPlayProgressRunnable? = null + private var playAudio: Boolean = false private var mediaPlayer: MediaPlayer? = null - private var mMediaPlayerError: Int = 0 + private var mediaPlayerError: Int = 0 + private var videoProgressRunnable: VideoPlayProgressRunnable? = null private var mediaDownloadEvent: MediaDownloadEvent? = null + private val isLoopEnabled: Boolean get() = arguments.getBoolean(EXTRA_LOOP, false) + + private val media: ParcelableMedia? get() = arguments.getParcelable(EXTRA_MEDIA) + + private val accountKey: UserKey get() = arguments.getParcelable(EXTRA_ACCOUNT_KEY) + + private var aspectRatioSource = object : AspectRatioSource { + override fun getHeight(): Int { + val height = media?.height ?: 0 + if (height <= 0) return view!!.measuredHeight + return height + } + + override fun getWidth(): Int { + val width = media?.width ?: 0 + if (width <= 0) return view!!.measuredWidth + return width + } + + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + setHasOptionsMenu(true) + + var handler: Handler? = videoViewProgress.handler + if (handler == null) { + handler = Handler(activity.mainLooper) + } + + val am = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager + + // Play audio by default if ringer mode on + playAudio = am.ringerMode == AudioManager.RINGER_MODE_NORMAL + + videoProgressRunnable = VideoPlayProgressRunnable(handler, videoViewProgress, + durationLabel, positionLabel, videoView) + + + videoViewOverlay.setOnClickListener(this) + videoView.setOnPreparedListener(this) + videoView.setOnErrorListener(this) + videoView.setOnCompletionListener(this) + + playPauseButton.setOnClickListener(this) + volumeButton.setOnClickListener(this) + videoControl.visibility = View.GONE + videoContainer.setAspectRatioSource(aspectRatioSource) + videoViewProgress.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { + private var paused: Boolean = false + + override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { + if (!fromUser) return + val mp = mediaPlayer ?: return + val duration = mp.duration + if (duration <= 0) return + mp.seekTo(Math.round(duration * (progress.toFloat() / seekBar.max))) + } + + override fun onStartTrackingTouch(seekBar: SeekBar) { + paused = pauseVideo() + } + + override fun onStopTrackingTouch(seekBar: SeekBar) { + if (paused) { + resumeVideo() + } + } + + }) + startLoading(false) + setMediaViewVisible(false) + updateVolume() + } + + + override fun onAttach(context: Context?) { + super.onAttach(context) + if (context is IControlBarActivity) { + context.registerControlBarOffsetListener(this) + } + } + + override fun onDetach() { + val activity = activity + if (activity is IControlBarActivity) { + activity.unregisterControlBarOffsetListener(this) + } + super.onDetach() + } + override fun getDownloadExtra(): Any? { val extra = MediaExtra() extra.isUseThumbor = false @@ -50,29 +144,24 @@ class VideoPageFragment : CacheDownloadMediaViewerFragment(), MediaPlayer.OnPrep return extra } - val isLoopEnabled: Boolean - get() = arguments.getBoolean(EXTRA_LOOP, false) - override fun isAbleToLoad(): Boolean { return downloadUri != null } + override fun getDownloadUri(): Uri? { - val bestVideoUrlAndType = getBestVideoUrlAndType(media, - SUPPORTED_VIDEO_TYPES) + val bestVideoUrlAndType = getBestVideoUrlAndType(media, SUPPORTED_VIDEO_TYPES) if (bestVideoUrlAndType != null && bestVideoUrlAndType.first != null) { return Uri.parse(bestVideoUrlAndType.first) } return arguments.getParcelable(SubsampleImageViewerFragment.EXTRA_MEDIA_URI) } - override fun displayMedia(result: CacheDownloadLoader.Result) { videoView.setVideoURI(result.cacheUri) videoControl.visibility = View.GONE setMediaViewVisible(true) - val activity = activity - activity?.supportInvalidateOptionsMenu() + activity.supportInvalidateOptionsMenu() } override fun recycleMedia() { @@ -81,29 +170,31 @@ class VideoPageFragment : CacheDownloadMediaViewerFragment(), MediaPlayer.OnPrep override fun onCompletion(mp: MediaPlayer) { updatePlayerState() - // mVideoViewProgress.removeCallbacks(mVideoProgressRunnable); - // mVideoViewProgress.setVisibility(View.GONE); + } + + override fun onControlBarOffsetChanged(activity: IControlBarActivity, offset: Float) { + videoControl.translationY = (1 - offset) * videoControl.height } override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean { mediaPlayer = null - videoViewProgress.removeCallbacks(mVideoProgressRunnable) + videoViewProgress.removeCallbacks(videoProgressRunnable) videoViewProgress.visibility = View.GONE videoControl.visibility = View.GONE - mMediaPlayerError = what + mediaPlayerError = what return true } override fun onPrepared(mp: MediaPlayer) { if (userVisibleHint) { mediaPlayer = mp - mMediaPlayerError = 0 + mediaPlayerError = 0 mp.setScreenOnWhilePlaying(true) updateVolume() mp.isLooping = isLoopEnabled mp.start() videoViewProgress.visibility = View.VISIBLE - videoViewProgress.post(mVideoProgressRunnable) + videoViewProgress.post(videoProgressRunnable) updatePlayerState() videoControl.visibility = View.VISIBLE } @@ -111,10 +202,10 @@ class VideoPageFragment : CacheDownloadMediaViewerFragment(), MediaPlayer.OnPrep private fun updateVolume() { - volumeButton.setImageResource(if (mPlayAudio) R.drawable.ic_action_speaker_max else R.drawable.ic_action_speaker_muted) + volumeButton.setImageResource(if (playAudio) R.drawable.ic_action_speaker_max else R.drawable.ic_action_speaker_muted) val mp = mediaPlayer ?: return try { - if (mPlayAudio) { + if (playAudio) { mp.setVolume(1f, 1f) } else { mp.setVolume(0f, 0f) @@ -136,103 +227,28 @@ class VideoPageFragment : CacheDownloadMediaViewerFragment(), MediaPlayer.OnPrep } } - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - setHasOptionsMenu(true) - - var handler: Handler? = videoViewProgress.handler - if (handler == null) { - handler = Handler(activity.mainLooper) - } - - val am = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager - - // Play audio by default if ringer mode on - mPlayAudio = am.ringerMode == AudioManager.RINGER_MODE_NORMAL - - mVideoProgressRunnable = VideoPlayProgressRunnable(handler, videoViewProgress, - durationLabel, positionLabel, videoView) - - - videoViewOverlay.setOnClickListener(this) - videoView.setOnPreparedListener(this) - videoView.setOnErrorListener(this) - videoView.setOnCompletionListener(this) - - playPauseButton.setOnClickListener(this) - volumeButton.setOnClickListener(this) - videoControl.visibility = View.GONE - startLoading(false) - setMediaViewVisible(false) - updateVolume() - } - - @SuppressLint("SwitchIntDef") - private fun getBestVideoUrlAndType(media: ParcelableMedia?, - supportedTypes: Array): Pair? { - if (media == null) return null - when (media.type) { - ParcelableMedia.Type.VIDEO, ParcelableMedia.Type.ANIMATED_GIF -> { - if (media.video_info == null) { - return Pair.create(media.media_url, null) - } - val firstMatch = media.video_info.variants.first { variant -> - supportedTypes.any { it.equals(variant.content_type, ignoreCase = true) } - } ?: return null - return Pair.create(firstMatch.url, firstMatch.content_type) - } - ParcelableMedia.Type.CARD_ANIMATED_GIF -> { - return Pair.create(media.media_url, "video/mp4") - } - else -> { - return null - } - } - } - - override fun onClick(v: View) { when (v.id) { R.id.volumeButton -> { - mPlayAudio = !mPlayAudio + playAudio = !playAudio updateVolume() } R.id.playPauseButton -> { - val mp = mediaPlayer - if (mp != null) { - if (mp.isPlaying) { - mp.pause() - } else { - mp.start() - } + val mp = mediaPlayer ?: return + if (mp.isPlaying) { + mp.pause() + } else { + mp.start() } updatePlayerState() } R.id.videoViewOverlay -> { val activity = activity as MediaViewerActivity - if (videoControl.visibility == View.VISIBLE) { - videoControl.visibility = View.GONE - activity.setBarVisibility(false) - } else { - videoControl.visibility = View.VISIBLE - activity.setBarVisibility(true) - } + activity.setBarVisibility(!activity.isBarShowing) } } } - private fun updatePlayerState() { - val mp = mediaPlayer - if (mp != null) { - val playing = mp.isPlaying - playPauseButton.contentDescription = getString(if (playing) R.string.pause else R.string.play) - playPauseButton.setImageResource(if (playing) R.drawable.ic_action_pause else R.drawable.ic_action_play_arrow) - } else { - playPauseButton.contentDescription = getString(R.string.play) - playPauseButton.setImageResource(R.drawable.ic_action_play_arrow) - } - } - override fun onCreateMediaView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { return inflater.inflate(R.layout.layout_media_viewer_texture_video_view, container, false) } @@ -267,11 +283,62 @@ class VideoPageFragment : CacheDownloadMediaViewerFragment(), MediaPlayer.OnPrep } } - private val media: ParcelableMedia? - get() = arguments.getParcelable(EXTRA_MEDIA) - private val accountKey: UserKey - get() = arguments.getParcelable(EXTRA_ACCOUNT_KEY) + @SuppressLint("SwitchIntDef") + private fun getBestVideoUrlAndType(media: ParcelableMedia?, supportedTypes: Array): Pair? { + if (media == null) return null + when (media.type) { + ParcelableMedia.Type.VIDEO, ParcelableMedia.Type.ANIMATED_GIF -> { + if (media.video_info == null) { + return Pair.create(media.media_url, null) + } + val firstMatch = media.video_info.variants.first { variant -> + supportedTypes.any { it.equals(variant.content_type, ignoreCase = true) } + } ?: return null + return Pair.create(firstMatch.url, firstMatch.content_type) + } + ParcelableMedia.Type.CARD_ANIMATED_GIF -> { + return Pair.create(media.media_url, "video/mp4") + } + else -> { + return null + } + } + } + + private fun updatePlayerState() { + val mp = mediaPlayer + if (mp != null) { + val playing = mp.isPlaying + playPauseButton.contentDescription = getString(if (playing) R.string.pause else R.string.play) + playPauseButton.setImageResource(if (playing) R.drawable.ic_action_pause else R.drawable.ic_action_play_arrow) + } else { + playPauseButton.contentDescription = getString(R.string.play) + playPauseButton.setImageResource(R.drawable.ic_action_play_arrow) + } + } + + private fun pauseVideo(): Boolean { + val mp = mediaPlayer ?: return false + var result = false + if (mp.isPlaying) { + mp.pause() + result = true + } + updatePlayerState() + return result + } + + private fun resumeVideo(): Boolean { + val mp = mediaPlayer ?: return false + var result = false + if (!mp.isPlaying) { + mp.start() + result = true + } + updatePlayerState() + return result + } private class VideoPlayProgressRunnable internal constructor( private val handler: Handler, @@ -302,7 +369,7 @@ class VideoPageFragment : CacheDownloadMediaViewerFragment(), MediaPlayer.OnPrep const val EXTRA_LOOP = "loop" private val SUPPORTED_VIDEO_TYPES: Array - private val FALLBACK_VIDEO_TYPES: Array + private val FALLBACK_VIDEO_TYPES: Array = arrayOf("video/mp4") init { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { @@ -310,7 +377,6 @@ class VideoPageFragment : CacheDownloadMediaViewerFragment(), MediaPlayer.OnPrep } else { SUPPORTED_VIDEO_TYPES = arrayOf("video/webm", "video/mp4") } - FALLBACK_VIDEO_TYPES = arrayOf("video/mp4") } } } \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreFunctions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreFunctions.kt index c103067f1..51ccd09ad 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreFunctions.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreFunctions.kt @@ -1,6 +1,7 @@ package org.mariotaku.twidere.util import android.annotation.SuppressLint +import android.content.ContentResolver import android.content.Context import android.content.SharedPreferences import android.net.Uri @@ -11,6 +12,7 @@ import org.mariotaku.twidere.constant.filterPossibilitySensitiveStatusesKey import org.mariotaku.twidere.constant.filterUnavailableQuoteStatusesKey import org.mariotaku.twidere.model.DraftCursorIndices import org.mariotaku.twidere.model.ParcelableStatus.FilterFlags +import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.provider.TwidereDataStore.* /** @@ -100,3 +102,14 @@ fun deleteDrafts(context: Context, draftIds: LongArray): Int { } return context.contentResolver.delete(Drafts.CONTENT_URI, where, whereArgs) } + +fun deleteAccountData(resolver: ContentResolver, accountKey: UserKey) { + val where = Expression.equalsArgs(AccountSupportColumns.ACCOUNT_KEY).sql + val whereArgs = arrayOf(accountKey.toString()) + // Also delete tweets related to the account we previously + // deleted. + resolver.delete(Statuses.CONTENT_URI, where, whereArgs) + resolver.delete(Mentions.CONTENT_URI, where, whereArgs) + resolver.delete(DirectMessages.Inbox.CONTENT_URI, where, whereArgs) + resolver.delete(DirectMessages.Outbox.CONTENT_URI, where, whereArgs) +} \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/ReadStateManager.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/ReadStateManager.kt index c70c63e56..f40b7670d 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/ReadStateManager.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/ReadStateManager.kt @@ -69,7 +69,7 @@ class ReadStateManager(context: Context) { preferences.unregisterOnSharedPreferenceChangeListener(listener) } - @JvmOverloads fun setPosition(key: String, keyId: String, position: Long, acceptOlder: Boolean = false): Boolean { + fun setPosition(key: String, keyId: String, position: Long, acceptOlder: Boolean = false): Boolean { if (TextUtils.isEmpty(key)) return false val set: MutableSet = preferences.getStringSet(key, null) ?: CompactHashSet() val prefix = keyId + ":" diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/viewer/MediaSwipeCloseContainer.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/viewer/MediaSwipeCloseContainer.kt index 221af2445..a7a589369 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/viewer/MediaSwipeCloseContainer.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/viewer/MediaSwipeCloseContainer.kt @@ -46,25 +46,22 @@ class MediaSwipeCloseContainer(context: Context, attrs: AttributeSet? = null) : val container = this@MediaSwipeCloseContainer val minVel = ViewConfiguration.get(context).scaledMinimumFlingVelocity when { - yvel > minVel -> { + yvel > minVel && childTop > 0 -> { // Settle downward container.dragHelper.settleCapturedViewAt(0, container.height) } - yvel < -minVel -> { + yvel < -minVel && childTop < 0 -> { // Settle upward container.dragHelper.settleCapturedViewAt(0, -container.height) - } - else -> when { - childTop < -container.height / 4 -> { - container.dragHelper.smoothSlideViewTo(releasedChild, 0, -container.height) - } - childTop > container.height / 4 -> { - container.dragHelper.smoothSlideViewTo(releasedChild, 0, container.height) - } - else -> { - container.dragHelper.smoothSlideViewTo(releasedChild, 0, 0) - } + yvel <= 0 && childTop < -container.height / 4 -> { + container.dragHelper.smoothSlideViewTo(releasedChild, 0, -container.height) + } + yvel >= 0 && childTop > container.height / 4 -> { + container.dragHelper.smoothSlideViewTo(releasedChild, 0, container.height) + } + else -> { + container.dragHelper.smoothSlideViewTo(releasedChild, 0, 0) } } ViewCompat.postInvalidateOnAnimation(container) diff --git a/twidere/src/main/res/layout/layout_media_viewer_texture_video_view.xml b/twidere/src/main/res/layout/layout_media_viewer_texture_video_view.xml index 1950c8e3d..c05d4ac2d 100644 --- a/twidere/src/main/res/layout/layout_media_viewer_texture_video_view.xml +++ b/twidere/src/main/res/layout/layout_media_viewer_texture_video_view.xml @@ -26,11 +26,18 @@ android:layout_height="match_parent" android:layout_gravity="center"> - + android:layout_height="match_parent" + android:layout_centerInParent="true"> + + + + - + android:layout_toStartOf="@+id/volumeButton"/>