Migrate to Glide (#1175)

* Replace Picasso library with Glide library tuskyapp#1082

* Replace Picasso library with Glide library tuskyapp#1082

* Update load emoji with glide

* Update context used for Glide

* Removed unused import

* Replace deprecated SimpleTarget with CustomTarget

* Fix crash at the view image fragment, remove override image size

* Replace Single.create with Single.fromCallable

* View image fragment refactor

* Fix after merge

* Try to load cached image first and show progress view on failure

* Try to load cached image first and show progress view on failure
This commit is contained in:
pandasoft0 2019-04-16 22:39:12 +03:00 committed by Konrad Pozniak
parent cea879e841
commit f7b577dfd1
32 changed files with 260 additions and 322 deletions

View File

@ -99,11 +99,9 @@ dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'com.squareup.okhttp3:okhttp:3.14.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.14.0'
implementation 'org.conscrypt:conscrypt-android:2.1.0'
implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
implementation 'com.github.connyduck:sparkbutton:2.0.0'
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
implementation 'com.mikepenz:google-material-typeface:3.0.1.3.original@aar'
@ -144,4 +142,9 @@ dependencies {
implementation 'com.uber.autodispose:autodispose-android-archcomponents:1.2.0'
implementation 'com.uber.autodispose:autodispose-ktx:1.2.0'
implementation 'androidx.paging:paging-runtime-ktx:2.1.0'
//Glide
implementation 'com.github.bumptech.glide:glide:4.9.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
implementation 'com.github.bumptech.glide:okhttp3-integration:4.9.0'
}

View File

@ -49,9 +49,6 @@
-dontwarn org.codehaus.mojo.animal_sniffer.*
-dontwarn okhttp3.internal.platform.ConscryptPlatform
## for picasso
-dontwarn com.squareup.okhttp.**
##for keep
-dontwarn android.arch.util.paging.CountedDataSource
-dontwarn android.arch.persistence.room.paging.LimitOffsetDataSource
@ -100,3 +97,11 @@
# work around a bug in proguard
# see https://sourceforge.net/p/proguard/bugs/729/
-keepnames public interface com.uber.autodispose.lifecycle.CorrespondingEventsFunction { *; }
# Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
**[] $VALUES;
public *;
}

View File

@ -37,6 +37,7 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.CollapsingToolbarLayout
import com.google.android.material.floatingactionbutton.FloatingActionButton
@ -52,7 +53,6 @@ import com.keylesspalace.tusky.interfaces.ReselectableFragment
import com.keylesspalace.tusky.pager.AccountPagerAdapter
import com.keylesspalace.tusky.util.*
import com.keylesspalace.tusky.viewmodel.AccountViewModel
import com.squareup.picasso.Picasso
import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector
@ -321,13 +321,12 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
accountLockedImageView.visible(account.locked)
accountBadgeTextView.visible(account.bot)
Picasso.with(this)
Glide.with(this)
.load(account.avatar)
.placeholder(R.drawable.avatar_default)
.into(accountAvatarImageView)
Picasso.with(this)
Glide.with(this)
.load(account.header)
.fit() // prevents crash with large header images
.centerCrop()
.into(accountHeaderImageView)
@ -357,7 +356,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
accountMovedDisplayName.text = movedAccount.name
accountMovedUsername.text = getString(R.string.status_username_format, movedAccount.username)
Picasso.with(this)
Glide.with(this)
.load(movedAccount.avatar)
.placeholder(R.drawable.avatar_default)
.into(accountMovedAvatar)

View File

