diff --git a/app/build.gradle b/app/build.gradle index d876ac5e..1f7d095f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index b2a6050f..06293b52 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -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 *; +} diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt index 2741b3b4..05a51454 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt @@ -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) diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt b/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt index 68d9671f..4a80b00b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt @@ -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 { diff --git a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java index 882db4eb..e26f9e64 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java @@ -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); diff --git a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt index 3c6e3b19..82b23223 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt @@ -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) } diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java index 93a1de5e..f4028e4e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java @@ -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); diff --git a/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.java b/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.java index d799b14e..ec9cfb97 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.java +++ b/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.java @@ -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; } diff --git a/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt b/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt index 411ea10c..a3343a40 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt @@ -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 = + 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) { diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountSelectionAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountSelectionAdapter.kt index 7db9a643..9c3d8f72 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountSelectionAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountSelectionAdapter.kt @@ -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(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) diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountViewHolder.java index 0753466a..a65b4666 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountViewHolder.java @@ -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); diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.java index 8bfc6be1..0fa288fb 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.java @@ -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); diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/ComposeAutoCompleteAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/ComposeAutoCompleteAdapter.java index 1a786c4b..3f8f296d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/ComposeAutoCompleteAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/ComposeAutoCompleteAdapter.java @@ -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); } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/EmojiAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/EmojiAdapter.kt index cc869048..ea401ec1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/EmojiAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/EmojiAdapter.kt @@ -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, private val onEmojiSelectedListener: OnEmojiSelectedListener) : RecyclerView.Adapter() { private val emojiList : List @@ -42,7 +42,7 @@ class EmojiAdapter(emojiList: List, 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) diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.java index b115dd72..8cb5b729 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.java @@ -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); diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java index eeee26a5..49c235e6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java @@ -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); diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java index f488216a..d554e60e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java @@ -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); diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java index 627ab76b..53cd96aa 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java @@ -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]); } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusDetailedViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusDetailedViewHolder.java index adf94d57..dcfd8bc4 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusDetailedViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusDetailedViewHolder.java @@ -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); diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusViewHolder.java index 72742057..52455efc 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusViewHolder.java @@ -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); diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java index 8a72075b..985b51eb 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java @@ -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); diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/AccountMediaFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/AccountMediaFragment.kt index 0c04ec2a..e6fb7a66 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/AccountMediaFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/AccountMediaFragment.kt @@ -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>?, 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>, response: Response>) { 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) } diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt index 8f15caaf..dce1ff91 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt @@ -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(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 { + override fun onLoadFailed(e: GlideException?, model: Any?, target: Target?, 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?, 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() } diff --git a/app/src/main/java/com/keylesspalace/tusky/service/AccountChooserService.kt b/app/src/main/java/com/keylesspalace/tusky/service/AccountChooserService.kt index 3463d43c..b97ca3ee 100644 --- a/app/src/main/java/com/keylesspalace/tusky/service/AccountChooserService.kt +++ b/app/src/main/java/com/keylesspalace/tusky/service/AccountChooserService.kt @@ -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) diff --git a/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.java b/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.java index 2cf51ab9..9152b61a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.java @@ -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 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 getTarget(){ + return new CustomTarget() { + @Override + public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition 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) {} } } diff --git a/app/src/main/java/com/keylesspalace/tusky/util/FocalPointUtil.kt b/app/src/main/java/com/keylesspalace/tusky/util/FocalPointUtil.kt index 43fc51ec..6f2542b5 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/FocalPointUtil.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/FocalPointUtil.kt @@ -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 } } diff --git a/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java b/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java index d14c8e89..84c67dd3 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java @@ -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 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); } diff --git a/app/src/main/java/com/keylesspalace/tusky/view/MediaPreviewImageView.kt b/app/src/main/java/com/keylesspalace/tusky/view/MediaPreviewImageView.kt index 9f55112e..cdff3225 100644 --- a/app/src/main/java/com/keylesspalace/tusky/view/MediaPreviewImageView.kt +++ b/app/src/main/java/com/keylesspalace/tusky/view/MediaPreviewImageView.kt @@ -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 { 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?, 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?, 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. diff --git a/app/src/main/java/com/keylesspalace/tusky/view/RoundedTransformation.java b/app/src/main/java/com/keylesspalace/tusky/view/RoundedTransformation.java deleted file mode 100644 index 0c992bb7..00000000 --- a/app/src/main/java/com/keylesspalace/tusky/view/RoundedTransformation.java +++ /dev/null @@ -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 . */ - -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+"%"; - } -} diff --git a/app/src/main/res/layout/activity_license.xml b/app/src/main/res/layout/activity_license.xml index 0dc11406..acc6b19b 100644 --- a/app/src/main/res/layout/activity_license.xml +++ b/app/src/main/res/layout/activity_license.xml @@ -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" /> + + \ No newline at end of file diff --git a/app/src/test/java/com/keylesspalace/tusky/FakeTuskyApplication.kt b/app/src/test/java/com/keylesspalace/tusky/FakeTuskyApplication.kt index 30c40d5a..c99effea 100644 --- a/app/src/test/java/com/keylesspalace/tusky/FakeTuskyApplication.kt +++ b/app/src/test/java/com/keylesspalace/tusky/FakeTuskyApplication.kt @@ -16,10 +16,6 @@ class FakeTuskyApplication : TuskyApplication() { // No-op } - override fun initPicasso() { - // No-op - } - override fun getServiceLocator(): ServiceLocator { return locator }