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 6df13b3ce..1cdaf0df6 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SignInActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SignInActivity.kt @@ -345,9 +345,10 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher, this[EXTRA_REQUEST_TOKEN_SECRET] = requestToken.oauthTokenSecret }) activity.startActivityForResult(intent, REQUEST_BROWSER_TWITTER_SIGN_IN) - }.failUi { + }.failUi { exception -> val activity = weakThis.get() ?: return@failUi - // TODO show error message + Toast.makeText(activity, exception.getErrorMessage(activity), Toast.LENGTH_LONG).show() + DebugLog.w(tr = exception) }.alwaysUi { executeAfterFragmentResumed { it.supportFragmentManager.dismissDialogFragment("get_request_token") diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/SpanItemExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/SpanItemExtensions.kt index f7c734742..c7bd6da31 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/SpanItemExtensions.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/SpanItemExtensions.kt @@ -20,10 +20,10 @@ package org.mariotaku.twidere.extension.model import android.annotation.SuppressLint -import android.graphics.drawable.Drawable import android.text.Spannable import android.text.Spanned import android.text.style.URLSpan +import android.widget.TextView import com.bumptech.glide.RequestManager import org.mariotaku.twidere.model.CustomEmoji import org.mariotaku.twidere.model.SpanItem @@ -36,7 +36,7 @@ val SpanItem.length: Int get() = end - start @SuppressLint("SwitchIntDef") fun Array.applyTo(spannable: Spannable, emojis: Map?, - requestManager: RequestManager, callback: Drawable.Callback) { + requestManager: RequestManager, textView: TextView) { forEach { span -> when (span.type) { SpanItem.SpanType.HIDE -> { @@ -54,7 +54,7 @@ fun Array.applyTo(spannable: Spannable, emojis: Map { val shortCode = span.link ?: return@forEach val emoji = emojis?.get(shortCode) ?: return@forEach - spannable.setSpan(CustomEmojiSpan(emoji.url, requestManager, callback), span.start, + spannable.setSpan(CustomEmojiSpan(emoji.url, requestManager, textView), span.start, span.end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) } else -> { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserFragment.kt index 37f180bfd..8212310f2 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserFragment.kt @@ -22,24 +22,19 @@ package org.mariotaku.twidere.fragment import android.accounts.AccountManager import android.animation.ArgbEvaluator import android.annotation.SuppressLint -import android.annotation.TargetApi import android.app.Activity import android.app.Dialog import android.content.Context import android.content.DialogInterface import android.content.Intent import android.graphics.Color -import android.graphics.Outline import android.graphics.PorterDuff import android.graphics.Rect import android.graphics.drawable.ColorDrawable -import android.graphics.drawable.Drawable -import android.graphics.drawable.LayerDrawable import android.net.Uri import android.nfc.NdefMessage import android.nfc.NdefRecord import android.nfc.NfcAdapter.CreateNdefMessageCallback -import android.os.Build import android.os.Bundle import android.os.Parcelable import android.support.annotation.ColorRes @@ -118,8 +113,8 @@ import org.mariotaku.twidere.fragment.timeline.AbsTimelineFragment import org.mariotaku.twidere.fragment.timeline.FavoritesTimelineFragment import org.mariotaku.twidere.fragment.timeline.UserMediaTimelineFragment import org.mariotaku.twidere.fragment.timeline.UserTimelineFragment -import org.mariotaku.twidere.graphic.ActionBarColorDrawable import org.mariotaku.twidere.graphic.ActionIconDrawable +import org.mariotaku.twidere.graphic.drawable.userprofile.ActionBarDrawable import org.mariotaku.twidere.loader.ParcelableUserLoader import org.mariotaku.twidere.model.* import org.mariotaku.twidere.model.event.FriendshipTaskEvent @@ -637,7 +632,7 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener, profileBanner.onSizeChangedListener = this profileBannerSpace.setOnTouchListener(this) - profileNameBackground.setBackgroundColor(cardBackgroundColor) + profileHeaderBackground.setBackgroundColor(cardBackgroundColor) toolbarTabs.setBackgroundColor(cardBackgroundColor) actionBarBackground = ActionBarDrawable(ResourcesCompat.getDrawable(activity.resources, @@ -1301,8 +1296,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener, location.setLinkTextColor(optimalAccentColor) url.setLinkTextColor(optimalAccentColor) profileBanner.setBackgroundColor(color) - - toolbarTabs.setBackgroundColor(primaryColor) } private fun setupBaseActionBar() { @@ -1370,11 +1363,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener, private fun updateScrollOffset(offset: Int) { val spaceHeight = profileBannerSpace.height val factor = (if (spaceHeight == 0) 0f else offset / spaceHeight.toFloat()).coerceIn(0f, 1f) - profileBannerContainer.translationY = (-offset).toFloat() - profileBanner.translationY = (offset / 2).toFloat() - if (profileBirthdayStub == null) { - profileBirthdayBanner.translationY = (offset / 2).toFloat() - } val activity = activity as BaseActivity @@ -1386,7 +1374,7 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener, val stackedTabColor = primaryColor - val profileContentHeight = profileNameBackground.height.toFloat() + val profileContentHeight = profileHeaderBackground.height.toFloat() val tabOutlineAlphaFactor: Float if (offset - spaceHeight > 0) { tabOutlineAlphaFactor = 1f - ((offset - spaceHeight) / profileContentHeight).coerceIn(0f, 1f) @@ -1514,70 +1502,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener, } } - private class ActionBarDrawable(shadow: Drawable) : LayerDrawable(arrayOf(shadow, ActionBarColorDrawable.create(true))) { - - private val shadowDrawable = getDrawable(0) - private val colorDrawable = getDrawable(1) as ColorDrawable - private var alphaValue: Int = 0 - - var factor: Float = 0f - set(value) { - field = value - updateValue() - } - - var color: Int = 0 - set(value) { - field = value - colorDrawable.color = value - updateValue() - } - - var outlineAlphaFactor: Float = 0f - set(value) { - field = value - updateValue() - } - - init { - alpha = 0xFF - updateValue() - } - - override fun setAlpha(alpha: Int) { - alphaValue = alpha - updateValue() - } - - override fun getAlpha(): Int { - return alphaValue - } - - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - override fun getOutline(outline: Outline) { - colorDrawable.getOutline(outline) - outline.alpha = factor * outlineAlphaFactor * 0.99f - } - - override fun getIntrinsicWidth(): Int { - return colorDrawable.intrinsicWidth - } - - override fun getIntrinsicHeight(): Int { - return colorDrawable.intrinsicHeight - } - - private fun updateValue() { - val shadowAlpha = Math.round(alpha * (1 - factor).coerceIn(0f, 1f)) - shadowDrawable.alpha = shadowAlpha - val hasColor = color != 0 - val colorAlpha = if (hasColor) Math.round(alpha * factor.coerceIn(0f, 1f)) else 0 - colorDrawable.alpha = colorAlpha - invalidateSelf() - } - - } - internal class UserRelationshipLoader( context: Context, diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/graphic/drawable/userprofile/ActionBarDrawable.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/graphic/drawable/userprofile/ActionBarDrawable.kt new file mode 100644 index 000000000..2f8b439a1 --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/graphic/drawable/userprofile/ActionBarDrawable.kt @@ -0,0 +1,92 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2017 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.graphic.drawable.userprofile + +import android.annotation.TargetApi +import android.graphics.Outline +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable +import android.graphics.drawable.LayerDrawable +import android.os.Build +import org.mariotaku.twidere.graphic.ActionBarColorDrawable + +internal class ActionBarDrawable(shadow: Drawable) : LayerDrawable(arrayOf(shadow, ActionBarColorDrawable.create(true))) { + + private val shadowDrawable = getDrawable(0) + private val colorDrawable = getDrawable(1) as ColorDrawable + private var alphaValue: Int = 0 + + var factor: Float = 0f + set(value) { + field = value + updateValue() + } + + var color: Int = 0 + set(value) { + field = value + colorDrawable.color = value + updateValue() + } + + var outlineAlphaFactor: Float = 0f + set(value) { + field = value + updateValue() + } + + init { + alpha = 0xFF + updateValue() + } + + override fun setAlpha(alpha: Int) { + alphaValue = alpha + updateValue() + } + + override fun getAlpha(): Int { + return alphaValue + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + override fun getOutline(outline: Outline) { + colorDrawable.getOutline(outline) + outline.alpha = factor * outlineAlphaFactor * 0.99f + } + + override fun getIntrinsicWidth(): Int { + return colorDrawable.intrinsicWidth + } + + override fun getIntrinsicHeight(): Int { + return colorDrawable.intrinsicHeight + } + + private fun updateValue() { + val shadowAlpha = Math.round(alpha * (1 - factor).coerceIn(0f, 1f)) + shadowDrawable.alpha = shadowAlpha + val hasColor = color != 0 + val colorAlpha = if (hasColor) Math.round(alpha * factor.coerceIn(0f, 1f)) else 0 + colorDrawable.alpha = colorAlpha + invalidateSelf() + } + +} \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/text/style/CustomEmojiSpan.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/text/style/CustomEmojiSpan.kt index d762673f2..3960ea6dd 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/text/style/CustomEmojiSpan.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/text/style/CustomEmojiSpan.kt @@ -19,77 +19,89 @@ package org.mariotaku.twidere.text.style +import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Paint -import android.graphics.Rect -import android.graphics.drawable.Animatable +import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable -import android.support.v4.widget.CircularProgressDrawable -import android.text.style.DynamicDrawableSpan +import android.text.style.ReplacementSpan import android.widget.TextView import com.bumptech.glide.RequestManager -import com.bumptech.glide.load.resource.drawable.GlideDrawable import com.bumptech.glide.request.animation.GlideAnimation import com.bumptech.glide.request.target.BaseTarget import com.bumptech.glide.request.target.SizeReadyCallback import org.mariotaku.twidere.R -import org.mariotaku.twidere.graphic.DrawableWrapper +import java.lang.ref.WeakReference class CustomEmojiSpan( val uri: String, requestManager: RequestManager, - callback: Drawable.Callback -) : DynamicDrawableSpan(DynamicDrawableSpan.ALIGN_BASELINE) { + val textView: TextView, + val alignBaseline: Boolean = false +) : ReplacementSpan() { - private val textSize = (callback as TextView).textSize.toInt() + private val textSize = textView.textSize.toInt() - private val emojiDrawable = EmojiDrawable(textSize) - private val emojiDirtyBounds = Rect() - - private val target = GlideTarget() + private val target = GlideTarget(textSize) init { - emojiDrawable.callback = callback requestManager.load(uri) - .placeholder(CircularProgressDrawable((callback as TextView).context)) + .asBitmap() + .placeholder(R.mipmap.ic_emoji_loading) .error(R.mipmap.ic_emoji_error) .fitCenter() + .dontAnimate() .into(target) } + override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, + fm: Paint.FontMetricsInt?): Int { + return textSize + } + override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) { + val b = target.drawable ?: return + canvas.save() - var transY = bottom - emojiDrawable.bounds.bottom - if (verticalAlignment == ALIGN_BASELINE) { + var transY = bottom - b.bounds.bottom + if (alignBaseline) { transY -= paint.fontMetricsInt.descent } - emojiDrawable.copyBounds(emojiDirtyBounds) - emojiDirtyBounds.offsetTo(x.toInt(), transY) - super.draw(canvas, text, start, end, x, top, y, bottom, paint) + + canvas.translate(x, transY.toFloat()) + b.setBounds(0, 0, textSize, textSize) + b.draw(canvas) + canvas.restore() } - override fun getDrawable(): Drawable = emojiDrawable + private inner class GlideTarget( + val textSize: Int + ) : BaseTarget() { - fun verify(who: Drawable): Boolean = who === emojiDrawable + var drawable: Drawable? + get() = drawableRef?.get() + private set(value) { + drawableRef = if (value != null) WeakReference(value) else null + textView.invalidate() + } - private inner class GlideTarget : BaseTarget() { + private var drawableRef: WeakReference? = null - override fun onResourceReady(resource: GlideDrawable, glideAnimation: GlideAnimation) { - resource.setLoopCount(GlideDrawable.LOOP_FOREVER) - emojiDrawable.setDrawable(resource) + override fun onResourceReady(resource: Bitmap, glideAnimation: GlideAnimation) { + drawable = BitmapDrawable(textView.resources, resource) } override fun onLoadCleared(placeholder: Drawable?) { - emojiDrawable.setDrawable(placeholder) + drawable = placeholder } override fun onLoadStarted(placeholder: Drawable?) { - emojiDrawable.setDrawable(placeholder) + drawable = placeholder } override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) { - emojiDrawable.setDrawable(errorDrawable) + drawable = errorDrawable } override fun getSize(cb: SizeReadyCallback) { @@ -98,20 +110,4 @@ class CustomEmojiSpan( } - private inner class EmojiDrawable(val textSize: Int) : DrawableWrapper() { - - override fun getDirtyBounds(): Rect { - return emojiDirtyBounds - } - - fun setDrawable(drawable: Drawable?) { - wrapped = drawable - drawable?.setBounds(0, 0, textSize, textSize) - setBounds(0, 0, textSize, textSize) - if (drawable is Animatable) { - drawable.start() - } - } - } - } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/TimelineContentTextView.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/TimelineContentTextView.kt index cda371337..91f8b5122 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/TimelineContentTextView.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/TimelineContentTextView.kt @@ -21,9 +21,7 @@ package org.mariotaku.twidere.view import android.annotation.SuppressLint import android.content.Context -import android.graphics.drawable.Drawable import android.text.Spannable -import android.text.Spanned import android.text.method.BaseMovementMethod import android.text.method.MovementMethod import android.text.style.ClickableSpan @@ -32,7 +30,6 @@ import android.view.MotionEvent import android.widget.TextView import org.mariotaku.chameleon.view.ChameleonTextView import org.mariotaku.twidere.extension.setupEmojiFactory -import org.mariotaku.twidere.text.style.CustomEmojiSpan import java.lang.ref.WeakReference /** @@ -90,16 +87,6 @@ class TimelineContentTextView( } } - override fun verifyDrawable(who: Drawable): Boolean { - val result = super.verifyDrawable(who) - if (result) return true - val spanned = text as? Spanned ?: return false - val spans = spanned.getSpans(0, length(), CustomEmojiSpan::class.java) - return spans.any { - it.verify(who) - } - } - internal class InternalMovementMethod : BaseMovementMethod() { private var targetSpan: WeakReference? = null diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/behavior/userprofile/HeaderBehavior.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/behavior/userprofile/HeaderBehavior.kt index 6f140bd6d..3602b554c 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/behavior/userprofile/HeaderBehavior.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/behavior/userprofile/HeaderBehavior.kt @@ -19,20 +19,40 @@ package org.mariotaku.twidere.view.behavior.userprofile +import android.annotation.SuppressLint import android.content.Context import android.support.design.widget.AccessorHeaderBehavior import android.support.design.widget.CoordinatorLayout -import android.support.v4.math.MathUtils +import android.support.graphics.drawable.ArgbEvaluator import android.util.AttributeSet import android.view.View +import kotlinx.android.synthetic.main.fragment_user.view.* +import kotlinx.android.synthetic.main.header_user.view.* +import org.mariotaku.chameleon.Chameleon +import org.mariotaku.chameleon.ChameleonUtils +import org.mariotaku.kpreferences.get import org.mariotaku.twidere.R +import org.mariotaku.twidere.constant.themeBackgroundAlphaKey +import org.mariotaku.twidere.constant.themeBackgroundOptionKey import org.mariotaku.twidere.extension.view.measureChildIgnoringInsets +import org.mariotaku.twidere.graphic.drawable.userprofile.ActionBarDrawable +import org.mariotaku.twidere.util.ThemeUtils +import org.mariotaku.twidere.util.dagger.DependencyHolder internal class HeaderBehavior(context: Context, attrs: AttributeSet? = null) : AccessorHeaderBehavior(context, attrs) { var offsetDelta: Int = 0 private set + private val cardBackgroundColor: Int + private var tabItemIsDark: Int = 0 + + init { + val preferences = DependencyHolder.get(context).preferences + cardBackgroundColor = ThemeUtils.getCardBackgroundColor(context, + preferences[themeBackgroundOptionKey], preferences[themeBackgroundAlphaKey]) + } + override fun onMeasureChild(parent: CoordinatorLayout, child: View, parentWidthMeasureSpec: Int, widthUsed: Int, parentHeightMeasureSpec: Int, heightUsed: Int): Boolean { @@ -40,6 +60,12 @@ internal class HeaderBehavior(context: Context, attrs: AttributeSet? = null) : A parentHeightMeasureSpec, heightUsed) } + override fun onLayoutChild(parent: CoordinatorLayout, child: View, layoutDirection: Int): Boolean { + val result = super.onLayoutChild(parent, child, layoutDirection) + updateTabColor(parent, child, topAndBottomOffset) + return result + } + override fun layoutChild(parent: CoordinatorLayout, child: View, layoutDirection: Int) { child.layout(0, 0, child.measuredWidth, child.measuredHeight) } @@ -62,38 +88,19 @@ internal class HeaderBehavior(context: Context, attrs: AttributeSet? = null) : A val curOffset = topBottomOffsetForScrollingSibling var consumed = 0 - var newOffset = newOffset if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) { // If we have some scrolling range, and we're currently within the min and max // offsets, calculate a new offset - newOffset = MathUtils.clamp(newOffset, minOffset, maxOffset) - if (curOffset != newOffset) { - val interpolatedOffset = if (header.hasChildWithInterpolator) - interpolateOffset(header, newOffset) - else - newOffset - - val offsetChanged = setTopAndBottomOffset(interpolatedOffset) + val clampedOffset = newOffset.coerceIn(minOffset, maxOffset) + if (curOffset != clampedOffset) { + topAndBottomOffset = clampedOffset + updateTabColor(parent, header, clampedOffset) // Update how much dy we have consumed - consumed = curOffset - newOffset + consumed = curOffset - clampedOffset // Update the stored sibling offset - offsetDelta = newOffset - interpolatedOffset + offsetDelta = clampedOffset - clampedOffset - if (!offsetChanged && header.hasChildWithInterpolator) { - // If the offset hasn't changed and we're using an interpolated scroll - // then we need to keep any dependent views updated. CoL will do this for - // us when we move, but we need to do it manually when we don't (as an - // interpolated scroll may finish early). - parent.dispatchDependentViewsChanged(header) - } - - // Dispatch the updates to any listeners -// header.dispatchOffsetUpdates(topAndBottomOffset) - - // Update the AppBarLayout's drawable state (for any elevation changes) -// updateAppBarLayoutDrawableState(parent, header, newOffset, -// if (newOffset < curOffset) -1 else 1, false) } } else { // Reset the offset delta @@ -103,13 +110,42 @@ internal class HeaderBehavior(context: Context, attrs: AttributeSet? = null) : A return consumed } - override fun canDragView(view: View) = true + @SuppressLint("RestrictedApi") + private fun updateTabColor(parent: CoordinatorLayout, header: View, offset: Int) { + val actionBarBackground = parent.toolbar.background as? ActionBarDrawable ?: return + val profileHeaderBackground = parent.profileHeaderBackground - private fun interpolateOffset(header: View, offset: Int): Int { - return offset + val toolbarBottom = parent.toolbar.bottom + val headerBackgroundOffset = offset + profileHeaderBackground.top + + val factor = ((toolbarBottom - headerBackgroundOffset) / profileHeaderBackground.height.toFloat()).coerceIn(0f, 1f) + + val toolbarTabs = header.toolbarTabs + + val colorPrimary = actionBarBackground.color + val currentTabColor = ArgbEvaluator.getInstance().evaluate(factor, + cardBackgroundColor, colorPrimary) as Int + + toolbarTabs.setBackgroundColor(currentTabColor) + + val tabItemIsDark = if (ThemeUtils.isLightColor(currentTabColor)) 1 else -1 + if (this.tabItemIsDark != tabItemIsDark) { + val context = parent.context + val activity = ChameleonUtils.getActivity(context) + val tabContrastColor = ThemeUtils.getColorDependent(currentTabColor) + toolbarTabs.setIconColor(tabContrastColor) + toolbarTabs.setLabelColor(tabContrastColor) + val theme = Chameleon.getOverrideTheme(context, activity) + if (theme.isToolbarColored) { + toolbarTabs.setStripColor(tabContrastColor) + } else { + toolbarTabs.setStripColor(ThemeUtils.getOptimalAccentColor(colorPrimary, tabContrastColor)) + } + toolbarTabs.updateAppearance() + } + this.tabItemIsDark = tabItemIsDark } - private val View.hasChildWithInterpolator: Boolean - get() = false + override fun canDragView(view: View) = true } \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/behavior/userprofile/StatusBarBehavior.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/behavior/userprofile/StatusBarBehavior.kt new file mode 100644 index 000000000..43f83e4d1 --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/behavior/userprofile/StatusBarBehavior.kt @@ -0,0 +1,75 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2017 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.view.behavior.userprofile + +import android.annotation.SuppressLint +import android.content.Context +import android.support.design.widget.CoordinatorLayout +import android.support.design.widget.lastWindowInsetsCompat +import android.support.graphics.drawable.ArgbEvaluator +import android.util.AttributeSet +import android.view.View +import android.view.Window +import kotlinx.android.synthetic.main.fragment_user.view.* +import org.mariotaku.chameleon.ChameleonUtils +import org.mariotaku.twidere.R +import org.mariotaku.twidere.graphic.drawable.userprofile.ActionBarDrawable +import org.mariotaku.twidere.util.ThemeUtils +import org.mariotaku.twidere.util.support.WindowSupport + +internal class StatusBarBehavior(context: Context, attrs: AttributeSet? = null) : CoordinatorLayout.Behavior(context, attrs) { + + private val window: Window = ChameleonUtils.getActivity(context)!!.window + private var lightStatusBar: Int = 0 + + override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean { + return dependency.id == R.id.profileHeader + } + + override fun onLayoutChild(parent: CoordinatorLayout, child: View, layoutDirection: Int): Boolean { + val lastInsets = parent.lastWindowInsetsCompat ?: return true + val height = lastInsets.systemWindowInsetTop + child.layout(0, 0, child.measuredWidth, height) + return true + } + + @SuppressLint("RestrictedApi") + override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean { + if (child.height == 0) return true + val actionBarBackground = parent.toolbar.background as? ActionBarDrawable ?: return true + val bannerContainer = parent.profileBannerContainer + val bannerBottom = dependency.top + bannerContainer.height + val currentOffset = bannerBottom - child.bottom + val maxOffset = (bannerContainer.height - child.bottom).toFloat() + val factor = (1 - currentOffset / maxOffset).coerceIn(0f, 1f) + + val primaryColorDark = ChameleonUtils.darkenColor(actionBarBackground.color) + val statusBarColor = ArgbEvaluator.getInstance().evaluate(factor, 0xA0000000.toInt(), + ChameleonUtils.darkenColor(primaryColorDark)) + child.setBackgroundColor(statusBarColor as Int) + val lightStatusBar = if (ThemeUtils.isLightColor(statusBarColor)) 1 else -1 + if (this.lightStatusBar != lightStatusBar) { + WindowSupport.setLightStatusBar(window, lightStatusBar == 1) + } + this.lightStatusBar = lightStatusBar + return true + } + +} \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/behavior/userprofile/ToolbarBehavior.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/behavior/userprofile/ToolbarBehavior.kt index 033a4e16e..174ffff76 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/behavior/userprofile/ToolbarBehavior.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/behavior/userprofile/ToolbarBehavior.kt @@ -19,11 +19,47 @@ package org.mariotaku.twidere.view.behavior.userprofile +import android.annotation.SuppressLint import android.content.Context import android.support.design.widget.CoordinatorLayout +import android.support.graphics.drawable.ArgbEvaluator import android.support.v7.widget.Toolbar import android.util.AttributeSet +import android.view.View +import kotlinx.android.synthetic.main.fragment_user.view.* +import org.mariotaku.twidere.R +import org.mariotaku.twidere.graphic.drawable.userprofile.ActionBarDrawable +import org.mariotaku.twidere.util.ThemeUtils internal class ToolbarBehavior(context: Context?, attrs: AttributeSet? = null) : CoordinatorLayout.Behavior(context, attrs) { + private val actionBarShadowColor: Int = 0xA0000000.toInt() + private var actionItemIsDark: Int = 0 + + override fun layoutDependsOn(parent: CoordinatorLayout, child: Toolbar, dependency: View): Boolean { + return dependency.id == R.id.profileHeader + } + + @SuppressLint("RestrictedApi") + override fun onDependentViewChanged(parent: CoordinatorLayout, child: Toolbar, dependency: View): Boolean { + val actionBarBackground = child.background as? ActionBarDrawable ?: return false + val bannerContainer = parent.profileBannerContainer + val bannerBottom = dependency.top + bannerContainer.height + val currentOffset = bannerBottom - child.bottom + val maxOffset = (bannerContainer.height - child.bottom).toFloat() + val factor = (1 - currentOffset / maxOffset).coerceIn(0f, 1f) + actionBarBackground.factor = factor + actionBarBackground.outlineAlphaFactor = factor + + val colorPrimary = actionBarBackground.color + val currentActionBarColor = ArgbEvaluator.getInstance().evaluate(factor, actionBarShadowColor, + colorPrimary) as Int + val actionItemIsDark = if (ThemeUtils.isLightColor(currentActionBarColor)) 1 else -1 + if (this.actionItemIsDark != actionItemIsDark) { + ThemeUtils.applyToolbarItemColor(parent.context, child, currentActionBarColor) + } + this.actionItemIsDark = actionItemIsDark + return true + } + } diff --git a/twidere/src/main/res/layout/fragment_user.xml b/twidere/src/main/res/layout/fragment_user.xml index ba09b0ae7..d560cbe94 100644 --- a/twidere/src/main/res/layout/fragment_user.xml +++ b/twidere/src/main/res/layout/fragment_user.xml @@ -117,7 +117,12 @@ - + +