improved custom emoji #990

This commit is contained in:
Mariotaku Lee 2017-10-29 20:59:07 +08:00
parent e3251bc72b
commit ed8fbd662e
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
10 changed files with 189 additions and 75 deletions

View File

@ -33,6 +33,6 @@ class ComposeActivityTestRule(initialTouchMode: Boolean = false, launchActivity:
}
override fun afterActivityFinished() {
TestAccountUtils.removeTestAccounts()
TestAccountUtils.removeTestAccounts().get()
}
}

View File

@ -0,0 +1,67 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.extension
import android.graphics.Canvas
import android.graphics.ColorFilter
import android.graphics.PixelFormat
import android.graphics.Rect
import android.graphics.drawable.Drawable
import junit.framework.Assert
import org.junit.Test
class DrawableExtensionsKtTest {
@Test
fun setBoundsFitCenter() {
val drawable = TestDrawable()
drawable.setBoundsFitCenter(0, 0, 100, 100)
Assert.assertEquals(Rect(25, 0, 75, 100), drawable.bounds)
drawable.setBoundsFitCenter(0, 0, 50, 100)
Assert.assertEquals(Rect(0, 0, 50, 100), drawable.bounds)
drawable.setBoundsFitCenter(0, 0, 10, 100)
Assert.assertEquals(Rect(0, 40, 10, 60), drawable.bounds)
}
private class TestDrawable : Drawable() {
override fun draw(canvas: Canvas) {
}
override fun setAlpha(alpha: Int) {
}
override fun getOpacity(): Int {
return PixelFormat.TRANSLUCENT
}
override fun setColorFilter(colorFilter: ColorFilter?) {
}
override fun getIntrinsicWidth(): Int {
return 100
}
override fun getIntrinsicHeight(): Int {
return 200
}
}
}

View File

@ -21,15 +21,15 @@ package org.mariotaku.twidere.util
import android.accounts.AccountManager
import android.support.test.InstrumentationRegistry
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.task
import org.mariotaku.twidere.extension.model.updateDetails
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.test.R
import org.mariotaku.twidere.util.support.removeAccountSupport
import java.lang.Exception
/**
* Created by mariotaku on 2017/4/16.
*/
object TestAccountUtils {
private val accountResources = intArrayOf(R.raw.account_4223092274_twitter_com)
@ -51,10 +51,12 @@ object TestAccountUtils {
}
}
fun removeTestAccounts() {
fun removeTestAccounts(): Promise<Unit, Exception> {
val targetContext = InstrumentationRegistry.getTargetContext()
val am = AccountManager.get(targetContext)
val existingAccounts = AccountUtils.getAllAccountDetails(am, false)
existingAccounts.filter { it.test }.forEach { am.removeAccountSupport(it.account) }
return task {
existingAccounts.filter { it.test }.forEach { am.removeAccountSupport(it.account).result }
}
}
}

View File

