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 db51c23717
commit 76ce28980c
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:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0' implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2: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:okhttp:3.14.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.14.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.14.0'
implementation 'org.conscrypt:conscrypt-android:2.1.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.connyduck:sparkbutton:2.0.0'
implementation 'com.github.chrisbanes:PhotoView:2.3.0' implementation 'com.github.chrisbanes:PhotoView:2.3.0'
implementation 'com.mikepenz:google-material-typeface:3.0.1.3.original@aar' 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-android-archcomponents:1.2.0'
implementation 'com.uber.autodispose:autodispose-ktx:1.2.0' implementation 'com.uber.autodispose:autodispose-ktx:1.2.0'
implementation 'androidx.paging:paging-runtime-ktx:2.1.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 org.codehaus.mojo.animal_sniffer.*
-dontwarn okhttp3.internal.platform.ConscryptPlatform -dontwarn okhttp3.internal.platform.ConscryptPlatform
## for picasso
-dontwarn com.squareup.okhttp.**
##for keep ##for keep
-dontwarn android.arch.util.paging.CountedDataSource -dontwarn android.arch.util.paging.CountedDataSource
-dontwarn android.arch.persistence.room.paging.LimitOffsetDataSource -dontwarn android.arch.persistence.room.paging.LimitOffsetDataSource
@ -100,3 +97,11 @@
# work around a bug in proguard # work around a bug in proguard
# see https://sourceforge.net/p/proguard/bugs/729/ # see https://sourceforge.net/p/proguard/bugs/729/
-keepnames public interface com.uber.autodispose.lifecycle.CorrespondingEventsFunction { *; } -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.Observer
import androidx.lifecycle.ViewModelProviders import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.CollapsingToolbarLayout import com.google.android.material.appbar.CollapsingToolbarLayout
import com.google.android.material.floatingactionbutton.FloatingActionButton 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.pager.AccountPagerAdapter
import com.keylesspalace.tusky.util.* import com.keylesspalace.tusky.util.*
import com.keylesspalace.tusky.viewmodel.AccountViewModel import com.keylesspalace.tusky.viewmodel.AccountViewModel
import com.squareup.picasso.Picasso
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector import dagger.android.support.HasSupportFragmentInjector
@ -321,13 +321,12 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
accountLockedImageView.visible(account.locked) accountLockedImageView.visible(account.locked)
accountBadgeTextView.visible(account.bot) accountBadgeTextView.visible(account.bot)
Picasso.with(this) Glide.with(this)
.load(account.avatar) .load(account.avatar)
.placeholder(R.drawable.avatar_default) .placeholder(R.drawable.avatar_default)
.into(accountAvatarImageView) .into(accountAvatarImageView)
Picasso.with(this) Glide.with(this)
.load(account.header) .load(account.header)
.fit() // prevents crash with large header images
.centerCrop() .centerCrop()
.into(accountHeaderImageView) .into(accountHeaderImageView)
@ -357,7 +356,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
accountMovedDisplayName.text = movedAccount.name accountMovedDisplayName.text = movedAccount.name
accountMovedUsername.text = getString(R.string.status_username_format, movedAccount.username) accountMovedUsername.text = getString(R.string.status_username_format, movedAccount.username)
Picasso.with(this) Glide.with(this)
.load(movedAccount.avatar) .load(movedAccount.avatar)
.placeholder(R.drawable.avatar_default) .placeholder(R.drawable.avatar_default)
.into(accountMovedAvatar) .into(accountMovedAvatar)

View File

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

View File

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

View File

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

View File

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

View File

@ -26,14 +26,12 @@ import android.preference.PreferenceManager;
import androidx.emoji.text.EmojiCompat; import androidx.emoji.text.EmojiCompat;
import com.evernote.android.job.JobManager; import com.evernote.android.job.JobManager;
import com.jakewharton.picasso.OkHttp3Downloader;
import com.keylesspalace.tusky.db.AccountManager; import com.keylesspalace.tusky.db.AccountManager;
import com.keylesspalace.tusky.db.AppDatabase; import com.keylesspalace.tusky.db.AppDatabase;
import com.keylesspalace.tusky.di.AppInjector; import com.keylesspalace.tusky.di.AppInjector;
import com.keylesspalace.tusky.util.EmojiCompatFont; import com.keylesspalace.tusky.util.EmojiCompatFont;
import com.keylesspalace.tusky.util.LocaleManager; import com.keylesspalace.tusky.util.LocaleManager;
import com.keylesspalace.tusky.util.NotificationPullJobCreator; import com.keylesspalace.tusky.util.NotificationPullJobCreator;
import com.squareup.picasso.Picasso;
import org.conscrypt.Conscrypt; import org.conscrypt.Conscrypt;
@ -98,7 +96,6 @@ public class TuskyApplication extends Application implements HasActivityInjector
}; };
initAppInjector(); initAppInjector();
initPicasso();
initEmojiCompat(); initEmojiCompat();
JobManager.create(this).addJobCreator(notificationPullJobCreator); JobManager.create(this).addJobCreator(notificationPullJobCreator);
@ -142,17 +139,6 @@ public class TuskyApplication extends Application implements HasActivityInjector
AppInjector.INSTANCE.init(this); 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() { public ServiceLocator getServiceLocator() {
return serviceLocator; return serviceLocator;
} }

