From ba373bef346f1fefb4caf2df81015f3d862ad151 Mon Sep 17 00:00:00 2001 From: sneazy-ibo <41344259+sneazy-ibo@users.noreply.github.com> Date: Mon, 10 Jun 2024 00:28:33 +0200 Subject: [PATCH] feat: added mutations --- .../connections/anilist/AnilistMutations.kt | 36 ++----- .../ani/dantotsu/profile/ProfileActivity.kt | 99 ++++++++++++------- 2 files changed, 74 insertions(+), 61 deletions(-) diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistMutations.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistMutations.kt index 489f537e..e207eee9 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistMutations.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistMutations.kt @@ -1,6 +1,5 @@ package ani.dantotsu.connections.anilist -import android.net.Uri import ani.dantotsu.connections.anilist.Anilist.executeQuery import ani.dantotsu.connections.anilist.api.FuzzyDate import ani.dantotsu.connections.anilist.api.Query @@ -154,36 +153,17 @@ class AnilistMutations { return errors == null } - suspend fun uploadAvatar(avatarUri: Uri): Boolean { - val query = """ - mutation (${"$"}avatar: Upload) { - UpdateUser( - avatar: ${"$"}avatar - ) { - id - } + suspend fun saveUserAvatar(base64Avatar: String): JsonObject? { + val query = "mutation(\$avatar: String) { SaveUserAvatar(avatar: \$avatar) { id avatar { large medium } } }" + val variables = """{"avatar":"$base64Avatar"}""" + return executeQuery(query, variables) } - """ - val variables = mapOf("avatar" to avatarUri.toString()) - val result = executeQuery(query, variables.toString()) - return result?.get("errors") == null && result != null - } - suspend fun uploadBanner(bannerUri: Uri): Boolean { - val query = """ - mutation (${"$"}banner: Upload) { - UpdateUser( - banner: ${"$"}banner - ) { - id - } + suspend fun saveUserBanner(base64Banner: String): JsonObject? { + val query = "mutation(\$banner: String) { SaveUserBanner(banner: \$banner) { id bannerImage } }" + val variables = """{"banner":"$base64Banner"}""" + return executeQuery(query, variables) } - """ - val variables = mapOf("banner" to bannerUri.toString()) - val result = executeQuery(query, variables.toString()) - return result?.get("errors") == null && result != null - } - private fun String.stringSanitizer(): String { val sb = StringBuilder() diff --git a/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt b/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt index eba50ce3..e74868d4 100644 --- a/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt +++ b/app/src/main/java/ani/dantotsu/profile/ProfileActivity.kt @@ -3,12 +3,17 @@ package ani.dantotsu.profile import android.animation.ObjectAnimator import android.content.Intent import android.content.res.Configuration +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.drawable.BitmapDrawable import android.net.Uri import android.os.Bundle +import android.util.Base64 import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.PopupMenu +import androidx.activity.result.PickVisualMediaRequest import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat @@ -23,7 +28,7 @@ import androidx.viewpager2.adapter.FragmentStateAdapter import ani.dantotsu.R import ani.dantotsu.blurImage import ani.dantotsu.connections.anilist.Anilist -import ani.dantotsu.connections.anilist.AnilistMutations +import ani.dantotsu.connections.anilist.Anilist.executeQuery import ani.dantotsu.connections.anilist.api.Query import ani.dantotsu.databinding.ActivityProfileBinding import ani.dantotsu.databinding.ItemProfileAppBarBinding @@ -42,10 +47,15 @@ import ani.dantotsu.statusBarHeight import ani.dantotsu.themes.ThemeManager import ani.dantotsu.toast import com.google.android.material.appbar.AppBarLayout +import com.google.gson.JsonObject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive import nl.joery.animatedbottombar.AnimatedBottomBar +import java.io.ByteArrayOutputStream import kotlin.math.abs @@ -132,7 +142,7 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene openMediaPickerForBanner() } binding.apply { - editProfileSave?.setOnClickListener { onSaveButtonClick() } + editProfileSave?.setOnClickListener { saveProfileImages() } } followButton.isGone = @@ -294,61 +304,84 @@ class ProfileActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedListene } } - private var avatarUri: Uri? = null private val avatarPicker = - registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? -> - uri?.let { avatarUri -> - uploadAvatar(avatarUri) + registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri: Uri? -> + if (uri != null) { + val bitmap = getBitmapFromUri(uri) + uploadAvatar(bitmap) } } private val bannerPicker = - registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? -> - uri?.let { bannerUri -> - uploadBanner(bannerUri) + registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri: Uri? -> + if (uri != null) { + val bitmap = getBitmapFromUri(uri) + uploadBanner(bitmap) } } private fun openMediaPickerForAvatar() { - avatarPicker.launch("image/*") + avatarPicker.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) } private fun openMediaPickerForBanner() { - bannerPicker.launch("image/*") + bannerPicker.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) } - private fun uploadAvatar(avatarUri: Uri) { - this.avatarUri = avatarUri - bindingProfileAppBar.profileUserAvatar.setImageURI(avatarUri) + private fun getBitmapFromUri(uri: Uri): Bitmap { + val parcelFileDescriptor = contentResolver.openFileDescriptor(uri, "r") + val fileDescriptor = parcelFileDescriptor?.fileDescriptor + val image = BitmapFactory.decodeFileDescriptor(fileDescriptor) + parcelFileDescriptor?.close() + return image + } - lifecycleScope.launch { - try { - val success = AnilistMutations().uploadAvatar(avatarUri) - if (success) { - toast("Avatar uploaded successfully") - } else { - toast("Failed to upload avatar") - } - } catch (e: Exception) { - toast("Error uploading avatar: ${e.message}") + private fun uploadAvatar(bitmap: Bitmap) { + bindingProfileAppBar.profileUserAvatar.setImageBitmap(bitmap) + val base64Avatar = bitmapToBase64(bitmap) + lifecycleScope.launch(Dispatchers.IO) { + val response = Anilist.mutation.saveUserAvatar(base64Avatar) + withContext(Dispatchers.Main) { + handleApiResponse(response, "Avatar") } } } - - private fun uploadBanner(bannerUri: Uri) { - // Logic incoming - bindingProfileAppBar.profileBannerImage.setImageURI(bannerUri) + private fun uploadBanner(bitmap: Bitmap) { + bindingProfileAppBar.profileBannerImage.setImageBitmap(bitmap) + val base64Banner = bitmapToBase64(bitmap) + lifecycleScope.launch(Dispatchers.IO) { + val response = Anilist.mutation.saveUserBanner(base64Banner) + withContext(Dispatchers.Main) { + handleApiResponse(response, "Banner") + } + } } - private fun onSaveButtonClick() { - val currentAvatarUri = avatarUri - if (currentAvatarUri != null) { - uploadAvatar(currentAvatarUri) - } + private fun saveProfileImages() { + val avatarBitmap = (bindingProfileAppBar.profileUserAvatar.drawable as BitmapDrawable).bitmap + val bannerBitmap = (bindingProfileAppBar.profileBannerImage.drawable as BitmapDrawable).bitmap + uploadAvatar(avatarBitmap) + uploadBanner(bannerBitmap) toast("Uploading avatar and banner images...") } + private fun handleApiResponse(response: kotlinx.serialization.json.JsonObject?, type: String) { + val errors = response?.get("errors")?.jsonArray + if (!errors.isNullOrEmpty()) { + val errorMessage = errors.joinToString(separator = "\n") { it.jsonObject["message"]?.jsonPrimitive?.content ?: "Unknown error" } + toast("Error uploading $type: $errorMessage") + } else { + toast("$type uploaded successfully") + } + } + + private fun bitmapToBase64(bitmap: Bitmap): String { + val outputStream = ByteArrayOutputStream() + bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream) + return Base64.encodeToString(outputStream.toByteArray(), Base64.NO_WRAP) + } + private var isCollapsed = false private val percent = 65