From 580f7ca911fdc92ba42e20a694679ac81b93cf93 Mon Sep 17 00:00:00 2001 From: Fred <76-fgerber@users.noreply.shinice.net> Date: Mon, 22 Jan 2024 22:44:35 +0100 Subject: [PATCH 1/6] Fix profile pic edit See https://github.com/pixelfed/pixelfed/issues/4250 --- .../app/profile/EditProfileActivity.kt | 28 +++++------ .../app/profile/EditProfileViewModel.kt | 49 +++++++++++++------ .../pixeldroid/app/utils/api/PixelfedAPI.kt | 2 +- 3 files changed, 49 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/org/pixeldroid/app/profile/EditProfileActivity.kt b/app/src/main/java/org/pixeldroid/app/profile/EditProfileActivity.kt index 239f3140..8bea0137 100644 --- a/app/src/main/java/org/pixeldroid/app/profile/EditProfileActivity.kt +++ b/app/src/main/java/org/pixeldroid/app/profile/EditProfileActivity.kt @@ -112,18 +112,18 @@ class EditProfileActivity : BaseActivity() { } } -// binding.changeImageButton.setOnClickListener { -// Intent(Intent.ACTION_GET_CONTENT).apply { -// type = "*/*" -// putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/*")) -// action = Intent.ACTION_GET_CONTENT -// addCategory(Intent.CATEGORY_OPENABLE) -// putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false) -// uploadImageResultContract.launch( -// Intent.createChooser(this, null) -// ) -// } -// } + binding.profilePic.setOnClickListener { + Intent(Intent.ACTION_GET_CONTENT).apply { + type = "*/*" + putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/*")) + action = Intent.ACTION_GET_CONTENT + addCategory(Intent.CATEGORY_OPENABLE) + putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false) + uploadImageResultContract.launch( + Intent.createChooser(this, null) + ) + } + } } private val uploadImageResultContract = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> @@ -137,10 +137,10 @@ class EditProfileActivity : BaseActivity() { val imageUri: String = clipData.getItemAt(i).uri.toString() images.add(imageUri) } - model.uploadImage(images.first()) + model.updateImage(images.first()) } else if (data.data != null) { images.add(data.data!!.toString()) - model.uploadImage(images.first()) + model.updateImage(images.first()) } } } diff --git a/app/src/main/java/org/pixeldroid/app/profile/EditProfileViewModel.kt b/app/src/main/java/org/pixeldroid/app/profile/EditProfileViewModel.kt index 33fe8388..a74fb285 100644 --- a/app/src/main/java/org/pixeldroid/app/profile/EditProfileViewModel.kt +++ b/app/src/main/java/org/pixeldroid/app/profile/EditProfileViewModel.kt @@ -97,12 +97,19 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat note = bio, locked = privateAccount, ) + val newAvatarUri = if (profilePictureChanged) { + uploadImage() + profilePictureUri + } else { + account.anyAvatar()?.toUri() + } oldProfile = account _uiState.update { currentUiState -> currentUiState.copy( - bio = account.source?.note ?: account.note?.let {fromHtml(it).toString()}, + bio = account.source?.note + ?: account.note?.let { fromHtml(it).toString() }, name = account.display_name, - profilePictureUri = account.anyAvatar()?.toUri(), + profilePictureUri = newAvatarUri, privateAccount = account.locked, sendingProfile = false, profileSent = true, @@ -153,12 +160,13 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat fun madeChanges(): Boolean = with(uiState.value) { - val bioUnchanged: Boolean = oldProfile?.source?.note?.let { it != bio } - // If source note is null, check note + val privateChanged = oldProfile?.locked != privateAccount + val displayNameChanged = oldProfile?.display_name != name + val bioChanged: Boolean = oldProfile?.source?.note?.let { it != bio } + // If source note is null, check note ?: oldProfile?.note?.let { fromHtml(it).toString() != bio } ?: true - oldProfile?.locked != privateAccount || oldProfile?.display_name != name - || bioUnchanged + profilePictureChanged || privateChanged || displayNameChanged || bioChanged } fun clickedCard() { @@ -178,16 +186,26 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat } } - fun uploadImage(image: String) { - //TODO fix + fun updateImage(image: String) { + _uiState.update { currentUiState -> + currentUiState.copy( + profilePictureUri = image.toUri(), + profilePictureChanged = true + ) + } + } + + private fun uploadImage() { + val image = uiState.value.profilePictureUri!! + val inputStream = - getApplication().contentResolver.openInputStream(image.toUri()) + getApplication().contentResolver.openInputStream(image) ?: return val size: Long = - if (image.toUri().scheme == "content") { + if (image.scheme == "content") { getApplication().contentResolver.query( - image.toUri(), + image, null, null, null, @@ -203,7 +221,7 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat cursor.getLong(sizeIndex) } ?: 0 } else { - image.toUri().toFile().length() + image.toFile().length() } val imagePart = ProgressRequestBody(inputStream, size, "image/*") @@ -232,8 +250,7 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat .observeOn(AndroidSchedulers.mainThread()) .subscribe( { it: Account -> - Log.e("qsdfqsdfs", it.toString()) - + Log.i("ACCOUNT", it.toString()) }, { e: Throwable -> _uiState.update { currentUiState -> @@ -250,6 +267,7 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat { _uiState.update { currentUiState -> currentUiState.copy( + profilePictureChanged = false, uploadProgress = 100, uploadingPicture = false ) @@ -265,7 +283,8 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat data class EditProfileActivityUiState( val name: String? = null, val bio: String? = null, - val profilePictureUri: Uri?= null, + val profilePictureUri: Uri? = null, + val profilePictureChanged: Boolean = false, val privateAccount: Boolean? = null, val loadingProfile: Boolean = true, val profileLoaded: Boolean = false, diff --git a/app/src/main/java/org/pixeldroid/app/utils/api/PixelfedAPI.kt b/app/src/main/java/org/pixeldroid/app/utils/api/PixelfedAPI.kt index e9377dab..6ed43155 100644 --- a/app/src/main/java/org/pixeldroid/app/utils/api/PixelfedAPI.kt +++ b/app/src/main/java/org/pixeldroid/app/utils/api/PixelfedAPI.kt @@ -348,7 +348,7 @@ interface PixelfedAPI { ): Account @Multipart - @PATCH("/api/v1/accounts/update_credentials") + @POST("/api/v1/accounts/update_credentials") fun updateProfilePicture( @Part avatar: MultipartBody.Part? ): Observable From 06478cf8a7ae7ea7674a0352dadef37e6912e3fd Mon Sep 17 00:00:00 2001 From: Fred <76-fgerber@users.noreply.shinice.net> Date: Mon, 22 Jan 2024 22:45:46 +0100 Subject: [PATCH 2/6] Increment version number --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 666b1890..38ce1c3a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,7 +27,7 @@ android { } defaultConfig { minSdkVersion 23 - versionCode 26 + versionCode 27 targetSdkVersion 34 versionName "1.0.beta" + versionCode From 1dcf6059763d8e125f8db0fb9c071f4c0debe41d Mon Sep 17 00:00:00 2001 From: Matthieu <24-artectrex@users.noreply.shinice.net> Date: Tue, 23 Jan 2024 17:27:45 +0100 Subject: [PATCH 3/6] Improve consistency of ViewModel and UI --- .../app/profile/EditProfileActivity.kt | 19 ++++--- .../app/profile/EditProfileViewModel.kt | 55 ++++++++++--------- .../pixeldroid/app/utils/api/PixelfedAPI.kt | 17 +++++- .../pixeldroid/app/utils/db/AppDatabase.kt | 7 ++- .../org/pixeldroid/app/utils/db/DBUtils.kt | 16 ++++-- .../app/utils/db/dao/InstanceDao.kt | 4 ++ .../db/entities/InstanceDatabaseEntity.kt | 28 +++++----- .../pixeldroid/app/utils/di/DatabaseModule.kt | 3 +- 8 files changed, 90 insertions(+), 59 deletions(-) diff --git a/app/src/main/java/org/pixeldroid/app/profile/EditProfileActivity.kt b/app/src/main/java/org/pixeldroid/app/profile/EditProfileActivity.kt index 8bea0137..04dd124f 100644 --- a/app/src/main/java/org/pixeldroid/app/profile/EditProfileActivity.kt +++ b/app/src/main/java/org/pixeldroid/app/profile/EditProfileActivity.kt @@ -58,23 +58,24 @@ class EditProfileActivity : BaseActivity() { lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { model.uiState.collect { uiState -> - if(uiState.profileLoaded){ - binding.bioEditText.setText(uiState.bio) - binding.nameEditText.setText(uiState.name) - model.changesApplied() - } - binding.progressCard.visibility = if(uiState.loadingProfile || uiState.sendingProfile || uiState.profileSent || uiState.error) View.VISIBLE else View.INVISIBLE + if(binding.bioEditText.text.toString() != uiState.bio) binding.bioEditText.setText(uiState.bio) + if(binding.nameEditText.text.toString() != uiState.name) binding.nameEditText.setText(uiState.name) + + binding.progressCard.visibility = if(uiState.loadingProfile || uiState.sendingProfile || uiState.uploadingPicture || uiState.profileSent || uiState.error) View.VISIBLE else View.INVISIBLE + if(uiState.loadingProfile) binding.progressText.setText(R.string.fetching_profile) else if(uiState.sendingProfile) binding.progressText.setText(R.string.saving_profile) + binding.privateSwitch.isChecked = uiState.privateAccount == true Glide.with(binding.profilePic).load(uiState.profilePictureUri) .apply(RequestOptions.circleCropTransform()) .into(binding.profilePic) - binding.savingProgressBar.visibility = if(uiState.error || uiState.profileSent) View.GONE - else View.VISIBLE + binding.savingProgressBar.visibility = + if(uiState.error || (uiState.profileSent && !uiState.uploadingPicture)) View.GONE + else View.VISIBLE - if(uiState.profileSent){ + if(uiState.profileSent && !uiState.uploadingPicture && !uiState.error){ binding.progressText.setText(R.string.profile_saved) binding.done.visibility = View.VISIBLE } else { diff --git a/app/src/main/java/org/pixeldroid/app/profile/EditProfileViewModel.kt b/app/src/main/java/org/pixeldroid/app/profile/EditProfileViewModel.kt index a74fb285..7c9d31b2 100644 --- a/app/src/main/java/org/pixeldroid/app/profile/EditProfileViewModel.kt +++ b/app/src/main/java/org/pixeldroid/app/profile/EditProfileViewModel.kt @@ -23,6 +23,7 @@ import org.pixeldroid.app.postCreation.ProgressRequestBody import org.pixeldroid.app.posts.fromHtml import org.pixeldroid.app.utils.PixelDroidApplication import org.pixeldroid.app.utils.api.objects.Account +import org.pixeldroid.app.utils.db.AppDatabase import org.pixeldroid.app.utils.di.PixelfedAPIHolder import javax.inject.Inject @@ -31,10 +32,13 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat @Inject lateinit var apiHolder: PixelfedAPIHolder + @Inject + lateinit var db: AppDatabase + private val _uiState = MutableStateFlow(EditProfileActivityUiState()) val uiState: StateFlow = _uiState - var oldProfile: Account? = null + private var oldProfile: Account? = null init { (application as PixelDroidApplication).getAppComponent().inject(this) @@ -76,15 +80,10 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat fun sendProfile() { val api = apiHolder.api ?: apiHolder.setToCurrentUser() - val requestBody = - null //MultipartBody.Part.createFormData("avatar", System.currentTimeMillis().toString(), avatarBody) - _uiState.update { currentUiState -> currentUiState.copy( sendingProfile = true, profileSent = false, - loadingProfile = false, - profileLoaded = false, error = false ) } @@ -97,19 +96,16 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat note = bio, locked = privateAccount, ) - val newAvatarUri = if (profilePictureChanged) { - uploadImage() - profilePictureUri - } else { - account.anyAvatar()?.toUri() - } oldProfile = account _uiState.update { currentUiState -> currentUiState.copy( bio = account.source?.note ?: account.note?.let { fromHtml(it).toString() }, name = account.display_name, - profilePictureUri = newAvatarUri, + profilePictureUri = if (profilePictureChanged) profilePictureUri + else account.anyAvatar()?.toUri(), + uploadProgress = 0, + uploadingPicture = profilePictureChanged, privateAccount = account.locked, sendingProfile = false, profileSent = true, @@ -118,14 +114,13 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat error = false ) } + if(profilePictureChanged) uploadImage() } catch (exception: Exception) { Log.e("TAG", exception.toString()) _uiState.update { currentUiState -> currentUiState.copy( sendingProfile = false, profileSent = false, - loadingProfile = false, - profileLoaded = false, error = true ) } @@ -152,12 +147,6 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat } } - fun changesApplied() { - _uiState.update { currentUiState -> - currentUiState.copy(profileLoaded = false) - } - } - fun madeChanges(): Boolean = with(uiState.value) { val privateChanged = oldProfile?.locked != privateAccount @@ -166,6 +155,7 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat // If source note is null, check note ?: oldProfile?.note?.let { fromHtml(it).toString() != bio } ?: true + profilePictureChanged || privateChanged || displayNameChanged || bioChanged } @@ -243,20 +233,31 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat var postSub: Disposable? = null val api = apiHolder.api ?: apiHolder.setToCurrentUser() - val inter = api.updateProfilePicture(requestBody.parts[0]) + + val pixelfed = db.instanceDao().getActiveInstance().pixelfed + + val inter = + if(pixelfed) api.updateProfilePicture(requestBody.parts[0]) + else api.updateProfilePictureMastodon((requestBody.parts[0])) postSub = inter .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( - { it: Account -> - Log.i("ACCOUNT", it.toString()) + /* onNext = */ { account: Account -> + account.anyAvatar()?.let { + _uiState.update { currentUiState -> + currentUiState.copy( + profilePictureUri = it.toUri() + ) + } + } }, - { e: Throwable -> + /* onError = */ { e: Throwable -> _uiState.update { currentUiState -> currentUiState.copy( uploadProgress = 0, - uploadingPicture = true, + uploadingPicture = false, error = true ) } @@ -264,7 +265,7 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat postSub?.dispose() sub.dispose() }, - { + /* onComplete = */ { _uiState.update { currentUiState -> currentUiState.copy( profilePictureChanged = false, diff --git a/app/src/main/java/org/pixeldroid/app/utils/api/PixelfedAPI.kt b/app/src/main/java/org/pixeldroid/app/utils/api/PixelfedAPI.kt index 6ed43155..c67fb1bf 100644 --- a/app/src/main/java/org/pixeldroid/app/utils/api/PixelfedAPI.kt +++ b/app/src/main/java/org/pixeldroid/app/utils/api/PixelfedAPI.kt @@ -338,21 +338,34 @@ interface PixelfedAPI { @Header("Authorization") authorization: String? = null ): Account - //@Multipart @PATCH("/api/v1/accounts/update_credentials") suspend fun updateCredentials( @Query(value = "display_name") displayName: String?, @Query(value = "note") note: String?, @Query(value = "locked") locked: Boolean?, - // @Part avatar: MultipartBody.Part?, ): Account + /** + * Pixelfed uses PHP, multipart uploads don't work through PATCH so we use POST as suggested + * here: https://github.com/pixelfed/pixelfed/issues/4250 + * However, changing to POST breaks the upload on Mastodon. + * + * To have this work on Pixelfed and Mastodon without special logic to distinguish the two, + * we'll have to wait for PHP 8.4 and https://wiki.php.net/rfc/rfc1867-non-post + * which should come out end of 2024 + */ @Multipart @POST("/api/v1/accounts/update_credentials") fun updateProfilePicture( @Part avatar: MultipartBody.Part? ): Observable + @Multipart + @PATCH("/api/v1/accounts/update_credentials") + fun updateProfilePictureMastodon( + @Part avatar: MultipartBody.Part? + ): Observable + @GET("/api/v1/accounts/{id}/statuses") suspend fun accountPosts( @Path("id") account_id: String, diff --git a/app/src/main/java/org/pixeldroid/app/utils/db/AppDatabase.kt b/app/src/main/java/org/pixeldroid/app/utils/db/AppDatabase.kt index 0b76b6ef..3158e18e 100644 --- a/app/src/main/java/org/pixeldroid/app/utils/db/AppDatabase.kt +++ b/app/src/main/java/org/pixeldroid/app/utils/db/AppDatabase.kt @@ -22,7 +22,7 @@ import org.pixeldroid.app.utils.api.objects.Notification PublicFeedStatusDatabaseEntity::class, Notification::class ], - version = 5 + version = 6 ) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { @@ -44,4 +44,9 @@ val MIGRATION_4_5 = object : Migration(4, 5) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("ALTER TABLE instances ADD COLUMN videoEnabled INTEGER NOT NULL DEFAULT 1") } +} +val MIGRATION_5_6 = object : Migration(5, 6) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE instances ADD COLUMN pixelfed INTEGER NOT NULL DEFAULT 1") + } } \ No newline at end of file diff --git a/app/src/main/java/org/pixeldroid/app/utils/db/DBUtils.kt b/app/src/main/java/org/pixeldroid/app/utils/db/DBUtils.kt index 40f52c76..946a6605 100644 --- a/app/src/main/java/org/pixeldroid/app/utils/db/DBUtils.kt +++ b/app/src/main/java/org/pixeldroid/app/utils/db/DBUtils.kt @@ -37,17 +37,21 @@ fun storeInstance(db: AppDatabase, nodeInfo: NodeInfo?, instance: Instance? = nu uri = normalizeDomain(metadata?.config?.site?.url!!), title = metadata.config.site.name!!, maxStatusChars = metadata.config.uploader?.max_caption_length!!.toInt(), - maxPhotoSize = metadata.config.uploader.max_photo_size?.toIntOrNull() ?: DEFAULT_MAX_PHOTO_SIZE, + maxPhotoSize = metadata.config.uploader.max_photo_size?.toIntOrNull() + ?: DEFAULT_MAX_PHOTO_SIZE, // Pixelfed doesn't distinguish between max photo and video size - maxVideoSize = metadata.config.uploader.max_photo_size?.toIntOrNull() ?: DEFAULT_MAX_VIDEO_SIZE, + maxVideoSize = metadata.config.uploader.max_photo_size?.toIntOrNull() + ?: DEFAULT_MAX_VIDEO_SIZE, albumLimit = metadata.config.uploader.album_limit?.toIntOrNull() ?: DEFAULT_ALBUM_LIMIT, - videoEnabled = metadata.config.features?.video ?: DEFAULT_VIDEO_ENABLED + videoEnabled = metadata.config.features?.video ?: DEFAULT_VIDEO_ENABLED, + pixelfed = metadata.software?.repo?.contains("pixelfed", ignoreCase = true) == true ) } ?: instance?.run { InstanceDatabaseEntity( - uri = normalizeDomain(uri.orEmpty()), - title = title.orEmpty(), - maxStatusChars = max_toot_chars?.toInt() ?: DEFAULT_MAX_TOOT_CHARS, + uri = normalizeDomain(uri.orEmpty()), + title = title.orEmpty(), + maxStatusChars = max_toot_chars?.toInt() ?: DEFAULT_MAX_TOOT_CHARS, + pixelfed = false ) } ?: throw IllegalArgumentException("Cannot store instance where both are null") diff --git a/app/src/main/java/org/pixeldroid/app/utils/db/dao/InstanceDao.kt b/app/src/main/java/org/pixeldroid/app/utils/db/dao/InstanceDao.kt index b3c214a4..4b894645 100644 --- a/app/src/main/java/org/pixeldroid/app/utils/db/dao/InstanceDao.kt +++ b/app/src/main/java/org/pixeldroid/app/utils/db/dao/InstanceDao.kt @@ -11,6 +11,10 @@ interface InstanceDao { @Query("SELECT * FROM instances WHERE uri=:instanceUri") fun getInstance(instanceUri: String): InstanceDatabaseEntity + + @Query("SELECT * FROM instances WHERE uri=(SELECT users.instance_uri FROM users WHERE isActive=1)") + fun getActiveInstance(): InstanceDatabaseEntity + /** * Insert an instance, if it already exists return -1 */ diff --git a/app/src/main/java/org/pixeldroid/app/utils/db/entities/InstanceDatabaseEntity.kt b/app/src/main/java/org/pixeldroid/app/utils/db/entities/InstanceDatabaseEntity.kt index e387e02c..10a015f5 100644 --- a/app/src/main/java/org/pixeldroid/app/utils/db/entities/InstanceDatabaseEntity.kt +++ b/app/src/main/java/org/pixeldroid/app/utils/db/entities/InstanceDatabaseEntity.kt @@ -4,20 +4,22 @@ import androidx.room.Entity import androidx.room.PrimaryKey @Entity(tableName = "instances") -data class InstanceDatabaseEntity ( - @PrimaryKey var uri: String, - var title: String, - var maxStatusChars: Int = DEFAULT_MAX_TOOT_CHARS, - // Per-file file-size limit in KB. Defaults to 15000 (15MB). Default limit for Mastodon is 8MB - var maxPhotoSize: Int = DEFAULT_MAX_PHOTO_SIZE, - // Mastodon has different file limits for videos, default of 40MB - var maxVideoSize: Int = DEFAULT_MAX_VIDEO_SIZE, - // How many photos can go into an album. Default limit for Pixelfed and Mastodon is 4 - var albumLimit: Int = DEFAULT_ALBUM_LIMIT, - // Is video functionality enabled on this instance? - var videoEnabled: Boolean = DEFAULT_VIDEO_ENABLED, +data class InstanceDatabaseEntity( + @PrimaryKey var uri: String, + var title: String, + var maxStatusChars: Int = DEFAULT_MAX_TOOT_CHARS, + // Per-file file-size limit in KB. Defaults to 15000 (15MB). Default limit for Mastodon is 8MB + var maxPhotoSize: Int = DEFAULT_MAX_PHOTO_SIZE, + // Mastodon has different file limits for videos, default of 40MB + var maxVideoSize: Int = DEFAULT_MAX_VIDEO_SIZE, + // How many photos can go into an album. Default limit for Pixelfed and Mastodon is 4 + var albumLimit: Int = DEFAULT_ALBUM_LIMIT, + // Is video functionality enabled on this instance? + var videoEnabled: Boolean = DEFAULT_VIDEO_ENABLED, + // Is this Pixelfed instance? + var pixelfed: Boolean = true, ) { - companion object{ + companion object { // Default max number of chars for Mastodon: used when their is no other value supplied by // either NodeInfo or the instance endpoint const val DEFAULT_MAX_TOOT_CHARS = 500 diff --git a/app/src/main/java/org/pixeldroid/app/utils/di/DatabaseModule.kt b/app/src/main/java/org/pixeldroid/app/utils/di/DatabaseModule.kt index 4dd5c8c3..9e505afe 100644 --- a/app/src/main/java/org/pixeldroid/app/utils/di/DatabaseModule.kt +++ b/app/src/main/java/org/pixeldroid/app/utils/di/DatabaseModule.kt @@ -7,6 +7,7 @@ import dagger.Module import dagger.Provides import org.pixeldroid.app.utils.db.MIGRATION_3_4 import org.pixeldroid.app.utils.db.MIGRATION_4_5 +import org.pixeldroid.app.utils.db.MIGRATION_5_6 import javax.inject.Singleton @Module @@ -18,7 +19,7 @@ class DatabaseModule(private val context: Context) { return Room.databaseBuilder( context, AppDatabase::class.java, "pixeldroid" - ).addMigrations(MIGRATION_3_4).addMigrations(MIGRATION_4_5) + ).addMigrations(MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6) .allowMainThreadQueries().build() } } \ No newline at end of file From 2e399884e9c9a9f4c323cc63f463b2fec5d5ea3e Mon Sep 17 00:00:00 2001 From: Matthieu <24-artectrex@users.noreply.shinice.net> Date: Wed, 24 Jan 2024 22:20:08 +0100 Subject: [PATCH 4/6] Fix #377 --- .../java/org/pixeldroid/app/MainActivity.kt | 88 +++++++++++-------- .../pixeldroid/app/MainActivityViewModel.kt | 52 +++++++++++ .../posts/feeds/CommonFeedFragmentUtils.kt | 1 + .../app/profile/EditProfileActivity.kt | 1 + .../app/profile/EditProfileViewModel.kt | 13 ++- .../pixeldroid/app/profile/ProfileActivity.kt | 24 +++-- .../org/pixeldroid/app/utils/db/DBUtils.kt | 41 ++++++--- .../pixeldroid/app/utils/db/dao/UserDao.kt | 14 ++- .../app/utils/di/ApplicationComponent.kt | 2 + 9 files changed, 175 insertions(+), 61 deletions(-) create mode 100644 app/src/main/java/org/pixeldroid/app/MainActivityViewModel.kt diff --git a/app/src/main/java/org/pixeldroid/app/MainActivity.kt b/app/src/main/java/org/pixeldroid/app/MainActivity.kt index f7e557bf..24edb5ee 100644 --- a/app/src/main/java/org/pixeldroid/app/MainActivity.kt +++ b/app/src/main/java/org/pixeldroid/app/MainActivity.kt @@ -14,6 +14,7 @@ import android.view.View import android.widget.ImageView import androidx.activity.addCallback import androidx.activity.result.contract.ActivityResultContracts +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat @@ -36,7 +37,12 @@ import com.mikepenz.materialdrawer.iconics.iconicsIcon import com.mikepenz.materialdrawer.model.PrimaryDrawerItem import com.mikepenz.materialdrawer.model.ProfileDrawerItem import com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem -import com.mikepenz.materialdrawer.model.interfaces.* +import com.mikepenz.materialdrawer.model.interfaces.IProfile +import com.mikepenz.materialdrawer.model.interfaces.descriptionRes +import com.mikepenz.materialdrawer.model.interfaces.descriptionText +import com.mikepenz.materialdrawer.model.interfaces.iconUrl +import com.mikepenz.materialdrawer.model.interfaces.nameRes +import com.mikepenz.materialdrawer.model.interfaces.nameText import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader import com.mikepenz.materialdrawer.util.DrawerImageLoader import com.mikepenz.materialdrawer.widget.AccountHeaderView @@ -53,10 +59,10 @@ import org.pixeldroid.app.searchDiscover.SearchDiscoverFragment import org.pixeldroid.app.settings.SettingsActivity import org.pixeldroid.app.utils.BaseActivity import org.pixeldroid.app.utils.api.objects.Notification -import org.pixeldroid.app.utils.db.addUser import org.pixeldroid.app.utils.db.entities.HomeStatusDatabaseEntity import org.pixeldroid.app.utils.db.entities.PublicFeedStatusDatabaseEntity import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity +import org.pixeldroid.app.utils.db.updateUserInfoDb import org.pixeldroid.app.utils.hasInternet import org.pixeldroid.app.utils.notificationsWorker.NotificationsWorker.Companion.INSTANCE_NOTIFICATION_TAG import org.pixeldroid.app.utils.notificationsWorker.NotificationsWorker.Companion.SHOW_NOTIFICATION_TAG @@ -71,6 +77,8 @@ class MainActivity : BaseActivity() { private lateinit var header: AccountHeaderView private var user: UserDatabaseEntity? = null + private lateinit var model: MainActivityViewModel + companion object { const val ADD_ACCOUNT_IDENTIFIER: Long = -13 } @@ -102,6 +110,12 @@ class MainActivity : BaseActivity() { } else { sendTraceDroidStackTracesIfExist("contact@pixeldroid.org", this) + val _model: MainActivityViewModel by viewModels { + MainActivityViewModelFactory(application) + } + model = _model + + setupDrawer() val tabs: List<() -> Fragment> = listOf( { @@ -281,16 +295,12 @@ class MainActivity : BaseActivity() { lifecycleScope.launchWhenCreated { try { - val domain = user?.instance_uri.orEmpty() - val accessToken = user?.accessToken.orEmpty() - val refreshToken = user?.refreshToken - val clientId = user?.clientId.orEmpty() - val clientSecret = user?.clientSecret.orEmpty() val api = apiHolder.api ?: apiHolder.setToCurrentUser() val account = api.verifyCredentials() - addUser(db, account, domain, accessToken = accessToken, refreshToken = refreshToken, clientId = clientId, clientSecret = clientSecret) - fillDrawerAccountInfo(account.id!!) + updateUserInfoDb(db, account) + + //No need to update drawer account info here, the ViewModel listens to db updates } catch (exception: Exception) { Log.e("ACCOUNT UPDATE:", exception.toString()) } @@ -337,35 +347,41 @@ class MainActivity : BaseActivity() { } private fun fillDrawerAccountInfo(account: String) { - val users = db.userDao().getAll().toMutableList() - users.sortWith { l, r -> - when { - l.isActive && !r.isActive -> -1 - r.isActive && !l.isActive -> 1 - else -> 0 + lifecycleScope.launch { + lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + model.users.collect { list -> + val users = list.toMutableList() + users.sortWith { l, r -> + when { + l.isActive && !r.isActive -> -1 + r.isActive && !l.isActive -> 1 + else -> 0 + } + } + val profiles: MutableList = users.map { user -> + ProfileDrawerItem().apply { + isSelected = user.isActive + nameText = user.display_name + iconUrl = user.avatar_static + isNameShown = true + identifier = user.user_id.toLong() + descriptionText = user.fullHandle + tag = user.instance_uri + } + }.toMutableList() + + // reuse the already existing "add account" item + header.profiles.orEmpty() + .filter { it.identifier == ADD_ACCOUNT_IDENTIFIER } + .take(1) + .forEach { profiles.add(it) } + + header.clear() + header.profiles = profiles + header.setActiveProfile(account.toLong()) + } } } - val profiles: MutableList = users.map { user -> - ProfileDrawerItem().apply { - isSelected = user.isActive - nameText = user.display_name - iconUrl = user.avatar_static - isNameShown = true - identifier = user.user_id.toLong() - descriptionText = user.fullHandle - tag = user.instance_uri - } - }.toMutableList() - - // reuse the already existing "add account" item - header.profiles.orEmpty() - .filter { it.identifier == ADD_ACCOUNT_IDENTIFIER } - .take(1) - .forEach { profiles.add(it) } - - header.clear() - header.profiles = profiles - header.setActiveProfile(account.toLong()) } /** diff --git a/app/src/main/java/org/pixeldroid/app/MainActivityViewModel.kt b/app/src/main/java/org/pixeldroid/app/MainActivityViewModel.kt new file mode 100644 index 00000000..77d3ce6b --- /dev/null +++ b/app/src/main/java/org/pixeldroid/app/MainActivityViewModel.kt @@ -0,0 +1,52 @@ +package org.pixeldroid.app + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import org.pixeldroid.app.utils.PixelDroidApplication +import org.pixeldroid.app.utils.db.AppDatabase +import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity +import javax.inject.Inject + +class MainActivityViewModel(application: Application) : AndroidViewModel(application) { + + @Inject + lateinit var db: AppDatabase + + // Mutable state flow that will be used internally in the ViewModel, empty list is given as initial value. + private val _users = MutableStateFlow(emptyList()) + + // Immutable state flow exposed to UI + val users = _users.asStateFlow() + + + init { + (application as PixelDroidApplication).getAppComponent().inject(this) + getUsers() + } + + private fun getUsers() { + viewModelScope.launch { + db.userDao().getAllFlow().flowOn(Dispatchers.IO) + .collect { users: List -> + _users.update { users } + } + } + } +} + +class MainActivityViewModelFactory( + val application: Application, +) : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + return modelClass.getConstructor(Application::class.java).newInstance(application) + } +} diff --git a/app/src/main/java/org/pixeldroid/app/posts/feeds/CommonFeedFragmentUtils.kt b/app/src/main/java/org/pixeldroid/app/posts/feeds/CommonFeedFragmentUtils.kt index cffb588c..02c2efbc 100644 --- a/app/src/main/java/org/pixeldroid/app/posts/feeds/CommonFeedFragmentUtils.kt +++ b/app/src/main/java/org/pixeldroid/app/posts/feeds/CommonFeedFragmentUtils.kt @@ -73,6 +73,7 @@ internal fun initAdapter( swipeRefreshLayout.setOnRefreshListener { adapter.refresh() + adapter.notifyDataSetChanged() header?.refreshStories() } diff --git a/app/src/main/java/org/pixeldroid/app/profile/EditProfileActivity.kt b/app/src/main/java/org/pixeldroid/app/profile/EditProfileActivity.kt index 04dd124f..c06c4789 100644 --- a/app/src/main/java/org/pixeldroid/app/profile/EditProfileActivity.kt +++ b/app/src/main/java/org/pixeldroid/app/profile/EditProfileActivity.kt @@ -51,6 +51,7 @@ class EditProfileActivity : BaseActivity() { }.show() } else { this.isEnabled = false + if (model.submittedChanges) setResult(RESULT_OK) super.onBackPressedDispatcher.onBackPressed() } } diff --git a/app/src/main/java/org/pixeldroid/app/profile/EditProfileViewModel.kt b/app/src/main/java/org/pixeldroid/app/profile/EditProfileViewModel.kt index 7c9d31b2..5b63f3e4 100644 --- a/app/src/main/java/org/pixeldroid/app/profile/EditProfileViewModel.kt +++ b/app/src/main/java/org/pixeldroid/app/profile/EditProfileViewModel.kt @@ -24,7 +24,9 @@ import org.pixeldroid.app.posts.fromHtml import org.pixeldroid.app.utils.PixelDroidApplication import org.pixeldroid.app.utils.api.objects.Account import org.pixeldroid.app.utils.db.AppDatabase +import org.pixeldroid.app.utils.db.updateUserInfoDb import org.pixeldroid.app.utils.di.PixelfedAPIHolder +import retrofit2.HttpException import javax.inject.Inject class EditProfileViewModel(application: Application) : AndroidViewModel(application) { @@ -40,6 +42,9 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat private var oldProfile: Account? = null + var submittedChanges = false + private set + init { (application as PixelDroidApplication).getAppComponent().inject(this) loadProfile() @@ -50,6 +55,7 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat val api = apiHolder.api ?: apiHolder.setToCurrentUser() try { val profile = api.verifyCredentials() + updateUserInfoDb(db, profile) if (oldProfile == null) oldProfile = profile _uiState.update { currentUiState -> currentUiState.copy( @@ -96,6 +102,7 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat note = bio, locked = privateAccount, ) + if (madeChanges()) submittedChanges = true oldProfile = account _uiState.update { currentUiState -> currentUiState.copy( @@ -180,7 +187,8 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat _uiState.update { currentUiState -> currentUiState.copy( profilePictureUri = image.toUri(), - profilePictureChanged = true + profilePictureChanged = true, + profileSent = false ) } } @@ -238,7 +246,7 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat val inter = if(pixelfed) api.updateProfilePicture(requestBody.parts[0]) - else api.updateProfilePictureMastodon((requestBody.parts[0])) + else api.updateProfilePictureMastodon(requestBody.parts[0]) postSub = inter .subscribeOn(Schedulers.io()) @@ -254,6 +262,7 @@ class EditProfileViewModel(application: Application) : AndroidViewModel(applicat } }, /* onError = */ { e: Throwable -> + Log.e("error", (e as? HttpException)?.message().orEmpty()) _uiState.update { currentUiState -> currentUiState.copy( uploadProgress = 0, diff --git a/app/src/main/java/org/pixeldroid/app/profile/ProfileActivity.kt b/app/src/main/java/org/pixeldroid/app/profile/ProfileActivity.kt index 7283de31..ab33818a 100644 --- a/app/src/main/java/org/pixeldroid/app/profile/ProfileActivity.kt +++ b/app/src/main/java/org/pixeldroid/app/profile/ProfileActivity.kt @@ -6,6 +6,7 @@ import android.text.method.LinkMovementMethod import android.util.Log import android.view.View import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope @@ -16,11 +17,14 @@ import com.google.android.material.tabs.TabLayoutMediator import kotlinx.coroutines.launch import org.pixeldroid.app.R import org.pixeldroid.app.databinding.ActivityProfileBinding +import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedFeedFragment import org.pixeldroid.app.posts.parseHTMLText import org.pixeldroid.app.utils.BaseActivity import org.pixeldroid.app.utils.api.PixelfedAPI import org.pixeldroid.app.utils.api.objects.Account +import org.pixeldroid.app.utils.api.objects.FeedContent import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity +import org.pixeldroid.app.utils.db.updateUserInfoDb import org.pixeldroid.app.utils.setProfileImageFromURL import retrofit2.HttpException import java.io.IOException @@ -56,7 +60,7 @@ class ProfileActivity : BaseActivity() { setContent(account) } - private fun createProfileTabs(account: Account?): Array{ + private fun createProfileTabs(account: Account?): Array> { val profileFeedFragment = ProfileFeedFragment() profileFeedFragment.arguments = Bundle().apply { @@ -80,7 +84,7 @@ class ProfileActivity : BaseActivity() { putSerializable(ProfileFeedFragment.COLLECTIONS, true) } - val returnArray: Array = arrayOf( + val returnArray: Array> = arrayOf( profileGridFragment, profileFeedFragment, profileCollectionsFragment @@ -100,7 +104,7 @@ class ProfileActivity : BaseActivity() { } private fun setupTabs( - tabs: Array + tabs: Array> ){ binding.viewPager.adapter = object : FragmentStateAdapter(this) { override fun createFragment(position: Int): Fragment { @@ -134,7 +138,6 @@ class ProfileActivity : BaseActivity() { }.attach() } - private fun setContent(account: Account?) { if(account != null) { setViews(account) @@ -152,6 +155,9 @@ class ProfileActivity : BaseActivity() { ).show() return@launchWhenResumed } + + updateUserInfoDb(db, myAccount) + setViews(myAccount) } } @@ -217,9 +223,15 @@ class ProfileActivity : BaseActivity() { ) } + private val editResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + if (it.resultCode == RESULT_OK) { + // Profile was edited, reload + setContent(null) + } + } + private fun onClickEditButton() { - val intent = Intent(this, EditProfileActivity::class.java) - ContextCompat.startActivity(this, intent, null) + editResult.launch(Intent(this, EditProfileActivity::class.java)) } private fun onClickFollowers(account: Account?) { diff --git a/app/src/main/java/org/pixeldroid/app/utils/db/DBUtils.kt b/app/src/main/java/org/pixeldroid/app/utils/db/DBUtils.kt index 946a6605..0eb9ecb2 100644 --- a/app/src/main/java/org/pixeldroid/app/utils/db/DBUtils.kt +++ b/app/src/main/java/org/pixeldroid/app/utils/db/DBUtils.kt @@ -13,21 +13,34 @@ import org.pixeldroid.app.utils.db.entities.InstanceDatabaseEntity.Companion.DEF import org.pixeldroid.app.utils.normalizeDomain import java.lang.IllegalArgumentException -fun addUser(db: AppDatabase, account: Account, instance_uri: String, activeUser: Boolean = true, - accessToken: String, refreshToken: String?, clientId: String, clientSecret: String) { +suspend fun addUser( + db: AppDatabase, account: Account, instance_uri: String, activeUser: Boolean = true, + accessToken: String, refreshToken: String?, clientId: String, clientSecret: String, +) { db.userDao().insertOrUpdate( - UserDatabaseEntity( - user_id = account.id!!, - instance_uri = normalizeDomain(instance_uri), - username = account.username!!, - display_name = account.getDisplayName(), - avatar_static = account.anyAvatar().orEmpty(), - isActive = activeUser, - accessToken = accessToken, - refreshToken = refreshToken, - clientId = clientId, - clientSecret = clientSecret - ) + UserDatabaseEntity( + user_id = account.id!!, + instance_uri = normalizeDomain(instance_uri), + username = account.username!!, + display_name = account.getDisplayName(), + avatar_static = account.anyAvatar().orEmpty(), + isActive = activeUser, + accessToken = accessToken, + refreshToken = refreshToken, + clientId = clientId, + clientSecret = clientSecret + ) + ) +} + +suspend fun updateUserInfoDb(db: AppDatabase, account: Account) { + val user = db.userDao().getActiveUser()!! + db.userDao().updateUserAccountDetails( + account.username.orEmpty(), + account.display_name.orEmpty(), + account.anyAvatar().orEmpty(), + user.user_id, + user.instance_uri ) } diff --git a/app/src/main/java/org/pixeldroid/app/utils/db/dao/UserDao.kt b/app/src/main/java/org/pixeldroid/app/utils/db/dao/UserDao.kt index 266cd19c..bf1afd55 100644 --- a/app/src/main/java/org/pixeldroid/app/utils/db/dao/UserDao.kt +++ b/app/src/main/java/org/pixeldroid/app/utils/db/dao/UserDao.kt @@ -1,6 +1,7 @@ package org.pixeldroid.app.utils.db.dao import androidx.room.* +import kotlinx.coroutines.flow.Flow import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity @Dao @@ -9,17 +10,21 @@ interface UserDao { * Insert a user, if it already exists return -1 */ @Insert(onConflict = OnConflictStrategy.IGNORE) - fun insertUser(user: UserDatabaseEntity): Long + suspend fun insertUser(user: UserDatabaseEntity): Long @Transaction - fun insertOrUpdate(user: UserDatabaseEntity) { + suspend fun insertOrUpdate(user: UserDatabaseEntity) { if (insertUser(user) == -1L) { updateUser(user) } } @Update - fun updateUser(user: UserDatabaseEntity) + suspend fun updateUser(user: UserDatabaseEntity) + + @Query("UPDATE users SET username = :username, display_name = :displayName, avatar_static = :avatarStatic WHERE user_id = :id and instance_uri = :instanceUri") + suspend fun updateUserAccountDetails(username: String, displayName: String, avatarStatic: String, id: String, instanceUri: String) + @Query("UPDATE users SET accessToken = :accessToken, refreshToken = :refreshToken WHERE user_id = :id and instance_uri = :instanceUri") fun updateAccessToken(accessToken: String, refreshToken: String, id: String, instanceUri: String) @@ -27,6 +32,9 @@ interface UserDao { @Query("SELECT * FROM users") fun getAll(): List + @Query("SELECT * FROM users") + fun getAllFlow(): Flow> + @Query("SELECT * FROM users WHERE isActive=1") fun getActiveUser(): UserDatabaseEntity? diff --git a/app/src/main/java/org/pixeldroid/app/utils/di/ApplicationComponent.kt b/app/src/main/java/org/pixeldroid/app/utils/di/ApplicationComponent.kt index c2490432..d1862c86 100644 --- a/app/src/main/java/org/pixeldroid/app/utils/di/ApplicationComponent.kt +++ b/app/src/main/java/org/pixeldroid/app/utils/di/ApplicationComponent.kt @@ -7,6 +7,7 @@ import org.pixeldroid.app.utils.PixelDroidApplication import org.pixeldroid.app.utils.db.AppDatabase import org.pixeldroid.app.utils.BaseFragment import dagger.Component +import org.pixeldroid.app.MainActivityViewModel import org.pixeldroid.app.postCreation.PostCreationViewModel import org.pixeldroid.app.profile.EditProfileViewModel import org.pixeldroid.app.stories.StoriesViewModel @@ -25,6 +26,7 @@ interface ApplicationComponent { fun inject(postCreationViewModel: PostCreationViewModel) fun inject(editProfileViewModel: EditProfileViewModel) fun inject(storiesViewModel: StoriesViewModel) + fun inject(mainActivityViewModel: MainActivityViewModel) val context: Context? val application: Application? From 1ea4371b3e84fb346662dad02231b9594435fc3b Mon Sep 17 00:00:00 2001 From: Fred <76-fgerber@users.noreply.shinice.net> Date: Wed, 24 Jan 2024 23:22:58 +0100 Subject: [PATCH 5/6] Crop newly changed profile pic to circle --- app/src/main/java/org/pixeldroid/app/MainActivity.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/org/pixeldroid/app/MainActivity.kt b/app/src/main/java/org/pixeldroid/app/MainActivity.kt index 24edb5ee..0c9ddf63 100644 --- a/app/src/main/java/org/pixeldroid/app/MainActivity.kt +++ b/app/src/main/java/org/pixeldroid/app/MainActivity.kt @@ -30,6 +30,7 @@ import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.color.DynamicColors import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial @@ -210,6 +211,7 @@ class MainActivity : BaseActivity() { Glide.with(this@MainActivity) .load(uri) .placeholder(placeholder) + .apply(RequestOptions.circleCropTransform()) .into(imageView) } From eb65e240999dd3e5f8cc3588105361e14a082e3f Mon Sep 17 00:00:00 2001 From: Matthieu <24-artectrex@users.noreply.shinice.net> Date: Fri, 26 Jan 2024 13:16:33 +0100 Subject: [PATCH 6/6] Circlecrop directly without RequestOptions --- app/src/main/java/org/pixeldroid/app/MainActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/pixeldroid/app/MainActivity.kt b/app/src/main/java/org/pixeldroid/app/MainActivity.kt index 0c9ddf63..5e7d46ca 100644 --- a/app/src/main/java/org/pixeldroid/app/MainActivity.kt +++ b/app/src/main/java/org/pixeldroid/app/MainActivity.kt @@ -211,7 +211,7 @@ class MainActivity : BaseActivity() { Glide.with(this@MainActivity) .load(uri) .placeholder(placeholder) - .apply(RequestOptions.circleCropTransform()) + .circleCrop() .into(imageView) }