View File

@ -26,7 +26,6 @@ import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.Drawable
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@ -39,6 +38,9 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import android.widget.Toast 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.BuildConfig.APPLICATION_ID
import com.keylesspalace.tusky.entity.Attachment import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.fragment.ViewImageFragment 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.CollectionUtil.map
import com.keylesspalace.tusky.util.getTemporaryMediaFilename import com.keylesspalace.tusky.util.getTemporaryMediaFilename
import com.keylesspalace.tusky.viewdata.AttachmentViewData import com.keylesspalace.tusky.viewdata.AttachmentViewData
import com.squareup.picasso.Picasso import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider
import com.squareup.picasso.Target 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.* import kotlinx.android.synthetic.main.activity_view_media.*
@ -110,20 +115,21 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
attachments = intent.getParcelableArrayListExtra(EXTRA_ATTACHMENTS) attachments = intent.getParcelableArrayListExtra(EXTRA_ATTACHMENTS)
val initialPosition = intent.getIntExtra(EXTRA_ATTACHMENT_INDEX, 0) val initialPosition = intent.getIntExtra(EXTRA_ATTACHMENT_INDEX, 0)
val adapter = if(attachments != null) { val adapter = if (attachments != null) {
val realAttachs = map(attachments, AttachmentViewData::attachment) val realAttachs = map(attachments, AttachmentViewData::attachment)
// Setup the view pager. // Setup the view pager.
ImagePagerAdapter(supportFragmentManager, realAttachs, initialPosition) ImagePagerAdapter(supportFragmentManager, realAttachs, initialPosition)
} else { } 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) AvatarImagePagerAdapter(supportFragmentManager, avatarUrl)
} }
viewPager.adapter = adapter viewPager.adapter = adapter
viewPager.currentItem = initialPosition viewPager.currentItem = initialPosition
viewPager.addOnPageChangeListener(object: ViewPager.SimpleOnPageChangeListener() { viewPager.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() {
override fun onPageSelected(position: Int) { override fun onPageSelected(position: Int) {
toolbar.title = adapter.getPageTitle(position) toolbar.title = adapter.getPageTitle(position)
} }
@ -153,13 +159,18 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
if(attachments != null) { if (attachments != null) {
menuInflater.inflate(R.menu.view_media_toolbar, menu) menuInflater.inflate(R.menu.view_media_toolbar, menu)
return true return true
} }
return false return false
} }
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
menu?.findItem(R.id.action_share_media)?.isEnabled = !isCreating
return true
}
override fun onBringUp() { override fun onBringUp() {
supportStartPostponedEnterTransition() supportStartPostponedEnterTransition()
} }
@ -173,11 +184,19 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
for (listener in toolbarVisibilityListeners) { for (listener in toolbarVisibilityListeners) {
listener.onToolbarVisiblityChanged(toolbarVisible) listener.onToolbarVisiblityChanged(toolbarVisible)
} }
val visibility = if(toolbarVisible){ View.VISIBLE } else { View.INVISIBLE } val visibility = if (toolbarVisible) {
val alpha = if(toolbarVisible){ 1.0f } else { 0.0f } View.VISIBLE
} else {
View.INVISIBLE
}
val alpha = if (toolbarVisible) {
1.0f
} else {
0.0f
}
toolbar.animate().alpha(alpha) toolbar.animate().alpha(alpha)
.setListener(object: AnimatorListenerAdapter() { .setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) { override fun onAnimationEnd(animation: Animator) {
toolbar.visibility = visibility toolbar.visibility = visibility
animation.removeListener(this) animation.removeListener(this)
@ -226,7 +245,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
} }
val attachment = attachments!![viewPager.currentItem].attachment val attachment = attachments!![viewPager.currentItem].attachment
when(attachment.type) { when (attachment.type) {
Attachment.Type.IMAGE -> shareImage(directory, attachment.url) Attachment.Type.IMAGE -> shareImage(directory, attachment.url)
Attachment.Type.VIDEO, Attachment.Type.VIDEO,
Attachment.Type.GIFV -> shareVideo(directory, attachment.url) 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) { private fun shareImage(directory: File, url: String) {
isCreating = true
progressBarShare.visibility = View.VISIBLE
invalidateOptionsMenu()
val file = File(directory, getTemporaryMediaFilename("png")) 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 { .subscribeOn(Schedulers.io())
val stream = FileOutputStream(file) .observeOn(AndroidSchedulers.mainThread())
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream) .doOnDispose {
stream.close() futureTask.cancel(true)
} catch (fnfe: FileNotFoundException) {
Log.e(TAG, "Error writing temporary media.")
} catch (ioe: IOException) {
Log.e(TAG, "Error writing temporary media.")
} }
} .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) { private fun shareVideo(directory: File, url: String) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,21 +18,24 @@ package com.keylesspalace.tusky.fragment
import android.animation.Animator import android.animation.Animator
import android.animation.AnimatorListenerAdapter import android.animation.AnimatorListenerAdapter
import android.content.Context import android.content.Context
import android.graphics.drawable.Drawable
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView 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.github.chrisbanes.photoview.PhotoViewAttacher
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.Attachment import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.visible 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.activity_view_media.*
import kotlinx.android.synthetic.main.fragment_view_image.* import kotlinx.android.synthetic.main.fragment_view_image.*
@ -46,7 +49,7 @@ class ViewImageFragment : ViewMediaFragment() {
private lateinit var attacher: PhotoViewAttacher private lateinit var attacher: PhotoViewAttacher
private lateinit var photoActionsListener: PhotoActionsListener private lateinit var photoActionsListener: PhotoActionsListener
private lateinit var toolbar: View private lateinit var toolbar: View
override lateinit var descriptionView : TextView override lateinit var descriptionView: TextView
override fun onAttach(context: Context) { override fun onAttach(context: Context) {
super.onAttach(context) super.onAttach(context)
@ -74,53 +77,7 @@ class ViewImageFragment : ViewMediaFragment() {
result result
} }
val maxW = photoView.context.resources.getInteger(R.integer.media_max_width) loadImageFromNetwork(url, photoView)
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)
}
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
@ -134,7 +91,7 @@ class ViewImageFragment : ViewMediaFragment() {
val arguments = this.arguments!! val arguments = this.arguments!!
val attachment = arguments.getParcelable<Attachment>(ARG_ATTACHMENT) val attachment = arguments.getParcelable<Attachment>(ARG_ATTACHMENT)
val url: String? val url: String?
var description : String? = null var description: String? = null
if (attachment != null) { if (attachment != null) {
url = attachment.url url = attachment.url
@ -169,35 +126,53 @@ class ViewImageFragment : ViewMediaFragment() {
.start() .start()
} }
override fun onDetach() { override fun onDestroyView() {
super.onDetach() Glide.with(this).clear(photoView)
Picasso.with(context).cancelRequest(photoView) super.onDestroyView()
} }
private fun loadImageFromNetwork(url: String, photoView: ImageView) { private fun loadImageFromNetwork(url: String, photoView: ImageView) =
val maxW = photoView.context.resources.getInteger(R.integer.media_max_width) //Request image from the any cache
val maxH = photoView.context.resources.getInteger(R.integer.media_max_height) 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() { private fun completeTransition() {
progressBar?.hide()
attacher.update() attacher.update()
photoActionsListener.onBringUp() photoActionsListener.onBringUp()
} }

View File

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

View File

@ -20,23 +20,26 @@ import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.text.SpannableStringBuilder; import android.text.SpannableStringBuilder;
import android.text.Spanned; import android.text.Spanned;
import android.text.SpannedString; import android.text.SpannedString;
import android.text.style.ReplacementSpan; import android.text.style.ReplacementSpan;
import android.view.View; 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.keylesspalace.tusky.entity.Emoji;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.List; import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class CustomEmojiHelper { public class CustomEmojiHelper {
/** /**
@ -55,13 +58,13 @@ public class CustomEmojiHelper {
CharSequence pattern = new StringBuilder(":").append(emoji.getShortcode()).append(':'); CharSequence pattern = new StringBuilder(":").append(emoji.getShortcode()).append(':');
Matcher matcher = Pattern.compile(pattern.toString()).matcher(text); Matcher matcher = Pattern.compile(pattern.toString()).matcher(text);
while (matcher.find()) { 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); EmojiSpan span = new EmojiSpan(view);
builder.setSpan(span, matcher.start(), matcher.end(), 0); builder.setSpan(span, matcher.start(), matcher.end(), 0);
Picasso.with(view.getContext()) Glide.with(view)
.asBitmap()
.load(emoji.getUrl()) .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 @Nullable Drawable imageDrawable;
private WeakReference<View> viewWeakReference; private WeakReference<View> viewWeakReference;
@ -118,20 +121,23 @@ public class CustomEmojiHelper {
canvas.restore(); canvas.restore();
} }
@Override Target<Bitmap> getTarget(){
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { return new CustomTarget<Bitmap>() {
View view = viewWeakReference.get(); @Override
if(view != null) { public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
imageDrawable = new BitmapDrawable(view.getContext().getResources(), bitmap); View view = viewWeakReference.get();
view.invalidate(); 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 package com.keylesspalace.tusky.util
import android.graphics.Matrix import android.graphics.Matrix
import android.widget.ImageView
import com.keylesspalace.tusky.entity.Attachment.Focus 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 * 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, fun calculateScaling(viewWidth: Float, viewHeight: Float,
imageWidth: Float, imageHeight: Float): Float { imageWidth: Float, imageHeight: Float): Float {
if (isVerticalCrop(viewWidth, viewHeight, imageWidth, imageHeight)) { return if (isVerticalCrop(viewWidth, viewHeight, imageWidth, imageHeight)) {
return viewWidth / imageWidth viewWidth / imageWidth
} else { // horizontal crop: } 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 androidx.core.text.BidiFormatter;
import android.util.Log; 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.JobManager;
import com.evernote.android.job.JobRequest; import com.evernote.android.job.JobRequest;
import com.keylesspalace.tusky.BuildConfig; 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.entity.Status;
import com.keylesspalace.tusky.receiver.NotificationClearBroadcastReceiver; import com.keylesspalace.tusky.receiver.NotificationClearBroadcastReceiver;
import com.keylesspalace.tusky.receiver.SendStatusBroadcastReceiver; import com.keylesspalace.tusky.receiver.SendStatusBroadcastReceiver;
import com.keylesspalace.tusky.view.RoundedTransformation;
import com.squareup.picasso.Picasso;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException;
public class NotificationHelper { public class NotificationHelper {
@ -169,11 +170,14 @@ public class NotificationHelper {
//load the avatar synchronously //load the avatar synchronously
Bitmap accountAvatar; Bitmap accountAvatar;
try { try {
accountAvatar = Picasso.with(context) FutureTarget<Bitmap> target = Glide.with(context)
.asBitmap()
.load(body.getAccount().getAvatar()) .load(body.getAccount().getAvatar())
.transform(new RoundedTransformation(20)) .transform(new RoundedCorners(20))
.get(); .submit();
} catch (IOException e) {
accountAvatar = target.get();
} catch (ExecutionException | InterruptedException e) {
Log.d(TAG, "error loading account avatar", e); Log.d(TAG, "error loading account avatar", e);
accountAvatar = BitmapFactory.decodeResource(context.getResources(), R.drawable.avatar_default); 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.content.Context
import android.graphics.Matrix import android.graphics.Matrix
import android.graphics.drawable.Drawable
import android.util.AttributeSet import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView 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.entity.Attachment
import com.keylesspalace.tusky.util.FocalPointUtil 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 * This is an extension of the standard android ImageView, which makes sure to update the custom
@ -39,7 +43,7 @@ class MediaPreviewImageView
context: Context, context: Context,
attrs: AttributeSet? = null, attrs: AttributeSet? = null,
defStyleAttr: Int = 0 defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr), Callback { ) : AppCompatImageView(context, attrs, defStyleAttr),RequestListener<Drawable> {
private var focus: Attachment.Focus? = null private var focus: Attachment.Focus? = null
private var focalMatrix: Matrix? = null private var focalMatrix: Matrix? = null
@ -94,18 +98,16 @@ defStyleAttr: Int = 0
} }
} }
/** override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
* Called when the image is first succesfully loaded by Picasso, this function makes sure return false
* that the custom matrix of this image is initialized if a focus point is set.
*/
override fun onSuccess() {
onSizeChanged(width, height, width, height)
} }
// We do not handle the error here, instead it will be handled higher up the call chain. override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
override fun onError() { onSizeChanged(width, height, width, height)
return false
} }
/** /**
* Called when the size of the view changes, it calls the FocalPointUtil to update the * 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. * 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_marginStart="12dp"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
license:license="@string/license_apache_2" license:license="@string/license_apache_2"
license:link="https://square.github.io/picasso/" license:link="https://bumptech.github.io/glide/"
license:name="Picasso" /> license:name="Glide" />
<com.keylesspalace.tusky.view.LicenseCard <com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -17,4 +17,12 @@
android:background="@color/toolbar_view_media" android:background="@color/toolbar_view_media"
android:theme="@style/AppTheme.Account.AppBarLayout"/> 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> </FrameLayout>

View File

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