Twidere-App-Android-Twitter.../twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserQrDialogFragment.kt

163 lines
6.5 KiB
Kotlin

/*
* 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.fragment
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.drawable.BitmapDrawable
import android.os.Bundle
import android.support.v7.graphics.Palette
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.drawable.GlideDrawable
import com.bumptech.glide.request.target.Target
import io.nayuki.qrcodegen.QrCode
import io.nayuki.qrcodegen.QrSegment
import kotlinx.android.synthetic.main.fragment_user_qr.*
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.combine.and
import nl.komponents.kovenant.task
import nl.komponents.kovenant.then
import nl.komponents.kovenant.ui.failUi
import nl.komponents.kovenant.ui.promiseOnUi
import nl.komponents.kovenant.ui.successUi
import org.mariotaku.twidere.R
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_USER
import org.mariotaku.twidere.extension.loadOriginalProfileImage
import org.mariotaku.twidere.extension.loadProfileImage
import org.mariotaku.twidere.model.ParcelableUser
import org.mariotaku.twidere.util.LinkCreator
import org.mariotaku.twidere.util.TwidereColorUtils
import org.mariotaku.twidere.util.qr.QrCodeData
import org.mariotaku.uniqr.AndroidPlatform
import org.mariotaku.uniqr.UniqR
import java.lang.ref.WeakReference
import java.util.concurrent.ExecutionException
/**
* Display QR code to user
* Created by mariotaku on 2017/4/3.
*/
class UserQrDialogFragment : BaseDialogFragment() {
private val user: ParcelableUser get() = arguments.getParcelable(EXTRA_USER)
init {
setStyle(STYLE_NO_TITLE, 0)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.fragment_user_qr, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val weakThis = WeakReference(this)
promiseOnUi {
val fragment = weakThis.get()?.takeIf { it.view != null } ?: return@promiseOnUi
fragment.qrView.visibility = View.INVISIBLE
fragment.qrProgress.visibility = View.VISIBLE
} and loadProfileImage().then { drawable ->
val fragment = weakThis.get()?.takeIf { it.context != null } ?: throw InterruptedException()
val background = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888)
val canvas = Canvas(background)
drawable.setBounds(0, 0, background.width, background.height)
drawable.draw(canvas)
val palette = Palette.from(background).generate()
val link = LinkCreator.getUserWebLink(fragment.user)
val segments = QrSegment.makeSegments(link.toString())
val qrCode = QrCode.encodeSegments(segments, QrCode.Ecc.HIGH, 5, 40, -1, true)
val uniqr = UniqR(AndroidPlatform(), background, QrCodeData(qrCode))
uniqr.scale = 3
uniqr.qrPatternColor = palette.patternColor
val result = uniqr.build().produceResult()
background.recycle()
return@then result
}.successUi { bitmap ->
val fragment = weakThis.get()?.takeIf { it.context != null && it.view != null } ?: return@successUi
fragment.qrView.visibility = View.VISIBLE
fragment.qrProgress.visibility = View.GONE
fragment.qrView.setImageDrawable(BitmapDrawable(fragment.resources, bitmap).apply {
this.setAntiAlias(false)
this.isFilterBitmap = false
})
}.failUi {
val fragment = weakThis.get()?.takeIf { it.dialog != null } ?: return@failUi
Toast.makeText(fragment.context, R.string.message_toast_error_occurred, Toast.LENGTH_SHORT).show()
fragment.dismiss()
}
}
private fun loadProfileImage(): Promise<GlideDrawable, Exception> {
if (context == null || isDetached || dialog == null || (activity.isFinishing ?: true)) {
return Promise.ofFail(InterruptedException())
}
val profileImageSize = getString(R.string.profile_image_size)
val context = context.applicationContext
val requestManager = Glide.with(context)
val user = this.user
return task {
try {
return@task requestManager.loadOriginalProfileImage(context, user, 0)
.into(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL).get()
} catch (e: ExecutionException) {
// Ignore
}
// Return fallback profile image
return@task requestManager.loadProfileImage(context, user, 0, size = profileImageSize)
.into(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL).get()
}
}
companion object {
private fun getOptimalPatternColor(color: Int): Int {
val yiq = IntArray(3)
TwidereColorUtils.colorToYIQ(color, yiq)
if (yiq[0] > 128) {
yiq[0] = 128
return TwidereColorUtils.YIQToColor(Color.alpha(color), yiq)
}
return color
}
private val Palette.patternColor: Int get() {
var color = getDarkVibrantColor(0)
if (color == 0) {
color = getDominantColor(0)
}
if (color == 0) {
color = getDarkMutedColor(0)
}
if (color == 0) {
return Color.BLACK
}
return getOptimalPatternColor(color)
}
}
}