diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml
index f4cd4bc47..475c54924 100644
--- a/Habitica/res/values/strings.xml
+++ b/Habitica/res/values/strings.xml
@@ -1405,6 +1405,9 @@
Check out the [Market](/shops/market) to buy just the eggs you need!
Check out the [Market](/shops/market) to buy food or a Saddle to instantly raise your Pet!
Check out the [Market](/shops/market) to buy standard and seasonal Hatching Potions!
+ Open Profile
+ Customize Avatar
+ Share Avatar
- You
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/LevelUpUseCase.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/LevelUpUseCase.kt
index 996c907f8..c9efd542c 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/LevelUpUseCase.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/LevelUpUseCase.kt
@@ -1,8 +1,6 @@
package com.habitrpg.android.habitica.interactors
-import android.graphics.Bitmap
import android.view.ViewGroup
-import androidx.core.graphics.scale
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.DialogLevelup10Binding
import com.habitrpg.android.habitica.helpers.SoundManager
@@ -64,14 +62,6 @@ constructor(
dialogAvatarView.setAvatar(requestValues.user)
}
- val message = requestValues.activity.getString(R.string.share_levelup, requestValues.newLevel)
- val avatarView = AvatarView(requestValues.activity, showBackground = true, showMount = true, showPet = true)
- avatarView.setAvatar(requestValues.user)
- var sharedImage: Bitmap? = null
- avatarView.onAvatarImageReady { image ->
- sharedImage = image?.scale(image.width * 3, image.height * 3, false)
- }
-
val alert = HabiticaAlertDialog(requestValues.activity)
alert.setTitle(requestValues.activity.getString(R.string.levelup_header, requestValues.newLevel))
alert.setAdditionalContentView(customView)
@@ -81,7 +71,16 @@ constructor(
}
}
alert.addButton(R.string.share, false) { _, _ ->
- requestValues.activity.shareContent("levelup", message, sharedImage)
+ MainScope().launchCatching {
+ val usecase = ShareAvatarUseCase()
+ usecase.callInteractor(ShareAvatarUseCase.RequestValues(
+ requestValues.activity,
+ requestValues.user,
+ requestValues.activity.getString(R.string.share_levelup, requestValues.newLevel),
+ "levelup"
+ ))
+ }
+
}
alert.isCelebratory = true
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
new file mode 100644
index 000000000..ae313dca1
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/ShareAvatarUseCase.kt
@@ -0,0 +1,23 @@
+package com.habitrpg.android.habitica.interactors
+
+import android.graphics.Bitmap
+import androidx.core.graphics.scale
+import com.habitrpg.android.habitica.ui.activities.BaseActivity
+import com.habitrpg.common.habitica.views.AvatarView
+import com.habitrpg.shared.habitica.models.Avatar
+import javax.inject.Inject
+
+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)
+ var sharedImage: Bitmap? = null
+ avatarView.onAvatarImageReady { image ->
+ sharedImage = image?.scale(image.width * 3, image.height * 3, false)
+ requestValues.activity.shareContent(requestValues.identifier, requestValues.message, sharedImage)
+ }
+ }
+
+ class RequestValues(val activity: BaseActivity, val avatar: Avatar, val message: String?, val identifier: String): UseCase.RequestValues
+}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/BaseActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/BaseActivity.kt
index 467a02a06..b551ea8a1 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/BaseActivity.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/BaseActivity.kt
@@ -224,11 +224,13 @@ abstract class BaseActivity : AppCompatActivity() {
open fun hideConnectionProblem() {
}
- fun shareContent(identifier: String, message: String, image: Bitmap? = null) {
+ fun shareContent(identifier: String, message: String?, image: Bitmap? = null) {
Analytics.sendEvent("shared", EventCategory.BEHAVIOUR, HitType.EVENT, mapOf("identifier" to identifier))
val sharingIntent = Intent(Intent.ACTION_SEND)
sharingIntent.type = "*/*"
- sharingIntent.putExtra(Intent.EXTRA_TEXT, message)
+ if (message?.isNotBlank() == true) {
+ sharingIntent.putExtra(Intent.EXTRA_TEXT, message)
+ }
if (image != null) {
val path = MediaStore.Images.Media.insertImage(this.contentResolver, image, "${(Date())}", null)
if (path != null) {
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 68cc8b6bc..6c6fdbf24 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
@@ -17,9 +17,18 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.annotation.RequiresApi
import androidx.appcompat.app.ActionBarDrawerToggle
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.Text
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
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.res.stringResource
+import androidx.compose.ui.unit.dp
import androidx.core.view.children
import androidx.core.view.setPadding
import androidx.drawerlayout.widget.DrawerLayout
@@ -45,13 +54,13 @@ import com.habitrpg.android.habitica.helpers.Analytics
import com.habitrpg.android.habitica.helpers.AppConfigManager
import com.habitrpg.android.habitica.helpers.EventCategory
import com.habitrpg.android.habitica.helpers.HitType
-import com.habitrpg.common.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.helpers.NotificationOpenHandler
import com.habitrpg.android.habitica.helpers.SoundManager
import com.habitrpg.android.habitica.helpers.collectAsStateLifecycleAware
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.models.TutorialStep
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.models.user.UserQuestStatus
@@ -61,7 +70,9 @@ import com.habitrpg.android.habitica.ui.theme.HabiticaTheme
import com.habitrpg.android.habitica.ui.viewmodels.MainActivityViewModel
import com.habitrpg.android.habitica.ui.viewmodels.NotificationsViewModel
import com.habitrpg.android.habitica.ui.views.AppHeaderView
+import com.habitrpg.android.habitica.ui.views.ComposableAvatarView
import com.habitrpg.android.habitica.ui.views.GroupPlanMemberList
+import com.habitrpg.android.habitica.ui.views.HabiticaButton
import com.habitrpg.android.habitica.ui.views.SnackbarActivity
import com.habitrpg.android.habitica.ui.views.dialogs.QuestCompletedDialog
import com.habitrpg.android.habitica.ui.views.showAsBottomSheet
@@ -75,6 +86,7 @@ import com.habitrpg.common.habitica.extensions.dpToPx
import com.habitrpg.common.habitica.extensions.getThemeColor
import com.habitrpg.common.habitica.extensions.isUsingNightModeResources
import com.habitrpg.common.habitica.helpers.ExceptionHandler
+import com.habitrpg.common.habitica.helpers.MainNavigationController
import com.habitrpg.common.habitica.helpers.launchCatching
import com.habitrpg.common.habitica.views.AvatarView
import com.habitrpg.shared.habitica.models.responses.MaintenanceResponse
@@ -284,6 +296,63 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
teamPlan = if (canShowTeamHeader) teamPlan else null,
teamPlanMembers = teamPlanMembers,
isMyProfile = true,
+ onAvatarClicked = {
+ showAsBottomSheet { dismiss ->
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(4.dp),
+ modifier = Modifier.padding(22.dp)
+ ) {
+ ComposableAvatarView(avatar = user)
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(15.dp),
+ ) {
+ HabiticaButton(
+ background = HabiticaTheme.colors.tintedUiSub,
+ color = Color.White,
+ onClick = {
+ dismiss()
+ MainNavigationController.navigate(R.id.openProfileActivity)
+ }) {
+ Text(stringResource(id = R.string.open_profile))
+ }
+
+ HabiticaButton(
+ background = HabiticaTheme.colors.tintedUiSub,
+ color = Color.White,
+ onClick = {
+ dismiss()
+ MainNavigationController.navigate(R.id.avatarOverviewFragment)
+ }) {
+ Text(stringResource(id = R.string.customize_avatar))
+ }
+
+ HabiticaButton(
+ background = HabiticaTheme.colors.tintedUiSub,
+ color = Color.White,
+ onClick = {
+ dismiss()
+ user?.let {
+ val usecase = ShareAvatarUseCase()
+ lifecycleScope.launchCatching {
+ usecase.callInteractor(
+ ShareAvatarUseCase.RequestValues(
+ this@MainActivity,
+ it,
+ null,
+ "avatar_bottomsheet"
+ )
+ )
+ }
+ }
+ }) {
+ Text(stringResource(id = R.string.share_avatar))
+ }
+ }
+ }
+ }
+ },
onMemberRowClicked = {
showAsBottomSheet { onClose ->
val group by viewModel.userViewModel.currentTeamPlanGroup.collectAsState(
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/AppHeaderView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/AppHeaderView.kt
index 72582c381..5e6d8786e 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/AppHeaderView.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/AppHeaderView.kt
@@ -106,6 +106,7 @@ fun AppHeaderView(
isMyProfile : Boolean = false,
teamPlan : TeamPlan? = null,
teamPlanMembers : List? = null,
+ onAvatarClicked: (() -> Unit)? = null,
onMemberRowClicked : () -> Unit,
onClassSelectionClicked: () -> Unit
) {
@@ -117,7 +118,7 @@ fun AppHeaderView(
.size(110.dp, 100.dp)
.padding(end = 16.dp)
.clickable {
- MainNavigationController.navigate(R.id.avatarOverviewFragment)
+ onAvatarClicked?.invoke()
}
)
val animationValue =
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/BottomSheetUtils.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/BottomSheetUtils.kt
index ace971c78..389c773d1 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/BottomSheetUtils.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/BottomSheetUtils.kt
@@ -71,7 +71,7 @@ private fun BottomSheetWrapper(
content: @Composable (() -> Unit) -> Unit
) {
val coroutineScope = rememberCoroutineScope()
- val modalBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
+ val modalBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden, skipHalfExpanded = true)
var isSheetOpened by remember { mutableStateOf(false) }
val systemUiController = rememberSystemUiController()
diff --git a/build.gradle b/build.gradle
index 358c13946..5c0337d41 100644
--- a/build.gradle
+++ b/build.gradle
@@ -42,7 +42,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:8.1.0'
+ classpath 'com.android.tools.build:gradle:8.1.1'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
classpath 'com.google.gms:google-services:4.3.15'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.8'
diff --git a/common/src/main/java/com/habitrpg/common/habitica/views/AvatarView.kt b/common/src/main/java/com/habitrpg/common/habitica/views/AvatarView.kt
index d92f56aef..e81c4d0c6 100644
--- a/common/src/main/java/com/habitrpg/common/habitica/views/AvatarView.kt
+++ b/common/src/main/java/com/habitrpg/common/habitica/views/AvatarView.kt
@@ -14,7 +14,6 @@ import android.text.TextUtils
import android.util.AttributeSet
import android.widget.FrameLayout
import android.widget.ImageView
-import androidx.core.graphics.drawable.toBitmap
import androidx.core.view.marginStart
import androidx.core.view.marginTop
import coil.dispose
@@ -73,8 +72,8 @@ class AvatarView : FrameLayout {
if (BuildConfig.DEBUG && (avatar == null || avatarRectF == null)) {
error("Assertion failed")
}
- val viewWidth = if (width > 0) width else layoutParams.width
- val viewHeight = if (height > 0) height else layoutParams.height
+ val viewWidth = if (width > 0) width else (layoutParams?.width ?: 140)
+ val viewHeight = if (height > 0) height else (layoutParams?.height ?: 147)
val canvasRect = Rect(0, 0, if (viewWidth > 0) viewWidth else 140.dpToPx(context), if (viewHeight > 0) viewHeight else 147.dpToPx(context))
if (canvasRect.isEmpty) return null
avatarBitmap = Bitmap.createBitmap(