diff --git a/Habitica/res/layout/mount_imageview.xml b/Habitica/res/layout/mount_imageview.xml index c30b027f0..9153b9409 100644 --- a/Habitica/res/layout/mount_imageview.xml +++ b/Habitica/res/layout/mount_imageview.xml @@ -1,10 +1,17 @@ + android:layout_width="match_parent" android:layout_height="124dp" + android:layout_gravity="center" + android:background="@drawable/layout_rounded_bg_content" + android:clipToPadding="true" + android:clipChildren="true"> + diff --git a/Habitica/res/layout/pet_imageview.xml b/Habitica/res/layout/pet_imageview.xml index 8fecaeab1..267823adb 100644 --- a/Habitica/res/layout/pet_imageview.xml +++ b/Habitica/res/layout/pet_imageview.xml @@ -1,10 +1,22 @@ + android:layout_width="match_parent" + android:layout_height="124dp" + xmlns:tools="http://schemas.android.com/tools" + android:layout_gravity="center" + android:background="@drawable/layout_rounded_bg_content" + android:clipToPadding="true" + android:clipChildren="true"> + + android:layout_gravity="center" + android:layout_marginTop="12dp" + tools:background="@color/red_10" + /> diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/AlertDialogExtensions.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/AlertDialogExtensions.kt index be53e7026..6309d4e2d 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/AlertDialogExtensions.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/AlertDialogExtensions.kt @@ -13,14 +13,14 @@ fun HabiticaAlertDialog.addOkButton( fun HabiticaAlertDialog.addCloseButton( isPrimary: Boolean = false, - listener: ((DialogInterface, Int) -> Unit)? = null + listener: ((HabiticaAlertDialog, Int) -> Unit)? = null ) { this.addButton(R.string.close, isPrimary, false, true, listener) } fun HabiticaAlertDialog.addCancelButton( isPrimary: Boolean = false, - listener: ((DialogInterface, Int) -> Unit)? = null + listener: ((HabiticaAlertDialog, Int) -> Unit)? = null ) { this.addButton(R.string.cancel, isPrimary, false, true, listener) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/FeedPetUseCase.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/FeedPetUseCase.kt index e51610671..d78f86ed5 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/FeedPetUseCase.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/FeedPetUseCase.kt @@ -3,48 +3,63 @@ package com.habitrpg.android.habitica.interactors import android.content.Context import android.graphics.Bitmap import android.graphics.Canvas -import android.graphics.drawable.BitmapDrawable -import android.view.View -import android.widget.FrameLayout +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.lifecycle.setViewTreeLifecycleOwner +import androidx.savedstate.setViewTreeSavedStateRegistryOwner +import com.habitrpg.android.habitica.HabiticaBaseApplication import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.data.InventoryRepository +import com.habitrpg.android.habitica.databinding.MountImageviewBinding import com.habitrpg.android.habitica.models.inventory.Food import com.habitrpg.android.habitica.models.inventory.Pet import com.habitrpg.android.habitica.ui.activities.BaseActivity +import com.habitrpg.android.habitica.ui.theme.HabiticaTheme +import com.habitrpg.android.habitica.ui.views.BackgroundScene import com.habitrpg.android.habitica.ui.views.SnackbarActivity import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog -import com.habitrpg.android.habitica.ui.views.stable.MountView -import com.habitrpg.common.habitica.extensions.loadImage -import com.habitrpg.common.habitica.views.PixelArtView +import com.habitrpg.common.habitica.extensions.dpToPx +import com.habitrpg.common.habitica.extensions.layoutInflater +import com.habitrpg.common.habitica.helpers.launchCatching import com.habitrpg.shared.habitica.models.responses.FeedResponse +import kotlinx.coroutines.MainScope import javax.inject.Inject class FeedPetUseCase @Inject constructor( private val inventoryRepository: InventoryRepository ) : UseCase() { - override suspend fun run(requestValues: FeedPetUseCase.RequestValues): FeedResponse? { + override suspend fun run(requestValues: RequestValues): FeedResponse? { val feedResponse = inventoryRepository.feedPet(requestValues.pet, requestValues.food) (requestValues.context as? SnackbarActivity)?.showSnackbar(content = feedResponse?.message) if (feedResponse?.value == -1) { - val mountWrapper = - View.inflate( - requestValues.context, - R.layout.mount_imageview, - null - ) as? FrameLayout - val mountImageView = - mountWrapper?.findViewById(R.id.mount_imageview) as? MountView + val mountWrapper = MountImageviewBinding.inflate(requestValues.context.layoutInflater) - mountImageView?.setMount("Mount_Icon_" + requestValues.pet.key) + mountWrapper.mountImageview.setMount(requestValues.pet.key) + val currentActivity = + HabiticaBaseApplication.getInstance(requestValues.context)?.currentActivity?.get() val dialog = HabiticaAlertDialog(requestValues.context) + if (currentActivity != null) { + mountWrapper.backgroundView.setContent { + HabiticaTheme { + BackgroundScene(Modifier.clip(HabiticaTheme.shapes.large)) + } + } + dialog.window?.let { + mountWrapper.root.setViewTreeSavedStateRegistryOwner(currentActivity) + it.decorView.setViewTreeSavedStateRegistryOwner(currentActivity) + mountWrapper.root.setViewTreeLifecycleOwner(currentActivity) + it.decorView.setViewTreeLifecycleOwner(currentActivity) + } + } dialog.setTitle( requestValues.context.getString( R.string.evolved_pet_title, requestValues.pet.text ) ) - dialog.setAdditionalContentView(mountWrapper) + dialog.isCelebratory = true + dialog.setAdditionalContentView(mountWrapper.root) dialog.addButton(R.string.onwards, true) dialog.addButton(R.string.share, false) { hatchingDialog, _ -> val message = @@ -52,19 +67,11 @@ constructor( R.string.share_raised, requestValues.pet.text ) - val mountImageSideLength = 99 - val sharedImage = Bitmap.createBitmap( - mountImageSideLength, - mountImageSideLength, - Bitmap.Config.ARGB_8888 - ) - val canvas = Canvas(sharedImage) - mountImageView?.draw(canvas) - (requestValues.context as? BaseActivity)?.shareContent( - "raisedPet", - message, - sharedImage - ) + MainScope().launchCatching { + ShareMountUseCase().callInteractor(ShareMountUseCase.RequestValues( + requestValues.pet.key, message, requestValues.context + )) + } hatchingDialog.dismiss() } dialog.enqueue() diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/HatchPetUseCase.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/HatchPetUseCase.kt index f6b64bf9f..4d7e7e3dc 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/HatchPetUseCase.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/HatchPetUseCase.kt @@ -1,21 +1,24 @@ package com.habitrpg.android.habitica.interactors import android.content.Context -import android.graphics.Bitmap -import android.graphics.Canvas -import android.graphics.drawable.BitmapDrawable import android.view.View -import android.widget.FrameLayout +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.lifecycle.setViewTreeLifecycleOwner +import androidx.savedstate.setViewTreeSavedStateRegistryOwner +import com.habitrpg.android.habitica.HabiticaBaseApplication import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.data.InventoryRepository +import com.habitrpg.android.habitica.databinding.PetImageviewBinding import com.habitrpg.android.habitica.models.inventory.Egg import com.habitrpg.android.habitica.models.inventory.HatchingPotion import com.habitrpg.android.habitica.models.user.Items -import com.habitrpg.android.habitica.ui.activities.BaseActivity +import com.habitrpg.android.habitica.ui.theme.HabiticaTheme +import com.habitrpg.android.habitica.ui.views.BackgroundScene import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog +import com.habitrpg.common.habitica.extensions.layoutInflater import com.habitrpg.common.habitica.extensions.loadImage import com.habitrpg.common.habitica.helpers.launchCatching -import com.habitrpg.common.habitica.views.PixelArtView import kotlinx.coroutines.MainScope import javax.inject.Inject @@ -25,29 +28,58 @@ constructor( ) : UseCase() { override suspend fun run(requestValues: RequestValues): Items? { return inventoryRepository.hatchPet(requestValues.egg, requestValues.potion) { - val petWrapper = View.inflate(requestValues.context, R.layout.pet_imageview, null) as? FrameLayout - val petImageView = petWrapper?.findViewById(R.id.pet_imageview) as? PixelArtView - - petImageView?.loadImage("stable_Pet-" + requestValues.egg.key + "-" + requestValues.potion.key) + val petWrapper = PetImageviewBinding.inflate(requestValues.context.layoutInflater) + val petKey = requestValues.egg.key + "-" + requestValues.potion.key + petWrapper.petImageview.loadImage("stable_Pet-" + petKey) val potionName = requestValues.potion.text val eggName = requestValues.egg.text + val currentActivity = + HabiticaBaseApplication.getInstance(requestValues.context)?.currentActivity?.get() val dialog = HabiticaAlertDialog(requestValues.context) - dialog.setTitle(requestValues.context.getString(R.string.hatched_pet_title, potionName, eggName)) - dialog.setAdditionalContentView(petWrapper) + if (currentActivity != null) { + petWrapper.backgroundView.setContent { + HabiticaTheme { + BackgroundScene(Modifier.clip(HabiticaTheme.shapes.large)) + } + } + dialog.window?.let { + petWrapper.root.setViewTreeSavedStateRegistryOwner(currentActivity) + it.decorView.setViewTreeSavedStateRegistryOwner(currentActivity) + petWrapper.root.setViewTreeLifecycleOwner(currentActivity) + it.decorView.setViewTreeLifecycleOwner(currentActivity) + } + } + dialog.isCelebratory = true + dialog.setTitle( + requestValues.context.getString( + R.string.hatched_pet_title, + potionName, + eggName + ) + ) + dialog.setAdditionalContentView(petWrapper.root) dialog.addButton(R.string.equip, true) { _, _ -> MainScope().launchCatching { - inventoryRepository.equip("pet", requestValues.egg.key + "-" + requestValues.potion.key) + inventoryRepository.equip( + "pet", + requestValues.egg.key + "-" + requestValues.potion.key + ) } } dialog.addButton(R.string.share, false) { hatchingDialog, _ -> - val message = requestValues.context.getString(R.string.share_hatched, potionName, eggName) - val petImageSideLength = 140 - val sharedImage = Bitmap.createBitmap(petImageSideLength, petImageSideLength, Bitmap.Config.ARGB_8888) - val canvas = Canvas(sharedImage) - petImageView?.drawable?.setBounds(0, 0, petImageSideLength, petImageSideLength) - val bitmap = (petImageView?.drawable as? BitmapDrawable)?.bitmap ?: petImageView?.bitmap ?: return@addButton - canvas.drawBitmap(bitmap, 0f, 0f, null) - (requestValues.context as? BaseActivity)?.shareContent("hatchedPet", message, sharedImage) + MainScope().launchCatching { + SharePetUseCase().callInteractor( + SharePetUseCase.RequestValues( + petKey, + requestValues.context.getString( + R.string.share_hatched, + potionName, + eggName + ), + requestValues.context + ) + ) + } hatchingDialog.dismiss() } dialog.setExtraCloseButtonVisibility(View.VISIBLE) @@ -55,5 +87,6 @@ constructor( } } - class RequestValues(val potion: HatchingPotion, val egg: Egg, val context: Context) : UseCase.RequestValues + class RequestValues(val potion: HatchingPotion, val egg: Egg, val context: Context) : + UseCase.RequestValues } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/ShareAvatarUseCase.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/ShareAvatarUseCase.kt index ae313dca1..a5ce03667 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/ShareAvatarUseCase.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/ShareAvatarUseCase.kt @@ -7,8 +7,7 @@ import com.habitrpg.common.habitica.views.AvatarView import com.habitrpg.shared.habitica.models.Avatar import javax.inject.Inject -class ShareAvatarUseCase @Inject -constructor() : UseCase() { +class ShareAvatarUseCase @Inject constructor() : UseCase() { override suspend fun run(requestValues: RequestValues) { val avatarView = AvatarView(requestValues.activity, showBackground = true, showMount = true, showPet = true) avatarView.setAvatar(requestValues.avatar) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/ShareMountUseCase.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/ShareMountUseCase.kt new file mode 100644 index 000000000..9da41a678 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/ShareMountUseCase.kt @@ -0,0 +1,72 @@ +package com.habitrpg.android.habitica.interactors + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Canvas +import android.view.ViewGroup +import android.widget.FrameLayout +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.core.view.doOnNextLayout +import androidx.lifecycle.setViewTreeLifecycleOwner +import androidx.savedstate.setViewTreeSavedStateRegistryOwner +import com.habitrpg.android.habitica.HabiticaBaseApplication +import com.habitrpg.android.habitica.databinding.MountImageviewBinding +import com.habitrpg.android.habitica.ui.activities.BaseActivity +import com.habitrpg.android.habitica.ui.theme.HabiticaTheme +import com.habitrpg.android.habitica.ui.views.BackgroundScene +import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog +import com.habitrpg.common.habitica.extensions.dpToPx +import com.habitrpg.common.habitica.extensions.layoutInflater +import com.habitrpg.common.habitica.extensions.loadImage +import kotlinx.coroutines.delay + +class ShareMountUseCase: UseCase() { + class RequestValues(val mountKey: String, val message: String, val context: Context) : + UseCase.RequestValues + + override suspend fun run(requestValues: RequestValues) { + val mountWrapper = MountImageviewBinding.inflate(requestValues.context.layoutInflater) + val width = if (mountWrapper.root.width > 0) mountWrapper.root.width else 300.dpToPx(requestValues.context) + val height = 124.dpToPx(requestValues.context) + mountWrapper.root.layout(0, 0, width, height) + mountWrapper.mountImageview.setMount(requestValues.mountKey) + val currentActivity = + HabiticaBaseApplication.getInstance(requestValues.context)?.currentActivity?.get() + // Add the view to the decorView so that it can be layouted + (currentActivity?.window?.decorView as? ViewGroup)?.addView(mountWrapper.root) + if (currentActivity != null) { + mountWrapper.backgroundView.setContent { + HabiticaTheme { + BackgroundScene(Modifier.clip(HabiticaTheme.shapes.large)) + } + } + mountWrapper.root.setViewTreeSavedStateRegistryOwner(currentActivity) + mountWrapper.root.setViewTreeLifecycleOwner(currentActivity) + } + mountWrapper.backgroundView.layout(0, 0, width, height) + mountWrapper.mountImageview.layout(0, 0, width, height) + val sharedImage = Bitmap.createBitmap( + width, + height, + Bitmap.Config.ARGB_8888 + ) + val canvas = Canvas(sharedImage) + var attempts = 0 + while (!mountWrapper.mountImageview.hasLoadedImages && attempts < 200) { + delay(100) + attempts++ + } + // Draw it to the canvas once it's layouted + mountWrapper.root.doOnNextLayout { + mountWrapper.root.draw(canvas) + ((requestValues.context as? BaseActivity) ?: HabiticaBaseApplication.getInstance( + requestValues.context + )?.currentActivity?.get())?.shareContent("pet", requestValues.message, sharedImage) + currentActivity?.toolbar?.removeView(mountWrapper.root) + } + // trigger layout + val m = FrameLayout.LayoutParams(width, height) + mountWrapper.root.layoutParams = m + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/SharePetUseCase.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/SharePetUseCase.kt new file mode 100644 index 000000000..a0b392bca --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/SharePetUseCase.kt @@ -0,0 +1,81 @@ +package com.habitrpg.android.habitica.interactors + +import android.app.Activity +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Rect +import android.os.Build +import android.os.Handler +import android.view.PixelCopy +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import androidx.annotation.RequiresApi +import androidx.appcompat.widget.Toolbar +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.findViewTreeCompositionContext +import androidx.core.view.doOnNextLayout +import androidx.core.view.isVisible +import androidx.lifecycle.setViewTreeLifecycleOwner +import androidx.savedstate.setViewTreeSavedStateRegistryOwner +import com.habitrpg.android.habitica.HabiticaBaseApplication +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.databinding.PetImageviewBinding +import com.habitrpg.android.habitica.models.inventory.Food +import com.habitrpg.android.habitica.models.inventory.Pet +import com.habitrpg.android.habitica.ui.activities.BaseActivity +import com.habitrpg.android.habitica.ui.theme.HabiticaTheme +import com.habitrpg.android.habitica.ui.views.BackgroundScene +import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog +import com.habitrpg.common.habitica.extensions.dpToPx +import com.habitrpg.common.habitica.extensions.layoutInflater +import com.habitrpg.common.habitica.extensions.loadImage +import kotlinx.coroutines.delay + +class SharePetUseCase: UseCase() { + class RequestValues(val petKey: String, val message: String, val context: Context) : + UseCase.RequestValues + + override suspend fun run(requestValues: RequestValues) { + val petWrapper = PetImageviewBinding.inflate(requestValues.context.layoutInflater) + petWrapper.petImageview.loadImage("stable_Pet-" + requestValues.petKey) + petWrapper.root.visibility = View.INVISIBLE + val currentActivity = + HabiticaBaseApplication.getInstance(requestValues.context)?.currentActivity?.get() + (currentActivity?.window?.decorView as? ViewGroup)?.addView(petWrapper.root) + if (currentActivity != null) { + petWrapper.backgroundView.setContent { + HabiticaTheme { + BackgroundScene(Modifier.clip(HabiticaTheme.shapes.large)) + } + } + petWrapper.backgroundView.setParentCompositionContext(currentActivity.toolbar?.findViewTreeCompositionContext()) + petWrapper.root.setViewTreeSavedStateRegistryOwner(currentActivity) + petWrapper.root.setViewTreeLifecycleOwner(currentActivity) + } + val width = if (petWrapper.root.width > 0) petWrapper.root.width else 300.dpToPx(requestValues.context) + val height = 124.dpToPx(requestValues.context) + val sharedImage = Bitmap.createBitmap( + width, + height, + Bitmap.Config.ARGB_8888 + ) + val canvas = Canvas(sharedImage) + var attempts = 0 + while (petWrapper.petImageview.bitmap == null && attempts < 200) { + delay(100) + attempts++ + } + petWrapper.root.doOnNextLayout { + petWrapper.root.draw(canvas) + ((requestValues.context as? BaseActivity) ?: HabiticaBaseApplication.getInstance( + requestValues.context + )?.currentActivity?.get())?.shareContent("pet", requestValues.message, sharedImage) + currentActivity?.toolbar?.removeView(petWrapper.root) + } + val m = FrameLayout.LayoutParams(width, height) + petWrapper.root.layoutParams = m + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt index d93f9a4cc..cb36d2fcc 100755 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt @@ -18,6 +18,7 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.annotation.RequiresApi import androidx.appcompat.app.ActionBarDrawerToggle +import androidx.appcompat.app.AlertDialog import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.height @@ -29,12 +30,14 @@ import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.core.view.children import androidx.core.view.setPadding import androidx.drawerlayout.widget.DrawerLayout import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.setViewTreeLifecycleOwner import androidx.navigation.NavDestination import androidx.navigation.findNavController import androidx.navigation.fragment.NavHostFragment @@ -65,6 +68,7 @@ import com.habitrpg.android.habitica.interactors.CheckClassSelectionUseCase import com.habitrpg.android.habitica.interactors.DisplayItemDropUseCase import com.habitrpg.android.habitica.interactors.NotifyUserUseCase import com.habitrpg.android.habitica.interactors.ShareAvatarUseCase +import com.habitrpg.android.habitica.interactors.SharePetUseCase import com.habitrpg.android.habitica.models.TutorialStep import com.habitrpg.android.habitica.models.user.User import com.habitrpg.android.habitica.models.user.UserQuestStatus diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/BackgroundScene.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/BackgroundScene.kt index 68b596bf8..c9edb3bbb 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/BackgroundScene.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/BackgroundScene.kt @@ -44,10 +44,10 @@ private fun getBackgroundPainter(): ImageBitmap { } @Composable -fun BackgroundScene() { +fun BackgroundScene(modifier: Modifier = Modifier) { val image = getBackgroundPainter() Canvas( - modifier = Modifier + modifier = modifier .height(124.dp) .fillMaxWidth() .zIndex(1f), onDraw = { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaSnackbar.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaSnackbar.kt index d479f8438..48520c4d4 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaSnackbar.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaSnackbar.kt @@ -15,6 +15,7 @@ import androidx.core.view.ViewCompat import com.google.android.material.snackbar.BaseTransientBottomBar import com.google.android.material.snackbar.Snackbar import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.databinding.SnackbarViewBinding import com.habitrpg.common.habitica.helpers.Animations import com.plattysoft.leonids.ParticleSystem @@ -28,18 +29,27 @@ class HabiticaSnackbar */ private constructor(parent: ViewGroup, content: View, callback: ContentViewCallback) : BaseTransientBottomBar(parent, content, callback) { + val binding: SnackbarViewBinding = SnackbarViewBinding.bind(content) fun setTitle(title: CharSequence?): HabiticaSnackbar { - val textView = view.findViewById(R.id.snackbar_title) as? TextView - textView?.text = title - textView?.visibility = if (title != null) View.VISIBLE else View.GONE + binding.snackbarTitle.text = title + binding.snackbarTitle.visibility = if (title != null) View.VISIBLE else View.GONE return this } fun setText(text: CharSequence?): HabiticaSnackbar { - val textView = view.findViewById(R.id.snackbar_text) as? TextView - textView?.text = text - textView?.visibility = if (text != null) View.VISIBLE else View.GONE + binding.snackbarText.text = text + binding.snackbarText.visibility = if (text != null) View.VISIBLE else View.GONE + return this + } + + fun setTitleColor(color: Int): HabiticaSnackbar { + binding.snackbarTitle.setTextColor(color) + return this + } + + fun setTextColor(color: Int): HabiticaSnackbar { + binding.snackbarText.setTextColor(color) return this } @@ -47,20 +57,16 @@ private constructor(parent: ViewGroup, content: View, callback: ContentViewCallb if (icon == null) { return this } - val rightView = view.findViewById(R.id.rightView) - rightView.visibility = View.VISIBLE - val rightIconView = view.findViewById(R.id.rightIconView) - rightIconView.setImageDrawable(icon) - val rightTextView = view.findViewById(R.id.rightTextView) - rightTextView.setTextColor(textColor) - rightTextView.text = text + binding.rightView.visibility = View.VISIBLE + binding.rightIconView.setImageDrawable(icon) + binding.rightTextView.setTextColor(textColor) + binding.rightTextView.text = text return this } fun setLeftIcon(image: Drawable?): HabiticaSnackbar { - val imageView = view.findViewById(R.id.leftImageView) - imageView.setImageDrawable(image) - imageView.visibility = if (image != null) View.VISIBLE else View.GONE + binding.leftImageView.setImageDrawable(image) + binding.leftImageView.visibility = if (image != null) View.VISIBLE else View.GONE return this } @@ -70,16 +76,14 @@ private constructor(parent: ViewGroup, content: View, callback: ContentViewCallb } fun setBackgroundResource(resourceId: Int): HabiticaSnackbar { - val snackbarView = view.findViewById(R.id.snackbar_view) - snackbarView.setBackgroundResource(resourceId) + binding.snackbarView.setBackgroundResource(resourceId) view.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent)) return this } private fun setSpecialView(specialView: View?): HabiticaSnackbar { if (specialView != null) { - val snackbarView = view.findViewById(R.id.content_container) as? LinearLayout - snackbarView?.addView(specialView) + binding.contentContainer.addView(specialView) } return this } @@ -261,14 +265,14 @@ private constructor(parent: ViewGroup, content: View, callback: ContentViewCallb SnackbarDisplayType.FAILURE_BLUE, SnackbarDisplayType.BLUE -> snackbar.setBackgroundResource( R.drawable.snackbar_background_blue ) - SnackbarDisplayType.DROP, SnackbarDisplayType.NORMAL -> snackbar.setBackgroundResource( R.drawable.snackbar_background_gray ) - SnackbarDisplayType.SUCCESS -> snackbar.setBackgroundResource(R.drawable.snackbar_background_green) SnackbarDisplayType.SUBSCRIBER_BENEFIT -> { snackbar.setBackgroundResource(R.drawable.subscriber_benefit_snackbar_bg) + snackbar.setTitleColor(ContextCompat.getColor(container.context, R.color.green_1)) + snackbar.setTextColor(ContextCompat.getColor(container.context, R.color.green_1)) } } @@ -291,15 +295,15 @@ private constructor(parent: ViewGroup, content: View, callback: ContentViewCallb { ParticleSystem( container, - 200, + 300, ContextCompat.getDrawable(container.context, R.drawable.confetti_subs), - 500L + 800L ) .setFadeOut(200L) .setSpeedRange(0.05f, 0.2f) .setScaleRange(0.8f, 1.2f) .setRotationSpeedRange(134f, 164f) - .emit(snackbar.getView(), 150, 500) + .emit(snackbar.getView(), 200, 600) }, 500L ) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/PetSuggestHatchDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/PetSuggestHatchDialog.kt index 9eec10ad2..f9de0e4e9 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/PetSuggestHatchDialog.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/PetSuggestHatchDialog.kt @@ -7,6 +7,7 @@ import android.view.View import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.toBitmap import androidx.lifecycle.lifecycleScope +import com.habitrpg.android.habitica.HabiticaBaseApplication import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.databinding.DialogHatchPetButtonBinding import com.habitrpg.android.habitica.databinding.DialogPetSuggestHatchBinding @@ -168,7 +169,7 @@ class PetSuggestHatchDialog(context: Context) : HabiticaAlertDialog(context) { binding.currencyView.currency = "gems" binding.currencyView.setTextColor(ContextCompat.getColor(context, R.color.white)) addButton(binding.root, true) { _, _ -> - val activity = (getActivity() as? MainActivity) ?: return@addButton + val activity = (getActivity() as? MainActivity) ?: (HabiticaBaseApplication.getInstance(context)?.currentActivity?.get() as? MainActivity) ?: return@addButton if ((userViewModel.user.value?.gemCount ?: hatchPrice) < hatchPrice) { InsufficientGemsDialog(activity, hatchPrice).show() Analytics.sendEvent("show insufficient gems modal", EventCategory.BEHAVIOUR, HitType.EVENT, mapOf("reason" to "pet suggest modal")) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stable/MountBottomSheet.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stable/MountBottomSheet.kt index 08dc3e4f3..78a865311 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stable/MountBottomSheet.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stable/MountBottomSheet.kt @@ -39,6 +39,7 @@ import androidx.compose.ui.draw.scale import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.imageResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight @@ -47,9 +48,12 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.zIndex import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.interactors.ShareMountUseCase import com.habitrpg.android.habitica.ui.theme.HabiticaTheme import com.habitrpg.android.habitica.ui.views.BackgroundScene import com.habitrpg.android.habitica.ui.views.HabiticaButton +import com.habitrpg.common.habitica.helpers.launchCatching +import kotlinx.coroutines.MainScope import java.util.Calendar import kotlin.math.sin @@ -146,6 +150,25 @@ fun MountBottomSheet( .zIndex(2f) ) } + val context = LocalContext.current + HabiticaButton( + background = HabiticaTheme.colors.tintedUiSub, + color = Color.White, + contentPadding = PaddingValues(12.dp), + modifier = Modifier.padding(bottom = 16.dp), + onClick = { + MainScope().launchCatching { + ShareMountUseCase().callInteractor( + ShareMountUseCase.RequestValues( + mount.key, + "", + context + )) + } + onDismiss() + }) { + Text(stringResource(id = R.string.share)) + } HabiticaButton( background = HabiticaTheme.colors.tintedUiSub, color = Color.White, diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stable/MountView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stable/MountView.kt index 68c66e2f2..d6045af30 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stable/MountView.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stable/MountView.kt @@ -15,6 +15,10 @@ class MountView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null ) : FrameLayout(context, attrs) { + val hasLoadedImages: Boolean + get() { + return bodyView.bitmap != null && headView.bitmap != null + } private val bodyView: PixelArtView = PixelArtView(context) private val headView: PixelArtView = PixelArtView(context) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stable/PetBottomSheet.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stable/PetBottomSheet.kt index 4f1b3b338..c9a32f316 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stable/PetBottomSheet.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stable/PetBottomSheet.kt @@ -65,6 +65,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.zIndex import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.interactors.ShareMountUseCase +import com.habitrpg.android.habitica.interactors.SharePetUseCase import com.habitrpg.android.habitica.models.inventory.Food import com.habitrpg.android.habitica.models.inventory.Pet import com.habitrpg.android.habitica.ui.theme.HabiticaTheme @@ -75,6 +77,7 @@ import com.habitrpg.common.habitica.extensions.getThemeColor import com.habitrpg.common.habitica.helpers.MainNavigationController import com.habitrpg.common.habitica.helpers.launchCatching import com.habitrpg.shared.habitica.models.responses.FeedResponse +import kotlinx.coroutines.MainScope import kotlinx.coroutines.delay import kotlin.math.sin @@ -310,6 +313,25 @@ fun PetBottomSheet( } } } + val context = LocalContext.current + HabiticaButton( + background = HabiticaTheme.colors.tintedUiSub, + color = Color.White, + contentPadding = PaddingValues(12.dp), + modifier = Modifier.padding(bottom = 16.dp), + onClick = { + MainScope().launchCatching { + SharePetUseCase().callInteractor( + SharePetUseCase.RequestValues( + pet.key, + "", + context + )) + } + onDismiss() + }) { + Text(stringResource(id = R.string.share)) + } HabiticaButton( background = HabiticaTheme.colors.tintedUiSub, color = Color.White, diff --git a/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/viewmodels/LoginViewModel.kt b/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/viewmodels/LoginViewModel.kt index 488fbe2d4..86a50db88 100644 --- a/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/viewmodels/LoginViewModel.kt +++ b/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/viewmodels/LoginViewModel.kt @@ -154,16 +154,6 @@ class LoginViewModel @Inject constructor( } } - fun login(username: String, password: String, onResult: (Boolean) -> Unit) { - viewModelScope.launch(exceptionBuilder.userFacing(this)) { - val response = apiClient.loginLocal(UserAuth(username, password)).responseData - handleAuthResponse(response) - onResult(response?.id != null) - }.invokeOnCompletion { - onResult(it == null) - } - } - companion object { private const val REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR = 1001 }