diff --git a/.travis.yml b/.travis.yml index 49b2c59ae..33f694044 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,8 +62,16 @@ before_install: install: # Extracts build configs into source tree - tar zxf travis/configs/twidere_private_config.tar.gz - # I REALLY don't want to use StrictHostKeyChecking, but SSH don't let me check hostname only - - GIT_SSH="ssh -i private/ssh_id_rsa -o StrictHostKeyChecking=no $@" git clone $COMPONENT_GOOGLE_REPO twidere/src/google > /dev/null 2>&1 + # Make sure ~/.ssh/ exists + - mkdir -p ~/.ssh/ + # Make it secure + - chmod 700 ~/.ssh/ + # Append ssh_config + - cat private/ssh_config >> ~/.ssh/config + # Append known_hosts + - cat private/ssh_known_hosts >> ~/.ssh/known_hosts + # Checkout Google components + - ssh-agent bash -c "ssh-add private/ssh_id_rsa; git clone $COMPONENT_GOOGLE_REPO twidere/src/google" > /dev/null 2>&1 before_script: # Validate if patches work diff --git a/travis/configs/twidere_private_config.tar.gz.enc b/travis/configs/twidere_private_config.tar.gz.enc index 49c9e5d69..87752b1ae 100644 Binary files a/travis/configs/twidere_private_config.tar.gz.enc and b/travis/configs/twidere_private_config.tar.gz.enc differ diff --git a/twidere/src/main/java/org/mariotaku/twidere/annotation/ImageShapeStyle.java b/twidere/src/main/java/org/mariotaku/twidere/annotation/ImageShapeStyle.java new file mode 100644 index 000000000..87c287f78 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/annotation/ImageShapeStyle.java @@ -0,0 +1,35 @@ +/* + * 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.annotation; + +import android.support.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Created by mariotaku on 2017/3/2. + */ +@IntDef({ImageShapeStyle.SHAPE_CIRCLE, ImageShapeStyle.SHAPE_RECTANGLE}) +@Retention(RetentionPolicy.SOURCE) +public @interface ImageShapeStyle { + int SHAPE_CIRCLE = 0x1; + int SHAPE_RECTANGLE = 0x2; +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/ShapedImageView.java b/twidere/src/main/java/org/mariotaku/twidere/view/ShapedImageView.java index 6d820cd40..ed90155cb 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/ShapedImageView.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/ShapedImageView.java @@ -33,7 +33,6 @@ import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import android.graphics.SweepGradient; import android.os.Build; -import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; @@ -41,23 +40,17 @@ import android.view.View; import android.widget.ImageView; import org.mariotaku.twidere.R; +import org.mariotaku.twidere.annotation.ImageShapeStyle; import org.mariotaku.twidere.util.support.ViewSupport; import org.mariotaku.twidere.util.support.graphics.OutlineCompat; import org.mariotaku.twidere.util.support.view.ViewOutlineProviderCompat; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - /** * An ImageView class with a circle mask so that all images are drawn in a * circle instead of a square. */ public class ShapedImageView extends ImageView { - @ShapeStyle - public static final int SHAPE_CIRCLE = 0x1; - @ShapeStyle - public static final int SHAPE_RECTANGLE = 0x2; private static final int SHADOW_START_COLOR = 0x37000000; public static final boolean OUTLINE_DRAW = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; @@ -100,8 +93,8 @@ public class ShapedImageView extends ImageView { } setBorderColor(a.getColor(R.styleable.ShapedImageView_sivBorderColor, Color.TRANSPARENT)); setBorderWidth(a.getDimensionPixelSize(R.styleable.ShapedImageView_sivBorderWidth, 0)); - @ShapeStyle - final int shapeStyle = a.getInt(R.styleable.ShapedImageView_sivShape, SHAPE_RECTANGLE); + @ImageShapeStyle + final int shapeStyle = a.getInt(R.styleable.ShapedImageView_sivShape, ImageShapeStyle.SHAPE_RECTANGLE); setStyle(shapeStyle); setCornerRadius(a.getDimension(R.styleable.ShapedImageView_sivCornerRadius, 0)); setCornerRadiusRatio(a.getFraction(R.styleable.ShapedImageView_sivCornerRadiusRatio, 1, 1, -1)); @@ -125,12 +118,12 @@ public class ShapedImageView extends ImageView { return mBorderColors; } - @ShapeStyle + @ImageShapeStyle public int getStyle() { return mStyle; } - public void setStyle(@ShapeStyle final int style) { + public void setStyle(@ImageShapeStyle final int style) { mStyle = style; } @@ -244,7 +237,7 @@ public class ShapedImageView extends ImageView { ViewCompat.setTranslationZ(this, 0); } mBorderPaint.setStrokeWidth(strokeWidth); - if (getStyle() == SHAPE_CIRCLE) { + if (getStyle() == ImageShapeStyle.SHAPE_CIRCLE) { final float circleRadius = Math.min(dest.width(), dest.height()) / 2f - strokeWidth / 2; canvas.drawCircle(dest.centerX(), dest.centerY(), circleRadius, mBorderPaint); } else { @@ -324,7 +317,7 @@ public class ShapedImageView extends ImageView { paint.setColor(0xFF000000); paint.setShadowLayer(radius, 0, radius * 1.5f / 2, SHADOW_START_COLOR); final RectF rect = new RectF(radius, radius, size - radius, size - radius); - if (getStyle() == SHAPE_CIRCLE) { + if (getStyle() == ImageShapeStyle.SHAPE_CIRCLE) { canvas.drawOval(rect, paint); paint.setShadowLayer(0, 0, 0, 0); paint.setXfermode(new PorterDuffXfermode(Mode.CLEAR)); @@ -343,11 +336,6 @@ public class ShapedImageView extends ImageView { return Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP && !isInEditMode(); } - @IntDef({SHAPE_CIRCLE, SHAPE_RECTANGLE}) - @Retention(RetentionPolicy.SOURCE) - public @interface ShapeStyle { - } - private static class CircularOutlineProvider extends ViewOutlineProviderCompat { boolean drawShadow; @@ -362,7 +350,7 @@ public class ShapedImageView extends ImageView { contentRight = viewWidth - view.getPaddingRight(), contentBottom = viewHeight - view.getPaddingBottom(); final ShapedImageView imageView = (ShapedImageView) view; - if (imageView.getStyle() == SHAPE_CIRCLE) { + if (imageView.getStyle() == ImageShapeStyle.SHAPE_CIRCLE) { final int contentWidth = contentRight - contentLeft, contentHeight = contentBottom - contentTop; final int size = Math.min(contentWidth, contentHeight); diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/iface/IContentAdapter.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/iface/IContentAdapter.kt index 84a58485b..fbd1e4d70 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/iface/IContentAdapter.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/iface/IContentAdapter.kt @@ -23,7 +23,7 @@ import android.support.v4.text.BidiFormatter import com.bumptech.glide.RequestManager import org.mariotaku.twidere.util.AsyncTwitterWrapper import org.mariotaku.twidere.util.UserColorNameManager -import org.mariotaku.twidere.view.ShapedImageView.ShapeStyle +import org.mariotaku.twidere.annotation.ImageShapeStyle /** * Created by mariotaku on 15/1/3. @@ -34,7 +34,7 @@ interface IContentAdapter { fun getItemCount(): Int - @ShapeStyle + @ImageShapeStyle val profileImageStyle: Int val profileImageEnabled: Boolean diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/constant/PreferenceKeys.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/constant/PreferenceKeys.kt index 9271025ad..4253ff249 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/constant/PreferenceKeys.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/constant/PreferenceKeys.kt @@ -9,6 +9,7 @@ import org.mariotaku.twidere.BuildConfig import org.mariotaku.twidere.Constants.* import org.mariotaku.twidere.TwidereConstants.KEY_MEDIA_PRELOAD import org.mariotaku.twidere.annotation.AccountType +import org.mariotaku.twidere.annotation.ImageShapeStyle import org.mariotaku.twidere.annotation.PreviewStyle import org.mariotaku.twidere.extension.getNonEmptyString import org.mariotaku.twidere.model.CustomAPIConfig @@ -82,14 +83,14 @@ object themeBackgroundAlphaKey : KSimpleKey(KEY_THEME_BACKGROUND_ALPHA, 0xF } } -object profileImageStyleKey : KSimpleKey(KEY_PROFILE_IMAGE_STYLE, ProfileImageView.SHAPE_CIRCLE) { +object profileImageStyleKey : KSimpleKey(KEY_PROFILE_IMAGE_STYLE, ImageShapeStyle.SHAPE_CIRCLE) { override fun read(preferences: SharedPreferences): Int { - if (preferences.getString(key, null) == VALUE_PROFILE_IMAGE_STYLE_SQUARE) return ProfileImageView.SHAPE_RECTANGLE - return ProfileImageView.SHAPE_CIRCLE + if (preferences.getString(key, null) == VALUE_PROFILE_IMAGE_STYLE_SQUARE) return ImageShapeStyle.SHAPE_RECTANGLE + return ImageShapeStyle.SHAPE_CIRCLE } override fun write(editor: SharedPreferences.Editor, value: Int): Boolean { - editor.putString(key, if (value == ProfileImageView.SHAPE_CIRCLE) VALUE_PROFILE_IMAGE_STYLE_ROUND else VALUE_PROFILE_IMAGE_STYLE_SQUARE) + editor.putString(key, if (value == ImageShapeStyle.SHAPE_CIRCLE) VALUE_PROFILE_IMAGE_STYLE_ROUND else VALUE_PROFILE_IMAGE_STYLE_SQUARE) return true } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/GlideExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/GlideExtensions.kt index e5b90f9f2..c8081c5e7 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/GlideExtensions.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/GlideExtensions.kt @@ -26,29 +26,33 @@ import com.bumptech.glide.RequestManager import com.bumptech.glide.load.engine.DiskCacheStrategy import jp.wasabeef.glide.transformations.CropCircleTransformation import org.mariotaku.twidere.R +import org.mariotaku.twidere.annotation.ImageShapeStyle import org.mariotaku.twidere.extension.model.user import org.mariotaku.twidere.model.* import org.mariotaku.twidere.util.Utils import org.mariotaku.twidere.view.ShapedImageView -fun RequestManager.loadProfileImage(context: Context, url: String?, - @ShapedImageView.ShapeStyle shapeStyle: Int = ShapedImageView.SHAPE_CIRCLE): DrawableRequestBuilder { - val size = context.getString(R.string.profile_image_size) - return configureLoadProfileImage(context, shapeStyle) { load(Utils.getTwitterProfileImageOfSize(url, size)) } +fun RequestManager.loadProfileImage( + context: Context, + url: String?, + @ImageShapeStyle style: Int = ImageShapeStyle.SHAPE_CIRCLE, + size: String = context.getString(R.string.profile_image_size) +): DrawableRequestBuilder { + return configureLoadProfileImage(context, style) { load(Utils.getTwitterProfileImageOfSize(url, size)) } } fun RequestManager.loadProfileImage(context: Context, resourceId: Int, - @ShapedImageView.ShapeStyle shapeStyle: Int = ShapedImageView.SHAPE_CIRCLE): DrawableRequestBuilder { + @ImageShapeStyle shapeStyle: Int = ImageShapeStyle.SHAPE_CIRCLE): DrawableRequestBuilder { return configureLoadProfileImage(context, shapeStyle) { load(resourceId) } } fun RequestManager.loadProfileImage(context: Context, account: AccountDetails, - @ShapedImageView.ShapeStyle shapeStyle: Int = ShapedImageView.SHAPE_CIRCLE): DrawableRequestBuilder { + @ImageShapeStyle shapeStyle: Int = ImageShapeStyle.SHAPE_CIRCLE): DrawableRequestBuilder { return loadProfileImage(context, account.user, shapeStyle) } fun RequestManager.loadProfileImage(context: Context, user: ParcelableUser, - @ShapedImageView.ShapeStyle shapeStyle: Int = ShapedImageView.SHAPE_CIRCLE): DrawableRequestBuilder { + @ImageShapeStyle shapeStyle: Int = ImageShapeStyle.SHAPE_CIRCLE): DrawableRequestBuilder { if (user.extras != null && user.extras.profile_image_url_fallback == null) { // No fallback image, use compatible logic return loadProfileImage(context, user.profile_image_url) @@ -57,17 +61,17 @@ fun RequestManager.loadProfileImage(context: Context, user: ParcelableUser, } fun RequestManager.loadProfileImage(context: Context, userList: ParcelableUserList, - @ShapedImageView.ShapeStyle shapeStyle: Int = ShapedImageView.SHAPE_CIRCLE): DrawableRequestBuilder { + @ImageShapeStyle shapeStyle: Int = ImageShapeStyle.SHAPE_CIRCLE): DrawableRequestBuilder { return configureLoadProfileImage(context, shapeStyle) { load(userList.user_profile_image_url) } } fun RequestManager.loadProfileImage(context: Context, group: ParcelableGroup, - @ShapedImageView.ShapeStyle shapeStyle: Int = ShapedImageView.SHAPE_CIRCLE): DrawableRequestBuilder { + @ImageShapeStyle shapeStyle: Int = ImageShapeStyle.SHAPE_CIRCLE): DrawableRequestBuilder { return configureLoadProfileImage(context, shapeStyle) { load(group.homepage_logo) } } fun RequestManager.loadProfileImage(context: Context, status: ParcelableStatus, - @ShapedImageView.ShapeStyle shapeStyle: Int = ShapedImageView.SHAPE_CIRCLE): DrawableRequestBuilder { + @ImageShapeStyle shapeStyle: Int = ImageShapeStyle.SHAPE_CIRCLE): DrawableRequestBuilder { if (status.extras != null && status.extras.user_profile_image_url_fallback == null) { // No fallback image, use compatible logic return loadProfileImage(context, status.user_profile_image_url) @@ -89,6 +93,13 @@ fun RequestManager.loadProfileImage(context: Context, conversation: ParcelableMe } } +fun RequestManager.loadOriginalProfileImage(context: Context, user: ParcelableUser, + @ImageShapeStyle shapeStyle: Int = ImageShapeStyle.SHAPE_CIRCLE): DrawableRequestBuilder { + val original = user.extras.profile_image_url_original?.takeIf(String::isEmpty) + ?: Utils.getOriginalTwitterProfileImage(user.profile_image_url) + return configureLoadProfileImage(context, shapeStyle) { load(original) } +} + internal inline fun configureLoadProfileImage(context: Context, shapeStyle: Int, create: () -> DrawableTypeRequest): DrawableRequestBuilder { val builder = create() @@ -96,10 +107,10 @@ internal inline fun configureLoadProfileImage(context: Context, shapeStyle: builder.dontAnimate() if (!ShapedImageView.OUTLINE_DRAW) { when (shapeStyle) { - ShapedImageView.SHAPE_CIRCLE -> { + ImageShapeStyle.SHAPE_CIRCLE -> { builder.bitmapTransform(CropCircleTransformation(context)) } - ShapedImageView.SHAPE_RECTANGLE -> { + ImageShapeStyle.SHAPE_RECTANGLE -> { } } } 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 067125b85..ca67a361f 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserFragment.kt @@ -79,7 +79,6 @@ import nl.komponents.kovenant.ui.alwaysUi import nl.komponents.kovenant.ui.failUi import nl.komponents.kovenant.ui.promiseOnUi import nl.komponents.kovenant.ui.successUi -import org.apache.commons.lang3.ObjectUtils import org.mariotaku.chameleon.Chameleon import org.mariotaku.chameleon.ChameleonUtils import org.mariotaku.kpreferences.get @@ -89,7 +88,6 @@ 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 @@ -106,6 +104,8 @@ import org.mariotaku.twidere.constant.lightFontKey import org.mariotaku.twidere.constant.newDocumentApiKey import org.mariotaku.twidere.constant.profileImageStyleKey import org.mariotaku.twidere.extension.applyTheme +import org.mariotaku.twidere.extension.loadOriginalProfileImage +import org.mariotaku.twidere.extension.loadProfileImage import org.mariotaku.twidere.extension.model.applyTo import org.mariotaku.twidere.fragment.AbsStatusesFragment.StatusesFragmentDelegate import org.mariotaku.twidere.fragment.UserTimelineFragment.UserTimelineFragmentDelegate @@ -126,7 +126,7 @@ import org.mariotaku.twidere.model.util.* import org.mariotaku.twidere.provider.TwidereDataStore.CachedRelationships import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers import org.mariotaku.twidere.util.* -import org.mariotaku.twidere.util.InternalTwitterContentUtils.* +import org.mariotaku.twidere.util.InternalTwitterContentUtils.getBestBannerUrl import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback import org.mariotaku.twidere.util.TwidereLinkify.OnLinkClickListener import org.mariotaku.twidere.util.UserColorNameManager.UserColorChangedListener @@ -511,7 +511,6 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener, listedContainer.visibility = if (user.listed_count < 0) View.GONE else View.VISIBLE groupsContainer.visibility = if (groupsCount < 0) View.GONE else View.VISIBLE - mediaLoader.displayOriginalProfileImage(profileImage, user) if (user.color != 0) { setUiColor(user.color) } else if (user.link_color != 0) { @@ -523,7 +522,9 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener, val defWidth = resources.displayMetrics.widthPixels val width = if (bannerWidth > 0) bannerWidth else defWidth val bannerUrl = getBestBannerUrl(ParcelableUserUtils.getProfileBannerUrl(user), width) - Glide.with(this).load(bannerUrl).into(profileBanner) + val requestManager = Glide.with(this) + requestManager.load(bannerUrl).into(profileBanner) + requestManager.loadOriginalProfileImage(context, user, profileImage.style).into(profileImage) val relationship = relationship if (relationship == null) { getFriendship()