improved QR code style

This commit is contained in:
Mariotaku Lee 2017-04-10 13:10:08 +08:00
parent fe9ee01765
commit 0aaeb32141
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
8 changed files with 112 additions and 47 deletions

View File

@ -62,6 +62,7 @@ subprojects {
Kovenant : '3.3.0',
ParcelablePlease : '1.0.2',
Chameleon : '0.9.17',
UniqR : '0.9',
]
}

View File

@ -36,8 +36,8 @@ android {
applicationId "org.mariotaku.twidere"
minSdkVersion project.properties['overrideMinSdkVersion'] ?: 14
targetSdkVersion 25
versionCode 322
versionName '3.5.5'
versionCode 323
versionName '3.5.6'
multiDexEnabled true
buildConfigField 'boolean', 'LEAK_CANARY_ENABLED', 'Boolean.parseBoolean("true")'
@ -138,6 +138,7 @@ dependencies {
compile "com.android.support:recyclerview-v7:${libVersions['SupportLib']}"
compile "com.android.support:preference-v7:${libVersions['SupportLib']}"
compile "com.android.support:preference-v14:${libVersions['SupportLib']}"
compile "com.android.support:palette-v7:${libVersions['SupportLib']}"
compile "com.android.support:customtabs:${libVersions['SupportLib']}"
compile "com.android.support:design:${libVersions['SupportLib']}"
compile "com.android.support:percent:${libVersions['SupportLib']}"
@ -188,7 +189,7 @@ dependencies {
compile "com.github.mariotaku:KPreferences:${libVersions['KPreferences']}"
compile "com.github.mariotaku:Chameleon:${libVersions['Chameleon']}"
compile 'com.github.mariotaku.QR-Code-generator:core:fcab3ea7f4'
compile 'com.github.mariotaku.QR-Code-generator:android:fcab3ea7f4'
compile 'com.github.mariotaku.UniqR:android:0.9'
compile "org.jetbrains.kotlin:kotlin-stdlib:${libVersions['Kotlin']}"
compile "nl.komponents.kovenant:kovenant:${libVersions['Kovenant']}"

View File

@ -19,12 +19,16 @@
package org.mariotaku.twidere.annotation;
import android.support.annotation.StringDef;
/**
* Created by mariotaku on 2017/3/6.
*/
@StringDef({ProfileImageSize.NORMAL, ProfileImageSize.BIGGER, ProfileImageSize.REASONABLY_SMALL,
ProfileImageSize.ORIGINAL})
public @interface ProfileImageSize {
String REASONABLY_SMALL = "reasonably_small";
String BIGGER = "bigger";
String NORMAL = "normal";
String ORIGINAL = "original";
}

View File

@ -87,6 +87,7 @@ import org.mariotaku.sqliteqb.library.Selectable;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.annotation.CustomTabType;
import org.mariotaku.twidere.annotation.ProfileImageSize;
import org.mariotaku.twidere.extension.model.AccountDetailsExtensionsKt;
import org.mariotaku.twidere.model.AccountDetails;
import org.mariotaku.twidere.model.ParcelableStatus;
@ -563,6 +564,9 @@ public final class Utils implements Constants {
public static String getTwitterProfileImageOfSize(@NonNull final String url, @NonNull final String size) {
if (ProfileImageSize.ORIGINAL.equals(size)) {
return getOriginalTwitterProfileImage(url);
}
final Matcher matcher = PATTERN_TWITTER_PROFILE_IMAGES.matcher(url);
if (matcher.matches()) {
return matcher.replaceFirst("$1$2/profile_images/$3/$4_" + size + "$6");

View File

@ -94,6 +94,7 @@ import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.model.FriendshipUpdate
import org.mariotaku.microblog.library.twitter.model.Paging
import org.mariotaku.microblog.library.twitter.model.UserList
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants.*
import org.mariotaku.twidere.R
import org.mariotaku.twidere.activity.AccountSelectorActivity
@ -832,7 +833,7 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
mentionItem.title = getString(R.string.mention_user_name, displayName)
}
MenuUtils.setItemAvailability(menu, R.id.mention, !isMyself)
MenuUtils.setItemAvailability(menu, R.id.qr_code, isMyself)
MenuUtils.setItemAvailability(menu, R.id.qr_code, isMyself || BuildConfig.DEBUG)
MenuUtils.setItemAvailability(menu, R.id.incoming_friendships, isMyself)
MenuUtils.setItemAvailability(menu, R.id.saved_searches, isMyself)

View File

@ -19,24 +19,37 @@
package org.mariotaku.twidere.fragment
import android.content.res.Resources
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 io.nayuki.qrcodegen.QrCode
import io.nayuki.qrcodegen.QrCodeAndroid
import io.nayuki.qrcodegen.QrSegment
import kotlinx.android.synthetic.main.fragment_user_qr.*
import nl.komponents.kovenant.combine.and
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.annotation.ProfileImageSize
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_USER
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.glide.DeferredTarget
import org.mariotaku.twidere.util.qr.QrCodeData
import org.mariotaku.uniqr.AndroidPlatform
import org.mariotaku.uniqr.UniqR
import java.lang.ref.WeakReference
/**
* Created by mariotaku on 2017/4/3.
@ -52,16 +65,72 @@ class UserQRDialogFragment : BaseDialogFragment() {
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val qrCode = QrCode.encodeText(LinkCreator.getUserWebLink(user).toString(), QrCode.Ecc.HIGH)
val bitmap = QrCodeAndroid.toBitmap(qrCode, 1, 0, Bitmap.Config.ARGB_8888)
val profileImageSize = getString(R.string.profile_image_size)
qrView.setImageDrawable(BitmapDrawable(resources, bitmap).apply {
this.setAntiAlias(false)
this.isFilterBitmap = false
})
profileImage.setShapeBackground(Color.WHITE)
Glide.with(this).loadProfileImage(context, user, profileImage.style, profileImage.cornerRadius,
profileImage.cornerRadiusRatio, profileImageSize).into(profileImage)
val weakThis = WeakReference(this)
val deferred = Glide.with(this).loadProfileImage(context, user, 0, 0f, 0f,
ProfileImageSize.ORIGINAL).into(DeferredTarget())
promiseOnUi {
val fragment = weakThis.get() ?: return@promiseOnUi
fragment.qrView.visibility = View.INVISIBLE
fragment.qrProgress.visibility = View.VISIBLE
} and deferred.promise.then { drawable ->
val fragment = weakThis.get() ?: 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 qrData = run {
val segments = QrSegment.makeSegments(LinkCreator.getUserWebLink(fragment.user).toString())
return@run QrCodeData(QrCode.encodeSegments(segments, QrCode.Ecc.HIGH, 5, 40, -1, true))
}
val uniqr = UniqR(AndroidPlatform(), background, qrData)
uniqr.setScale(3)
uniqr.setQrPatternColor(palette.patternColor)
val result = uniqr.build().produceResult()
background.recycle()
return@then result
}.successUi { bitmap ->
val fragment = weakThis.get() ?: 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() ?: return@failUi
Toast.makeText(fragment.context, R.string.message_toast_error_occurred, Toast.LENGTH_SHORT).show()
fragment.dismiss()
}
}
companion object {
private fun getOptimalPatternColor(color: Int): Int {
val yiq = IntArray(3)
TwidereColorUtils.colorToYIQ(color, yiq)
if (yiq[0] > 72) {
yiq[0] = 72
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)
}
}
}

View File

@ -17,35 +17,25 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.graphic
package org.mariotaku.twidere.util.qr
import android.graphics.Canvas
import android.graphics.ColorFilter
import android.graphics.PixelFormat
import android.graphics.drawable.Drawable
import io.nayuki.qrcodegen.QrCode
import org.mariotaku.uniqr.QrData
/**
* Created by mariotaku on 2017/4/3.
* Created by mariotaku on 2017/4/10.
*/
data class QrCodeData(private val qrCode: QrCode) : QrData {
class QrCodeDrawable(val qrCode: QrCode) : Drawable() {
override fun draw(canvas: Canvas) {
for (x in 0 until qrCode.size) {
for (y in 0 until qrCode.size) {
}
}
override fun getSize(): Int {
return qrCode.size
}
override fun getOpacity() = PixelFormat.OPAQUE
override fun setAlpha(alpha: Int) {
// NO-OP
override fun getVersion(): Int {
return qrCode.version
}
override fun setColorFilter(colorFilter: ColorFilter?) {
// NO-OP
override fun get(x: Int, y: Int): Boolean {
return qrCode.getModule(x, y) == 1
}
}

View File

@ -20,7 +20,6 @@
<org.mariotaku.twidere.view.SquareRelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
@ -38,14 +37,10 @@
android:padding="@dimen/element_spacing_xlarge"
android:scaleType="fitCenter"/>
<org.mariotaku.twidere.view.ProfileImageView
android:id="@+id/profileImage"
style="?profileImageStyle"
android:layout_width="@dimen/element_size_mlarge"
android:layout_height="@dimen/element_size_mlarge"
android:layout_centerInParent="true"
app:sivBorderColor="@android:color/white"
app:sivBorderWidth="2dp"
app:sivShape="rectangle"/>
<ProgressBar
android:id="@+id/qrProgress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
</org.mariotaku.twidere.view.SquareRelativeLayout>