mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-05-19 20:29:02 +00:00
Refactor Refactor
This commit is contained in:
parent
8ebb743b10
commit
d7523c7bdf
33 changed files with 916 additions and 900 deletions
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#801A181D" />
|
||||
<corners android:radius="@dimen/bar_radius"/>
|
||||
<padding android:left="0dip" android:top="0dip" android:right="0dip" android:bottom="0dip" />
|
||||
</shape>
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="?colorPrimaryDark" />
|
||||
<solid android:color="?colorPrimaryDistinct" />
|
||||
<corners android:radius="4dp" android:topLeftRadius="8dp" android:topRightRadius="8dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:parentTag="android.widget.LinearLayout">
|
||||
<FrameLayout
|
||||
android:id="@+id/icon_wrapper"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:background="@drawable/layout_rounded_bg_content">
|
||||
<com.habitrpg.common.habitica.views.PixelArtView
|
||||
android:id="@+id/icon_view"
|
||||
android:layout_width="@dimen/gear_image_size"
|
||||
android:layout_height="@dimen/gear_image_size"
|
||||
android:layout_gravity="center"/>
|
||||
<ImageView
|
||||
android:id="@+id/local_icon_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/equipment_nothing_equipped"
|
||||
android:layout_gravity="center" />
|
||||
<ImageView
|
||||
android:id="@+id/two_handed_indicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top|end"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="4dp"/>
|
||||
</FrameLayout>
|
||||
<TextView
|
||||
android:id="@+id/title_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/text_dimmed"
|
||||
style="@style/Caption2"
|
||||
android:layout_marginTop="4dp"
|
||||
android:gravity="center_horizontal"/>
|
||||
</merge>
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:parentTag="android.widget.LinearLayout">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="19dp">
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentOverviewItem
|
||||
android:id="@+id/weapon_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:title="@string/outfit_weapon" />
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="1dp" />
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentOverviewItem
|
||||
android:id="@+id/shield_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:title="@string/outfit_shield" />
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="1dp" />
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentOverviewItem
|
||||
android:id="@+id/head_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:title="@string/outfit_head" />
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="1dp" />
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentOverviewItem
|
||||
android:id="@+id/armor_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:title="@string/outfit_armor" />
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="6dp">
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentOverviewItem
|
||||
android:id="@+id/head_accessory_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:title="@string/outfit_headAccessory" />
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="1dp" />
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentOverviewItem
|
||||
android:id="@+id/body_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:title="@string/outfit_body" />
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="1dp" />
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentOverviewItem
|
||||
android:id="@+id/back_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:title="@string/outfit_back" />
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="1dp" />
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentOverviewItem
|
||||
android:id="@+id/eyewear_item"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:title="@string/outfit_eyewear" />
|
||||
</LinearLayout>
|
||||
</merge>
|
||||
|
|
@ -49,12 +49,12 @@
|
|||
android:layout_marginEnd="8dp"
|
||||
android:entries="@array/avatar_sizes"/>
|
||||
</LinearLayout>
|
||||
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentItemRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/avatarShirtView"
|
||||
app:equipmentTitle="@string/avatar_shirt"/>
|
||||
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentItemRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/avatarSkinView"
|
||||
|
|
@ -73,27 +73,27 @@
|
|||
android:divider="?android:listDivider"
|
||||
android:showDividers="middle"
|
||||
android:background="@drawable/layout_rounded_bg_window">
|
||||
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentItemRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/avatarHairColorView"
|
||||
app:equipmentTitle="@string/avatar_color" />
|
||||
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentItemRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/avatarHairBaseView"
|
||||
app:equipmentTitle="@string/avatar_style" />
|
||||
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentItemRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/avatarHairBangsView"
|
||||
app:equipmentTitle="@string/avatar_bangs" />
|
||||
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentItemRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/avatarHairBeardView"
|
||||
app:equipmentTitle="@string/avatar_beard" />
|
||||
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentItemRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/avatarHairMustacheView"
|
||||
|
|
@ -112,34 +112,34 @@
|
|||
android:divider="?android:listDivider"
|
||||
android:showDividers="middle"
|
||||
android:background="@drawable/layout_rounded_bg_window">
|
||||
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentItemRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/avatar_glasses_view"
|
||||
app:equipmentTitle="@string/avatar_glasses"/>
|
||||
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentItemRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/avatarChairView"
|
||||
app:equipmentTitle="@string/avatar_wheelchair"/>
|
||||
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentItemRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/avatar_accent_view"
|
||||
app:equipmentTitle="@string/avatar_accent" />
|
||||
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentItemRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/avatar_animal_ears_view"
|
||||
android:visibility="gone"
|
||||
app:equipmentTitle="@string/animal_ears"/>
|
||||
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentItemRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/avatar_animal_tail_view"
|
||||
android:visibility="gone"
|
||||
app:equipmentTitle="@string/animal_tail"/>
|
||||
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentItemRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/avatar_headband_view"
|
||||
|
|
@ -156,7 +156,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/layout_rounded_bg_window">
|
||||
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentItemRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/avatarBackgroundView"
|
||||
|
|
|
|||
|
|
@ -1,73 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbarSize="3dp"
|
||||
android:scrollbarThumbVertical="@color/scrollbarThumb"
|
||||
android:scrollbars="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginTop="@dimen/spacing_medium">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/row_padding">
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/SectionTitle"
|
||||
android:text="@string/battle_gear"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content" />
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/equip_automatically"
|
||||
android:layout_marginEnd="6dp"/>
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/autoEquipSwitch"/>
|
||||
</LinearLayout>
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentOverviewView
|
||||
android:id="@+id/battlegear_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/spacing_medium" />
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/row_padding">
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/SectionTitle"
|
||||
android:text="@string/costume"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content" />
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:text="@string/wear_costume"/>
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/costumeSwitch"/>
|
||||
</LinearLayout>
|
||||
<com.habitrpg.android.habitica.ui.views.equipment.EquipmentOverviewView
|
||||
android:id="@+id/costume_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/spacing_medium" />
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbarSize="3dp"
|
||||
android:scrollbarThumbVertical="@color/scrollbarThumb"
|
||||
android:scrollbars="vertical" />
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
tools:parentTag="android.widget.LinearLayout">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/positive_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
android:foreground="?selectableItemBackground"
|
||||
android:layout_margin="@dimen/spacing_large">
|
||||
<ImageView
|
||||
android:id="@+id/positive_image_view"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginBottom="@dimen/spacing_medium"
|
||||
tools:src="@drawable/plus"
|
||||
tools:background="@drawable/habit_scoring_circle_selected"/>
|
||||
<TextView
|
||||
android:id="@+id/positive_text_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/positive_habit_form"/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/negative_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
android:foreground="?selectableItemBackground"
|
||||
android:layout_margin="@dimen/spacing_large">
|
||||
<ImageView
|
||||
android:id="@+id/negative_image_view"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginBottom="@dimen/spacing_medium"
|
||||
tools:src="@drawable/minus"
|
||||
tools:background="@drawable/habit_scoring_circle"/>
|
||||
<TextView
|
||||
android:id="@+id/negative_text_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/negative_habit_form"
|
||||
android:textSize="14sp"/>
|
||||
</LinearLayout>
|
||||
</merge>
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
android:foreground="?selectableItemBackground">
|
||||
<ImageView
|
||||
android:id="@+id/image_view"
|
||||
android:layout_width="57dp"
|
||||
android:layout_height="57dp"
|
||||
android:background="@drawable/layout_rounded_bg_task_form"
|
||||
android:scaleType="center"
|
||||
android:layout_marginBottom="@dimen/spacing_medium"/>
|
||||
<TextView
|
||||
android:id="@+id/text_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/trivial"/>
|
||||
</LinearLayout>
|
||||
|
|
@ -1269,6 +1269,7 @@
|
|||
<string name="assign">Assign</string>
|
||||
<string name="edit_assignees">Edit assignees</string>
|
||||
<string name="assign_to">Assign to...</string>
|
||||
<string name="promote_to_manager">Promote to Manager</string>
|
||||
<plurals name="you_x_others">
|
||||
<item quantity="zero">You</item>
|
||||
<item quantity="one">You, %d other</item>
|
||||
|
|
|
|||
|
|
@ -61,6 +61,12 @@ abstract class HabiticaBaseApplication : Application(), Application.ActivityLife
|
|||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
if (!BuildConfig.DEBUG) {
|
||||
try {
|
||||
AmplitudeManager.initialize(this)
|
||||
} catch (ignored: Resources.NotFoundException) {
|
||||
}
|
||||
}
|
||||
registerActivityLifecycleCallbacks(this)
|
||||
setupRealm()
|
||||
setupDagger()
|
||||
|
|
@ -73,12 +79,7 @@ abstract class HabiticaBaseApplication : Application(), Application.ActivityLife
|
|||
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||
|
||||
if (!BuildConfig.DEBUG) {
|
||||
try {
|
||||
AmplitudeManager.initialize(this, sharedPrefs)
|
||||
} catch (ignored: Resources.NotFoundException) {
|
||||
}
|
||||
}
|
||||
|
||||
setupCoil()
|
||||
|
||||
ExceptionHandler.init(analyticsManager)
|
||||
|
|
|
|||
|
|
@ -51,14 +51,16 @@ object AmplitudeManager {
|
|||
sendEvent("navigated", EVENT_CATEGORY_NAVIGATION, EVENT_HITTYPE_PAGEVIEW, additionalData)
|
||||
}
|
||||
|
||||
fun initialize(context: Context, sharedPrefs: SharedPreferences) {
|
||||
fun initialize(context: Context) {
|
||||
amplitude = Amplitude(
|
||||
Configuration(
|
||||
context.getString(R.string.amplitude_app_id),
|
||||
context
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun identify(sharedPrefs: SharedPreferences) {
|
||||
val identify = Identify()
|
||||
.setOnce("androidStore", BuildConfig.STORE)
|
||||
sharedPrefs.getString("launch_screen", "")?.let {
|
||||
|
|
|
|||
|
|
@ -55,12 +55,19 @@ open class Group : RealmObject(), BaseMainObject {
|
|||
}
|
||||
|
||||
fun hasTaskEditPrivileges(userID: String): Boolean {
|
||||
if (leaderID == userID) {
|
||||
if (isLeader(userID)) {
|
||||
return true
|
||||
}
|
||||
return managers?.contains(userID) == true
|
||||
return isManager(userID)
|
||||
}
|
||||
|
||||
fun canManageManagers(userID: String): Boolean {
|
||||
return isLeader(userID)
|
||||
}
|
||||
|
||||
fun isLeader(userID: String): Boolean = leaderID == userID
|
||||
fun isManager(userID: String): Boolean = managers?.contains(userID) == true
|
||||
|
||||
companion object {
|
||||
const val TAVERN_ID = "00000000-0000-4000-A000-000000000000"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import android.view.ViewGroup
|
|||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.children
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
|
@ -53,8 +54,10 @@ 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.GroupPlanMemberList
|
||||
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
|
||||
import com.habitrpg.android.habitica.ui.views.yesterdailies.YesterdailyDialog
|
||||
import com.habitrpg.android.habitica.widget.AvatarStatsWidgetProvider
|
||||
import com.habitrpg.android.habitica.widget.DailiesWidgetProvider
|
||||
|
|
@ -83,18 +86,25 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
|
|||
|
||||
@Inject
|
||||
internal lateinit var apiClient: ApiClient
|
||||
|
||||
@Inject
|
||||
internal lateinit var soundManager: SoundManager
|
||||
|
||||
@Inject
|
||||
internal lateinit var checkClassSelectionUseCase: CheckClassSelectionUseCase
|
||||
|
||||
@Inject
|
||||
internal lateinit var displayItemDropUseCase: DisplayItemDropUseCase
|
||||
|
||||
@Inject
|
||||
internal lateinit var notifyUserUseCase: NotifyUserUseCase
|
||||
|
||||
@Inject
|
||||
internal lateinit var taskRepository: TaskRepository
|
||||
|
||||
@Inject
|
||||
internal lateinit var inventoryRepository: InventoryRepository
|
||||
|
||||
@Inject
|
||||
internal lateinit var appConfigManager: AppConfigManager
|
||||
|
||||
|
|
@ -171,7 +181,8 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
|
|||
}
|
||||
|
||||
val drawerLayout = findViewById<DrawerLayout>(R.id.drawer_layout)
|
||||
drawerFragment = supportFragmentManager.findFragmentById(R.id.navigation_drawer) as? NavigationDrawerFragment
|
||||
drawerFragment =
|
||||
supportFragmentManager.findFragmentById(R.id.navigation_drawer) as? NavigationDrawerFragment
|
||||
drawerFragment?.setUp(R.id.navigation_drawer, drawerLayout, notificationsViewModel)
|
||||
|
||||
drawerToggle = object : ActionBarDrawerToggle(
|
||||
|
|
@ -191,7 +202,10 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
|
|||
window.updateStatusBarColor(getThemeColor(R.attr.colorPrimaryDark), false)
|
||||
isOpeningDrawer = true
|
||||
} else if (slideOffset > 0.5f && isOpeningDrawer == null) {
|
||||
window.updateStatusBarColor(getThemeColor(R.attr.headerBackgroundColor), true)
|
||||
window.updateStatusBarColor(
|
||||
getThemeColor(R.attr.headerBackgroundColor),
|
||||
true
|
||||
)
|
||||
isOpeningDrawer = false
|
||||
}
|
||||
}
|
||||
|
|
@ -221,10 +235,23 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
|
|||
supportActionBar?.setHomeButtonEnabled(true)
|
||||
setupNotifications()
|
||||
setupBottomnavigationLayoutListener()
|
||||
|
||||
|
||||
binding.content.headerView.setContent {
|
||||
HabiticaTheme {
|
||||
AppHeaderView(viewModel.userViewModel)
|
||||
AppHeaderView(viewModel.userViewModel) {
|
||||
showAsBottomSheet { onClose ->
|
||||
GroupPlanMemberList(it, {
|
||||
onClose()
|
||||
FullProfileActivity.open(it)
|
||||
}, { member ->
|
||||
onClose()
|
||||
MainNavigationController.navigate(
|
||||
R.id.inboxMessageListFragment,
|
||||
bundleOf(Pair("username", member.username), Pair("userID", member.id))
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -268,7 +295,12 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
|
|||
private fun setupBottomnavigationLayoutListener() {
|
||||
binding.content.bottomNavigation.viewTreeObserver.addOnGlobalLayoutListener {
|
||||
if (binding.content.bottomNavigation.visibility == View.VISIBLE) {
|
||||
snackbarContainer.setPadding(0, 0, 0, binding.content.bottomNavigation.barHeight + 12.dpToPx(this))
|
||||
snackbarContainer.setPadding(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
binding.content.bottomNavigation.barHeight + 12.dpToPx(this)
|
||||
)
|
||||
} else {
|
||||
snackbarContainer.setPadding(0, 0, 0, 0)
|
||||
}
|
||||
|
|
@ -310,10 +342,16 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
|
|||
|
||||
viewModel.onResume()
|
||||
|
||||
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
|
||||
val navHostFragment =
|
||||
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
|
||||
val navigationController = navHostFragment.navController
|
||||
MainNavigationController.setup(navigationController)
|
||||
navigationController.addOnDestinationChangedListener { _, destination, arguments -> updateToolbarTitle(destination, arguments) }
|
||||
navigationController.addOnDestinationChangedListener { _, destination, arguments ->
|
||||
updateToolbarTitle(
|
||||
destination,
|
||||
arguments
|
||||
)
|
||||
}
|
||||
|
||||
viewModel.requestNotificationPermission.observe(this) { requestNotificationPermission ->
|
||||
if (requestNotificationPermission && (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)) {
|
||||
|
|
@ -337,7 +375,11 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
|
|||
}
|
||||
resumeFromActivity = false
|
||||
|
||||
if ((intent.hasExtra("notificationIdentifier") || intent.hasExtra("openURL")) && lastNotificationOpen != intent.getLongExtra("notificationTimeStamp", 0)) {
|
||||
if ((intent.hasExtra("notificationIdentifier") || intent.hasExtra("openURL")) && lastNotificationOpen != intent.getLongExtra(
|
||||
"notificationTimeStamp",
|
||||
0
|
||||
)
|
||||
) {
|
||||
lastNotificationOpen = intent.getLongExtra("notificationTimeStamp", 0)
|
||||
val identifier = intent.getStringExtra("notificationIdentifier") ?: ""
|
||||
if (intent.hasExtra("sendAnalytics")) {
|
||||
|
|
@ -387,7 +429,8 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
|
|||
private fun updateWidget(widgetClass: Class<*>) {
|
||||
val intent = Intent(this, widgetClass)
|
||||
intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
val ids = AppWidgetManager.getInstance(application).getAppWidgetIds(ComponentName(application, widgetClass))
|
||||
val ids = AppWidgetManager.getInstance(application)
|
||||
.getAppWidgetIds(ComponentName(application, widgetClass))
|
||||
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
|
||||
sendBroadcast(intent)
|
||||
}
|
||||
|
|
@ -412,7 +455,9 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
|
|||
val quest = user.party?.quest
|
||||
if (quest?.completed?.isNotBlank() == true) {
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine()) {
|
||||
val questContent = inventoryRepository.getQuestContent(user.party?.quest?.completed ?: "").firstOrNull()
|
||||
val questContent =
|
||||
inventoryRepository.getQuestContent(user.party?.quest?.completed ?: "")
|
||||
.firstOrNull()
|
||||
if (questContent != null) {
|
||||
QuestCompletedDialog.showWithQuest(this@MainActivity, questContent)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,46 +22,11 @@ import android.view.WindowManager
|
|||
import android.widget.CheckBox
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.widget.AppCompatCheckBox
|
||||
import androidx.compose.animation.animateColor
|
||||
import androidx.compose.animation.core.FastOutLinearInEasing
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.forEachIndexed
|
||||
|
|
@ -79,7 +44,6 @@ import com.habitrpg.android.habitica.extensions.addCancelButton
|
|||
import com.habitrpg.android.habitica.helpers.ExceptionHandler
|
||||
import com.habitrpg.android.habitica.helpers.TaskAlarmManager
|
||||
import com.habitrpg.android.habitica.helpers.launchCatching
|
||||
import com.habitrpg.android.habitica.models.Assignable
|
||||
import com.habitrpg.android.habitica.models.Tag
|
||||
import com.habitrpg.android.habitica.models.members.Member
|
||||
import com.habitrpg.android.habitica.models.social.Challenge
|
||||
|
|
@ -89,10 +53,10 @@ import com.habitrpg.android.habitica.ui.helpers.dismissKeyboard
|
|||
import com.habitrpg.android.habitica.ui.theme.HabiticaTheme
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.TaskFormViewModel
|
||||
import com.habitrpg.android.habitica.ui.views.CompletedAt
|
||||
import com.habitrpg.android.habitica.ui.views.UserRow
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
|
||||
import com.habitrpg.android.habitica.ui.views.showAsBottomSheet
|
||||
import com.habitrpg.android.habitica.ui.views.tasks.AssignSheet
|
||||
import com.habitrpg.android.habitica.ui.views.tasks.AssignedView
|
||||
import com.habitrpg.android.habitica.ui.views.tasks.form.HabitScoringSelector
|
||||
import com.habitrpg.android.habitica.ui.views.tasks.form.LabeledValue
|
||||
import com.habitrpg.android.habitica.ui.views.tasks.form.TaskDifficultySelector
|
||||
|
|
@ -863,7 +827,7 @@ class TaskFormActivity : BaseActivity() {
|
|||
}
|
||||
|
||||
private fun showAssignDialog() {
|
||||
showAsBottomSheet {
|
||||
showAsBottomSheet { onClose ->
|
||||
AssignSheet(
|
||||
groupMembers,
|
||||
assignedIDs,
|
||||
|
|
@ -873,7 +837,8 @@ class TaskFormActivity : BaseActivity() {
|
|||
} else {
|
||||
assignedIDs.add(it)
|
||||
}
|
||||
}
|
||||
},
|
||||
onClose
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -901,152 +866,3 @@ private fun String.toIntCatchOverflow(): Int? {
|
|||
0
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AssignedView(
|
||||
assigned: List<Assignable>,
|
||||
completedAt: Map<String, Date>,
|
||||
backgroundColor: Color,
|
||||
color: Color,
|
||||
onEditClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
showEditButton: Boolean = false
|
||||
) {
|
||||
Column(modifier.fillMaxWidth()) {
|
||||
val rowModifier = Modifier
|
||||
.padding(vertical = 4.dp)
|
||||
.background(
|
||||
backgroundColor,
|
||||
MaterialTheme.shapes.medium
|
||||
)
|
||||
.padding(15.dp, 12.dp)
|
||||
.heightIn(min = 24.dp)
|
||||
.fillMaxWidth()
|
||||
for (assignable in assigned) {
|
||||
UserRow(
|
||||
username = assignable.identifiableName, modifier = rowModifier,
|
||||
color = color,
|
||||
extraContent = {
|
||||
completedAt[assignable.id]?.let { CompletedAt(completedAt = it) }
|
||||
}
|
||||
)
|
||||
}
|
||||
if (showEditButton) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier
|
||||
.clickable {
|
||||
onEditClick()
|
||||
}
|
||||
.padding(vertical = 4.dp)
|
||||
.background(
|
||||
backgroundColor,
|
||||
MaterialTheme.shapes.medium
|
||||
)
|
||||
.padding(15.dp, 12.dp)
|
||||
.heightIn(min = 24.dp)
|
||||
.fillMaxWidth()) {
|
||||
Image(
|
||||
painterResource(R.drawable.edit),
|
||||
null,
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colors.primary)
|
||||
)
|
||||
Text(
|
||||
stringResource(R.string.edit_assignees), color = color,
|
||||
modifier = Modifier.padding(start = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AssignSheet(
|
||||
members: List<Member>,
|
||||
assignedMembers: List<String>,
|
||||
onAssignClick: (String) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(modifier) {
|
||||
Box {
|
||||
Text(
|
||||
stringResource(R.string.assign_to),
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = colorResource(R.color.gray_200),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 8.dp)
|
||||
)
|
||||
TextButton(
|
||||
onClick = {
|
||||
|
||||
},
|
||||
colors = ButtonDefaults.textButtonColors(),
|
||||
modifier = Modifier.align(Alignment.CenterEnd)
|
||||
) {
|
||||
Text(stringResource(R.string.done))
|
||||
}
|
||||
}
|
||||
for (member in members) {
|
||||
val isAssigned = assignedMembers.contains(member.id)
|
||||
val transition = updateTransition(isAssigned, label = "isAssigned")
|
||||
val rotation = transition.animateFloat(
|
||||
label = "isAssigned",
|
||||
transitionSpec = { spring(Spring.DampingRatioLowBouncy, Spring.StiffnessLow) }) {
|
||||
if (it) 0f else 45f
|
||||
}
|
||||
val backgroundColor = transition.animateColor(
|
||||
label = "isAssigned",
|
||||
transitionSpec = { tween(400, easing = FastOutLinearInEasing) }) {
|
||||
if (it) MaterialTheme.colors.primary else colorResource(id = R.color.transparent)
|
||||
}
|
||||
val color = transition.animateColor(
|
||||
label = "isAssigned",
|
||||
transitionSpec = { tween(400, easing = FastOutLinearInEasing) }) {
|
||||
fadeIn(tween(10000))
|
||||
colorResource(if (it) R.color.white else R.color.text_dimmed)
|
||||
}
|
||||
val borderColor = transition.animateColor(
|
||||
label = "isAssigned",
|
||||
transitionSpec = { tween(400, easing = FastOutLinearInEasing) }) {
|
||||
fadeIn(tween(10000))
|
||||
if (it) MaterialTheme.colors.primary else colorResource(id = R.color.text_dimmed)
|
||||
}
|
||||
UserRow(
|
||||
username = member.displayName,
|
||||
color = colorResource(R.color.text_primary),
|
||||
extraContent = {
|
||||
Text(
|
||||
member.formattedUsername ?: "",
|
||||
color = colorResource(R.color.text_ternary)
|
||||
)
|
||||
}, endContent = {
|
||||
Image(
|
||||
painterResource(R.drawable.ic_close_white_24dp),
|
||||
null,
|
||||
colorFilter = ColorFilter.tint(color.value),
|
||||
modifier = Modifier
|
||||
.rotate(rotation.value)
|
||||
.size(24.dp)
|
||||
.background(
|
||||
backgroundColor.value,
|
||||
CircleShape
|
||||
)
|
||||
.border(
|
||||
2.dp,
|
||||
borderColor.value,
|
||||
CircleShape
|
||||
)
|
||||
.padding(3.dp)
|
||||
)
|
||||
}, modifier = Modifier
|
||||
.clickable {
|
||||
member.id?.let { onAssignClick(it) }
|
||||
}
|
||||
.padding(30.dp, 12.dp)
|
||||
.heightIn(min = 24.dp)
|
||||
.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ import androidx.core.content.ContextCompat
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.components.UserComponent
|
||||
import com.habitrpg.android.habitica.databinding.FragmentSkillsBinding
|
||||
import com.habitrpg.android.habitica.databinding.FragmentRecyclerviewBinding
|
||||
import com.habitrpg.android.habitica.helpers.ExceptionHandler
|
||||
import com.habitrpg.android.habitica.helpers.launchCatching
|
||||
import com.habitrpg.android.habitica.models.Skill
|
||||
|
|
@ -32,17 +32,17 @@ import kotlinx.coroutines.flow.combine
|
|||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class SkillsFragment : BaseMainFragment<FragmentSkillsBinding>() {
|
||||
class SkillsFragment : BaseMainFragment<FragmentRecyclerviewBinding>() {
|
||||
internal var adapter: SkillsRecyclerViewAdapter? = null
|
||||
private var selectedSkill: Skill? = null
|
||||
|
||||
override var binding: FragmentSkillsBinding? = null
|
||||
override var binding: FragmentRecyclerviewBinding? = null
|
||||
|
||||
@Inject
|
||||
lateinit var userViewModel: MainUserViewModel
|
||||
|
||||
override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentSkillsBinding {
|
||||
return FragmentSkillsBinding.inflate(inflater, container, false)
|
||||
override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRecyclerviewBinding {
|
||||
return FragmentRecyclerviewBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import androidx.compose.material.MaterialTheme
|
|||
import androidx.compose.material.Shapes
|
||||
import androidx.compose.material.Typography
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
|
|
@ -12,7 +13,10 @@ import androidx.compose.ui.text.font.FontFamily
|
|||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.google.android.material.composethemeadapter.createMdcTheme
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.common.habitica.extensions.getThemeColor
|
||||
|
||||
@Composable
|
||||
fun HabiticaTheme(
|
||||
|
|
@ -113,6 +117,36 @@ val Typography.subtitle3
|
|||
)
|
||||
object HabiticaTheme {
|
||||
val typography: Typography
|
||||
@Composable
|
||||
get() = MaterialTheme.typography
|
||||
}
|
||||
@Composable
|
||||
get() = MaterialTheme.typography
|
||||
|
||||
|
||||
val shapes: Shapes
|
||||
@Composable
|
||||
get() = MaterialTheme.shapes
|
||||
|
||||
val colors: HabiticaColors
|
||||
@Composable
|
||||
get() {
|
||||
val context = LocalContext.current
|
||||
return HabiticaColors(
|
||||
windowBackground = Color(context.getThemeColor(R.attr.colorWindowBackground)),
|
||||
contentBackground = Color(context.getThemeColor(R.attr.colorContentBackground)),
|
||||
contentBackgroundOffset = Color(context.getThemeColor(R.attr.colorContentBackgroundOffset)),
|
||||
textPrimary = Color(context.getThemeColor(R.attr.textColorPrimary)),
|
||||
textSecondary = Color(context.getThemeColor(R.attr.textColorSecondary)),
|
||||
textTertiary = Color(context.getThemeColor(R.attr.colorTertiary)),
|
||||
textDimmed = Color(ContextCompat.getColor(context, R.color.text_dimmed)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class HabiticaColors(
|
||||
val windowBackground: Color,
|
||||
val contentBackground: Color,
|
||||
val contentBackgroundOffset: Color,
|
||||
val textPrimary: Color,
|
||||
val textSecondary: Color,
|
||||
val textTertiary: Color,
|
||||
val textDimmed: Color
|
||||
)
|
||||
|
|
@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.Box
|
|||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
|
|
@ -32,7 +33,6 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.colorResource
|
||||
|
|
@ -44,6 +44,7 @@ import androidx.compose.ui.unit.sp
|
|||
import androidx.core.os.bundleOf
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.helpers.MainNavigationController
|
||||
import com.habitrpg.android.habitica.models.members.Member
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
|
||||
|
||||
|
|
@ -71,6 +72,7 @@ fun UserLevelText(user: User) {
|
|||
@Composable
|
||||
fun AppHeaderView(
|
||||
viewModel: MainUserViewModel,
|
||||
onMemberRowClicked: (List<Member>) -> Unit
|
||||
) {
|
||||
val user by viewModel.user.observeAsState(null)
|
||||
val teamPlan by viewModel.currentTeamPlan.collectAsState(null)
|
||||
|
|
@ -82,6 +84,9 @@ fun AppHeaderView(
|
|||
Modifier
|
||||
.size(110.dp, 100.dp)
|
||||
.padding(end = 16.dp)
|
||||
.clickable {
|
||||
MainNavigationController.navigate(R.id.avatarOverviewFragment)
|
||||
}
|
||||
)
|
||||
Column(modifier = Modifier.height(100.dp)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.weight(1f)) {
|
||||
|
|
@ -112,6 +117,20 @@ fun AppHeaderView(
|
|||
value = user?.stats?.mp ?: 0.0,
|
||||
maxValue = user?.stats?.maxMP?.toDouble() ?: 0.0,
|
||||
displayCompact = teamPlan != null,
|
||||
modifier = Modifier.weight(1f)
|
||||
.clickable {
|
||||
MainNavigationController.navigate(R.id.skillsFragment)
|
||||
}
|
||||
)
|
||||
} else if ((user?.stats?.lvl ?: 0) < 10) {
|
||||
LabeledBar(
|
||||
icon = HabiticaIconsHelper.imageOfMagic(),
|
||||
label = stringResource(R.string.unlock_level, 10),
|
||||
color = colorResource(R.color.mpColor),
|
||||
value = 0.0,
|
||||
maxValue = 1.0,
|
||||
displayCompact = teamPlan != null,
|
||||
disabled = true,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
}
|
||||
|
|
@ -162,10 +181,7 @@ fun AppHeaderView(
|
|||
colorResource(R.color.window_background)
|
||||
)
|
||||
.clickable {
|
||||
MainNavigationController.navigate(
|
||||
R.id.guildFragment,
|
||||
bundleOf("groupID" to teamPlan?.id)
|
||||
)
|
||||
teamPlanMembers?.let { onMemberRowClicked(it) }
|
||||
}
|
||||
) {
|
||||
for (member in teamPlanMembers?.filter { it.id != user?.id }?.take(6) ?: emptyList()) {
|
||||
|
|
@ -185,25 +201,16 @@ fun AppHeaderView(
|
|||
}
|
||||
}
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
if (user?.hasClass == true) {
|
||||
val icon = when (user?.stats?.habitClass) {
|
||||
"warrior" -> HabiticaIconsHelper.imageOfWarriorLightBg().asImageBitmap()
|
||||
"wizard" -> HabiticaIconsHelper.imageOfMageLightBg().asImageBitmap()
|
||||
"healer" -> HabiticaIconsHelper.imageOfHealerLightBg().asImageBitmap()
|
||||
"rogue" -> HabiticaIconsHelper.imageOfRogueLightBg().asImageBitmap()
|
||||
else -> null
|
||||
}
|
||||
if (icon != null) {
|
||||
Image(bitmap = icon, "", modifier = Modifier.padding(end = 4.dp))
|
||||
}
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.defaultMinSize(minHeight = 28.dp)) {
|
||||
ClassIcon(className = user?.stats?.habitClass, hasClass = user?.hasClass ?: false, modifier = Modifier.padding(4.dp))
|
||||
user?.let { UserLevelText(it) }
|
||||
Spacer(Modifier.weight(1f))
|
||||
user?.hourglassCount?.toDouble()
|
||||
?.let { CurrencyText("hourglasses", it, modifier = Modifier.padding(end = 12.dp)) }
|
||||
CurrencyText("gold", user?.stats?.gp ?: 0.0, modifier = Modifier.padding(end = 12.dp))
|
||||
CurrencyText("gems", user?.gemCount?.toDouble() ?: 0.0)
|
||||
CurrencyText("gems", user?.gemCount?.toDouble() ?: 0.0, modifier = Modifier.clickable {
|
||||
MainNavigationController.navigate(R.id.gemPurchaseActivity, bundleOf(Pair("openSubscription", false)))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.habitrpg.android.habitica.ui.views
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
|
||||
@Composable
|
||||
fun ClassIcon(className: String?, hasClass: Boolean, modifier: Modifier = Modifier) {
|
||||
if (hasClass) {
|
||||
val icon = when (className) {
|
||||
"warrior" -> HabiticaIconsHelper.imageOfWarriorLightBg().asImageBitmap()
|
||||
"wizard" -> HabiticaIconsHelper.imageOfMageLightBg().asImageBitmap()
|
||||
"healer" -> HabiticaIconsHelper.imageOfHealerLightBg().asImageBitmap()
|
||||
"rogue" -> HabiticaIconsHelper.imageOfRogueLightBg().asImageBitmap()
|
||||
else -> null
|
||||
}
|
||||
if (icon != null) {
|
||||
Image(bitmap = icon, "", modifier = modifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,218 +0,0 @@
|
|||
package com.habitrpg.android.habitica.ui.views
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.models.user.Outfit
|
||||
import com.habitrpg.android.habitica.models.user.Preferences
|
||||
import com.habitrpg.android.habitica.ui.theme.HabiticaTheme
|
||||
import com.habitrpg.android.habitica.ui.theme.caption2
|
||||
|
||||
@Composable
|
||||
fun OverviewItem(
|
||||
text: String,
|
||||
iconName: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
isTwoHanded: Boolean = false
|
||||
) {
|
||||
val hasIcon = iconName?.isNotBlank() == true
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier
|
||||
.width(70.dp)
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.size(70.dp)
|
||||
.clip(MaterialTheme.shapes.small)
|
||||
.background(colorResource(if (hasIcon) R.color.gray_700 else R.color.gray_10)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (isTwoHanded) {
|
||||
Image(painterResource(R.drawable.equipment_two_handed), null)
|
||||
} else if (hasIcon) {
|
||||
PixelArtView(
|
||||
imageName = iconName, Modifier
|
||||
.size(70.dp)
|
||||
)
|
||||
} else {
|
||||
Image(painterResource(R.drawable.equipment_nothing_equipped), null)
|
||||
}
|
||||
}
|
||||
Text(
|
||||
text,
|
||||
style = HabiticaTheme.typography.caption2,
|
||||
color = colorResource(R.color.gray_400),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EquipmentOverviewView(
|
||||
outfit: Outfit?,
|
||||
onEquipmentTap: (String, String?) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(18.dp),
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.background(colorResource(R.color.gray_50))
|
||||
.padding(12.dp)
|
||||
) {
|
||||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
OverviewItem(stringResource(R.string.outfit_weapon), outfit?.weapon.let { "shop_$it" }, Modifier.clickable {
|
||||
onEquipmentTap("weapon", null)
|
||||
})
|
||||
OverviewItem(stringResource(R.string.outfit_shield), outfit?.shield.let { "shop_$it" }, Modifier.clickable {
|
||||
onEquipmentTap("shield", null)
|
||||
})
|
||||
OverviewItem(stringResource(R.string.outfit_head), outfit?.head.let { "shop_$it" }, Modifier.clickable {
|
||||
onEquipmentTap("head", null)
|
||||
})
|
||||
OverviewItem(stringResource(R.string.outfit_armor), outfit?.armor.let { "shop_$it" }, Modifier.clickable {
|
||||
onEquipmentTap("armor", null)
|
||||
})
|
||||
}
|
||||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
OverviewItem(
|
||||
stringResource(R.string.outfit_headAccessory),
|
||||
outfit?.headAccessory.let { "shop_$it" }, Modifier.clickable {
|
||||
onEquipmentTap("headAccessory", null)
|
||||
})
|
||||
OverviewItem(stringResource(R.string.outfit_body), outfit?.body.let { "shop_$it" }, Modifier.clickable {
|
||||
onEquipmentTap("body", null)
|
||||
})
|
||||
OverviewItem(stringResource(R.string.outfit_back), outfit?.back.let { "shop_$it" }, Modifier.clickable {
|
||||
onEquipmentTap("back", null)
|
||||
})
|
||||
OverviewItem(
|
||||
stringResource(R.string.outfit_eyewear),
|
||||
outfit?.eyeWear.let { "shop_$it" }, Modifier.clickable {
|
||||
onEquipmentTap("eyewear", null)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AvatarCustomizationOverviewView(
|
||||
preferences: Preferences?,
|
||||
onCustomizationTap: (String, String?) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(18.dp),
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.background(colorResource(R.color.gray_50))
|
||||
.padding(12.dp)
|
||||
) {
|
||||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_shirt),
|
||||
preferences?.shirt.let { "${preferences?.size}_shirt$it" }, Modifier.clickable {
|
||||
onCustomizationTap("shirt", null)
|
||||
})
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_skin),
|
||||
preferences?.skin.let { "skin_$it" },
|
||||
Modifier.clickable {
|
||||
onCustomizationTap("skin", null)
|
||||
})
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_hair_color),
|
||||
if (preferences?.hair?.color != null && preferences.hair?.color != "") "hair_bangs_1_" + preferences.hair?.color else "",
|
||||
Modifier.clickable {
|
||||
onCustomizationTap("hair", "color")
|
||||
}
|
||||
)
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_hair_bangs),
|
||||
if (preferences?.hair?.bangs != null && preferences.hair?.bangs != 0) "hair_bangs_" + preferences.hair?.bangs + "_" + preferences.hair?.color else "",
|
||||
Modifier.clickable {
|
||||
onCustomizationTap("hair", "bangs")
|
||||
}
|
||||
)
|
||||
}
|
||||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_style),
|
||||
if (preferences?.hair?.base != null && preferences.hair?.base != 0) "hair_base_" + preferences.hair?.base + "_" + preferences.hair?.color else "",
|
||||
Modifier.clickable {
|
||||
onCustomizationTap("hair", "base")
|
||||
}
|
||||
)
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_mustache),
|
||||
if (preferences?.hair?.mustache != null && preferences.hair?.mustache != 0) "hair_mustache_" + preferences.hair?.mustache + "_" + preferences.hair?.color else "",
|
||||
Modifier.clickable {
|
||||
onCustomizationTap("hair", "mustache")
|
||||
}
|
||||
)
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_beard),
|
||||
if (preferences?.hair?.beard != null && preferences.hair?.beard != 0) "hair_beard_" + preferences.hair?.beard + "_" + preferences.hair?.color else "",
|
||||
Modifier.clickable {
|
||||
onCustomizationTap("hair", "beard")
|
||||
}
|
||||
)
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_flower),
|
||||
if (preferences?.hair?.flower != null && preferences.hair?.flower != 0) "hair_flower_" + preferences.hair?.flower else "",
|
||||
Modifier.clickable {
|
||||
onCustomizationTap("hair", "flower")
|
||||
}
|
||||
)
|
||||
}
|
||||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_wheelchair),
|
||||
preferences?.chair?.let { if (it.startsWith("handleless")) "chair_$it" else it })
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_background),
|
||||
preferences?.background.let { "background_$it" })
|
||||
Box(Modifier.size(70.dp))
|
||||
Box(Modifier.size(70.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun EquipmentOverviewItemPreview() {
|
||||
Column(Modifier.width(320.dp)) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
OverviewItem("Main-Hand", "shop_weapon_warrior_1")
|
||||
OverviewItem("Off-Hand", null, isTwoHanded = true)
|
||||
OverviewItem("Armor", null)
|
||||
}
|
||||
EquipmentOverviewView(null, { _, _ -> })
|
||||
AvatarCustomizationOverviewView(null, { _, _ -> })
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
package com.habitrpg.android.habitica.ui.views
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.models.auth.LocalAuthentication
|
||||
import com.habitrpg.android.habitica.models.members.Member
|
||||
import com.habitrpg.android.habitica.models.user.Authentication
|
||||
import com.habitrpg.android.habitica.models.user.Profile
|
||||
import com.habitrpg.android.habitica.models.user.Stats
|
||||
import com.habitrpg.android.habitica.ui.theme.HabiticaTheme
|
||||
import com.habitrpg.common.habitica.extensions.getThemeColor
|
||||
import kotlin.random.Random
|
||||
|
||||
@Composable
|
||||
fun GroupPlanMemberList(
|
||||
members: List<Member>,
|
||||
onMemberClicked: (String) -> Unit,
|
||||
onMoreClicked: (Member) -> Unit
|
||||
) {
|
||||
LazyColumn {
|
||||
for (member in members) {
|
||||
item {
|
||||
MemberItem(
|
||||
member,
|
||||
onMemberClicked,
|
||||
onMoreClicked,
|
||||
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MemberItem(
|
||||
member: Member,
|
||||
onMemberClicked: (String) -> Unit,
|
||||
onMoreClicked: (Member) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Box(
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.clip(HabiticaTheme.shapes.large)
|
||||
.background(HabiticaTheme.colors.windowBackground)
|
||||
.clickable {
|
||||
member.id?.let { onMemberClicked(it) }
|
||||
}
|
||||
) {
|
||||
TextButton(
|
||||
onClick = { onMoreClicked(member) }, modifier = Modifier
|
||||
.size(32.dp)
|
||||
.background(
|
||||
Color(LocalContext.current.getThemeColor(R.attr.colorAccent)),
|
||||
WobblyCircle
|
||||
)
|
||||
.align(Alignment.TopEnd)
|
||||
) {
|
||||
Image(painterResource(R.drawable.menu_messages), null)
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
modifier = Modifier.padding(14.dp)
|
||||
) {
|
||||
ComposableAvatarView(avatar = member, modifier = Modifier.size(94.dp, 98.dp))
|
||||
Column(verticalArrangement = Arrangement.SpaceBetween, modifier = Modifier.height(100.dp)) {
|
||||
Text(
|
||||
member.displayName,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
fontSize = 16.sp,
|
||||
color = HabiticaTheme.colors.textPrimary
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
member.formattedUsername ?: "",
|
||||
color = HabiticaTheme.colors.textSecondary
|
||||
)
|
||||
Spacer(
|
||||
Modifier
|
||||
.weight(1.0f)
|
||||
.background(Color.Red)
|
||||
)
|
||||
ClassIcon(
|
||||
member.stats?.habitClass,
|
||||
member.hasClass,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
BuffIcon(member.stats?.isBuffed)
|
||||
CurrencyText(currency = "gold", value = member.stats?.gp ?: 0.0)
|
||||
}
|
||||
LabeledBar(
|
||||
color = colorResource(R.color.hpColor),
|
||||
barColor = HabiticaTheme.colors.contentBackgroundOffset,
|
||||
value = member.stats?.hp ?: 0.0,
|
||||
maxValue = (member.stats?.maxHealth ?: 0).toDouble(),
|
||||
displayCompact = true
|
||||
)
|
||||
LabeledBar(
|
||||
color = colorResource(R.color.xpColor),
|
||||
barColor = HabiticaTheme.colors.contentBackgroundOffset,
|
||||
value = member.stats?.exp ?: 0.0,
|
||||
maxValue = (member.stats?.toNextLevel ?: 0).toDouble(),
|
||||
displayCompact = true
|
||||
)
|
||||
if (member.hasClass) {
|
||||
LabeledBar(
|
||||
color = colorResource(R.color.mpColor),
|
||||
barColor = HabiticaTheme.colors.contentBackgroundOffset,
|
||||
value = member.stats?.mp ?: 0.0,
|
||||
maxValue = (member.stats?.maxMP ?: 0).toDouble(),
|
||||
displayCompact = true
|
||||
)
|
||||
}
|
||||
Row(horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
Text(
|
||||
stringResource(R.string.level_unabbreviated, member.stats?.lvl ?: 0),
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = HabiticaTheme.colors.textPrimary
|
||||
)
|
||||
Text(
|
||||
"", fontWeight = FontWeight.SemiBold, fontSize = 14.sp,
|
||||
color = HabiticaTheme.colors.textPrimary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BuffIcon(buffed: Boolean?, modifier: Modifier = Modifier) {
|
||||
if (buffed == true) {
|
||||
Image(HabiticaIconsHelper.imageOfBuffIcon().asImageBitmap(), null, modifier = modifier)
|
||||
}
|
||||
}
|
||||
|
||||
private class MemberProvider : PreviewParameterProvider<Member> {
|
||||
override val values: Sequence<Member>
|
||||
get() {
|
||||
val list = mutableListOf<Member>()
|
||||
for (x in 0..5) {
|
||||
val member = Member()
|
||||
member.profile = Profile()
|
||||
member.profile?.name = "User $x"
|
||||
member.authentication = Authentication()
|
||||
member.authentication?.localAuthentication = LocalAuthentication()
|
||||
member.authentication?.localAuthentication?.username = "user${x}"
|
||||
member.stats = Stats()
|
||||
member.stats?.hp = Random.nextDouble()
|
||||
member.stats?.maxHealth = 50
|
||||
member.stats?.toNextLevel = Random.nextInt()
|
||||
member.stats?.exp =
|
||||
Random.nextDouble(until = (member.stats?.toNextLevel ?: 0).toDouble())
|
||||
member.stats?.maxMP = Random.nextInt()
|
||||
member.stats?.mp = Random.nextDouble(until = (member.stats?.maxMP ?: 0).toDouble())
|
||||
member.stats?.lvl = Random.nextInt()
|
||||
list.add(member)
|
||||
}
|
||||
return list.asSequence()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
private fun Preview(@PreviewParameter(MemberProvider::class) member: Member) {
|
||||
MemberItem(member = member, onMemberClicked = {}, onMoreClicked = {})
|
||||
}
|
||||
|
|
@ -6,12 +6,14 @@ import androidx.compose.animation.core.animateFloatAsState
|
|||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.LinearProgressIndicator
|
||||
import androidx.compose.material.ProgressIndicatorDefaults
|
||||
|
|
@ -19,43 +21,50 @@ import androidx.compose.material.Text
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.habitrpg.android.habitica.R
|
||||
import java.math.RoundingMode
|
||||
import java.text.NumberFormat
|
||||
import com.habitrpg.android.habitica.ui.theme.HabiticaTheme
|
||||
import com.habitrpg.common.habitica.helpers.NumberAbbreviator
|
||||
|
||||
@Composable
|
||||
fun LabeledBar(
|
||||
icon: Bitmap,
|
||||
label: String,
|
||||
color: Color,
|
||||
modifier: Modifier = Modifier,
|
||||
icon: Bitmap? = null,
|
||||
label: String? = null,
|
||||
color: Color = colorResource(R.color.brand),
|
||||
barColor: Color = HabiticaTheme.colors.windowBackground,
|
||||
value: Double,
|
||||
maxValue: Double,
|
||||
displayCompact: Boolean,
|
||||
modifier: Modifier = Modifier
|
||||
disabled: Boolean = false
|
||||
) {
|
||||
val formatter = NumberFormat.getInstance()
|
||||
formatter.maximumFractionDigits = 1
|
||||
formatter.roundingMode = RoundingMode.UP
|
||||
formatter.isGroupingUsed = true
|
||||
|
||||
val cleanedMaxVlaue = java.lang.Double.max(1.0, maxValue)
|
||||
|
||||
val animatedValue = animateFloatAsState(
|
||||
targetValue = value.toFloat(),
|
||||
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec,
|
||||
).value
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = modifier) {
|
||||
AnimatedVisibility(
|
||||
visible = !displayCompact,
|
||||
enter = slideInHorizontally { -18 },
|
||||
exit = slideOutHorizontally { -18 }) {
|
||||
Image(icon.asImageBitmap(), null, modifier = Modifier.padding(end = 8.dp))
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier.alpha(if (disabled) 0.5f else 1.0f)
|
||||
) {
|
||||
icon?.let {
|
||||
AnimatedVisibility(visible = !displayCompact,
|
||||
enter = slideInHorizontally { -18 },
|
||||
exit = slideOutHorizontally { -18 }) {
|
||||
Image(
|
||||
it.asImageBitmap(), null, modifier = Modifier.padding(end = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
LinearProgressIndicator(
|
||||
|
|
@ -64,7 +73,7 @@ fun LabeledBar(
|
|||
.fillMaxWidth()
|
||||
.clip(CircleShape)
|
||||
.height(8.dp),
|
||||
backgroundColor = colorResource(R.color.window_background),
|
||||
backgroundColor = barColor,
|
||||
color = color
|
||||
)
|
||||
AnimatedVisibility(visible = !displayCompact) {
|
||||
|
|
@ -72,15 +81,51 @@ fun LabeledBar(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.padding(top = 2.dp)
|
||||
) {
|
||||
Text(
|
||||
"${formatter.format(animatedValue)} / ${formatter.format(cleanedMaxVlaue)}",
|
||||
fontSize = 12.sp,
|
||||
color = colorResource(R.color.text_ternary)
|
||||
)
|
||||
if (!disabled) {
|
||||
Text(
|
||||
"${NumberAbbreviator.abbreviate(LocalContext.current, animatedValue)} / ${NumberAbbreviator.abbreviate(LocalContext.current, cleanedMaxVlaue)}",
|
||||
fontSize = 12.sp,
|
||||
color = colorResource(R.color.text_ternary)
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.weight(1f))
|
||||
Text(label, fontSize = 12.sp, color = colorResource(R.color.text_ternary))
|
||||
if (label != null) {
|
||||
Text(label, fontSize = 12.sp, color = colorResource(R.color.text_ternary))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
private fun Preview() {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(10.dp), modifier = Modifier.width(180.dp)) {
|
||||
LabeledBar(
|
||||
icon = HabiticaIconsHelper.imageOfHeartLightBg(),
|
||||
label = stringResource(id = R.string.health),
|
||||
color = colorResource(R.color.hpColor),
|
||||
value = 10.0,
|
||||
maxValue = 50.0,
|
||||
displayCompact = false
|
||||
)
|
||||
LabeledBar(
|
||||
icon = HabiticaIconsHelper.imageOfExperience(),
|
||||
label = stringResource(id = R.string.XP_default),
|
||||
color = colorResource(R.color.xpColor),
|
||||
value = 100123.0,
|
||||
maxValue = 50000000000000.0,
|
||||
displayCompact = false
|
||||
)
|
||||
LabeledBar(
|
||||
icon = HabiticaIconsHelper.imageOfMagic(),
|
||||
label = stringResource(id = R.string.unlock_level, 10),
|
||||
color = colorResource(R.color.mpColor),
|
||||
value = 10.0,
|
||||
maxValue = 5000.0,
|
||||
displayCompact = false,
|
||||
disabled = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.habitrpg.android.habitica.ui.views
|
||||
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Outline
|
||||
import androidx.compose.ui.graphics.Path
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
|
||||
object WobblyCircle : Shape {
|
||||
override fun createOutline(
|
||||
size: Size, layoutDirection: LayoutDirection, density: Density
|
||||
): Outline {
|
||||
val path = Path().apply {
|
||||
addOval(Rect(Offset(0f, -size.height / 0.4f),
|
||||
Size(size.width * 1.8f, size.height * 1.6f)))
|
||||
}
|
||||
|
||||
return Outline.Generic(path)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.habitrpg.android.habitica.ui.views
|
||||
package com.habitrpg.android.habitica.ui.views.equipment
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
package com.habitrpg.android.habitica.ui.views.equipment
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.EquipmentOverviewItemBinding
|
||||
import com.habitrpg.common.habitica.extensions.layoutInflater
|
||||
import com.habitrpg.common.habitica.extensions.loadImage
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
|
||||
|
||||
class EquipmentOverviewItem @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
private var binding: EquipmentOverviewItemBinding = EquipmentOverviewItemBinding.inflate(context.layoutInflater, this)
|
||||
|
||||
init {
|
||||
if (attrs != null) {
|
||||
val styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.EquipmentOverviewItem)
|
||||
binding.titleView.text = styledAttrs.getString(R.styleable.EquipmentOverviewItem_title)
|
||||
styledAttrs.recycle()
|
||||
}
|
||||
orientation = VERTICAL
|
||||
}
|
||||
|
||||
var identifier: String = ""
|
||||
|
||||
fun set(key: String?, isTwoHanded: Boolean = false, isDisabledFromTwoHand: Boolean = false) {
|
||||
identifier = key ?: ""
|
||||
binding.twoHandedIndicator.setImageDrawable(null)
|
||||
if (identifier.isNotEmpty() && !identifier.endsWith("base_0")) {
|
||||
binding.iconView.loadImage("shop_$key")
|
||||
binding.localIconView.visibility = View.GONE
|
||||
binding.iconView.visibility = View.VISIBLE
|
||||
binding.iconWrapper.background = ContextCompat.getDrawable(context, R.drawable.layout_rounded_bg_content)
|
||||
if (isTwoHanded) {
|
||||
binding.twoHandedIndicator.setImageDrawable(BitmapDrawable(context.resources, HabiticaIconsHelper.imageOfTwoHandedIcon()))
|
||||
}
|
||||
} else {
|
||||
binding.localIconView.visibility = View.VISIBLE
|
||||
binding.iconView.visibility = View.GONE
|
||||
if (isDisabledFromTwoHand) {
|
||||
binding.iconWrapper.background = ContextCompat.getDrawable(context, R.drawable.layout_rounded_bg_content)
|
||||
binding.localIconView.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.equipment_two_handed))
|
||||
} else {
|
||||
binding.iconWrapper.background = ContextCompat.getDrawable(context, R.drawable.layout_rounded_bg_gray_10)
|
||||
binding.localIconView.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.equipment_nothing_equipped))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +1,217 @@
|
|||
package com.habitrpg.android.habitica.ui.views.equipment
|
||||
package com.habitrpg.android.habitica.ui.views
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.LinearLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.EquipmentOverviewViewBinding
|
||||
import com.habitrpg.common.habitica.extensions.layoutInflater
|
||||
import com.habitrpg.android.habitica.extensions.setScaledPadding
|
||||
import com.habitrpg.android.habitica.models.user.Outfit
|
||||
import com.habitrpg.android.habitica.models.user.Preferences
|
||||
import com.habitrpg.android.habitica.ui.theme.HabiticaTheme
|
||||
import com.habitrpg.android.habitica.ui.theme.caption2
|
||||
|
||||
class EquipmentOverviewView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
var onNavigate: ((String, String) -> Unit)? = null
|
||||
private var binding: EquipmentOverviewViewBinding = EquipmentOverviewViewBinding.inflate(context.layoutInflater, this)
|
||||
|
||||
init {
|
||||
background = ContextCompat.getDrawable(context, R.drawable.layout_rounded_bg_gray_50)
|
||||
setScaledPadding(context, 12, 12, 12, 12)
|
||||
orientation = VERTICAL
|
||||
|
||||
binding.weaponItem.setOnClickListener { onNavigate?.invoke("weapon", binding.weaponItem.identifier) }
|
||||
binding.shieldItem.setOnClickListener { onNavigate?.invoke("shield", binding.shieldItem.identifier) }
|
||||
binding.headItem.setOnClickListener { onNavigate?.invoke("head", binding.headItem.identifier) }
|
||||
binding.armorItem.setOnClickListener { onNavigate?.invoke("armor", binding.armorItem.identifier) }
|
||||
binding.headAccessoryItem.setOnClickListener { onNavigate?.invoke("headAccessory", binding.headAccessoryItem.identifier) }
|
||||
binding.bodyItem.setOnClickListener { onNavigate?.invoke("body", binding.bodyItem.identifier) }
|
||||
binding.backItem.setOnClickListener { onNavigate?.invoke("back", binding.backItem.identifier) }
|
||||
binding.eyewearItem.setOnClickListener { onNavigate?.invoke("eyewear", binding.eyewearItem.identifier) }
|
||||
}
|
||||
|
||||
fun updateData(outfit: Outfit?, isWeaponTwoHanded: Boolean = false) {
|
||||
binding.weaponItem.set(outfit?.weapon, isWeaponTwoHanded)
|
||||
binding.shieldItem.set(outfit?.shield, false, isWeaponTwoHanded)
|
||||
binding.headItem.set(outfit?.head)
|
||||
binding.armorItem.set(outfit?.armor)
|
||||
binding.headAccessoryItem.set(outfit?.headAccessory)
|
||||
binding.bodyItem.set(outfit?.body)
|
||||
binding.backItem.set(outfit?.back)
|
||||
binding.eyewearItem.set(outfit?.eyeWear)
|
||||
@Composable
|
||||
fun OverviewItem(
|
||||
text: String,
|
||||
iconName: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
isTwoHanded: Boolean = false
|
||||
) {
|
||||
val hasIcon = iconName?.isNotBlank() == true
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier
|
||||
.width(70.dp)
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.size(70.dp)
|
||||
.clip(MaterialTheme.shapes.small)
|
||||
.background(colorResource(if (hasIcon) R.color.gray_700 else R.color.gray_10)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (isTwoHanded) {
|
||||
Image(painterResource(R.drawable.equipment_two_handed), null)
|
||||
} else if (hasIcon) {
|
||||
PixelArtView(
|
||||
imageName = iconName, Modifier
|
||||
.size(70.dp)
|
||||
)
|
||||
} else {
|
||||
Image(painterResource(R.drawable.equipment_nothing_equipped), null)
|
||||
}
|
||||
}
|
||||
Text(
|
||||
text,
|
||||
style = HabiticaTheme.typography.caption2,
|
||||
color = colorResource(R.color.gray_400),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EquipmentOverviewView(
|
||||
outfit: Outfit?,
|
||||
onEquipmentTap: (String, String?) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(18.dp),
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.background(colorResource(R.color.gray_50))
|
||||
.padding(12.dp)
|
||||
) {
|
||||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
OverviewItem(stringResource(R.string.outfit_weapon), outfit?.weapon.let { "shop_$it" }, Modifier.clickable {
|
||||
onEquipmentTap("weapon", null)
|
||||
})
|
||||
OverviewItem(stringResource(R.string.outfit_shield), outfit?.shield.let { "shop_$it" }, Modifier.clickable {
|
||||
onEquipmentTap("shield", null)
|
||||
})
|
||||
OverviewItem(stringResource(R.string.outfit_head), outfit?.head.let { "shop_$it" }, Modifier.clickable {
|
||||
onEquipmentTap("head", null)
|
||||
})
|
||||
OverviewItem(stringResource(R.string.outfit_armor), outfit?.armor.let { "shop_$it" }, Modifier.clickable {
|
||||
onEquipmentTap("armor", null)
|
||||
})
|
||||
}
|
||||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
OverviewItem(
|
||||
stringResource(R.string.outfit_headAccessory),
|
||||
outfit?.headAccessory.let { "shop_$it" }, Modifier.clickable {
|
||||
onEquipmentTap("headAccessory", null)
|
||||
})
|
||||
OverviewItem(stringResource(R.string.outfit_body), outfit?.body.let { "shop_$it" }, Modifier.clickable {
|
||||
onEquipmentTap("body", null)
|
||||
})
|
||||
OverviewItem(stringResource(R.string.outfit_back), outfit?.back.let { "shop_$it" }, Modifier.clickable {
|
||||
onEquipmentTap("back", null)
|
||||
})
|
||||
OverviewItem(
|
||||
stringResource(R.string.outfit_eyewear),
|
||||
outfit?.eyeWear.let { "shop_$it" }, Modifier.clickable {
|
||||
onEquipmentTap("eyewear", null)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AvatarCustomizationOverviewView(
|
||||
preferences: Preferences?,
|
||||
onCustomizationTap: (String, String?) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(18.dp),
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.background(colorResource(R.color.gray_50))
|
||||
.padding(12.dp)
|
||||
) {
|
||||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_shirt),
|
||||
preferences?.shirt.let { "${preferences?.size}_shirt$it" }, Modifier.clickable {
|
||||
onCustomizationTap("shirt", null)
|
||||
})
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_skin),
|
||||
preferences?.skin.let { "skin_$it" },
|
||||
Modifier.clickable {
|
||||
onCustomizationTap("skin", null)
|
||||
})
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_hair_color),
|
||||
if (preferences?.hair?.color != null && preferences.hair?.color != "") "hair_bangs_1_" + preferences.hair?.color else "",
|
||||
Modifier.clickable {
|
||||
onCustomizationTap("hair", "color")
|
||||
}
|
||||
)
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_hair_bangs),
|
||||
if (preferences?.hair?.bangs != null && preferences.hair?.bangs != 0) "hair_bangs_" + preferences.hair?.bangs + "_" + preferences.hair?.color else "",
|
||||
Modifier.clickable {
|
||||
onCustomizationTap("hair", "bangs")
|
||||
}
|
||||
)
|
||||
}
|
||||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_style),
|
||||
if (preferences?.hair?.base != null && preferences.hair?.base != 0) "hair_base_" + preferences.hair?.base + "_" + preferences.hair?.color else "",
|
||||
Modifier.clickable {
|
||||
onCustomizationTap("hair", "base")
|
||||
}
|
||||
)
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_mustache),
|
||||
if (preferences?.hair?.mustache != null && preferences.hair?.mustache != 0) "hair_mustache_" + preferences.hair?.mustache + "_" + preferences.hair?.color else "",
|
||||
Modifier.clickable {
|
||||
onCustomizationTap("hair", "mustache")
|
||||
}
|
||||
)
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_beard),
|
||||
if (preferences?.hair?.beard != null && preferences.hair?.beard != 0) "hair_beard_" + preferences.hair?.beard + "_" + preferences.hair?.color else "",
|
||||
Modifier.clickable {
|
||||
onCustomizationTap("hair", "beard")
|
||||
}
|
||||
)
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_flower),
|
||||
if (preferences?.hair?.flower != null && preferences.hair?.flower != 0) "hair_flower_" + preferences.hair?.flower else "",
|
||||
Modifier.clickable {
|
||||
onCustomizationTap("hair", "flower")
|
||||
}
|
||||
)
|
||||
}
|
||||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_wheelchair),
|
||||
preferences?.chair?.let { if (it.startsWith("handleless")) "chair_$it" else it })
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_background),
|
||||
preferences?.background.let { "background_$it" })
|
||||
Box(Modifier.size(70.dp))
|
||||
Box(Modifier.size(70.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun EquipmentOverviewItemPreview() {
|
||||
Column(Modifier.width(320.dp)) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
OverviewItem("Main-Hand", "shop_weapon_warrior_1")
|
||||
OverviewItem("Off-Hand", null, isTwoHanded = true)
|
||||
OverviewItem("Armor", null)
|
||||
}
|
||||
EquipmentOverviewView(null, { _, _ -> })
|
||||
AvatarCustomizationOverviewView(null, { _, _ -> })
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
package com.habitrpg.android.habitica.ui.views.tasks
|
||||
|
||||
import androidx.compose.animation.animateColor
|
||||
import androidx.compose.animation.core.FastOutLinearInEasing
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.models.members.Member
|
||||
import com.habitrpg.android.habitica.ui.views.UserRow
|
||||
|
||||
@Composable
|
||||
fun AssignSheet(
|
||||
members: List<Member>,
|
||||
assignedMembers: List<String>,
|
||||
onAssignClick: (String) -> Unit,
|
||||
onCloseClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(modifier) {
|
||||
Box {
|
||||
Text(
|
||||
stringResource(R.string.assign_to),
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = colorResource(R.color.gray_200),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 8.dp)
|
||||
)
|
||||
TextButton(
|
||||
onClick = onCloseClick,
|
||||
colors = ButtonDefaults.textButtonColors(),
|
||||
modifier = Modifier.align(Alignment.CenterEnd)
|
||||
) {
|
||||
Text(stringResource(R.string.done))
|
||||
}
|
||||
}
|
||||
for (member in members) {
|
||||
val isAssigned = assignedMembers.contains(member.id)
|
||||
val transition = updateTransition(isAssigned, label = "isAssigned")
|
||||
val rotation = transition.animateFloat(
|
||||
label = "isAssigned",
|
||||
transitionSpec = { spring(Spring.DampingRatioLowBouncy, Spring.StiffnessLow) }) {
|
||||
if (it) 0f else 45f
|
||||
}
|
||||
val backgroundColor = transition.animateColor(
|
||||
label = "isAssigned",
|
||||
transitionSpec = { tween(400, easing = FastOutLinearInEasing) }) {
|
||||
if (it) MaterialTheme.colors.primary else colorResource(id = R.color.transparent)
|
||||
}
|
||||
val color = transition.animateColor(
|
||||
label = "isAssigned",
|
||||
transitionSpec = { tween(400, easing = FastOutLinearInEasing) }) {
|
||||
fadeIn(tween(10000))
|
||||
colorResource(if (it) R.color.white else R.color.text_dimmed)
|
||||
}
|
||||
val borderColor = transition.animateColor(
|
||||
label = "isAssigned",
|
||||
transitionSpec = { tween(400, easing = FastOutLinearInEasing) }) {
|
||||
fadeIn(tween(10000))
|
||||
if (it) MaterialTheme.colors.primary else colorResource(id = R.color.text_dimmed)
|
||||
}
|
||||
UserRow(
|
||||
username = member.displayName,
|
||||
color = colorResource(R.color.text_primary),
|
||||
extraContent = {
|
||||
Text(
|
||||
member.formattedUsername ?: "",
|
||||
color = colorResource(R.color.text_ternary)
|
||||
)
|
||||
}, endContent = {
|
||||
Image(
|
||||
painterResource(R.drawable.ic_close_white_24dp),
|
||||
null,
|
||||
colorFilter = ColorFilter.tint(color.value),
|
||||
modifier = Modifier
|
||||
.rotate(rotation.value)
|
||||
.size(24.dp)
|
||||
.background(
|
||||
backgroundColor.value,
|
||||
CircleShape
|
||||
)
|
||||
.border(
|
||||
2.dp,
|
||||
borderColor.value,
|
||||
CircleShape
|
||||
)
|
||||
.padding(3.dp)
|
||||
)
|
||||
}, modifier = Modifier
|
||||
.clickable {
|
||||
member.id?.let { onAssignClick(it) }
|
||||
}
|
||||
.padding(30.dp, 12.dp)
|
||||
.heightIn(min = 24.dp)
|
||||
.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package com.habitrpg.android.habitica.ui.views.tasks
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.models.Assignable
|
||||
import com.habitrpg.android.habitica.ui.views.CompletedAt
|
||||
import com.habitrpg.android.habitica.ui.views.UserRow
|
||||
import java.util.Date
|
||||
|
||||
@Composable
|
||||
fun AssignedView(
|
||||
assigned: List<Assignable>,
|
||||
completedAt: Map<String, Date>,
|
||||
backgroundColor: Color,
|
||||
color: Color,
|
||||
onEditClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
showEditButton: Boolean = false
|
||||
) {
|
||||
Column(modifier.fillMaxWidth()) {
|
||||
val rowModifier = Modifier
|
||||
.padding(vertical = 4.dp)
|
||||
.background(
|
||||
backgroundColor,
|
||||
MaterialTheme.shapes.medium
|
||||
)
|
||||
.padding(15.dp, 12.dp)
|
||||
.heightIn(min = 24.dp)
|
||||
.fillMaxWidth()
|
||||
for (assignable in assigned) {
|
||||
UserRow(
|
||||
username = assignable.identifiableName, modifier = rowModifier,
|
||||
color = color,
|
||||
extraContent = {
|
||||
completedAt[assignable.id]?.let { CompletedAt(completedAt = it) }
|
||||
}
|
||||
)
|
||||
}
|
||||
if (showEditButton) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier
|
||||
.clickable {
|
||||
onEditClick()
|
||||
}
|
||||
.padding(vertical = 4.dp)
|
||||
.background(
|
||||
backgroundColor,
|
||||
MaterialTheme.shapes.medium
|
||||
)
|
||||
.padding(15.dp, 12.dp)
|
||||
.heightIn(min = 24.dp)
|
||||
.fillMaxWidth()) {
|
||||
Image(
|
||||
painterResource(R.drawable.edit),
|
||||
null,
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colors.primary)
|
||||
)
|
||||
Text(
|
||||
stringResource(R.string.edit_assignees), color = color,
|
||||
modifier = Modifier.padding(start = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,10 @@ import java.text.DecimalFormat
|
|||
|
||||
object NumberAbbreviator {
|
||||
|
||||
fun abbreviate(context: Context?, number: Float, numberOfDecimals: Int = 2, minForAbbrevation: Int = 0): String {
|
||||
return abbreviate(context, number.toDouble(), numberOfDecimals, minForAbbrevation)
|
||||
}
|
||||
|
||||
fun abbreviate(context: Context?, number: Double, numberOfDecimals: Int = 2, minForAbbrevation: Int = 0): String {
|
||||
var usedNumber = number
|
||||
var counter = 0
|
||||
|
|
@ -28,6 +32,7 @@ object NumberAbbreviator {
|
|||
2 -> context?.getString(R.string.million_abbrev) ?: "m"
|
||||
3 -> context?.getString(R.string.billion_abbrev) ?: "b"
|
||||
4 -> context?.getString(R.string.trillion_abbrev) ?: "t"
|
||||
5 -> context?.getString(R.string.quadrillion_abbrev) ?: "q"
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,13 +7,10 @@ import android.graphics.drawable.Drawable
|
|||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
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.extensions.layoutInflater
|
||||
import com.habitrpg.common.habitica.R
|
||||
import com.habitrpg.common.habitica.databinding.ValueBarBinding
|
||||
import com.habitrpg.common.habitica.extensions.dpToPx
|
||||
import com.habitrpg.common.habitica.extensions.layoutInflater
|
||||
import java.math.RoundingMode
|
||||
import java.text.NumberFormat
|
||||
|
||||
|
|
@ -95,7 +92,6 @@ class ValueBar(context: Context, attrs: AttributeSet?) : FrameLayout(context, at
|
|||
R.styleable.ValueBar,
|
||||
0, 0
|
||||
)
|
||||
setLightBackground(attributes?.getBoolean(R.styleable.ValueBar_lightBackground, !context.isUsingNightModeResources()) == true)
|
||||
|
||||
binding.progressBar.barForegroundColor = attributes?.getColor(R.styleable.ValueBar_barForegroundColor, 0) ?: 0
|
||||
binding.progressBar.barPendingColor = attributes?.getColor(R.styleable.ValueBar_barPendingColor, 0) ?: 0
|
||||
|
|
@ -163,19 +159,6 @@ class ValueBar(context: Context, attrs: AttributeSet?) : FrameLayout(context, at
|
|||
binding.valueTextView.text = valueText
|
||||
}
|
||||
|
||||
fun setLightBackground(lightBackground: Boolean) {
|
||||
val textColor: Int
|
||||
/*if (lightBackground) {
|
||||
textColor = ContextCompat.getColor(context, R.color.text_ternary)
|
||||
binding.progressBar.setBackgroundResource(R.drawable.layout_rounded_bg_light_gray)
|
||||
} else {
|
||||
textColor = context.getThemeColor(R.attr.textColorPrimaryDark)
|
||||
binding.progressBar.setBackgroundResource(R.drawable.layout_rounded_bg_header_bar)
|
||||
}
|
||||
binding.valueTextView.setTextColor(textColor)
|
||||
binding.descriptionTextView.setTextColor(textColor)*/
|
||||
}
|
||||
|
||||
var animationDuration = 500L
|
||||
var animationDelay = 0L
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
<string name="million_abbrev">m</string>
|
||||
<string name="billion_abbrev">b</string>
|
||||
<string name="trillion_abbrev">t</string>
|
||||
<string name="quadrillion_abbrev">q</string>
|
||||
<string name="gems">Gems</string>
|
||||
<string name="gold_plural">gold</string>
|
||||
<string name="gold_capitalized">Gold</string>
|
||||
|
|
|
|||
|
|
@ -27,12 +27,14 @@ interface Avatar {
|
|||
|
||||
val hasClass: Boolean
|
||||
get() {
|
||||
return preferences?.disableClasses != true && flags?.classSelected ?: true == true && stats?.habitClass?.isNotEmpty() == true
|
||||
return preferences?.disableClasses != true
|
||||
&& flags?.classSelected != false
|
||||
&& stats?.habitClass?.isNotEmpty() == true
|
||||
&& (stats?.lvl ?: 0) >= 10
|
||||
}
|
||||
|
||||
val currentMount: String?
|
||||
get() = items?.currentMount ?: ""
|
||||
|
||||
val currentPet: String?
|
||||
get() = items?.currentPet ?: ""
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
NAME=4.1
|
||||
CODE=4771
|
||||
CODE=4781
|
||||
Loading…
Reference in a new issue