@ -26,7 +26,6 @@ import android.view.View
internal abstract class AccessorHeaderScrollingViewBehavior(context: Context, attrs: AttributeSet? = null) : HeaderScrollingViewBehavior(context, attrs) {
internal val tempRect1 = mTempRect1
internal val tempRect2 = mTempRect2
internal override fun getOverlapRatioForOffset(header: View): Float {
return super.getOverlapRatioForOffset(header)

View File

@ -0,0 +1,44 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.extension
import android.graphics.drawable.Drawable
fun Drawable.setBoundsFitCenter(left: Int, top: Int, right: Int, bottom: Int) {
val boundsWidth = right - left
val boundsHeight = bottom - top
if (intrinsicWidth <= 0 || intrinsicHeight <= 0 || boundsWidth <= 0 || boundsHeight <= 0) {
setBounds(left, top, right, bottom)
return
}
val intrinsicAspectRatio = intrinsicWidth / intrinsicHeight.toFloat()
val boundsAspectRatio = boundsWidth / boundsHeight.toFloat()
if (intrinsicAspectRatio < boundsAspectRatio) {
// Match height
val actualWidth = (boundsWidth * (intrinsicAspectRatio / boundsAspectRatio)).toInt()
setBounds(boundsWidth / 2 - actualWidth / 2, top,
boundsWidth / 2 + actualWidth / 2, bottom)
} else {
// Match width
val actualHeight = (boundsHeight * (boundsAspectRatio / intrinsicAspectRatio)).toInt()
setBounds(left, boundsHeight / 2 - actualHeight / 2, right,
boundsHeight / 2 + actualHeight / 2)
}
}

View File

@ -54,8 +54,8 @@ fun Array<SpanItem>.applyTo(spannable: Spannable, emojis: Map<String, CustomEmoj
SpanItem.SpanType.EMOJI -> {
val shortCode = span.link ?: return@forEach
val emoji = emojis?.get(shortCode) ?: return@forEach
spannable.setSpan(CustomEmojiSpan(emoji.url, requestManager, textView), span.start,
span.end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
spannable.setSpan(CustomEmojiSpan(emoji.url, requestManager, textView),
span.start, span.end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
else -> {
spannable.setSpan(URLSpan(span.link), span.start, span.end,

View File

@ -501,7 +501,7 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
override fun getSystemWindowInsets(caller: Fragment, insets: Rect): Boolean {
insetsCallback?.getSystemWindowInsets(this, insets)
if (caller.parentFragment === this) {
insets.top = toolbar.height + toolbarTabs.height
insets.top = toolbar.measuredHeight + toolbarTabs.measuredHeight
}
return true
}

View File

@ -27,59 +27,69 @@ import android.graphics.drawable.Drawable
import android.text.style.ReplacementSpan
import android.widget.TextView
import com.bumptech.glide.RequestManager
import com.bumptech.glide.load.DecodeFormat
import com.bumptech.glide.request.animation.GlideAnimation
import com.bumptech.glide.request.target.BaseTarget
import com.bumptech.glide.request.target.SizeReadyCallback
import org.mariotaku.ktextension.weak
import com.bumptech.glide.request.target.SimpleTarget
import org.mariotaku.twidere.R
import org.mariotaku.twidere.extension.setBoundsFitCenter
class CustomEmojiSpan(
val uri: String,
uri: String,
requestManager: RequestManager,
val textView: TextView,
val alignBaseline: Boolean = false
textView: TextView
) : ReplacementSpan() {
private val textSize = textView.textSize.toInt()
private val target = GlideTarget(textSize)
private val emojiSize = textView.textSize.toInt()
private val target = GlideTarget(textView, emojiSize, emojiSize)
init {
requestManager.load(uri)
.asBitmap()
.placeholder(R.mipmap.ic_emoji_loading)
.error(R.mipmap.ic_emoji_error)
.format(DecodeFormat.PREFER_ARGB_8888)
.fitCenter()
.dontAnimate()
.into(target)
}
override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int,
fm: Paint.FontMetricsInt?): Int {
return textSize
if (fm != null) {
fm.ascent = -target.height
fm.descent = 0
fm.top = fm.ascent
fm.bottom = 0
}
return target.width
}
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 - b.bounds.bottom
if (alignBaseline) {
transY -= paint.fontMetricsInt.descent
}
val transY = bottom - b.bounds.bottom
canvas.translate(x, transY.toFloat())
b.setBounds(0, 0, textSize, textSize)
b.draw(canvas)
canvas.restore()
}
private inner class GlideTarget(
val textSize: Int
) : BaseTarget<Bitmap>() {
private class GlideTarget(
val textView: TextView,
val width: Int,
val height: Int
) : SimpleTarget<Bitmap>(width, height) {
var drawable: Drawable? by weak()
var drawable: Drawable? = null
set(value) {
field = value
value?.setBoundsFitCenter(0, 0, width, height)
textView.postInvalidate()
}
override fun onResourceReady(resource: Bitmap, glideAnimation: GlideAnimation<in Bitmap>) {
drawable = BitmapDrawable(textView.resources, resource)
@ -97,10 +107,7 @@ class CustomEmojiSpan(
drawable = errorDrawable
}
override fun getSize(cb: SizeReadyCallback) {
cb.onSizeReady(textSize, textSize)
}
}
}

View File

@ -136,6 +136,7 @@ internal class HeaderBehavior(context: Context, attrs: AttributeSet? = null) :
}
override fun canDragView(view: ViewGroup): Boolean {
if (view.translationY != 0f) return false
// Else we'll use the default behaviour of seeing if it can scroll down
val scrollingView = lastNestedScrollingChild
return if (scrollingView != null) {

View File

@ -29,11 +29,43 @@
app:statusBarBackground="@null"
tools:theme="@style/Theme.Twidere.NoActionBar">
<org.mariotaku.twidere.view.ExtendedViewPager
android:id="@+id/viewPager"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="org.mariotaku.twidere.view.behavior.userprofile.PagerBehavior"/>
android:visibility="visible"
app:layout_behavior="org.mariotaku.twidere.view.behavior.userprofile.PagerBehavior">
<org.mariotaku.twidere.view.ExtendedViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<LinearLayout
android:id="@+id/pagesErrorContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/element_spacing_large"
android:visibility="gone">
<org.mariotaku.twidere.view.IconActionView
android:id="@+id/pagesErrorIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:color="?android:textColorSecondary"
android:src="@drawable/ic_info_error_generic"/>
<org.mariotaku.twidere.view.FixedTextView
android:id="@+id/pagesErrorText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/element_spacing_normal"
android:textAppearance="?android:textAppearanceMedium"/>
</LinearLayout>
</FrameLayout>
<RelativeLayout
android:id="@+id/profileBannerContainer"
@ -78,44 +110,6 @@
android:background="@drawable/shadow_bottom"
app:layout_behavior="org.mariotaku.twidere.view.behavior.userprofile.HeaderShadowBehavior"/>
<FrameLayout
android:id="@+id/pagesErrorContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/element_spacing_large">
<org.mariotaku.twidere.view.IconActionView
android:id="@+id/pagesErrorIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:color="?android:textColorSecondary"
android:src="@drawable/ic_info_error_generic"/>
<org.mariotaku.twidere.view.FixedTextView
android:id="@+id/pagesErrorText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/element_spacing_normal"
android:textAppearance="?android:textAppearanceMedium"/>
</LinearLayout>
<View
android:id="@+id/errorWindowOverlay"
android:layout_width="match_parent"
android:layout_height="@dimen/element_spacing_normal"
android:layout_gravity="top"
android:background="@drawable/shadow_bottom"/>
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">