@ -27,6 +27,8 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.entity.Account
@ -35,7 +37,6 @@ import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show
import com.keylesspalace.tusky.viewmodel.AccountsInListViewModel
import com.keylesspalace.tusky.viewmodel.State
import com.squareup.picasso.Picasso
import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from
import com.uber.autodispose.autoDisposable
import io.reactivex.android.schedulers.AndroidSchedulers
@ -208,9 +209,8 @@ class AccountsInListFragment : DialogFragment(), Injectable {
fun bind(account: Account) {
usernameTextView.text = account.username
displayNameTextView.text = account.displayName
Picasso.with(avatar.context)
Glide.with(this@AccountsInListFragment)
.load(account.avatar)
.fit()
.placeholder(R.drawable.avatar_default)
.into(avatar)
}
@ -255,9 +255,8 @@ class AccountsInListFragment : DialogFragment(), Injectable {
fun bind(account: Account, inAList: Boolean) {
usernameTextView.text = account.username
displayNameTextView.text = account.displayName
Picasso.with(avatar.context)
Glide.with(this@AccountsInListFragment)
.load(account.avatar)
.fit()
.placeholder(R.drawable.avatar_default)
.into(avatar)
rejectButton.apply {

View File

@ -62,6 +62,7 @@ import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.snackbar.Snackbar;
import com.google.gson.Gson;
@ -97,7 +98,6 @@ import com.keylesspalace.tusky.view.ProgressImageView;
import com.keylesspalace.tusky.view.TootButton;
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
import com.mikepenz.iconics.IconicsDrawable;
import com.squareup.picasso.Picasso;
import org.jetbrains.annotations.NotNull;
@ -286,7 +286,7 @@ public final class ComposeActivity
if (TextUtils.isEmpty(activeAccount.getProfilePictureUrl())) {
composeAvatar.setImageResource(R.drawable.avatar_default);
} else {
Picasso.with(this).load(activeAccount.getProfilePictureUrl())
Glide.with(this).load(activeAccount.getProfilePictureUrl())
.error(R.drawable.avatar_default)
.placeholder(R.drawable.avatar_default)
.into(composeAvatar);

View File

@ -34,6 +34,7 @@ import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.ImageView
import com.bumptech.glide.Glide
import com.keylesspalace.tusky.adapter.AccountFieldEditAdapter
import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.di.ViewModelFactory
@ -42,7 +43,6 @@ import com.keylesspalace.tusky.util.*
import com.keylesspalace.tusky.viewmodel.EditProfileViewModel
import com.mikepenz.google_material_typeface_library.GoogleMaterial
import com.mikepenz.iconics.IconicsDrawable
import com.squareup.picasso.Picasso
import com.theartofdev.edmodo.cropper.CropImage
import kotlinx.android.synthetic.main.activity_edit_profile.*
import kotlinx.android.synthetic.main.toolbar_basic.*
@ -133,14 +133,14 @@ class EditProfileActivity : BaseActivity(), Injectable {
addFieldButton.isEnabled = me.source?.fields?.size ?: 0 < MAX_ACCOUNT_FIELDS
if(viewModel.avatarData.value == null) {
Picasso.with(this)
Glide.with(this)
.load(me.avatar)
.placeholder(R.drawable.avatar_default)
.into(avatarPreview)
}
if(viewModel.headerData.value == null) {
Picasso.with(this)
Glide.with(this)
.load(me.header)
.into(headerPreview)
}

View File

@ -26,6 +26,7 @@ import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.bumptech.glide.Glide;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout;
@ -68,7 +69,6 @@ import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem;
import com.mikepenz.materialdrawer.model.interfaces.IProfile;
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader;
import com.mikepenz.materialdrawer.util.DrawerImageLoader;
import com.squareup.picasso.Picasso;
import java.util.ArrayList;
import java.util.List;
@ -334,12 +334,12 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
DrawerImageLoader.init(new AbstractDrawerImageLoader() {
@Override
public void set(ImageView imageView, Uri uri, Drawable placeholder, String tag) {
Picasso.with(imageView.getContext()).load(uri).placeholder(placeholder).into(imageView);
Glide.with(MainActivity.this).load(uri).placeholder(placeholder).into(imageView);
}
@Override
public void cancel(ImageView imageView) {
Picasso.with(imageView.getContext()).cancelRequest(imageView);
Glide.with(MainActivity.this).clear(imageView);
}
});
@ -541,7 +541,7 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
ImageView background = headerResult.getHeaderBackgroundView();
Picasso.with(MainActivity.this)
Glide.with(MainActivity.this)
.load(me.getHeader())
.into(background);

View File

@ -26,14 +26,12 @@ import android.preference.PreferenceManager;
import androidx.emoji.text.EmojiCompat;
import com.evernote.android.job.JobManager;
import com.jakewharton.picasso.OkHttp3Downloader;
import com.keylesspalace.tusky.db.AccountManager;
import com.keylesspalace.tusky.db.AppDatabase;
import com.keylesspalace.tusky.di.AppInjector;
import com.keylesspalace.tusky.util.EmojiCompatFont;
import com.keylesspalace.tusky.util.LocaleManager;
import com.keylesspalace.tusky.util.NotificationPullJobCreator;
import com.squareup.picasso.Picasso;
import org.conscrypt.Conscrypt;
@ -98,7 +96,6 @@ public class TuskyApplication extends Application implements HasActivityInjector
};
initAppInjector();
initPicasso();
initEmojiCompat();
JobManager.create(this).addJobCreator(notificationPullJobCreator);
@ -142,17 +139,6 @@ public class TuskyApplication extends Application implements HasActivityInjector
AppInjector.INSTANCE.init(this);
}
protected void initPicasso() {
// Initialize Picasso configuration
Picasso.Builder builder = new Picasso.Builder(this);
builder.downloader(new OkHttp3Downloader(okHttpClient));
if (BuildConfig.DEBUG) {
builder.listener((picasso, uri, exception) -> exception.printStackTrace());
}
Picasso.setSingletonInstance(builder.build());
}
public ServiceLocator getServiceLocator() {
return serviceLocator;
}

View File

@ -26,7 +26,6 @@ import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build
import android.os.Bundle
@ -39,6 +38,9 @@ import android.view.MenuItem
import android.view.View
import android.webkit.MimeTypeMap
import android.widget.Toast
import androidx.lifecycle.Lifecycle
import com.bumptech.glide.Glide
import com.bumptech.glide.request.FutureTarget
import com.keylesspalace.tusky.BuildConfig.APPLICATION_ID
import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.fragment.ViewImageFragment
@ -48,8 +50,11 @@ import com.keylesspalace.tusky.pager.ImagePagerAdapter
import com.keylesspalace.tusky.util.CollectionUtil.map
import com.keylesspalace.tusky.util.getTemporaryMediaFilename
import com.keylesspalace.tusky.viewdata.AttachmentViewData
import com.squareup.picasso.Picasso
import com.squareup.picasso.Target
import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider
import com.uber.autodispose.autoDisposable
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.activity_view_media.*
@ -110,20 +115,21 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
attachments = intent.getParcelableArrayListExtra(EXTRA_ATTACHMENTS)
val initialPosition = intent.getIntExtra(EXTRA_ATTACHMENT_INDEX, 0)
val adapter = if(attachments != null) {
val adapter = if (attachments != null) {
val realAttachs = map(attachments, AttachmentViewData::attachment)
// Setup the view pager.
ImagePagerAdapter(supportFragmentManager, realAttachs, initialPosition)
} else {
val avatarUrl = intent.getStringExtra(EXTRA_AVATAR_URL) ?: throw IllegalArgumentException("attachment list or avatar url has to be set")
val avatarUrl = intent.getStringExtra(EXTRA_AVATAR_URL)
?: throw IllegalArgumentException("attachment list or avatar url has to be set")
AvatarImagePagerAdapter(supportFragmentManager, avatarUrl)
}
viewPager.adapter = adapter
viewPager.currentItem = initialPosition
viewPager.addOnPageChangeListener(object: ViewPager.SimpleOnPageChangeListener() {
viewPager.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() {
override fun onPageSelected(position: Int) {
toolbar.title = adapter.getPageTitle(position)
}
@ -153,13 +159,18 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
if(attachments != null) {
if (attachments != null) {
menuInflater.inflate(R.menu.view_media_toolbar, menu)
return true
}
return false
}
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
menu?.findItem(R.id.action_share_media)?.isEnabled = !isCreating
return true
}
override fun onBringUp() {
supportStartPostponedEnterTransition()
}
@ -173,11 +184,19 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
for (listener in toolbarVisibilityListeners) {
listener.onToolbarVisiblityChanged(toolbarVisible)
}
val visibility = if(toolbarVisible){ View.VISIBLE } else { View.INVISIBLE }
val alpha = if(toolbarVisible){ 1.0f } else { 0.0f }
val visibility = if (toolbarVisible) {
View.VISIBLE
} else {
View.INVISIBLE
}
val alpha = if (toolbarVisible) {
1.0f
} else {
0.0f
}
toolbar.animate().alpha(alpha)
.setListener(object: AnimatorListenerAdapter() {
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
toolbar.visibility = visibility
animation.removeListener(this)
@ -226,7 +245,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
}
val attachment = attachments!![viewPager.currentItem].attachment
when(attachment.type) {
when (attachment.type) {
Attachment.Type.IMAGE -> shareImage(directory, attachment.url)
Attachment.Type.VIDEO,
Attachment.Type.GIFV -> shareVideo(directory, attachment.url)
@ -243,30 +262,54 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
}
private var isCreating: Boolean = false
private fun shareImage(directory: File, url: String) {
isCreating = true
progressBarShare.visibility = View.VISIBLE
invalidateOptionsMenu()
val file = File(directory, getTemporaryMediaFilename("png"))
val futureTask: FutureTarget<Bitmap> =
Glide.with(applicationContext).asBitmap().load(Uri.parse(url)).submit()
Single.fromCallable {
val bitmap = futureTask.get()
try {
val stream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream)
stream.close()
return@fromCallable true
} catch (fnfe: FileNotFoundException) {
Log.e(TAG, "Error writing temporary media.")
} catch (ioe: IOException) {
Log.e(TAG, "Error writing temporary media.")
}
return@fromCallable false
Picasso.with(applicationContext).load(Uri.parse(url)).into(object: Target {
override fun onBitmapLoaded(bitmap: Bitmap, from: Picasso.LoadedFrom) {
try {
val stream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream)
stream.close()
} catch (fnfe: FileNotFoundException) {
Log.e(TAG, "Error writing temporary media.")
} catch (ioe: IOException) {
Log.e(TAG, "Error writing temporary media.")
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnDispose {
futureTask.cancel(true)
}
}
.autoDisposable(AndroidLifecycleScopeProvider.from(this, Lifecycle.Event.ON_DESTROY))
.subscribe(
{ result ->
Log.d(TAG, "Download image result: $result")
isCreating = false
invalidateOptionsMenu()
progressBarShare.visibility = View.GONE
if (result)
shareFile(file, "image/png")
},
{ error ->
isCreating = false
invalidateOptionsMenu()
progressBarShare.visibility = View.GONE
Log.e(TAG, "Failed to download image", error)
}
)
override fun onBitmapFailed(errorDrawable: Drawable?) {
Log.e(TAG, "Error loading temporary media.")
}
override fun onPrepareLoad(placeHolderDrawable: Drawable?) { }
})
shareFile(file, "image/png")
}
private fun shareVideo(directory: File, url: String) {

View File

@ -21,10 +21,10 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import com.bumptech.glide.Glide
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.db.AccountEntity
import com.keylesspalace.tusky.util.CustomEmojiHelper
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.item_autocomplete_account.view.*
@ -46,7 +46,7 @@ class AccountSelectionAdapter(context: Context): ArrayAdapter<AccountEntity>(con
username.text = account.fullName
displayName.text = CustomEmojiHelper.emojifyString(account.displayName, account.emojis, displayName)
if (!TextUtils.isEmpty(account.profilePictureUrl)) {
Picasso.with(context)
Glide.with(avatar)
.load(account.profilePictureUrl)
.placeholder(R.drawable.avatar_default)
.into(avatar)

View File

@ -8,12 +8,12 @@ import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.entity.Account;
import com.keylesspalace.tusky.interfaces.AccountActionListener;
import com.keylesspalace.tusky.interfaces.LinkListener;
import com.keylesspalace.tusky.util.CustomEmojiHelper;
import com.squareup.picasso.Picasso;
class AccountViewHolder extends RecyclerView.ViewHolder {
private TextView username;
@ -39,8 +39,7 @@ class AccountViewHolder extends RecyclerView.ViewHolder {
username.setText(formattedUsername);
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(account.getName(), account.getEmojis(), displayName);
displayName.setText(emojifiedName);
Context context = avatar.getContext();
Picasso.with(context)
Glide.with(avatar)
.load(account.getAvatar())
.placeholder(R.drawable.avatar_default)
.into(avatar);

View File

@ -24,11 +24,11 @@ import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.entity.Account;
import com.keylesspalace.tusky.interfaces.AccountActionListener;
import com.keylesspalace.tusky.util.CustomEmojiHelper;
import com.squareup.picasso.Picasso;
public class BlocksAdapter extends AccountAdapter {
@ -85,7 +85,7 @@ public class BlocksAdapter extends AccountAdapter {
String format = username.getContext().getString(R.string.status_username_format);
String formattedUsername = String.format(format, account.getUsername());
username.setText(formattedUsername);
Picasso.with(avatar.getContext())
Glide.with(avatar)
.load(account.getAvatar())
.placeholder(R.drawable.avatar_default)
.into(avatar);

View File

@ -25,11 +25,11 @@ import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.entity.Account;
import com.keylesspalace.tusky.entity.Emoji;
import com.keylesspalace.tusky.util.CustomEmojiHelper;
import com.squareup.picasso.Picasso;
import java.util.ArrayList;
import java.util.List;
@ -147,7 +147,7 @@ public class ComposeAutoCompleteAdapter extends BaseAdapter
account.getEmojis(), accountViewHolder.displayName);
accountViewHolder.displayName.setText(emojifiedName);
if (!account.getAvatar().isEmpty()) {
Picasso.with(context)
Glide.with(accountViewHolder.avatar)
.load(account.getAvatar())
.placeholder(R.drawable.avatar_default)
.into(accountViewHolder.avatar);
@ -188,7 +188,7 @@ public class ComposeAutoCompleteAdapter extends BaseAdapter
emoji.getShortcode()
);
emojiViewHolder.shortcode.setText(formattedShortcode);
Picasso.with(context)
Glide.with(emojiViewHolder.preview)
.load(emoji.getUrl())
.into(emojiViewHolder.preview);
}

View File

@ -19,9 +19,9 @@ import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
import com.bumptech.glide.Glide
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.Emoji
import com.squareup.picasso.Picasso
class EmojiAdapter(emojiList: List<Emoji>, private val onEmojiSelectedListener: OnEmojiSelectedListener) : RecyclerView.Adapter<EmojiAdapter.EmojiHolder>() {
private val emojiList : List<Emoji>
@ -42,7 +42,7 @@ class EmojiAdapter(emojiList: List<Emoji>, private val onEmojiSelectedListener:
override fun onBindViewHolder(viewHolder: EmojiAdapter.EmojiHolder, position: Int) {
val emoji = emojiList[position]
Picasso.with(viewHolder.emojiImageView.context)
Glide.with(viewHolder.emojiImageView)
.load(emoji.url)
.into(viewHolder.emojiImageView)

View File

@ -24,11 +24,11 @@ import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.entity.Account;
import com.keylesspalace.tusky.interfaces.AccountActionListener;
import com.keylesspalace.tusky.util.CustomEmojiHelper;
import com.squareup.picasso.Picasso;
public class FollowRequestsAdapter extends AccountAdapter {
@ -87,7 +87,7 @@ public class FollowRequestsAdapter extends AccountAdapter {
String format = username.getContext().getString(R.string.status_username_format);
String formattedUsername = String.format(format, account.getUsername());
username.setText(formattedUsername);
Picasso.with(avatar.getContext())
Glide.with(avatar)
.load(account.getAvatar())
.placeholder(R.drawable.avatar_default)
.into(avatar);

View File

@ -9,11 +9,11 @@ import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.entity.Account;
import com.keylesspalace.tusky.interfaces.AccountActionListener;
import com.keylesspalace.tusky.util.CustomEmojiHelper;
import com.squareup.picasso.Picasso;
public class MutesAdapter extends AccountAdapter {
@ -71,7 +71,7 @@ public class MutesAdapter extends AccountAdapter {
String format = username.getContext().getString(R.string.status_username_format);
String formattedUsername = String.format(format, account.getUsername());
username.setText(formattedUsername);
Picasso.with(avatar.getContext())
Glide.with(avatar)
.load(account.getAvatar())
.placeholder(R.drawable.avatar_default)
.into(avatar);

View File

@ -33,6 +33,7 @@ import android.widget.ImageView;
import android.widget.TextView;
import android.widget.ToggleButton;
import com.bumptech.glide.Glide;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.entity.Account;
import com.keylesspalace.tusky.entity.Emoji;
@ -46,7 +47,6 @@ import com.keylesspalace.tusky.util.SmartLengthInputFilter;
import com.keylesspalace.tusky.viewdata.NotificationViewData;
import com.keylesspalace.tusky.viewdata.StatusViewData;
import com.mikepenz.iconics.utils.Utils;
import com.squareup.picasso.Picasso;
import java.text.SimpleDateFormat;
import java.util.Date;
@ -309,9 +309,8 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
if (TextUtils.isEmpty(account.getAvatar())) {
avatar.setImageResource(R.drawable.avatar_default);
} else {
Picasso.with(context)
Glide.with(avatar)
.load(account.getAvatar())
.fit()
.placeholder(R.drawable.avatar_default)
.into(avatar);
}
@ -487,12 +486,11 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
}
void setAvatars(@Nullable String statusAvatarUrl, @Nullable String notificationAvatarUrl) {
Context context = statusAvatar.getContext();
if (TextUtils.isEmpty(statusAvatarUrl)) {
statusAvatar.setImageResource(R.drawable.avatar_default);
} else {
Picasso.with(context)
Glide.with(statusAvatar)
.load(statusAvatarUrl)
.placeholder(R.drawable.avatar_default)
.into(statusAvatar);
@ -501,7 +499,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
if (TextUtils.isEmpty(notificationAvatarUrl)) {
notificationAvatar.setImageResource(R.drawable.avatar_default);
} else {
Picasso.with(context)
Glide.with(notificationAvatar)
.load(notificationAvatarUrl)
.placeholder(R.drawable.avatar_default)
.into(notificationAvatar);

View File

@ -12,6 +12,7 @@ import android.widget.ImageView;
import android.widget.TextView;
import android.widget.ToggleButton;
import com.bumptech.glide.Glide;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.entity.Attachment;
import com.keylesspalace.tusky.entity.Attachment.Focus;
@ -27,7 +28,6 @@ import com.keylesspalace.tusky.util.ThemeUtils;
import com.keylesspalace.tusky.view.MediaPreviewImageView;
import com.keylesspalace.tusky.viewdata.StatusViewData;
import com.mikepenz.iconics.utils.Utils;
import com.squareup.picasso.Picasso;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
@ -182,7 +182,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
if (TextUtils.isEmpty(url)) {
avatar.setImageResource(R.drawable.avatar_default);
} else {
Picasso.with(avatar.getContext())
Glide.with(avatar)
.load(url)
.placeholder(R.drawable.avatar_default)
.into(avatar);
@ -320,9 +320,6 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
final int n = Math.min(attachments.size(), Status.MAX_MEDIA_ATTACHMENTS);
final int maxW = context.getResources().getInteger(R.integer.media_max_width);
final int maxH = context.getResources().getInteger(R.integer.media_max_height);
for (int i = 0; i < n; i++) {
String previewUrl = attachments.get(i).getPreviewUrl();
String description = attachments.get(i).getDescription();
@ -336,10 +333,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
mediaPreviews[i].setVisibility(View.VISIBLE);
if (TextUtils.isEmpty(previewUrl)) {
Picasso.with(context)
Glide.with(mediaPreviews[i])
.load(mediaPreviewUnloadedId)
.resize(maxW, maxH)
.onlyScaleDown()
.centerInside()
.into(mediaPreviews[i]);
} else {
@ -349,23 +344,18 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
if (focus != null) { // If there is a focal point for this attachment:
mediaPreviews[i].setFocalPoint(focus);
Picasso.with(context)
Glide.with(mediaPreviews[i])
.load(previewUrl)
.placeholder(mediaPreviewUnloadedId)
.resize(maxW, maxH)
.onlyScaleDown()
.centerInside()
// Also pass the mediaPreview as a callback to ensure it is called
// initially when the image gets loaded:
.into(mediaPreviews[i], mediaPreviews[i]);
.addListener(mediaPreviews[i])
.into(mediaPreviews[i]);
} else {
mediaPreviews[i].removeFocalPoint();
Picasso.with(context)
Glide.with(mediaPreviews[i])
.load(previewUrl)
.placeholder(mediaPreviewUnloadedId)
.resize(maxW, maxH)
.onlyScaleDown()
.centerInside()
.into(mediaPreviews[i]);
}

View File

@ -16,6 +16,7 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.entity.Card;
import com.keylesspalace.tusky.entity.Status;
@ -23,7 +24,6 @@ import com.keylesspalace.tusky.interfaces.StatusActionListener;
import com.keylesspalace.tusky.util.CustomURLSpan;
import com.keylesspalace.tusky.util.LinkHelper;
import com.keylesspalace.tusky.viewdata.StatusViewData;
import com.squareup.picasso.Picasso;
import java.text.DateFormat;
import java.util.Date;
@ -176,9 +176,8 @@ class StatusDetailedViewHolder extends StatusBaseViewHolder {
cardView.setClipToOutline(true);
Picasso.with(cardImage.getContext())
Glide.with(cardImage)
.load(card.getImage())
.fit()
.centerCrop()
.into(cardImage);

View File

@ -19,15 +19,14 @@ import android.content.Context;
import android.text.InputFilter;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.ToggleButton;
import com.bumptech.glide.Glide;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.interfaces.StatusActionListener;
import com.keylesspalace.tusky.util.SmartLengthInputFilter;
import com.keylesspalace.tusky.viewdata.StatusViewData;
import com.squareup.picasso.Picasso;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
@ -55,7 +54,7 @@ public class StatusViewHolder extends StatusBaseViewHolder {
int padding = Utils.dpToPx(context, 12);
avatar.setPaddingRelative(0, 0, padding, padding);
avatarInset.setVisibility(View.VISIBLE);
Picasso.with(context)
Glide.with(context)
.load(rebloggedUrl)
.placeholder(R.drawable.avatar_default)
.into(avatarInset);

View File

@ -23,12 +23,12 @@ import android.widget.ImageView;
import android.widget.TextView;
import android.widget.ToggleButton;
import com.bumptech.glide.Glide;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.adapter.StatusBaseViewHolder;
import com.keylesspalace.tusky.entity.Attachment;
import com.keylesspalace.tusky.interfaces.StatusActionListener;
import com.keylesspalace.tusky.util.SmartLengthInputFilter;
import com.squareup.picasso.Picasso;
import java.util.List;
@ -122,7 +122,7 @@ public class ConversationViewHolder extends StatusBaseViewHolder {
for(int i=0; i < avatars.length; i++) {
ImageView avatarView = avatars[i];
if(i < accounts.size()) {
Picasso.with(avatarView.getContext())
Glide.with(avatarView)
.load(accounts.get(i).getAvatar())
.into(avatarView);
avatarView.setVisibility(View.VISIBLE);

View File

@ -27,6 +27,8 @@ import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.ViewMediaActivity
import com.keylesspalace.tusky.di.Injectable
@ -38,7 +40,6 @@ import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show
import com.keylesspalace.tusky.view.SquareImageView
import com.keylesspalace.tusky.viewdata.AttachmentViewData
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.fragment_timeline.*
import retrofit2.Call
import retrofit2.Callback
@ -82,7 +83,7 @@ class AccountMediaFragment : BaseFragment(), Injectable {
override fun onFailure(call: Call<List<Status>>?, t: Throwable?) {
fetchingStatus = FetchingStatus.NOT_FETCHING
if(isAdded) {
if (isAdded) {
swipeRefreshLayout.isRefreshing = false
progressBar.visibility = View.GONE
statusView.show()
@ -102,7 +103,7 @@ class AccountMediaFragment : BaseFragment(), Injectable {
override fun onResponse(call: Call<List<Status>>, response: Response<List<Status>>) {
fetchingStatus = FetchingStatus.NOT_FETCHING
if(isAdded) {
if (isAdded) {
swipeRefreshLayout.isRefreshing = false
progressBar.visibility = View.GONE
@ -302,13 +303,8 @@ class AccountMediaFragment : BaseFragment(), Injectable {
holder.imageView.setBackgroundColor(Color.HSVToColor(itemBgBaseHSV))
val item = items[position]
val maxW = holder.imageView.context.resources.getInteger(R.integer.media_max_width)
val maxH = holder.imageView.context.resources.getInteger(R.integer.media_max_height)
Picasso.with(holder.imageView.context)
Glide.with(holder.imageView)
.load(item.attachment.previewUrl)
.resize(maxW, maxH)
.onlyScaleDown()
.centerInside()
.into(holder.imageView)
}

View File

@ -18,21 +18,24 @@ package com.keylesspalace.tusky.fragment
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.content.Context
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.github.chrisbanes.photoview.PhotoViewAttacher
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.visible
import com.squareup.picasso.Callback
import com.squareup.picasso.NetworkPolicy
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.activity_view_media.*
import kotlinx.android.synthetic.main.fragment_view_image.*
@ -46,7 +49,7 @@ class ViewImageFragment : ViewMediaFragment() {
private lateinit var attacher: PhotoViewAttacher
private lateinit var photoActionsListener: PhotoActionsListener
private lateinit var toolbar: View
override lateinit var descriptionView : TextView
override lateinit var descriptionView: TextView
override fun onAttach(context: Context) {
super.onAttach(context)
@ -74,53 +77,7 @@ class ViewImageFragment : ViewMediaFragment() {
result
}
val maxW = photoView.context.resources.getInteger(R.integer.media_max_width)
val maxH = photoView.context.resources.getInteger(R.integer.media_max_height)
// If we are the view to be shown initially...
if (arguments!!.getBoolean(ViewMediaFragment.ARG_START_POSTPONED_TRANSITION)) {
// Try to load image from disk.
Picasso.with(context)
.load(url)
.noFade()
.networkPolicy(NetworkPolicy.OFFLINE)
.resize(maxW, maxH)
.onlyScaleDown()
.centerInside()
.into(photoView, object : Callback {
override fun onSuccess() {
// if we loaded image from disk, we should check that view is attached.
if (photoView?.isAttachedToWindow == true) {
finishLoadingSuccessfully()
} else {
// if view is not attached yet, wait for an attachment and
// start transition when it's finally ready.
photoView?.addOnAttachStateChangeListener(
object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View?) {
finishLoadingSuccessfully()
photoView.removeOnAttachStateChangeListener(this)
}
override fun onViewDetachedFromWindow(v: View?) {}
})
}
}
override fun onError() {
// if there's no image in cache, load from network and start transition
// immediately.
if (isAdded) {
photoActionsListener.onBringUp()
loadImageFromNetwork(url, photoView)
}
}
})
} else {
// if we're not initial page, don't bother.
loadImageFromNetwork(url, photoView)
}
loadImageFromNetwork(url, photoView)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
@ -134,7 +91,7 @@ class ViewImageFragment : ViewMediaFragment() {
val arguments = this.arguments!!
val attachment = arguments.getParcelable<Attachment>(ARG_ATTACHMENT)
val url: String?
var description : String? = null
var description: String? = null
if (attachment != null) {
url = attachment.url
@ -169,35 +126,53 @@ class ViewImageFragment : ViewMediaFragment() {
.start()
}
override fun onDetach() {
super.onDetach()
Picasso.with(context).cancelRequest(photoView)
override fun onDestroyView() {
Glide.with(this).clear(photoView)
super.onDestroyView()
}
private fun loadImageFromNetwork(url: String, photoView: ImageView) {
val maxW = photoView.context.resources.getInteger(R.integer.media_max_width)
val maxH = photoView.context.resources.getInteger(R.integer.media_max_height)
private fun loadImageFromNetwork(url: String, photoView: ImageView) =
//Request image from the any cache
Glide.with(this)
.load(url)
.dontAnimate()
.onlyRetrieveFromCache(true)
.error(
//Request image from the network on fail load image from cache
Glide.with(this)
.load(url)
.centerInside()
.addListener(ImageRequestListener(false))
)
.centerInside()
.addListener(ImageRequestListener(true))
.into(photoView)
Picasso.with(context)
.load(url)
.noPlaceholder()
.networkPolicy(NetworkPolicy.NO_STORE)
.resize(maxW, maxH)
.onlyScaleDown()
.centerInside()
.into(photoView, object : Callback {
override fun onSuccess() {
finishLoadingSuccessfully()
}
override fun onError() {
progressBar?.hide()
}
})
/**
* @param isCacheRequest - is this listener for request image from cache or from the network
*/
private inner class ImageRequestListener(private val isCacheRequest: Boolean) : RequestListener<Drawable> {
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
if (isCacheRequest) //Complete the transition on failed image from cache
completeTransition()
else
progressBar?.hide() //Hide progress bar only on fail request from internet
return false
}
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
progressBar?.hide() //Always hide the progress bar on success
resource?.let {
target?.onResourceReady(resource, null)
if (isCacheRequest) completeTransition() //Complete transition on cache request only, because transition already completed on Network request
return true
}
return false
}
}
private fun finishLoadingSuccessfully() {
progressBar?.hide()
private fun completeTransition() {
attacher.update()
photoActionsListener.onBringUp()
}

View File

@ -23,12 +23,12 @@ import android.os.Bundle
import android.service.chooser.ChooserTarget
import android.service.chooser.ChooserTargetService
import android.text.TextUtils
import com.bumptech.glide.Glide
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.TuskyApplication
import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.util.NotificationHelper
import com.squareup.picasso.Picasso
@TargetApi(23)
@ -48,10 +48,13 @@ class AccountChooserService : ChooserTargetService(), Injectable {
val icon: Icon = if (TextUtils.isEmpty(account.profilePictureUrl)) {
Icon.createWithResource(applicationContext, R.drawable.avatar_default)
} else {
Icon.createWithBitmap(Picasso.with(this).load(account.profilePictureUrl)
val bmp = Glide.with(this)
.asBitmap()
.load(account.profilePictureUrl)
.error(R.drawable.avatar_default)
.placeholder(R.drawable.avatar_default)
.get())
.submit()
Icon.createWithBitmap(bmp.get())
}
val bundle = Bundle()
bundle.putLong(NotificationHelper.ACCOUNT_ID, account.id)

View File

@ -20,23 +20,26 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.SpannedString;
import android.text.style.ReplacementSpan;
import android.view.View;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.target.Target;
import com.bumptech.glide.request.transition.Transition;
import com.keylesspalace.tusky.entity.Emoji;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class CustomEmojiHelper {
/**
@ -55,13 +58,13 @@ public class CustomEmojiHelper {
CharSequence pattern = new StringBuilder(":").append(emoji.getShortcode()).append(':');
Matcher matcher = Pattern.compile(pattern.toString()).matcher(text);
while (matcher.find()) {
// We keep a span as a Picasso target, because Picasso keeps weak reference to
// the target so an anonymous class would likely be garbage collected.
EmojiSpan span = new EmojiSpan(view);
builder.setSpan(span, matcher.start(), matcher.end(), 0);
Picasso.with(view.getContext())
Glide.with(view)
.asBitmap()
.load(emoji.getUrl())
.into(span);
.into(span.getTarget());
}
}
@ -76,7 +79,7 @@ public class CustomEmojiHelper {
}
public static class EmojiSpan extends ReplacementSpan implements Target {
public static class EmojiSpan extends ReplacementSpan {
private @Nullable Drawable imageDrawable;
private WeakReference<View> viewWeakReference;
@ -118,20 +121,23 @@ public class CustomEmojiHelper {
canvas.restore();
}
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
View view = viewWeakReference.get();
if(view != null) {
imageDrawable = new BitmapDrawable(view.getContext().getResources(), bitmap);
view.invalidate();
}
Target<Bitmap> getTarget(){
return new CustomTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
View view = viewWeakReference.get();
if (view != null) {
imageDrawable = new BitmapDrawable(view.getContext().getResources(), resource);
view.invalidate();
}
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
//Do nothing on load cleared
}
};
}
@Override
public void onBitmapFailed(Drawable errorDrawable) {}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {}
}
}

View File

@ -16,10 +16,8 @@
package com.keylesspalace.tusky.util
import android.graphics.Matrix
import android.widget.ImageView
import com.keylesspalace.tusky.entity.Attachment.Focus
import com.squareup.picasso.Callback
/**
* Calculates the image matrix needed to maintain the correct cropping for image views based on
@ -88,10 +86,10 @@ object FocalPointUtil {
*/
fun calculateScaling(viewWidth: Float, viewHeight: Float,
imageWidth: Float, imageHeight: Float): Float {
if (isVerticalCrop(viewWidth, viewHeight, imageWidth, imageHeight)) {
return viewWidth / imageWidth
return if (isVerticalCrop(viewWidth, viewHeight, imageWidth, imageHeight)) {
viewWidth / imageWidth
} else { // horizontal crop:
return viewHeight / imageHeight
viewHeight / imageHeight
}
}

View File

@ -37,6 +37,9 @@ import androidx.core.content.ContextCompat;
import androidx.core.text.BidiFormatter;
import android.util.Log;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.FutureTarget;
import com.evernote.android.job.JobManager;
import com.evernote.android.job.JobRequest;
import com.keylesspalace.tusky.BuildConfig;
@ -48,17 +51,15 @@ import com.keylesspalace.tusky.entity.Notification;
import com.keylesspalace.tusky.entity.Status;
import com.keylesspalace.tusky.receiver.NotificationClearBroadcastReceiver;
import com.keylesspalace.tusky.receiver.SendStatusBroadcastReceiver;
import com.keylesspalace.tusky.view.RoundedTransformation;
import com.squareup.picasso.Picasso;
import org.json.JSONArray;
import org.json.JSONException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class NotificationHelper {
@ -169,11 +170,14 @@ public class NotificationHelper {
//load the avatar synchronously
Bitmap accountAvatar;
try {
accountAvatar = Picasso.with(context)
FutureTarget<Bitmap> target = Glide.with(context)
.asBitmap()
.load(body.getAccount().getAvatar())
.transform(new RoundedTransformation(20))
.get();
} catch (IOException e) {
.transform(new RoundedCorners(20))
.submit();
accountAvatar = target.get();
} catch (ExecutionException | InterruptedException e) {
Log.d(TAG, "error loading account avatar", e);
accountAvatar = BitmapFactory.decodeResource(context.getResources(), R.drawable.avatar_default);
}

View File

@ -16,12 +16,16 @@ package com.keylesspalace.tusky.view
import android.content.Context
import android.graphics.Matrix
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.util.FocalPointUtil
import com.squareup.picasso.Callback
/**
* This is an extension of the standard android ImageView, which makes sure to update the custom
@ -39,7 +43,7 @@ class MediaPreviewImageView
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr), Callback {
) : AppCompatImageView(context, attrs, defStyleAttr),RequestListener<Drawable> {
private var focus: Attachment.Focus? = null
private var focalMatrix: Matrix? = null
@ -94,18 +98,16 @@ defStyleAttr: Int = 0
}
}
/**
* Called when the image is first succesfully loaded by Picasso, this function makes sure
* that the custom matrix of this image is initialized if a focus point is set.
*/
override fun onSuccess() {
onSizeChanged(width, height, width, height)
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
return false
}
// We do not handle the error here, instead it will be handled higher up the call chain.
override fun onError() {
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
onSizeChanged(width, height, width, height)
return false
}
/**
* Called when the size of the view changes, it calls the FocalPointUtil to update the
* matrix if we have a set focal point. It then reassigns the matrix to this imageView.

View File

@ -1,70 +0,0 @@
/* Copyright 2017 Andrew Dawson
*
* This file is a part of Tusky.
*
* 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.
*
* Tusky 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 Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.view;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import com.squareup.picasso.Transformation;
public class RoundedTransformation implements Transformation {
private final float percent;
/** 100% would mean a perfectly round image **/
public RoundedTransformation(final float percent) {
this.percent = percent;
}
@Override
public Bitmap transform(Bitmap source) {
final int width = source.getWidth();
final int height = source.getHeight();
final int shorterSide;
if (width > height) {
shorterSide = height;
} else {
shorterSide = width;
}
final float radius = shorterSide / 2 * percent / 100;
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
canvas.drawRoundRect(new RectF(0, 0, width, height), radius, radius, paint);
if (source != output) {
source.recycle();
}
return output;
}
@Override
public String key() {
return "rounded "+percent+"%";
}
}

View File

@ -89,8 +89,8 @@
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
license:license="@string/license_apache_2"
license:link="https://square.github.io/picasso/"
license:name="Picasso" />
license:link="https://bumptech.github.io/glide/"
license:name="Glide" />
<com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent"

View File

@ -17,4 +17,12 @@
android:background="@color/toolbar_view_media"
android:theme="@style/AppTheme.Account.AppBarLayout"/>
<ProgressBar
android:id="@+id/progressBarShare"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_gravity="center"/>
</FrameLayout>

View File

@ -16,10 +16,6 @@ class FakeTuskyApplication : TuskyApplication() {
// No-op
}
override fun initPicasso() {
// No-op
}
override fun getServiceLocator(): ServiceLocator {
return locator
}