diff --git a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/CollectionResources.java b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/CollectionResources.java new file mode 100644 index 000000000..f8e9f593f --- /dev/null +++ b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/CollectionResources.java @@ -0,0 +1,13 @@ +package org.mariotaku.microblog.library.twitter.api; + +import org.mariotaku.microblog.library.twitter.template.StatusAnnotationTemplate; +import org.mariotaku.restfu.annotation.param.Queries; + +/** + * Created by mariotaku on 2017/1/29. + */ + +@SuppressWarnings("RedundantThrows") +@Queries(template = StatusAnnotationTemplate.class) +public interface CollectionResources { +} diff --git a/twidere/build.gradle b/twidere/build.gradle index 0b65596c1..768ed611a 100644 --- a/twidere/build.gradle +++ b/twidere/build.gradle @@ -157,7 +157,7 @@ dependencies { compile 'com.bluelinelabs:logansquare:1.3.7' compile 'com.soundcloud.android:android-crop:1.0.1@aar' compile 'com.hannesdorfmann.parcelableplease:annotation:1.0.2' - compile 'com.github.mariotaku:PickNCrop:0.9.15' + compile 'com.github.mariotaku:PickNCrop:0.9.16' compile "com.github.mariotaku.RestFu:library:$mariotaku_restfu_version" compile "com.github.mariotaku.RestFu:okhttp3:$mariotaku_restfu_version" compile 'com.squareup.okhttp3:okhttp:3.5.0' @@ -177,7 +177,7 @@ dependencies { compile "com.github.mariotaku.CommonsLibrary:text:$mariotaku_commons_library_version" 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.11' + compile 'com.github.mariotaku:Chameleon:0.9.12' 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' diff --git a/twidere/src/google/kotlin/org/mariotaku/twidere/util/FabricAnalyzer.kt b/twidere/src/google/kotlin/org/mariotaku/twidere/util/FabricAnalyzer.kt index 730b012a7..ae6649934 100644 --- a/twidere/src/google/kotlin/org/mariotaku/twidere/util/FabricAnalyzer.kt +++ b/twidere/src/google/kotlin/org/mariotaku/twidere/util/FabricAnalyzer.kt @@ -130,10 +130,14 @@ class FabricAnalyzer : Analyzer(), Constants { Crashlytics.setString("build.model", Build.MODEL) Crashlytics.setString("build.product", Build.PRODUCT) val am = AccountManager.get(application) - am.addOnAccountsUpdatedListenerSafe(OnAccountsUpdateListener { accounts -> - Crashlytics.setString("twidere.accounts", accounts.filter { it.type == ACCOUNT_TYPE } - .joinToString(transform = Account::name)) - }, updateImmediately = true) + try { + am.addOnAccountsUpdatedListenerSafe(OnAccountsUpdateListener { accounts -> + Crashlytics.setString("twidere.accounts", accounts.filter { it.type == ACCOUNT_TYPE } + .joinToString(transform = Account::name)) + }, updateImmediately = true) + } catch (e: SecurityException) { + // Permission managers (like some Xposed plugins) may block Twidere from getting accounts + } } private fun AnswersEvent<*>.putAttributes(event: Analyzer.Event) { diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/iface/IControlBarActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/iface/IControlBarActivity.java deleted file mode 100644 index 8b8fd59fe..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/iface/IControlBarActivity.java +++ /dev/null @@ -1,124 +0,0 @@ -package org.mariotaku.twidere.activity.iface; - -import android.animation.Animator; -import android.animation.Animator.AnimatorListener; -import android.animation.ObjectAnimator; -import android.util.Property; -import android.view.animation.DecelerateInterpolator; - -/** - * Created by mariotaku on 14/10/21. - */ -public interface IControlBarActivity { - - /** - * @param offset 0: invisible, 1: visible - */ - void setControlBarOffset(float offset); - - void setControlBarVisibleAnimate(boolean visible); - - void setControlBarVisibleAnimate(boolean visible, ControlBarShowHideHelper.ControlBarAnimationListener listener); - - float getControlBarOffset(); - - int getControlBarHeight(); - - void notifyControlBarOffsetChanged(); - - void registerControlBarOffsetListener(ControlBarOffsetListener listener); - - void unregisterControlBarOffsetListener(ControlBarOffsetListener listener); - - interface ControlBarOffsetListener { - void onControlBarOffsetChanged(IControlBarActivity activity, float offset); - } - - final class ControlBarShowHideHelper { - - private static final long DURATION = 200L; - - private final IControlBarActivity activity; - private int controlAnimationDirection; - private ObjectAnimator currentControlAnimation; - - public ControlBarShowHideHelper(IControlBarActivity activity) { - this.activity = activity; - } - - private static class ControlBarOffsetProperty extends Property { - public static final ControlBarOffsetProperty SINGLETON = new ControlBarOffsetProperty(); - - @Override - public void set(IControlBarActivity object, Float value) { - object.setControlBarOffset(value); - } - - public ControlBarOffsetProperty() { - super(Float.TYPE, null); - } - - @Override - public Float get(IControlBarActivity object) { - return object.getControlBarOffset(); - } - } - - public interface ControlBarAnimationListener { - void onControlBarVisibleAnimationFinish(boolean visible); - } - - public void setControlBarVisibleAnimate(boolean visible) { - setControlBarVisibleAnimate(visible, null); - } - - public void setControlBarVisibleAnimate(final boolean visible, final ControlBarAnimationListener listener) { - final int newDirection = visible ? 1 : -1; - if (controlAnimationDirection == newDirection) return; - if (currentControlAnimation != null && controlAnimationDirection != 0) { - currentControlAnimation.cancel(); - currentControlAnimation = null; - controlAnimationDirection = newDirection; - } - final ObjectAnimator animator; - final float offset = activity.getControlBarOffset(); - if (visible) { - if (offset >= 1) return; - animator = ObjectAnimator.ofFloat(activity, ControlBarOffsetProperty.SINGLETON, offset, 1); - } else { - if (offset <= 0) return; - animator = ObjectAnimator.ofFloat(activity, ControlBarOffsetProperty.SINGLETON, offset, 0); - } - animator.setInterpolator(new DecelerateInterpolator()); - animator.addListener(new AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { - } - - @Override - public void onAnimationEnd(Animator animation) { - controlAnimationDirection = 0; - currentControlAnimation = null; - if (listener != null) { - listener.onControlBarVisibleAnimationFinish(visible); - } - } - - @Override - public void onAnimationCancel(Animator animation) { - controlAnimationDirection = 0; - currentControlAnimation = null; - } - - @Override - public void onAnimationRepeat(Animator animation) { - - } - }); - animator.setDuration(DURATION); - animator.start(); - currentControlAnimation = animator; - controlAnimationDirection = newDirection; - } - } -} diff --git a/twidere/src/main/kotlin/android/support/v7/app/WindowDecorActionBarAccessor.kt b/twidere/src/main/kotlin/android/support/v7/app/WindowDecorActionBarAccessor.kt new file mode 100644 index 000000000..bd460e67f --- /dev/null +++ b/twidere/src/main/kotlin/android/support/v7/app/WindowDecorActionBarAccessor.kt @@ -0,0 +1,5 @@ +package android.support.v7.app + +import android.support.v7.widget.ActionBarContainer + +val WindowDecorActionBar.containerView: ActionBarContainer get() = mContainerView \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/BaseActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/BaseActivity.kt index b276efe28..73f698cd2 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/BaseActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/BaseActivity.kt @@ -240,26 +240,6 @@ open class BaseActivity : ChameleonActivity(), IExtendedActivity, super.onPause() } - override fun setControlBarOffset(offset: Float) { - - } - - override fun setControlBarVisibleAnimate(visible: Boolean) { - - } - - override fun setControlBarVisibleAnimate(visible: Boolean, listener: IControlBarActivity.ControlBarShowHideHelper.ControlBarAnimationListener) { - - } - - override fun getControlBarOffset(): Float { - return 0f - } - - override fun getControlBarHeight(): Int { - return 0 - } - override fun notifyControlBarOffsetChanged() { val offset = controlBarOffset for (l in controlBarOffsetListeners) { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/HomeActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/HomeActivity.kt index 0396bafc4..92ec588ec 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/HomeActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/HomeActivity.kt @@ -139,6 +139,11 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp } } + override val controlBarHeight: Int + get() { + return mainTabs.height - mainTabs.stripHeight + } + fun closeAccountsDrawer() { if (homeMenu == null) return homeMenu.closeDrawers() @@ -175,11 +180,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp return true } - override fun setControlBarVisibleAnimate(visible: Boolean) { - controlBarShowHideHelper.setControlBarVisibleAnimate(visible) - } - - override fun setControlBarVisibleAnimate(visible: Boolean, listener: IControlBarActivity.ControlBarShowHideHelper.ControlBarAnimationListener) { + override fun setControlBarVisibleAnimate(visible: Boolean, listener: IControlBarActivity.ControlBarShowHideHelper.ControlBarAnimationListener?) { controlBarShowHideHelper.setControlBarVisibleAnimate(visible, listener) } @@ -555,33 +556,33 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp drawerToggle.onConfigurationChanged(newConfig) } - override fun getControlBarOffset(): Float { - if (mainTabs.columns > 1) { - val lp = actionsButton.layoutParams - val total: Float - if (lp is MarginLayoutParams) { - total = (lp.bottomMargin + actionsButton.height).toFloat() - } else { - total = actionsButton.height.toFloat() + override var controlBarOffset: Float + get() { + if (mainTabs.columns > 1) { + val lp = actionsButton.layoutParams + val total: Float + if (lp is MarginLayoutParams) { + total = (lp.bottomMargin + actionsButton.height).toFloat() + } else { + total = actionsButton.height.toFloat() + } + return 1 - actionsButton.translationY / total } - return 1 - actionsButton.translationY / total + val totalHeight = controlBarHeight.toFloat() + return 1 + toolbar.translationY / totalHeight } - val totalHeight = controlBarHeight.toFloat() - return 1 + toolbar.translationY / totalHeight - } - - override fun setControlBarOffset(offset: Float) { - val translationY = if (mainTabs.columns > 1) 0 else (controlBarHeight * (offset - 1)).toInt() - toolbar.translationY = translationY.toFloat() - windowOverlay.translationY = translationY.toFloat() - val lp = actionsButton.layoutParams - if (lp is MarginLayoutParams) { - actionsButton.translationY = (lp.bottomMargin + actionsButton.height) * (1 - offset) - } else { - actionsButton.translationY = actionsButton.height * (1 - offset) + set(offset) { + val translationY = if (mainTabs.columns > 1) 0 else (controlBarHeight * (offset - 1)).toInt() + toolbar.translationY = translationY.toFloat() + windowOverlay.translationY = translationY.toFloat() + val lp = actionsButton.layoutParams + if (lp is MarginLayoutParams) { + actionsButton.translationY = (lp.bottomMargin + actionsButton.height) * (1 - offset) + } else { + actionsButton.translationY = actionsButton.height * (1 - offset) + } + notifyControlBarOffsetChanged() } - notifyControlBarOffsetChanged() - } override fun onDrawerSlide(drawerView: View, slideOffset: Float) { @@ -606,10 +607,6 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp return homeDrawerToggleDelegate } - override fun getControlBarHeight(): Int { - return mainTabs.height - mainTabs.stripHeight - } - private val keyboardShortcutRecipient: Fragment? get() { if (homeMenu.isDrawerOpen(GravityCompat.START)) { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/LinkHandlerActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/LinkHandlerActivity.kt index d9dff7237..4cf1c8b94 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/LinkHandlerActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/LinkHandlerActivity.kt @@ -183,6 +183,7 @@ class LinkHandlerActivity : BaseActivity(), SystemWindowsInsetsCallback, IContro } else { NavUtils.navigateUpFromSameTask(this) } + return true } } return super.onOptionsItemSelected(item) @@ -239,58 +240,53 @@ class LinkHandlerActivity : BaseActivity(), SystemWindowsInsetsCallback, IContro setupActionBarOption() } - override fun setControlBarVisibleAnimate(visible: Boolean) { - // Currently only search page needs this pattern, so we only enable this feature for it. - if (currentVisibleFragment !is HideUiOnScroll) return - controlBarShowHideHelper.setControlBarVisibleAnimate(visible) - } - - override fun setControlBarVisibleAnimate(visible: Boolean, listener: ControlBarShowHideHelper.ControlBarAnimationListener) { + override fun setControlBarVisibleAnimate(visible: Boolean, listener: ControlBarShowHideHelper.ControlBarAnimationListener?) { // Currently only search page needs this pattern, so we only enable this feature for it. if (currentVisibleFragment !is HideUiOnScroll) return controlBarShowHideHelper.setControlBarVisibleAnimate(visible, listener) } - override fun getControlBarOffset(): Float { - val fragment = currentVisibleFragment - val actionBar = supportActionBar - if (fragment is IToolBarSupportFragment) { - return fragment.controlBarOffset - } else if (actionBar != null) { - return actionBar.hideOffset / controlBarHeight.toFloat() - } - return 0f - } - - override fun setControlBarOffset(offset: Float) { - val fragment = currentVisibleFragment - val actionBar = supportActionBar - if (fragment is IToolBarSupportFragment) { - fragment.controlBarOffset = offset - } else if (actionBar != null && !hideOffsetNotSupported) { - try { - actionBar.hideOffset = (controlBarHeight * offset).toInt() - } catch (e: UnsupportedOperationException) { - // Some device will throw this exception - hideOffsetNotSupported = true + override var controlBarOffset: Float + get() { + val fragment = currentVisibleFragment + val actionBar = supportActionBar + if (fragment is IToolBarSupportFragment) { + return fragment.controlBarOffset + } else if (actionBar != null) { + return actionBar.hideOffset / controlBarHeight.toFloat() } - + return 0f } - notifyControlBarOffsetChanged() - } + set(offset) { + val fragment = currentVisibleFragment + val actionBar = supportActionBar + if (fragment is IToolBarSupportFragment) { + fragment.controlBarOffset = offset + } else if (actionBar != null && !hideOffsetNotSupported) { + try { + actionBar.hideOffset = (controlBarHeight * offset).toInt() + } catch (e: UnsupportedOperationException) { + // Some device will throw this exception + hideOffsetNotSupported = true + } - override fun getControlBarHeight(): Int { - val fragment = currentVisibleFragment - val actionBar = supportActionBar - if (fragment is IToolBarSupportFragment) { - return fragment.controlBarHeight - } else if (actionBar != null) { - return actionBar.height + } + notifyControlBarOffsetChanged() + } + + override val controlBarHeight: Int + get() { + val fragment = currentVisibleFragment + val actionBar = supportActionBar + if (fragment is IToolBarSupportFragment) { + return fragment.controlBarHeight + } else if (actionBar != null) { + return actionBar.height + } + if (actionBarHeight != 0) return actionBarHeight + actionBarHeight = ThemeUtils.getActionBarHeight(this) + return actionBarHeight } - if (actionBarHeight != 0) return actionBarHeight - actionBarHeight = ThemeUtils.getActionBarHeight(this) - return actionBarHeight - } private fun setTitle(linkId: Int, uri: Uri): Boolean { setSubtitle(null) 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 d47a9909a..17fd482bf 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/MediaViewerActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/MediaViewerActivity.kt @@ -19,6 +19,7 @@ package org.mariotaku.twidere.activity import android.annotation.SuppressLint import android.content.ActivityNotFoundException import android.content.Intent +import android.graphics.Color import android.net.Uri import android.os.Build import android.os.Bundle @@ -27,7 +28,11 @@ import android.support.v4.app.DialogFragment import android.support.v4.app.Fragment import android.support.v4.app.hasRunningLoadersSafe import android.support.v4.content.ContextCompat +import android.support.v4.graphics.ColorUtils import android.support.v4.view.ViewPager +import android.support.v4.widget.ViewDragHelper +import android.support.v7.app.WindowDecorActionBar +import android.support.v7.app.containerView import android.view.Menu import android.view.MenuItem import android.widget.Toast @@ -39,6 +44,7 @@ import org.mariotaku.mediaviewer.library.* import org.mariotaku.mediaviewer.library.subsampleimageview.SubsampleImageViewerFragment.EXTRA_MEDIA_URI import org.mariotaku.twidere.R import org.mariotaku.twidere.TwidereConstants.* +import org.mariotaku.twidere.activity.iface.IControlBarActivity.ControlBarShowHideHelper import org.mariotaku.twidere.activity.iface.IExtendedActivity import org.mariotaku.twidere.fragment.* import org.mariotaku.twidere.model.ParcelableMedia @@ -52,21 +58,26 @@ import org.mariotaku.twidere.util.IntentUtils import org.mariotaku.twidere.util.MenuUtils import org.mariotaku.twidere.util.PermissionUtils import org.mariotaku.twidere.util.dagger.GeneralComponentHelper +import org.mariotaku.twidere.util.support.WindowSupport +import org.mariotaku.twidere.view.viewer.MediaSwipeCloseContainer import java.io.File import javax.inject.Inject import android.Manifest.permission as AndroidPermissions -class MediaViewerActivity : BaseActivity(), IMediaViewerActivity { +class MediaViewerActivity : BaseActivity(), IMediaViewerActivity, MediaSwipeCloseContainer.Listener { + @Inject + lateinit var mediaFileCache: FileCache @Inject - lateinit var mFileCache: FileCache - @Inject - lateinit var mMediaDownloader: MediaDownloader - + lateinit var mediaDownloader: MediaDownloader private var saveToStoragePosition = -1 - private var shareMediaPosition = -1 + private var shareMediaPosition = -1 + private var wasBarShowing = 0 + private var hideOffsetNotSupported = false private lateinit var mediaViewerHelper: IMediaViewerActivity.Helper + private lateinit var controlBarShowHideHelper: ControlBarShowHideHelper + private var tempLocation = IntArray(2) private val status: ParcelableStatus? get() = intent.getParcelableExtra(EXTRA_STATUS) @@ -82,13 +93,16 @@ class MediaViewerActivity : BaseActivity(), IMediaViewerActivity { super.onCreate(savedInstanceState) GeneralComponentHelper.build(this).inject(this) mediaViewerHelper = IMediaViewerActivity.Helper(this) + controlBarShowHideHelper = ControlBarShowHideHelper(this) mediaViewerHelper.onCreate(savedInstanceState) - val actionBar = supportActionBar!! - actionBar.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + swipeContainer.listener = this + swipeContainer.backgroundAlpha = 1f + WindowSupport.setStatusBarColor(window, Color.TRANSPARENT) + activityLayout.setStatusBarColor(overrideTheme.colorToolbar) } - - public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { when (requestCode) { REQUEST_SHARE_MEDIA -> { ShareProvider.clearTempFiles(this) @@ -96,6 +110,7 @@ class MediaViewerActivity : BaseActivity(), IMediaViewerActivity { } } + override fun onContentChanged() { super.onContentChanged() mediaViewerHelper.onContentChanged() @@ -114,6 +129,7 @@ class MediaViewerActivity : BaseActivity(), IMediaViewerActivity { val currentItem = viewPager.currentItem 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() @@ -219,25 +235,19 @@ class MediaViewerActivity : BaseActivity(), IMediaViewerActivity { } override fun isBarShowing(): Boolean { - val actionBar = supportActionBar - return actionBar != null && actionBar.isShowing + return controlBarOffset >= 1 } override fun setBarVisibility(visible: Boolean) { - val actionBar = supportActionBar ?: return - if (visible) { - actionBar.show() - } else { - actionBar.hide() - } + setControlBarVisibleAnimate(visible) } override fun getDownloader(): MediaDownloader { - return mMediaDownloader + return mediaDownloader } override fun getFileCache(): FileCache { - return mFileCache + return mediaFileCache } @SuppressLint("SwitchIntDef") @@ -284,6 +294,71 @@ class MediaViewerActivity : BaseActivity(), IMediaViewerActivity { return theme } + override fun onSwipeCloseFinished() { + finish() + overridePendingTransition(0, 0) + } + + override fun onSwipeOffsetChanged(offset: Int) { + val offsetFactor = 1 - (Math.abs(offset).toFloat() / swipeContainer.height) + swipeContainer.backgroundAlpha = offsetFactor + val colorToolbar = overrideTheme.colorToolbar + val alpha = Math.round(Color.alpha(colorToolbar) * offsetFactor) + activityLayout.setStatusBarColor(ColorUtils.setAlphaComponent(colorToolbar, alpha)) + } + + override fun onSwipeStateChanged(state: Int) { + supportActionBar?.let { bar -> + if (state == ViewDragHelper.STATE_IDLE) { + if (wasBarShowing == 1 && !isBarShowing) { + setBarVisibility(true) + } + wasBarShowing = 0 + } else { + if (wasBarShowing == 0) { + wasBarShowing = if (isBarShowing) 1 else -1 + } + if (isBarShowing) { + setBarVisibility(false) + } + } + } + } + + + override val controlBarHeight: Int + get() = supportActionBar?.height ?: 0 + + override var controlBarOffset: Float + get() { + val actionBar = supportActionBar + if (actionBar != null) { + return 1 - actionBar.hideOffset / controlBarHeight.toFloat() + } + return 0f + } + set(offset) { + val actionBar = supportActionBar + if (actionBar != null && !hideOffsetNotSupported) { + if (actionBar is WindowDecorActionBar) { + val toolbar = actionBar.containerView + toolbar.alpha = offset + } + try { + actionBar.hideOffset = Math.round(controlBarHeight * (1f - offset)) + } catch (e: UnsupportedOperationException) { + // Some device will throw this exception + hideOffsetNotSupported = true + } + + } + notifyControlBarOffsetChanged() + } + + override fun setControlBarVisibleAnimate(visible: Boolean, listener: ControlBarShowHideHelper.ControlBarAnimationListener?) { + controlBarShowHideHelper.setControlBarVisibleAnimate(visible, listener) + } + private fun processShareIntent(intent: Intent) { val status = status ?: return intent.putExtra(Intent.EXTRA_SUBJECT, IntentUtils.getStatusShareSubject(this, status)) @@ -406,3 +481,4 @@ class MediaViewerActivity : BaseActivity(), IMediaViewerActivity { private val REQUEST_PERMISSION_SHARE_MEDIA = 203 } } + diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/PremiumDashboardActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/PremiumDashboardActivity.kt index 2e12ede65..729b47708 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/PremiumDashboardActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/PremiumDashboardActivity.kt @@ -2,6 +2,7 @@ package org.mariotaku.twidere.activity import android.os.Bundle import android.support.v4.app.DialogFragment +import android.support.v4.app.NavUtils import android.view.Menu import android.view.MenuItem import android.view.View @@ -43,6 +44,9 @@ class PremiumDashboardActivity : BaseActivity() { override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { + android.R.id.home -> { + NavUtils.navigateUpFromSameTask(this) + } R.id.consume_purchase -> { if (BuildConfig.DEBUG) { return true 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 new file mode 100644 index 000000000..7976d95d6 --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/iface/IControlBarActivity.kt @@ -0,0 +1,102 @@ +package org.mariotaku.twidere.activity.iface + +import android.animation.Animator +import android.animation.Animator.AnimatorListener +import android.animation.ObjectAnimator +import android.util.Property +import android.view.animation.DecelerateInterpolator + +/** + * Created by mariotaku on 14/10/21. + */ +interface IControlBarActivity { + + /** + * 0: invisible, 1: visible + */ + var controlBarOffset: Float + get() = 0f + set(value) {} + + val controlBarHeight: Int get() = 0 + + fun setControlBarVisibleAnimate(visible: Boolean, listener: ControlBarShowHideHelper.ControlBarAnimationListener? = null) {} + + fun registerControlBarOffsetListener(listener: ControlBarOffsetListener) {} + + fun unregisterControlBarOffsetListener(listener: ControlBarOffsetListener) {} + + fun notifyControlBarOffsetChanged() {} + + interface ControlBarOffsetListener { + fun onControlBarOffsetChanged(activity: IControlBarActivity, offset: Float) {} + } + + class ControlBarShowHideHelper(private val activity: IControlBarActivity) { + private var controlAnimationDirection: Int = 0 + private var currentControlAnimation: ObjectAnimator? = null + + private object ControlBarOffsetProperty : Property(Float::class.java, null) { + + override fun set(obj: IControlBarActivity, value: Float) { + obj.controlBarOffset = (value) + } + + override fun get(obj: IControlBarActivity): Float { + return obj.controlBarOffset + } + + } + + interface ControlBarAnimationListener { + fun onControlBarVisibleAnimationFinish(visible: Boolean) + } + + fun setControlBarVisibleAnimate(visible: Boolean, listener: ControlBarAnimationListener? = null) { + val newDirection = if (visible) 1 else -1 + if (controlAnimationDirection == newDirection) return + if (currentControlAnimation != null && controlAnimationDirection != 0) { + currentControlAnimation!!.cancel() + currentControlAnimation = null + controlAnimationDirection = newDirection + } + val animator: ObjectAnimator + val offset = activity.controlBarOffset + if (visible) { + if (offset >= 1) return + animator = ObjectAnimator.ofFloat(activity, ControlBarOffsetProperty, offset, 1f) + } else { + if (offset <= 0) return + animator = ObjectAnimator.ofFloat(activity, ControlBarOffsetProperty, offset, 0f) + } + animator.interpolator = DecelerateInterpolator() + animator.addListener(object : AnimatorListener { + override fun onAnimationStart(animation: Animator) {} + + override fun onAnimationEnd(animation: Animator) { + controlAnimationDirection = 0 + currentControlAnimation = null + listener?.onControlBarVisibleAnimationFinish(visible) + } + + override fun onAnimationCancel(animation: Animator) { + controlAnimationDirection = 0 + currentControlAnimation = null + } + + override fun onAnimationRepeat(animation: Animator) { + + } + }) + animator.duration = DURATION + animator.start() + currentControlAnimation = animator + controlAnimationDirection = newDirection + } + + companion object { + + private const val DURATION = 200L + } + } +} diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsActivitiesFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsActivitiesFragment.kt index 3d5e7843c..77c843b2b 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsActivitiesFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsActivitiesFragment.kt @@ -218,15 +218,18 @@ abstract class AbsActivitiesFragment protected constructor() : var lastReadId: Long = -1 var lastReadViewTop: Int = 0 var loadMore = false + var wasAtTop = false // 1. Save current read position if not first load if (!firstLoad) { + val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition() val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() + wasAtTop = firstVisibleItemPosition == 0 val statusRange = rangeOfSize(adapter.activityStartIndex, adapter.activityCount - 1) val lastReadPosition = if (readFromBottom) { lastVisibleItemPosition } else { - layoutManager.findFirstVisibleItemPosition() + firstVisibleItemPosition }.coerceIn(statusRange) lastReadId = adapter.getTimestamp(lastReadPosition) lastReadViewTop = layoutManager.findViewByPosition(lastReadPosition)?.top ?: 0 @@ -251,7 +254,8 @@ abstract class AbsActivitiesFragment protected constructor() : restorePosition = adapter.findPositionBySortTimestamp(lastReadId) } - if (restorePosition != -1 && adapter.isActivity(restorePosition) && (loadMore || readFromBottom + if (restorePosition != -1 && adapter.isActivity(restorePosition) && (loadMore || !wasAtTop || + readFromBottom || (rememberPosition && firstLoad))) { if (layoutManager.height == 0) { // RecyclerView has not currently laid out, ignore padding. @@ -292,7 +296,8 @@ abstract class AbsActivitiesFragment protected constructor() : override fun onMediaClick(holder: IStatusViewHolder, view: View, media: ParcelableMedia, position: Int) { val status = adapter.getActivity(position)?.getActivityStatus() ?: return - IntentUtils.openMedia(activity, status, media, preferences[newDocumentApiKey], preferences[displaySensitiveContentsKey], + IntentUtils.openMedia(activity, status, media, preferences[newDocumentApiKey], + preferences[displaySensitiveContentsKey], null) // BEGIN HotMobi val event = MediaEvent.create(activity, status, media, timelineType, adapter.mediaPreviewEnabled) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsStatusesFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsStatusesFragment.kt index d7e76be71..ef8a6447d 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsStatusesFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsStatusesFragment.kt @@ -30,7 +30,6 @@ import android.support.v4.content.Loader import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.RecyclerView import android.support.v7.widget.RecyclerView.OnScrollListener -import android.util.Log import android.view.* import com.squareup.otto.Subscribe import edu.tsinghua.hotmobi.HotMobiLogger @@ -39,10 +38,8 @@ import kotlinx.android.synthetic.main.fragment_content_recyclerview.* import org.mariotaku.kpreferences.get import org.mariotaku.ktextension.isNullOrEmpty import org.mariotaku.ktextension.rangeOfSize -import org.mariotaku.twidere.BuildConfig import org.mariotaku.twidere.R import org.mariotaku.twidere.TwidereConstants -import org.mariotaku.twidere.TwidereConstants.LOGTAG import org.mariotaku.twidere.adapter.ParcelableStatusesAdapter import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter @@ -275,14 +272,17 @@ abstract class AbsStatusesFragment protected constructor() : var lastReadId: Long = -1 var lastReadViewTop: Int = 0 var loadMore = false + var wasAtTop = false // 1. Save current read position if not first load if (!firstLoad) { + val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition() val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() + wasAtTop = firstVisibleItemPosition == 0 val statusRange = rangeOfSize(adapter.statusStartIndex, adapter.statusCount - 1) val lastReadPosition = if (readFromBottom) { lastVisibleItemPosition } else { - layoutManager.findFirstVisibleItemPosition() + firstVisibleItemPosition }.coerceIn(statusRange) lastReadId = if (useSortIdAsReadPosition) { adapter.getStatusSortId(lastReadPosition) @@ -318,8 +318,8 @@ abstract class AbsStatusesFragment protected constructor() : } else { onHasMoreDataChanged(false) } - if (restorePosition != -1 && adapter.isStatus(restorePosition) && (loadMore || readFromBottom - || (rememberPosition && firstLoad))) { + if (restorePosition != -1 && adapter.isStatus(restorePosition) && (loadMore || !wasAtTop + || readFromBottom || (rememberPosition && firstLoad))) { if (layoutManager.height == 0) { // RecyclerView has not currently laid out, ignore padding. layoutManager.scrollToPositionWithOffset(restorePosition, lastReadViewTop) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt index 3a3ceedc3..ae9a6b944 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt @@ -317,7 +317,8 @@ class StatusFragment : BaseFragment(), LoaderCallbacks 0 -> { + // Settle downward + container.dragHelper.settleCapturedViewAt(0, container.height) + } + yvel < 0 -> { + // Settle upward + container.dragHelper.settleCapturedViewAt(0, -container.height) + + } + else -> when { + childTop < -container.height / 2 -> { + container.dragHelper.settleCapturedViewAt(0, -container.height) + } + childTop > container.height / 2 -> { + container.dragHelper.settleCapturedViewAt(0, container.height) + } + else -> { + container.dragHelper.settleCapturedViewAt(0, 0) + } + } + } + ViewCompat.postInvalidateOnAnimation(container) + } + }) + + private var childTop: Int = 0 + + var listener: Listener? = null + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + measureChildren(widthMeasureSpec, heightMeasureSpec) + setMeasuredDimension(getDefaultSize(suggestedMinimumWidth, widthMeasureSpec), + getDefaultSize(suggestedMinimumHeight, heightMeasureSpec)) + } + + override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { + for (i in 0 until childCount) { + val child = getChildAt(i) + if (child.visibility != View.GONE) { + child.layout(0, childTop, child.measuredWidth, childTop + child.measuredHeight) + } + } + } + + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { + if (dragHelper.shouldInterceptTouchEvent(ev)) { + return true + } + return super.onInterceptTouchEvent(ev) + } + + override fun onTouchEvent(event: MotionEvent): Boolean { + dragHelper.processTouchEvent(event) + return true + } + + override fun computeScroll() { + if (dragHelper.continueSettling(true)) { + ViewCompat.postInvalidateOnAnimation(this) + } else if (childTop <= -height || childTop >= height) { + listener?.onSwipeCloseFinished() + } + } + + interface Listener { + fun onSwipeCloseFinished() {} + + fun onSwipeOffsetChanged(offset: Int) {} + + fun onSwipeStateChanged(state: Int) {} + } + + + var backgroundAlpha: Float + get() = (background?.alpha ?: 0) / 255f + set(@FloatRange(from = 0.0, to = 1.0) value) { + background?.alpha = Math.round(value * 0xFF) + } +} \ No newline at end of file diff --git a/twidere/src/main/res/layout/activity_media_viewer.xml b/twidere/src/main/res/layout/activity_media_viewer.xml index a0e55cc26..67aa9cb32 100644 --- a/twidere/src/main/res/layout/activity_media_viewer.xml +++ b/twidere/src/main/res/layout/activity_media_viewer.xml @@ -17,15 +17,27 @@ ~ along with this program. If not, see . --> - + - + android:layout_height="match_parent" + android:background="#000000"> - \ No newline at end of file + + + + \ No newline at end of file diff --git a/twidere/src/main/res/values-v21/themes_base.xml b/twidere/src/main/res/values-v21/themes_base.xml deleted file mode 100644 index c3b209aed..000000000 --- a/twidere/src/main/res/values-v21/themes_base.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/twidere/src/main/res/values/themes.xml b/twidere/src/main/res/values/themes.xml index dcd7e33f5..21476dd99 100644 --- a/twidere/src/main/res/values/themes.xml +++ b/twidere/src/main/res/values/themes.xml @@ -39,7 +39,8 @@