preparing
|
|
@ -80,6 +80,12 @@
|
|||
android:screenOrientation="unspecified"
|
||||
tools:ignore="UnusedAttribute">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.activities.BirthdayActivity"
|
||||
android:parentActivityName=".ui.activities.MainActivity"
|
||||
android:screenOrientation="unspecified"
|
||||
tools:ignore="UnusedAttribute">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.activities.NotificationsActivity"
|
||||
android:parentActivityName=".ui.activities.MainActivity"
|
||||
|
|
|
|||
|
|
@ -56,29 +56,31 @@ dependencies {
|
|||
implementation "com.android.billingclient:billing-ktx:5.1.0"
|
||||
implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1@aar'
|
||||
|
||||
implementation("io.coil-kt:coil-compose:$coil_version")
|
||||
|
||||
//Analytics
|
||||
implementation "com.amplitude:analytics-android:$amplitude_version"
|
||||
|
||||
//Tests
|
||||
testImplementation 'io.kotest:kotest-runner-junit5:5.3.0'
|
||||
testImplementation 'androidx.test:core:1.4.0'
|
||||
testImplementation 'io.mockk:mockk:1.12.3'
|
||||
testImplementation 'io.mockk:mockk-android:1.12.3'
|
||||
testImplementation 'androidx.test:core:1.5.0'
|
||||
testImplementation 'io.mockk:mockk:1.13.3'
|
||||
testImplementation 'io.mockk:mockk-android:1.13.3'
|
||||
testImplementation 'io.kotest:kotest-assertions-core:5.3.0'
|
||||
testImplementation 'io.kotest:kotest-framework-datatest:5.3.0'
|
||||
androidTestImplementation ('com.kaspersky.android-components:kaspresso:1.4.1') {
|
||||
exclude module: "protobuf-lite"
|
||||
}
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.4.0'
|
||||
androidTestImplementation 'androidx.test:rules:1.4.0'
|
||||
debugImplementation 'androidx.fragment:fragment-testing:1.5.4'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.5.1'
|
||||
androidTestImplementation 'androidx.test:rules:1.5.0'
|
||||
debugImplementation 'androidx.fragment:fragment-testing:1.5.5'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.4'
|
||||
androidTestImplementation 'androidx.test:core-ktx:1.4.0'
|
||||
androidTestImplementation 'androidx.test:core-ktx:1.5.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.4'
|
||||
androidTestImplementation 'io.mockk:mockk-android:1.12.3'
|
||||
androidTestImplementation 'io.mockk:mockk-android:1.13.3'
|
||||
androidTestImplementation 'io.kotest:kotest-assertions-core:5.3.0'
|
||||
androidTestUtil("androidx.test:orchestrator:1.4.1")
|
||||
androidTestUtil("androidx.test:orchestrator:1.4.2")
|
||||
|
||||
implementation 'com.facebook.shimmer:shimmer:0.5.0'
|
||||
|
||||
|
|
@ -91,7 +93,7 @@ dependencies {
|
|||
implementation 'com.google.firebase:firebase-messaging-ktx'
|
||||
implementation 'com.google.firebase:firebase-config-ktx'
|
||||
implementation 'com.google.firebase:firebase-perf-ktx'
|
||||
implementation 'com.google.android.gms:play-services-ads:21.3.0'
|
||||
implementation 'com.google.android.gms:play-services-ads:21.4.0'
|
||||
implementation "com.google.android.gms:play-services-auth:$play_auth_version"
|
||||
implementation 'com.google.android.flexbox:flexbox:3.0.0'
|
||||
implementation "com.google.android.gms:play-services-wearable:$play_wearables_version"
|
||||
|
|
|
|||
BIN
Habitica/res/drawable-hdpi/birthday_gifts.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Habitica/res/drawable-hdpi/birthday_header.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
Habitica/res/drawable-hdpi/birthday_menu_text.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
Habitica/res/drawable-hdpi/birthday_textdeco_left.png
Normal file
|
After Width: | Height: | Size: 752 B |
BIN
Habitica/res/drawable-hdpi/birthday_textdeco_right.png
Normal file
|
After Width: | Height: | Size: 742 B |
BIN
Habitica/res/drawable-hdpi/icon_birthday.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Habitica/res/drawable-mdpi/birthday_gifts.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Habitica/res/drawable-mdpi/birthday_header.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
Habitica/res/drawable-mdpi/birthday_menu_text.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
Habitica/res/drawable-mdpi/birthday_textdeco_left.png
Normal file
|
After Width: | Height: | Size: 533 B |
BIN
Habitica/res/drawable-mdpi/birthday_textdeco_right.png
Normal file
|
After Width: | Height: | Size: 518 B |
BIN
Habitica/res/drawable-mdpi/icon_birthday.png
Normal file
|
After Width: | Height: | Size: 852 B |
BIN
Habitica/res/drawable-night-hdpi/icon_birthday.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Habitica/res/drawable-night-mdpi/icon_birthday.png
Normal file
|
After Width: | Height: | Size: 848 B |
BIN
Habitica/res/drawable-night-xhdpi/birthday_menu_text.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
Habitica/res/drawable-night-xhdpi/icon_birthday.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
Habitica/res/drawable-night-xxhdpi/birthday_menu_text.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
Habitica/res/drawable-night-xxhdpi/icon_birthday.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Habitica/res/drawable-xhdpi/birthday_gifts.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
Habitica/res/drawable-xhdpi/birthday_header.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
Habitica/res/drawable-xhdpi/birthday_textdeco_left.png
Normal file
|
After Width: | Height: | Size: 889 B |
BIN
Habitica/res/drawable-xhdpi/birthday_textdeco_right.png
Normal file
|
After Width: | Height: | Size: 878 B |
BIN
Habitica/res/drawable-xhdpi/icon_birthday.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
Habitica/res/drawable-xxhdpi/birthday_gifts.png
Normal file
|
After Width: | Height: | Size: 6 KiB |
BIN
Habitica/res/drawable-xxhdpi/birthday_header.png
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
Habitica/res/drawable-xxhdpi/birthday_textdeco_left.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Habitica/res/drawable-xxhdpi/birthday_textdeco_right.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Habitica/res/drawable-xxhdpi/icon_birthday.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
|
|
@ -16,6 +16,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="@style/Toolbar.Modern"
|
||||
style="@style/ToolbarTitleStyle"
|
||||
app:layout_scrollFlags="scroll|enterAlways"/>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="@style/Toolbar.Modern"
|
||||
style="@style/ToolbarTitleStyle"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:popupTheme="@style/PopupTheme"/>
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:background="?attr/colorContentBackground"
|
||||
android:theme="@style/Toolbar.Modern"
|
||||
style="@style/ToolbarTitleStyle"
|
||||
app:layout_collapseMode="pin"
|
||||
app:popupTheme="@style/PopupTheme" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="@style/Toolbar.Modern"
|
||||
style="@style/ToolbarTitleStyle"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:popupTheme="@style/PopupTheme"/>
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="@style/Toolbar.Modern"
|
||||
style="@style/ToolbarTitleStyle"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:popupTheme="@style/PopupTheme" />
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="@style/Toolbar.Modern"
|
||||
style="@style/ToolbarTitleStyle"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="@style/Toolbar.Modern"
|
||||
style="@style/ToolbarTitleStyle"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:popupTheme="@style/ThemeOverlay.AppCompat.Dark"/>
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="@style/Toolbar.Modern"
|
||||
style="@style/ToolbarTitleStyle"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:popupTheme="@style/PopupTheme"/>
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="@style/Toolbar.Modern"
|
||||
style="@style/ToolbarTitleStyle"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:popupTheme="@style/PopupTheme"/>
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
android:layout_height="match_parent"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@color/transparent"
|
||||
android:textColor="?colorPrimaryDark"
|
||||
android:textColor="?textColorTintedPrimary"
|
||||
android:textSize="16sp"
|
||||
tools:text="100.0"
|
||||
android:inputType="numberDecimal"
|
||||
|
|
|
|||
|
|
@ -10,6 +10,11 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<androidx.compose.ui.platform.ComposeView
|
||||
android:id="@+id/promo_compose_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone" />
|
||||
<RelativeLayout
|
||||
android:id="@+id/promo_banner"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
|||
|
|
@ -257,8 +257,8 @@
|
|||
android:layout_marginTop="4dp"
|
||||
android:fontFamily="sans-serif"
|
||||
android:gravity="center"
|
||||
android:paddingStart="50dp"
|
||||
android:paddingEnd="50dp"
|
||||
android:paddingStart="30dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:text="@string/subscribers_mythic_hourglasses" />
|
||||
|
||||
<ImageView
|
||||
|
|
|
|||
|
|
@ -123,6 +123,20 @@
|
|||
<action
|
||||
android:id="@+id/openEquipmentDetail"
|
||||
app:destination="@id/equipmentDetailFragment" />
|
||||
<action
|
||||
android:id="@+id/openAvatarEquipment"
|
||||
app:destination="@id/avatarEquipmentFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/avatarEquipmentFragment"
|
||||
android:name="com.habitrpg.android.habitica.ui.fragments.inventory.customization.AvatarEquipmentFragment"
|
||||
android:label="@string/sidebar_avatar" >
|
||||
<argument
|
||||
android:name="type"
|
||||
app:argType="string" />
|
||||
<argument
|
||||
android:name="category"
|
||||
app:argType="string" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/equipmentOverviewFragment"
|
||||
|
|
@ -226,6 +240,12 @@
|
|||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
</activity>
|
||||
<activity
|
||||
android:id="@+id/birthdayActivity"
|
||||
android:name="com.habitrpg.android.habitica.ui.activities.BirthdayActivity"
|
||||
android:label="@string/gem_purchase_toolbartitle" >
|
||||
<deepLink app:uri="habitica.com/birthday" />
|
||||
</activity>
|
||||
<fragment
|
||||
android:id="@+id/newsFragment"
|
||||
android:name="com.habitrpg.android.habitica.ui.fragments.NewsFragment"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<color name="text_quad">@color/gray_300</color>
|
||||
<color name="text_dimmed">@color/gray_200</color>
|
||||
<color name="text_inverted">@color/gray_10</color>
|
||||
<color name="text_brand">@color/brand_400</color>
|
||||
<color name="text_brand">@color/brand_600</color>
|
||||
<color name="text_brand_neon">@color/brand_500</color>
|
||||
<color name="text_red">@color/red_100</color>
|
||||
<color name="text_orange">@color/orange_100</color>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
<attr name="colorWindowBackground" format="color" />
|
||||
<attr name="textColorPrimary" format="color" />
|
||||
<attr name="textColorSecondary" format="color" />
|
||||
<attr name="textColorTintedPrimary" format="color" />
|
||||
<attr name="textColorTintedSecondary" format="color" />
|
||||
<attr name="textColorPrimaryDark" format="color" />
|
||||
<attr name="barColor" format="color" />
|
||||
<attr name="toolbarContentColor" format="color" />
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@
|
|||
<string name="start_date">Start Date</string>
|
||||
<string name="positive_habit_form">Positive</string>
|
||||
<string name="negative_habit_form">Negative</string>
|
||||
<string name="positive_sentence">positive</string>
|
||||
<string name="negative_sentence">negative</string>
|
||||
<string name="on">On</string>
|
||||
<string name="off">Off</string>
|
||||
<string name="selected">Selected</string>
|
||||
|
|
@ -739,8 +741,8 @@
|
|||
<string name="gift_gems_subtitle">Choose the gem packet you’d like to gift below!</string>
|
||||
<string name="server">Server</string>
|
||||
<string name="gift_confirmation_title">Your gift was sent!</string>
|
||||
<string name="gift_confirmation_text_sub_g1g1">You sent %s a %s-month Habitica subscription and the same subscription was applied to your account for our Gift One Get One promotion!</string>
|
||||
<string name="gift_confirmation_text_sub">You sent @%s a %s-month Habitica subscription.</string>
|
||||
<string name="gift_confirmation_text_sub_g1g1">You sent %1$s a %2$s-month Habitica subscription and the same subscription was applied to your account for our Gift One Get One promotion!</string>
|
||||
<string name="gift_confirmation_text_sub">You sent @%1$s a %2$s-month Habitica subscription.</string>
|
||||
<string name="gift_confirmation_text_gems_new">You sent @%s %s gems.</string>
|
||||
<string name="subscription_confirmation">You are now subscribed for 1 month</string>
|
||||
<string name="subscription_confirmation_multiple">You are now subscribed for %s months</string>
|
||||
|
|
@ -1048,11 +1050,11 @@
|
|||
<string name="view_onboarding_tasks">View Onboarding Tasks</string>
|
||||
<string name="suggest_pet_hatch_missing_egg">You still need a %s Egg to hatch this pet</string>
|
||||
<string name="suggest_pet_hatch_missing_potion">You still need a %s Potion to hatch this pet</string>
|
||||
<string name="suggest_pet_hatch_missing_both">You need a %s and %s Potion to hatch this pet</string>
|
||||
<string name="suggest_pet_hatch_missing_both">You need a %1$s and %2$s Potion to hatch this pet</string>
|
||||
<string name="suggest_pet_hatch_again_missing_egg">You still need a %s Egg to hatch this pet again</string>
|
||||
<string name="suggest_pet_hatch_again_missing_potion">You still need a %s Potion to hatch this pet again</string>
|
||||
<string name="suggest_pet_hatch_again_missing_both">You need a %s and %s Potion to hatch this pet again</string>
|
||||
<string name="can_hatch_pet">Combine your %s Egg and %s Potion to hatch this pet!</string>
|
||||
<string name="suggest_pet_hatch_again_missing_both">You need a %1$s and %2$s Potion to hatch this pet again</string>
|
||||
<string name="can_hatch_pet">Combine your %1$s Egg and %2$s Potion to hatch this pet!</string>
|
||||
<string name="hatch_pet">Hatch Pet</string>
|
||||
<string name="unhatched_pet">Unhatched Pet</string>
|
||||
<string name="hatch_pet_again">Hatch Pet again</string>
|
||||
|
|
@ -1081,7 +1083,7 @@
|
|||
<string name="pms_disabled">Private Messages are disabled</string>
|
||||
<string name="pms_disabled_description">You can still send messages, but no one can send them to you. You can enable again from Settings.</string>
|
||||
<string name="usually_x_gems">Usually %d Gems</string>
|
||||
<string name="x_to_y">%s to %s</string>
|
||||
<string name="x_to_y">%1$s to %2$s</string>
|
||||
<string name="how_it_works">How it works</string>
|
||||
<string name="limitations">Limitations</string>
|
||||
<string name="fall_promo_info_instructions">Between %s and %s, simply purchase any Gem bundle like usual and your account will be credited with the promotional amount of Gems. More Gems to spend, share, or save for any future releases!</string>
|
||||
|
|
@ -1259,10 +1261,10 @@
|
|||
<string name="copy_shared_tasks">Copy shared tasks</string>
|
||||
<string name="group_plan_settings">Group Plan Settings</string>
|
||||
<string name="task_summary">Task Summary</string>
|
||||
<string name="habit_summary_description">This is a **%s** Habit that is **%s**.</string>
|
||||
<string name="todo_summary_description_duedate">This is a **%s** Task that is due **%s**.</string>
|
||||
<string name="todo_summary_description">This is a **%s** Task that does not have a due date.</string>
|
||||
<string name="daily_summary_description">This is a **%s** Task that repeats **%s**.</string>
|
||||
<string name="habit_summary_description">This is a **%1$s** Habit that is **%2$s**.</string>
|
||||
<string name="todo_summary_description_duedate">This is a **%1$s** Task that is due **%2$s**.</string>
|
||||
<string name="todo_summary_description">This is a **%1$s** Task that does not have a due date.</string>
|
||||
<string name="daily_summary_description">This is a **%1$s** Task that repeats **%2$s%3$s**.</string>
|
||||
<string name="positive_and_negative">positive and negative</string>
|
||||
<string name="assigned_to">Assigned to</string>
|
||||
<string name="completed_at">Completed at %s</string>
|
||||
|
|
@ -1293,6 +1295,36 @@
|
|||
<string name="paypal">PayPal</string>
|
||||
<string name="stripe_payment">Stripe</string>
|
||||
<string name="shadow_muted_hidden">Shadow muted, hidden</string>
|
||||
<string name="assigned_to_you_by">Assigned to you by @%1$s on %2$s</string>
|
||||
<string name="never">never</string>
|
||||
<string name="on_x">on %s</string>
|
||||
<string name="on_weekdays">on weekdays</string>
|
||||
<string name="on_weekends">on weekends</string>
|
||||
<string name="on_every_day_of_week">on every day of the week</string>
|
||||
<string name="x_and_y">%1$s and %2$s</string>
|
||||
<string name="on_the_x">on the %s</string>
|
||||
<string name="first">first</string>
|
||||
<string name="on_the_x_of_month">on the %1$s %2$s of the month</string>
|
||||
<string name="second">second</string>
|
||||
<string name="third">third</string>
|
||||
<string name="fourth">fourth</string>
|
||||
<string name="fifth">fifth</string>
|
||||
<string name="animated_gryphatrice_pet">Animated Gryphatrice Pet</string>
|
||||
<string name="birthday_title_description">Celebrate Habitica’s 10th birthday with gifts and exclusive items below!</string>
|
||||
<string name="limited_edition">Limited Edition</string>
|
||||
<string name="gryphatrice_description">The rare, mystical Gryphatrice joins the birthday bash! Don’t miss your chance to own this exclusive animated Pet.</string>
|
||||
<string name="thanks_for_support">Thanks for your support!</string>
|
||||
<string name="plenty_of_potions">Plenty of Potions</string>
|
||||
<string name="for_for_free">For for Free</string>
|
||||
<string name="buy_for_x">Buy for %s</string>
|
||||
<string name="buy_for">Buy for</string>
|
||||
<string name="plenty_of_potions_description">We’re bringing back 10 of the community’s favorite Magic Hatching Potions. Head over to the Market to fill out your collection!</string>
|
||||
<string name="for_for_free_description">To keep the party going, we’ll be giving away Party Robes, 20 Gems, and a limited edition Cape set and Background!</string>
|
||||
<string name="birthday_limitations">This is a limited time event that starts on January 23rd at 8:00 AM ET (13:00 UTC) and will end February 1st at 8:00 PM ET (01:00 UTC). The Limited Edition Gryphatrice and ten Magic Hatching Potions will be available to buy during this time. The other Gifts will be automatically delivered to all accounts active in the previous 30 days.</string>
|
||||
<string name="visit_the_market">Visit the Market</string>
|
||||
<string name="exclusive_items_await">Exclusive items and gifts await</string>
|
||||
<string name="ends_in_x">Ends in %s</string>
|
||||
<string name="see_more">See More</string>
|
||||
<plurals name="you_x_others">
|
||||
<item quantity="zero">You</item>
|
||||
<item quantity="one">You, %d other</item>
|
||||
|
|
@ -1302,4 +1334,24 @@
|
|||
<item quantity="one">%d person</item>
|
||||
<item quantity="other">%d people</item>
|
||||
</plurals>
|
||||
<plurals name="repeat_daily">
|
||||
<item quantity="one">every day</item>
|
||||
<item quantity="two">every other day</item>
|
||||
<item quantity="other">every %d days</item>
|
||||
</plurals>
|
||||
<plurals name="repeat_weekly">
|
||||
<item quantity="one">every week</item>
|
||||
<item quantity="two">every other week</item>
|
||||
<item quantity="other">every %d weeks</item>
|
||||
</plurals>
|
||||
<plurals name="repeat_monthly">
|
||||
<item quantity="one">every month</item>
|
||||
<item quantity="two">every other month</item>
|
||||
<item quantity="other">every %d months</item>
|
||||
</plurals>
|
||||
<plurals name="repeat_yearly">
|
||||
<item quantity="one">every year</item>
|
||||
<item quantity="two">every other year</item>
|
||||
<item quantity="other">every %d years</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@
|
|||
<item name="colorWindowBackground">@color/window_background</item>
|
||||
<item name="colorTintedBackground">@color/brand_800</item>
|
||||
<item name="colorTintedBackgroundOffset">@color/brand_700</item>
|
||||
<item name="textColorTintedSecondary">@color/brand_sub_text</item>
|
||||
<item name="textColorTintedPrimary">@color/brand_100</item>
|
||||
|
||||
<item name="popupMenuStyle">@style/PopupTheme</item>
|
||||
<item name="actionOverflowMenuStyle">@style/PopupTheme</item>
|
||||
|
|
@ -70,11 +72,14 @@
|
|||
<item name="colorBoxStroke">@color/brand_50</item>
|
||||
<item name="colorPrimaryOffset">@color/gray_10</item>
|
||||
<item name="textColorPrimaryDark">@color/gray_200</item>
|
||||
<item name="colorPrimaryDistinct">@color/white</item>
|
||||
<item name="toolbarContentColor">@color/white</item>
|
||||
<item name="colorTintedBackground">@color/brand_00</item>
|
||||
<item name="colorTintedBackgroundOffset">@color/brand_0</item>
|
||||
<item name="textColorTintedSecondary">@color/brand_500</item>
|
||||
<item name="colorPrimaryText">@color/brand_600</item>
|
||||
<item name="colorPrimaryDark">@color/brand_400</item>
|
||||
<item name="textColorTintedPrimary">@color/brand_700</item>
|
||||
</style>
|
||||
|
||||
|
||||
|
|
@ -98,6 +103,8 @@
|
|||
<item name="colorPrimaryText">@color/red_1</item>
|
||||
<item name="colorTintedBackground">@color/red_700</item>
|
||||
<item name="colorTintedBackgroundOffset">@color/red_600</item>
|
||||
<item name="textColorTintedSecondary">@color/red_sub_text</item>
|
||||
<item name="textColorTintedPrimary">@color/red_100</item>
|
||||
</style>
|
||||
|
||||
<style name="MainAppTheme.Red.Dark">
|
||||
|
|
@ -109,6 +116,8 @@
|
|||
<item name="colorTintedBackground">@color/red_00</item>
|
||||
<item name="colorTintedBackgroundOffset">@color/red_0</item>
|
||||
<item name="colorPrimaryText">@color/red_600</item>
|
||||
<item name="textColorTintedSecondary">@color/red_500</item>
|
||||
<item name="textColorTintedPrimary">@color/red_600</item>
|
||||
</style>
|
||||
|
||||
<style name="MainAppTheme.Maroon">
|
||||
|
|
@ -130,6 +139,8 @@
|
|||
<item name="colorPrimaryText">@color/maroon_1</item>
|
||||
<item name="colorTintedBackground">@color/maroon_700</item>
|
||||
<item name="colorTintedBackgroundOffset">@color/maroon_600</item>
|
||||
<item name="textColorTintedSecondary">@color/maroon_sub_text</item>
|
||||
<item name="textColorTintedPrimary">@color/maroon_100</item>
|
||||
</style>
|
||||
|
||||
<style name="MainAppTheme.Maroon.Dark">
|
||||
|
|
@ -140,6 +151,8 @@
|
|||
<item name="colorTintedBackground">@color/maroon_00</item>
|
||||
<item name="colorTintedBackgroundOffset">@color/maroon_0</item>
|
||||
<item name="colorPrimaryText">@color/maroon_600</item>
|
||||
<item name="textColorTintedSecondary">@color/maroon_500</item>
|
||||
<item name="textColorTintedPrimary">@color/maroon_600</item>
|
||||
</style>
|
||||
|
||||
<style name="MainAppTheme.Orange">
|
||||
|
|
@ -162,6 +175,8 @@
|
|||
<item name="colorPrimaryText">@color/orange_1</item>
|
||||
<item name="colorTintedBackground">@color/orange_700</item>
|
||||
<item name="colorTintedBackgroundOffset">@color/orange_600</item>
|
||||
<item name="textColorTintedSecondary">@color/orange_sub_text</item>
|
||||
<item name="textColorTintedPrimary">@color/orange_100</item>
|
||||
</style>
|
||||
|
||||
<style name="MainAppTheme.Orange.Dark">
|
||||
|
|
@ -173,6 +188,8 @@
|
|||
<item name="colorTintedBackground">@color/orange_00</item>
|
||||
<item name="colorTintedBackgroundOffset">@color/orange_0</item>
|
||||
<item name="colorPrimaryText">@color/orange_600</item>
|
||||
<item name="textColorTintedSecondary">@color/orange_500</item>
|
||||
<item name="textColorTintedPrimary">@color/orange_600</item>
|
||||
</style>
|
||||
|
||||
<style name="MainAppTheme.Yellow">
|
||||
|
|
@ -181,7 +198,7 @@
|
|||
<item name="colorAccent">@color/yellow_100</item>
|
||||
<item name="android:colorPrimary">@color/yellow_10</item>
|
||||
<item name="android:colorPrimaryDark">@color/yellow_5</item>
|
||||
<item name="android:colorAccent">@color/yellow_100</item>
|
||||
<item name="android:colorAccent">@color/yellow_50</item>
|
||||
<item name="colorBoxStroke">@color/yellow_5</item>
|
||||
<item name="colorPrimaryOffset">@color/yellow_50</item>
|
||||
<item name="colorPrimaryDistinct">@color/yellow_500</item>
|
||||
|
|
@ -195,6 +212,8 @@
|
|||
<item name="colorPrimaryText">@color/yellow_1</item>
|
||||
<item name="colorTintedBackground">@color/yellow_700</item>
|
||||
<item name="colorTintedBackgroundOffset">@color/yellow_600</item>
|
||||
<item name="textColorTintedSecondary">@color/yellow_sub_text</item>
|
||||
<item name="textColorTintedPrimary">@color/yellow_10</item>
|
||||
</style>
|
||||
|
||||
<style name="MainAppTheme.Yellow.Dark">
|
||||
|
|
@ -206,6 +225,8 @@
|
|||
<item name="colorTintedBackground">@color/yellow_00</item>
|
||||
<item name="colorTintedBackgroundOffset">@color/yellow_0</item>
|
||||
<item name="colorPrimaryText">@color/yellow_600</item>
|
||||
<item name="textColorTintedSecondary">@color/yellow_500</item>
|
||||
<item name="textColorTintedPrimary">@color/yellow_600</item>
|
||||
</style>
|
||||
|
||||
<style name="MainAppTheme.Green">
|
||||
|
|
@ -228,6 +249,8 @@
|
|||
<item name="colorPrimaryText">@color/green_1</item>
|
||||
<item name="colorTintedBackground">@color/green_700</item>
|
||||
<item name="colorTintedBackgroundOffset">@color/green_600</item>
|
||||
<item name="textColorTintedSecondary">@color/green_sub_text</item>
|
||||
<item name="textColorTintedPrimary">@color/green_100</item>
|
||||
</style>
|
||||
|
||||
<style name="MainAppTheme.Green.Dark">
|
||||
|
|
@ -239,6 +262,8 @@
|
|||
<item name="colorTintedBackground">@color/green_00</item>
|
||||
<item name="colorTintedBackgroundOffset">@color/green_0</item>
|
||||
<item name="colorPrimaryText">@color/green_600</item>
|
||||
<item name="textColorTintedSecondary">@color/green_500</item>
|
||||
<item name="textColorTintedPrimary">@color/green_600</item>
|
||||
</style>
|
||||
|
||||
<style name="MainAppTheme.Teal">
|
||||
|
|
@ -261,6 +286,8 @@
|
|||
<item name="colorPrimaryText">@color/teal_1</item>
|
||||
<item name="colorTintedBackground">@color/teal_700</item>
|
||||
<item name="colorTintedBackgroundOffset">@color/teal_600</item>
|
||||
<item name="textColorTintedSecondary">@color/teal_sub_text</item>
|
||||
<item name="textColorTintedPrimary">@color/teal_100</item>
|
||||
</style>
|
||||
|
||||
<style name="MainAppTheme.Teal.Dark">
|
||||
|
|
@ -272,6 +299,8 @@
|
|||
<item name="colorTintedBackground">@color/teal_00</item>
|
||||
<item name="colorTintedBackgroundOffset">@color/teal_0</item>
|
||||
<item name="colorPrimaryText">@color/teal_600</item>
|
||||
<item name="textColorTintedSecondary">@color/teal_500</item>
|
||||
<item name="textColorTintedPrimary">@color/teal_600</item>
|
||||
</style>
|
||||
|
||||
<style name="MainAppTheme.Blue">
|
||||
|
|
@ -294,6 +323,8 @@
|
|||
<item name="colorPrimaryText">@color/blue_1</item>
|
||||
<item name="colorTintedBackground">@color/blue_700</item>
|
||||
<item name="colorTintedBackgroundOffset">@color/blue_600</item>
|
||||
<item name="textColorTintedSecondary">@color/blue_sub_text</item>
|
||||
<item name="textColorTintedPrimary">@color/blue_100</item>
|
||||
</style>
|
||||
|
||||
<style name="MainAppTheme.Blue.Dark">
|
||||
|
|
@ -305,6 +336,8 @@
|
|||
<item name="colorTintedBackground">@color/blue_00</item>
|
||||
<item name="colorTintedBackgroundOffset">@color/blue_0</item>
|
||||
<item name="colorPrimaryText">@color/blue_600</item>
|
||||
<item name="textColorTintedSecondary">@color/blue_500</item>
|
||||
<item name="textColorTintedPrimary">@color/blue_600</item>
|
||||
</style>
|
||||
|
||||
<style name="MyWidgetTheme">
|
||||
|
|
@ -920,7 +953,7 @@
|
|||
|
||||
<style name="TaskFormSectionheader">
|
||||
<item name="android:textSize">16sp</item>
|
||||
<item name="android:textColor">?colorPrimaryDark</item>
|
||||
<item name="android:textColor">?textColorTintedPrimary</item>
|
||||
<item name="android:layout_marginTop">@dimen/spacing_large</item>
|
||||
<item name="android:layout_marginBottom">@dimen/spacing_medium</item>
|
||||
<item name="android:accessibilityHeading">true</item>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import com.habitrpg.android.habitica.data.ApiClient
|
|||
import com.habitrpg.android.habitica.helpers.AdHandler
|
||||
import com.habitrpg.android.habitica.helpers.AmplitudeManager
|
||||
import com.habitrpg.android.habitica.helpers.ExceptionHandler
|
||||
import com.habitrpg.android.habitica.helpers.launchCatching
|
||||
import com.habitrpg.android.habitica.helpers.notifications.PushNotificationManager
|
||||
import com.habitrpg.android.habitica.modules.UserModule
|
||||
import com.habitrpg.android.habitica.modules.UserRepositoryModule
|
||||
|
|
@ -41,6 +42,7 @@ import com.habitrpg.common.habitica.helpers.LanguageHelper
|
|||
import com.habitrpg.common.habitica.helpers.MarkdownParser
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
import kotlinx.coroutines.MainScope
|
||||
import java.lang.ref.WeakReference
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -250,26 +252,28 @@ abstract class HabiticaBaseApplication : Application(), Application.ActivityLife
|
|||
}
|
||||
|
||||
fun logout(context: Context) {
|
||||
getInstance(context)?.pushNotificationManager?.removePushDeviceUsingStoredToken()
|
||||
val realm = Realm.getDefaultInstance()
|
||||
getInstance(context)?.deleteDatabase(realm.path)
|
||||
realm.close()
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val useReminder = preferences.getBoolean("use_reminder", false)
|
||||
val reminderTime = preferences.getString("reminder_time", "19:00")
|
||||
val lightMode = preferences.getString("theme_mode", "system")
|
||||
val launchScreen = preferences.getString("launch_screen", "")
|
||||
preferences.edit {
|
||||
clear()
|
||||
putBoolean("use_reminder", useReminder)
|
||||
putString("reminder_time", reminderTime)
|
||||
putString("theme_mode", lightMode)
|
||||
putString("launch_screen", launchScreen)
|
||||
MainScope().launchCatching {
|
||||
getInstance(context)?.pushNotificationManager?.removePushDeviceUsingStoredToken()
|
||||
val realm = Realm.getDefaultInstance()
|
||||
getInstance(context)?.deleteDatabase(realm.path)
|
||||
realm.close()
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val useReminder = preferences.getBoolean("use_reminder", false)
|
||||
val reminderTime = preferences.getString("reminder_time", "19:00")
|
||||
val lightMode = preferences.getString("theme_mode", "system")
|
||||
val launchScreen = preferences.getString("launch_screen", "")
|
||||
preferences.edit {
|
||||
clear()
|
||||
putBoolean("use_reminder", useReminder)
|
||||
putString("reminder_time", reminderTime)
|
||||
putString("theme_mode", lightMode)
|
||||
putString("launch_screen", launchScreen)
|
||||
}
|
||||
reloadUserComponent()
|
||||
getInstance(context)?.lazyApiHelper?.updateAuthenticationCredentials(null, null)
|
||||
Wearable.getCapabilityClient(context).removeLocalCapability("provide_auth")
|
||||
startActivity(LoginActivity::class.java, context)
|
||||
}
|
||||
reloadUserComponent()
|
||||
getInstance(context)?.lazyApiHelper?.updateAuthenticationCredentials(null, null)
|
||||
Wearable.getCapabilityClient(context).removeLocalCapability("provide_auth")
|
||||
startActivity(LoginActivity::class.java, context)
|
||||
}
|
||||
|
||||
fun reloadUserComponent() {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import com.habitrpg.android.habitica.receivers.TaskAlarmBootReceiver;
|
|||
import com.habitrpg.android.habitica.receivers.TaskReceiver;
|
||||
import com.habitrpg.android.habitica.ui.activities.AdventureGuideActivity;
|
||||
import com.habitrpg.android.habitica.ui.activities.ArmoireActivity;
|
||||
import com.habitrpg.android.habitica.ui.activities.BirthdayActivity;
|
||||
import com.habitrpg.android.habitica.ui.activities.ChallengeFormActivity;
|
||||
import com.habitrpg.android.habitica.ui.activities.ClassSelectionActivity;
|
||||
import com.habitrpg.android.habitica.ui.activities.DeathActivity;
|
||||
|
|
@ -53,6 +54,7 @@ import com.habitrpg.android.habitica.ui.fragments.PromoInfoFragment;
|
|||
import com.habitrpg.android.habitica.ui.fragments.PromoWebFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.StatsFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.inventory.customization.AvatarCustomizationFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.inventory.customization.AvatarEquipmentFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.inventory.customization.AvatarOverviewFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.inventory.equipment.EquipmentDetailFragment;
|
||||
import com.habitrpg.android.habitica.ui.fragments.inventory.items.ItemDialogFragment;
|
||||
|
|
@ -370,4 +372,8 @@ public interface UserComponent {
|
|||
void inject(@NotNull TaskSummaryViewModel taskSummaryViewModel);
|
||||
|
||||
void inject(@NotNull TaskFormViewModel taskFormViewModel);
|
||||
|
||||
void inject(@NotNull AvatarEquipmentFragment avatarEquipmentFragment);
|
||||
|
||||
void inject(@NotNull BirthdayActivity birthdayActivity);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -231,11 +231,6 @@ class ApiClientImpl(
|
|||
return
|
||||
}
|
||||
|
||||
if (status == 401 && !hostConfig.hasAuthentication()) {
|
||||
// if a request was accidentally made that needs authentication, before the user has logged in just ignore the error
|
||||
return
|
||||
}
|
||||
|
||||
if (status in 400..499) {
|
||||
if (res.displayMessage.isNotEmpty()) {
|
||||
showConnectionProblemDialog("", res.displayMessage)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
package com.habitrpg.android.habitica.extensions
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.os.Build
|
||||
import com.habitrpg.android.habitica.ui.activities.BaseActivity
|
||||
import java.util.Locale
|
||||
|
||||
fun Resources.forceLocale(activity: BaseActivity, locale: Locale) {
|
||||
Locale.setDefault(locale)
|
||||
val configuration = Configuration()
|
||||
configuration.setLocale(locale)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
activity.createConfigurationContext(configuration)
|
||||
}
|
||||
updateConfiguration(configuration, displayMetrics)
|
||||
}
|
||||
|
|
@ -140,11 +140,6 @@ class AppConfigManager(contentRepository: ContentRepository?): com.habitrpg.comm
|
|||
return Gson().fromJson(remoteConfig.getString("knownIssues"), type)
|
||||
}
|
||||
|
||||
fun enableTeamBoards(): Boolean {
|
||||
return true
|
||||
return remoteConfig.getBoolean("enableTeamBoards")
|
||||
}
|
||||
|
||||
fun enableArmoireAds(): Boolean {
|
||||
return remoteConfig.getBoolean("enableArmoireAds")
|
||||
}
|
||||
|
|
@ -160,4 +155,8 @@ class AppConfigManager(contentRepository: ContentRepository?): com.habitrpg.comm
|
|||
fun enableNewArmoire(): Boolean {
|
||||
return remoteConfig.getBoolean("enableNewArmoire")
|
||||
}
|
||||
|
||||
fun isBirthday(): Boolean {
|
||||
return BuildConfig.DEBUG || BuildConfig.TESTING_LEVEL == AppTestingLevel.STAFF.name
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,48 @@
|
|||
package com.habitrpg.android.habitica.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.icu.text.MessageFormat
|
||||
import android.os.Build
|
||||
import android.text.format.DateUtils
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.common.habitica.extensions.nameRes
|
||||
import com.habitrpg.common.habitica.extensions.nameSentenceRes
|
||||
import com.habitrpg.shared.habitica.models.tasks.Frequency
|
||||
import com.habitrpg.shared.habitica.models.tasks.TaskDifficulty
|
||||
import com.habitrpg.shared.habitica.models.tasks.TaskType
|
||||
import java.text.DateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
class TaskDescriptionBuilder(private val context: Context) {
|
||||
|
||||
fun describe(task: Task): String {
|
||||
return when (task.type) {
|
||||
TaskType.HABIT -> context.getString(R.string.habit_summary_description, describeHabitDirections(task.up ?: false, task.down ?: false), describeDifficulty(task.priority))
|
||||
TaskType.HABIT -> context.getString(
|
||||
R.string.habit_summary_description,
|
||||
describeHabitDirections(task.up ?: false, task.down ?: false),
|
||||
describeDifficulty(task.priority)
|
||||
)
|
||||
TaskType.TODO -> {
|
||||
if (task.dueDate != null) {
|
||||
context.getString(R.string.todo_summary_description_duedate, describeDifficulty(task.priority), describeDate(task.dueDate!!))
|
||||
context.getString(
|
||||
R.string.todo_summary_description_duedate,
|
||||
describeDifficulty(task.priority),
|
||||
describeDate(task.dueDate!!)
|
||||
)
|
||||
} else {
|
||||
context.getString(R.string.todo_summary_description, describeDifficulty(task.priority))
|
||||
context.getString(
|
||||
R.string.todo_summary_description,
|
||||
describeDifficulty(task.priority)
|
||||
)
|
||||
}
|
||||
}
|
||||
TaskType.DAILY -> context.getString(R.string.daily_summary_description, describeDifficulty(task.priority), "sometimes")
|
||||
TaskType.DAILY -> context.getString(
|
||||
R.string.daily_summary_description,
|
||||
describeDifficulty(task.priority),
|
||||
describeRepeatInterval(task.frequency, task.everyX ?: 1),
|
||||
describeRepeatDays(task)
|
||||
)
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
|
@ -32,17 +53,99 @@ class TaskDescriptionBuilder(private val context: Context) {
|
|||
return dateFormatter.format(date)
|
||||
}
|
||||
|
||||
private fun describeRepeatDays(task: Task): Any {
|
||||
if (task.everyX == 0) {
|
||||
return ""
|
||||
}
|
||||
return when (task.frequency) {
|
||||
Frequency.WEEKLY -> {
|
||||
" " + if (task.repeat?.isEveryDay == true) {
|
||||
context.getString(R.string.on_every_day_of_week)
|
||||
} else {
|
||||
if (task.repeat?.isOnlyWeekdays == true) {
|
||||
context.getString(R.string.on_weekdays)
|
||||
} else if (task.repeat?.isOnlyWeekends == true) {
|
||||
context.getString(R.string.on_weekends)
|
||||
} else {
|
||||
val dayStrings = task.repeat?.dayStrings(context) ?: listOf()
|
||||
joinToCount(dayStrings)
|
||||
}
|
||||
}
|
||||
}
|
||||
Frequency.MONTHLY -> {
|
||||
" " + if (task.getDaysOfMonth()?.isNotEmpty() == true) {
|
||||
val dayList = task.getDaysOfMonth()?.map {
|
||||
withOrdinal(it)
|
||||
}
|
||||
context.getString(R.string.on_the_x, joinToCount(dayList))
|
||||
} else if (task.getWeeksOfMonth()?.isNotEmpty() == true) {
|
||||
val occurrence = when (task.getWeeksOfMonth()?.first()) {
|
||||
0 -> context.getString(R.string.first)
|
||||
1 -> context.getString(R.string.second)
|
||||
2 -> context.getString(R.string.third)
|
||||
3 -> context.getString(R.string.fourth)
|
||||
4 -> context.getString(R.string.fifth)
|
||||
else -> return ""
|
||||
}
|
||||
val dayStrings = task.repeat?.dayStrings(context) ?: listOf()
|
||||
|
||||
context.getString(R.string.on_the_x_of_month, occurrence, joinToCount(dayStrings))
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
Frequency.YEARLY -> " " + context.getString(R.string.on_x,
|
||||
task.startDate?.let {
|
||||
val flags = DateUtils.FORMAT_SHOW_DATE + DateUtils.FORMAT_NO_YEAR
|
||||
DateUtils.formatDateTime(context, it.time, flags)
|
||||
} ?: "")
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
||||
private fun joinToCount(dayStrings: List<String>?) =
|
||||
if (dayStrings?.size == 2) {
|
||||
context.getString(R.string.x_and_y, dayStrings[0], dayStrings[1])
|
||||
} else {
|
||||
dayStrings?.joinToString(", ") ?: ""
|
||||
}
|
||||
|
||||
private fun withOrdinal(day: Int): String {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
val formatter = MessageFormat("{0,ordinal}", Locale.getDefault())
|
||||
formatter.format(arrayOf(day))
|
||||
} else {
|
||||
day.toString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun describeRepeatInterval(interval: Frequency?, everyX: Int): String {
|
||||
if (everyX == 0) {
|
||||
return context.getString(R.string.never)
|
||||
}
|
||||
return when (interval) {
|
||||
Frequency.DAILY -> context.resources.getQuantityString(R.plurals.repeat_daily, everyX, everyX)
|
||||
Frequency.WEEKLY -> context.resources.getQuantityString(R.plurals.repeat_weekly, everyX, everyX)
|
||||
Frequency.MONTHLY -> context.resources.getQuantityString(
|
||||
R.plurals.repeat_monthly,
|
||||
everyX, everyX
|
||||
)
|
||||
Frequency.YEARLY -> context.resources.getQuantityString(R.plurals.repeat_yearly, everyX, everyX)
|
||||
null -> ""
|
||||
}
|
||||
}
|
||||
|
||||
private fun describeHabitDirections(up: Boolean, down: Boolean): String {
|
||||
return if (up && down) {
|
||||
context.getString(R.string.positive_and_negative)
|
||||
} else if (up) {
|
||||
context.getString(R.string.positive_habit_form)
|
||||
context.getString(R.string.positive_sentence)
|
||||
} else {
|
||||
context.getString(R.string.negative_habit_form)
|
||||
context.getString(R.string.negative_sentence)
|
||||
}
|
||||
}
|
||||
|
||||
private fun describeDifficulty(difficulty: Float): String {
|
||||
return context.getString(TaskDifficulty.valueOf(difficulty).nameRes)
|
||||
return context.getString(TaskDifficulty.valueOf(difficulty).nameSentenceRes)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,6 @@ package com.habitrpg.android.habitica.helpers.notifications
|
|||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.edit
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
|
|
@ -73,13 +70,11 @@ class PushNotificationManager(
|
|||
}
|
||||
}
|
||||
|
||||
fun removePushDeviceUsingStoredToken() {
|
||||
suspend fun removePushDeviceUsingStoredToken() {
|
||||
if (this.refreshedToken.isEmpty() || !userHasPushDevice()) {
|
||||
return
|
||||
}
|
||||
MainScope().launchCatching {
|
||||
apiClient.deletePushDevice(refreshedToken)
|
||||
}
|
||||
}
|
||||
|
||||
private fun userHasPushDevice(): Boolean {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,18 @@
|
|||
package com.habitrpg.android.habitica.models.tasks
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import com.habitrpg.android.habitica.R
|
||||
|
||||
@io.realm.annotations.RealmClass(embedded = true)
|
||||
open class Days() : io.realm.RealmObject(), Parcelable {
|
||||
val isEveryDay: Boolean
|
||||
get() = m && t && w && th && f && s && su
|
||||
val isOnlyWeekdays: Boolean
|
||||
get() = m && t && w && th && f && !s && !su
|
||||
val isOnlyWeekends: Boolean
|
||||
get() = !m && !t && !w && !th && !f && s && su
|
||||
var m: Boolean = true
|
||||
var t: Boolean = true
|
||||
var w: Boolean = true
|
||||
|
|
@ -37,6 +45,18 @@ open class Days() : io.realm.RealmObject(), Parcelable {
|
|||
return 0
|
||||
}
|
||||
|
||||
fun dayStrings(context: Context): List<String> {
|
||||
val days = mutableListOf<String>()
|
||||
if (m) days.add(context.getString(R.string.monday))
|
||||
if (t) days.add(context.getString(R.string.tuesday))
|
||||
if (w) days.add(context.getString(R.string.wednesday))
|
||||
if (th) days.add(context.getString(R.string.thursday))
|
||||
if (f) days.add(context.getString(R.string.friday))
|
||||
if (s) days.add(context.getString(R.string.saturday))
|
||||
if (su) days.add(context.getString(R.string.sunday))
|
||||
return days
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<Days> {
|
||||
override fun createFromParcel(parcel: Parcel): Days {
|
||||
return Days(parcel)
|
||||
|
|
|
|||
|
|
@ -104,8 +104,8 @@ open class Task : RealmObject, BaseMainObject, Parcelable, BaseTask {
|
|||
var isCreating: Boolean = false
|
||||
var yesterDaily: Boolean = true
|
||||
|
||||
private var daysOfMonthString: String? = null
|
||||
private var weeksOfMonthString: String? = null
|
||||
var daysOfMonthString: String? = null
|
||||
var weeksOfMonthString: String? = null
|
||||
|
||||
@Ignore
|
||||
private var daysOfMonth: List<Int>? = null
|
||||
|
|
@ -246,6 +246,19 @@ open class Task : RealmObject, BaseMainObject, Parcelable, BaseTask {
|
|||
}
|
||||
}
|
||||
|
||||
val lowSaturationTaskColor: Int
|
||||
get() {
|
||||
return when {
|
||||
this.value < -20 -> return R.color.maroon_sub_text
|
||||
this.value < -10 -> return R.color.red_sub_text
|
||||
this.value < -1 -> return R.color.orange_sub_text
|
||||
this.value < 1 -> return R.color.yellow_sub_text
|
||||
this.value < 5 -> return R.color.green_sub_text
|
||||
this.value < 10 -> return R.color.teal_sub_text
|
||||
else -> R.color.blue_sub_text
|
||||
}
|
||||
}
|
||||
|
||||
val extraExtraDarkTaskColor: Int
|
||||
get() {
|
||||
return when {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ open class GroupAssignedDetails: RealmObject(), BaseObject {
|
|||
|
||||
@RealmClass(embedded = true)
|
||||
open class TaskGroupPlan : RealmObject(), BaseObject {
|
||||
fun assignedDetailsFor(userID: String): GroupAssignedDetails? {
|
||||
return assignedUsersDetail.firstOrNull { it.assignedUserID == userID }
|
||||
}
|
||||
|
||||
@SerializedName("id")
|
||||
var groupID: String? = null
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import com.habitrpg.android.habitica.HabiticaBaseApplication
|
|||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.components.UserComponent
|
||||
import com.habitrpg.android.habitica.data.UserRepository
|
||||
import com.habitrpg.android.habitica.extensions.forceLocale
|
||||
import com.habitrpg.android.habitica.extensions.updateStatusBarColor
|
||||
import com.habitrpg.android.habitica.helpers.ExceptionHandler
|
||||
import com.habitrpg.android.habitica.helpers.NotificationsManager
|
||||
|
|
@ -251,14 +252,4 @@ abstract class BaseActivity : AppCompatActivity() {
|
|||
overridePendingTransition(R.anim.activity_fade_in, R.anim.activity_fade_out)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Resources.forceLocale(activity: BaseActivity, locale: Locale) {
|
||||
Locale.setDefault(locale)
|
||||
val configuration = Configuration()
|
||||
configuration.setLocale(locale)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
activity.createConfigurationContext(configuration)
|
||||
}
|
||||
updateConfiguration(configuration, displayMetrics)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,365 @@
|
|||
package com.habitrpg.android.habitica.ui.activities
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import androidx.activity.compose.setContent
|
||||
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.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.ProvideTextStyle
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.scale
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
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.SpanStyle
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.toUpperCase
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.tooling.preview.Devices
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil.compose.AsyncImage
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.components.UserComponent
|
||||
import com.habitrpg.android.habitica.helpers.MainNavigationController
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.ui.theme.HabiticaTheme
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
|
||||
import com.habitrpg.android.habitica.ui.views.CurrencyText
|
||||
import com.habitrpg.common.habitica.extensions.DataBindingUtils
|
||||
import javax.inject.Inject
|
||||
|
||||
class BirthdayActivity : BaseActivity() {
|
||||
@Inject
|
||||
lateinit var userViewModel: MainUserViewModel
|
||||
|
||||
override fun getLayoutResId(): Int? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
HabiticaTheme {
|
||||
val user = userViewModel.user.observeAsState()
|
||||
BirthdayActivityView(user.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun injectActivity(component: UserComponent?) {
|
||||
component?.inject(this)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BirthdayTitle(text: String) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically, modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 20.dp, bottom = 8.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(1.dp)
|
||||
.weight(1f)
|
||||
.background(colorResource(id = R.color.brand_50))
|
||||
)
|
||||
Image(painterResource(id = R.drawable.birthday_textdeco_left), null)
|
||||
Text(
|
||||
text,
|
||||
fontSize = 16.sp,
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.Bold,
|
||||
modifier = Modifier.padding(horizontal = 16.dp)
|
||||
)
|
||||
Image(painterResource(id = R.drawable.birthday_textdeco_right), null)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(1.dp)
|
||||
.weight(1f)
|
||||
.background(colorResource(id = R.color.brand_50))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BirthdayActivityView(user: User?) {
|
||||
val activity = LocalContext.current as? Activity
|
||||
val textColor = Color.White
|
||||
val specialTextColor = colorResource(R.color.yellow_50)
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier
|
||||
.background(
|
||||
Brush.verticalGradient(
|
||||
Pair(0.0f, colorResource(id = R.color.brand_300)),
|
||||
Pair(1.0f, colorResource(id = R.color.brand_200))
|
||||
)
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
Button(
|
||||
onClick = {
|
||||
if (activity != null) {
|
||||
activity.finish()
|
||||
return@Button
|
||||
}
|
||||
MainNavigationController.navigateBack()
|
||||
},
|
||||
colors = ButtonDefaults.textButtonColors(contentColor = textColor),
|
||||
elevation = ButtonDefaults.elevation(0.dp, 0.dp),
|
||||
modifier = Modifier.align(Alignment.Start)
|
||||
) {
|
||||
Image(
|
||||
painterResource(R.drawable.arrow_back),
|
||||
stringResource(R.string.action_back),
|
||||
colorFilter = ColorFilter.tint(
|
||||
textColor
|
||||
)
|
||||
)
|
||||
}
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier
|
||||
.padding(horizontal = 20.dp)
|
||||
.fillMaxWidth()) {
|
||||
Image(painterResource(R.drawable.birthday_header), null, Modifier.padding(bottom = 8.dp))
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Image(painterResource(R.drawable.birthday_gifts), null)
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(horizontal = 22.dp)
|
||||
) {
|
||||
Text(
|
||||
stringResource(id = R.string.limited_event).toUpperCase(Locale.current),
|
||||
fontSize = 12.sp,
|
||||
color = specialTextColor,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Text(
|
||||
"X to Y",
|
||||
fontSize = 12.sp,
|
||||
color = textColor,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
// right image should be flipped
|
||||
Image(
|
||||
painterResource(id = R.drawable.birthday_gifts),
|
||||
null,
|
||||
modifier = Modifier.scale(-1f, 1f)
|
||||
)
|
||||
}
|
||||
Text(
|
||||
stringResource(R.string.birthday_title_description),
|
||||
fontSize = 16.sp,
|
||||
color = specialTextColor,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 22.dp)
|
||||
)
|
||||
BirthdayTitle(stringResource(id = R.string.animated_gryphatrice_pet))
|
||||
Box(
|
||||
Modifier
|
||||
.padding(vertical = 20.dp)
|
||||
.size(161.dp, 129.dp)
|
||||
.background(colorResource(R.color.brand_50), RoundedCornerShape(8.dp))
|
||||
) {
|
||||
|
||||
}
|
||||
Text(
|
||||
stringResource(R.string.limited_edition).toUpperCase(Locale.current),
|
||||
fontSize = 12.sp,
|
||||
color = specialTextColor,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Text(
|
||||
stringResource(R.string.gryphatrice_description),
|
||||
fontSize = 16.sp,
|
||||
color = textColor,
|
||||
textAlign = TextAlign.Center,
|
||||
lineHeight = 20.sp,
|
||||
modifier = Modifier.padding(bottom = 16.dp)
|
||||
)
|
||||
val ownsGryphatrice = false
|
||||
if (ownsGryphatrice) {
|
||||
Text(
|
||||
stringResource(R.string.thanks_for_support),
|
||||
fontSize = 12.sp,
|
||||
color = textColor,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
HabiticaButton(
|
||||
Color.White,
|
||||
colorResource(R.color.brand_200),
|
||||
{},
|
||||
modifier = Modifier.padding(top = 20.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.equip))
|
||||
}
|
||||
} else {
|
||||
Text(buildAnnotatedString {
|
||||
append("Buy for ")
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append("")
|
||||
}
|
||||
append(" or ")
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append("60 Gems")
|
||||
}
|
||||
}, color = Color.White)
|
||||
HabiticaButton(
|
||||
Color.White,
|
||||
colorResource(R.color.brand_200),
|
||||
{},
|
||||
modifier = Modifier.padding(top = 20.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.buy_for_x, ""))
|
||||
}
|
||||
HabiticaButton(
|
||||
Color.White,
|
||||
colorResource(R.color.brand_200),
|
||||
{},
|
||||
modifier = Modifier.padding(top = 20.dp)
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(stringResource(R.string.buy_for))
|
||||
CurrencyText(currency = "gems", value = 60)
|
||||
}
|
||||
}
|
||||
}
|
||||
BirthdayTitle(stringResource(id = R.string.plenty_of_potions))
|
||||
Text(
|
||||
stringResource(R.string.plenty_of_potions_description),
|
||||
fontSize = 16.sp,
|
||||
color = textColor,
|
||||
textAlign = TextAlign.Center,
|
||||
lineHeight = 20.sp
|
||||
)
|
||||
PotionGrid()
|
||||
HabiticaButton(
|
||||
Color.White,
|
||||
colorResource(R.color.brand_200),
|
||||
{},
|
||||
modifier = Modifier.padding(top = 20.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.visit_the_market))
|
||||
}
|
||||
BirthdayTitle(stringResource(id = R.string.for_for_free))
|
||||
Text(
|
||||
stringResource(R.string.for_for_free_description),
|
||||
fontSize = 16.sp,
|
||||
color = textColor,
|
||||
textAlign = TextAlign.Center,
|
||||
lineHeight = 20.sp
|
||||
)
|
||||
}
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.padding(top = 20.dp)
|
||||
.background(colorResource(R.color.brand_50))
|
||||
.padding(horizontal = 20.dp)
|
||||
.padding(top = 20.dp, bottom = 60.dp)
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.limitations),
|
||||
fontSize = 16.sp,
|
||||
color = colorResource(R.color.brand_600),
|
||||
fontWeight = FontWeight.Bold,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Text(
|
||||
stringResource(R.string.birthday_limitations),
|
||||
fontSize = 14.sp,
|
||||
color = colorResource(R.color.brand_600),
|
||||
lineHeight = 20.sp,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PotionGrid() {
|
||||
val potions = listOf(
|
||||
"Porcelain",
|
||||
"Vampire",
|
||||
"Aquatic",
|
||||
"StainedGlass",
|
||||
"Celestial",
|
||||
"Glow",
|
||||
"AutumnLeaf",
|
||||
"SandSculpture",
|
||||
"Peppermint",
|
||||
"Shimmer"
|
||||
).windowed(4, 4, true)
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp), horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(top = 20.dp)) {
|
||||
for (potionGroup in potions) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
for (potion in potionGroup) {
|
||||
Box(Modifier.size(68.dp).background(colorResource(R.color.brand_50), RoundedCornerShape(8.dp))) {
|
||||
AsyncImage(model = DataBindingUtils.BASE_IMAGE_URL + DataBindingUtils.getFullFilename("Pet_HatchingPotion_$potion"), null, Modifier.size(68.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HabiticaButton(
|
||||
background: Color,
|
||||
color: Color,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
Box(contentAlignment = Alignment.Center, modifier = modifier
|
||||
.background(background, HabiticaTheme.shapes.medium)
|
||||
.clickable { onClick() }
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)) {
|
||||
ProvideTextStyle(
|
||||
value = TextStyle(
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = color
|
||||
)
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(device = Devices.PIXEL_4)
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
BirthdayActivityView(null)
|
||||
}
|
||||
|
|
@ -131,6 +131,7 @@ class FullProfileActivity : BaseActivity() {
|
|||
binding.adminStatusView.isVisible = isModerator
|
||||
if (isModerator) {
|
||||
val member = socialRepository.retrieveMember(userID, true)
|
||||
member?.stats = this@FullProfileActivity.member.value?.stats
|
||||
if (member != null) {
|
||||
updateView(member)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import android.text.InputType
|
|||
import android.text.SpannableString
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.text.style.UnderlineSpan
|
||||
import android.util.Log
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
|
|
@ -43,7 +42,6 @@ import com.habitrpg.android.habitica.helpers.AmplitudeManager
|
|||
import com.habitrpg.android.habitica.helpers.AppConfigManager
|
||||
import com.habitrpg.android.habitica.helpers.ExceptionHandler
|
||||
import com.habitrpg.android.habitica.helpers.launchCatching
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.ui.helpers.dismissKeyboard
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.AuthenticationViewModel
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
|
||||
|
|
@ -70,43 +68,59 @@ class LoginActivity : BaseActivity() {
|
|||
private val loginClick = View.OnClickListener {
|
||||
binding.PBAsyncTask.visibility = View.VISIBLE
|
||||
if (isRegistering) {
|
||||
val username: String = binding.username.text.toString().trim { it <= ' ' }
|
||||
val email: String = binding.email.text.toString().trim { it <= ' ' }
|
||||
val password: String = binding.password.text.toString()
|
||||
val confirmPassword: String = binding.confirmPassword.text.toString()
|
||||
if (username.isEmpty() || password.isEmpty() || email.isEmpty() || confirmPassword.isEmpty()) {
|
||||
showValidationError(R.string.login_validation_error_fieldsmissing)
|
||||
return@OnClickListener
|
||||
}
|
||||
if (password.length < configManager.minimumPasswordLength()) {
|
||||
showValidationError(getString(R.string.password_too_short, configManager.minimumPasswordLength()))
|
||||
return@OnClickListener
|
||||
}
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine {
|
||||
hideProgress()
|
||||
ExceptionHandler.reportError(it)
|
||||
}) {
|
||||
val response = apiClient.registerUser(username, email, password, confirmPassword)
|
||||
if (response != null) {
|
||||
handleAuthResponse(response)
|
||||
}
|
||||
}
|
||||
registerWithPassword()
|
||||
} else {
|
||||
val username: String = binding.username.text.toString().trim { it <= ' ' }
|
||||
val password: String = binding.password.text.toString()
|
||||
if (username.isEmpty() || password.isEmpty()) {
|
||||
showValidationError(R.string.login_validation_error_fieldsmissing)
|
||||
return@OnClickListener
|
||||
}
|
||||
Log.d("LoginActivity", ": $username, $password")
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine {
|
||||
loginWithPassword()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loginWithPassword() {
|
||||
val username: String = binding.username.text.toString().trim { it <= ' ' }
|
||||
val password: String = binding.password.text.toString()
|
||||
if (username.isEmpty() || password.isEmpty()) {
|
||||
showValidationError(R.string.login_validation_error_fieldsmissing)
|
||||
return
|
||||
}
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine {
|
||||
hideProgress()
|
||||
ExceptionHandler.reportError(it)
|
||||
}) {
|
||||
val response = apiClient.connectUser(username, password)
|
||||
if (response != null) {
|
||||
handleAuthResponse(response)
|
||||
} else {
|
||||
hideProgress()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerWithPassword() {
|
||||
val username: String = binding.username.text.toString().trim { it <= ' ' }
|
||||
val email: String = binding.email.text.toString().trim { it <= ' ' }
|
||||
val password: String = binding.password.text.toString()
|
||||
val confirmPassword: String = binding.confirmPassword.text.toString()
|
||||
if (username.isEmpty() || password.isEmpty() || email.isEmpty() || confirmPassword.isEmpty()) {
|
||||
showValidationError(R.string.login_validation_error_fieldsmissing)
|
||||
return
|
||||
}
|
||||
if (password.length < configManager.minimumPasswordLength()) {
|
||||
showValidationError(
|
||||
getString(
|
||||
R.string.password_too_short,
|
||||
configManager.minimumPasswordLength()
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine {
|
||||
hideProgress()
|
||||
ExceptionHandler.reportError(it)
|
||||
}) {
|
||||
val response = apiClient.registerUser(username, email, password, confirmPassword)
|
||||
if (response != null) {
|
||||
handleAuthResponse(response)
|
||||
} else {
|
||||
hideProgress()
|
||||
ExceptionHandler.reportError(it)
|
||||
}) {
|
||||
val response = apiClient.connectUser(username, password)
|
||||
if (response != null) {
|
||||
handleAuthResponse(response)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -264,15 +278,10 @@ class LoginActivity : BaseActivity() {
|
|||
} catch (e: Exception) {
|
||||
// Wearable API is not available on this device.
|
||||
}
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine()) {
|
||||
val user = userRepository.retrieveUser(true)
|
||||
if (user != null) {
|
||||
handleAuthResponse(user, response.newUser)
|
||||
}
|
||||
}
|
||||
handleAuthResponse(response.newUser)
|
||||
}
|
||||
|
||||
private fun handleAuthResponse(user: User, isNew: Boolean) {
|
||||
private fun handleAuthResponse(isNew: Boolean) {
|
||||
hideProgress()
|
||||
dismissKeyboard()
|
||||
|
||||
|
|
@ -319,8 +328,8 @@ class LoginActivity : BaseActivity() {
|
|||
private val pickAccountResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
viewModel.googleEmail = it?.data?.getStringExtra(AccountManager.KEY_ACCOUNT_NAME)
|
||||
viewModel.handleGoogleLoginResult(this, recoverFromPlayServicesErrorResult) { user, isNew ->
|
||||
handleAuthResponse(user, isNew)
|
||||
viewModel.handleGoogleLoginResult(this, recoverFromPlayServicesErrorResult) { isNew ->
|
||||
handleAuthResponse(isNew)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -329,8 +338,8 @@ class LoginActivity : BaseActivity() {
|
|||
ActivityResultContracts.StartActivityForResult()
|
||||
) {
|
||||
if (it.resultCode != Activity.RESULT_CANCELED) {
|
||||
viewModel.handleGoogleLoginResult(this, null) { user, isNew ->
|
||||
handleAuthResponse(user, isNew)
|
||||
viewModel.handleGoogleLoginResult(this, null) { isNew ->
|
||||
handleAuthResponse(isNew)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import android.content.res.Configuration
|
|||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
|
@ -17,6 +18,7 @@ import androidx.activity.result.contract.ActivityResultContracts
|
|||
import androidx.activity.viewModels
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import com.habitrpg.android.habitica.models.user.User
|
|||
import com.habitrpg.android.habitica.ui.fragments.setup.AvatarSetupFragment
|
||||
import com.habitrpg.android.habitica.ui.fragments.setup.TaskSetupFragment
|
||||
import com.habitrpg.android.habitica.ui.fragments.setup.WelcomeFragment
|
||||
import com.habitrpg.common.habitica.api.HostConfig
|
||||
import com.viewpagerindicator.IconPagerAdapter
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.Calendar
|
||||
|
|
@ -44,8 +43,6 @@ class SetupActivity : BaseActivity(), ViewPager.OnPageChangeListener {
|
|||
@Inject
|
||||
lateinit var apiClient: ApiClient
|
||||
@Inject
|
||||
lateinit var hostConfig: HostConfig
|
||||
@Inject
|
||||
lateinit var inventoryRepository: InventoryRepository
|
||||
@Inject
|
||||
lateinit var taskRepository: TaskRepository
|
||||
|
|
|
|||
|
|
@ -4,15 +4,14 @@ import android.app.Activity
|
|||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.TextUtils
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.text.util.Linkify
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.MotionEvent
|
||||
|
|
@ -25,10 +24,12 @@ import androidx.appcompat.widget.AppCompatCheckBox
|
|||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.forEachIndexed
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.iterator
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.habitrpg.android.habitica.R
|
||||
|
|
@ -50,6 +51,7 @@ import com.habitrpg.android.habitica.models.members.Member
|
|||
import com.habitrpg.android.habitica.models.social.Challenge
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.android.habitica.models.tasks.TaskGroupPlan
|
||||
import com.habitrpg.android.habitica.ui.helpers.ToolbarColorHelper
|
||||
import com.habitrpg.android.habitica.ui.helpers.dismissKeyboard
|
||||
import com.habitrpg.android.habitica.ui.theme.HabiticaTheme
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
|
||||
|
|
@ -169,6 +171,9 @@ class TaskFormActivity : BaseActivity() {
|
|||
setSupportActionBar(binding.toolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||
if (forcedTheme == "taskform" || forcedTheme == "maroon") {
|
||||
ToolbarColorHelper.colorizeToolbar(binding.toolbar, this, ContextCompat.getColor(this, R.color.white))
|
||||
}
|
||||
tintColor = getThemeColor(R.attr.taskFormTint)
|
||||
val upperTintColor =
|
||||
if (forcedTheme == "taskform") getThemeColor(R.attr.taskFormTint) else getThemeColor(R.attr.colorAccent)
|
||||
|
|
@ -388,6 +393,13 @@ class TaskFormActivity : BaseActivity() {
|
|||
menuInflater.inflate(R.menu.menu_task_edit, menu)
|
||||
}
|
||||
menu.findItem(R.id.action_save).isEnabled = canSave
|
||||
if (forcedTheme == "taskform" || forcedTheme == "maroon") {
|
||||
menu.iterator().forEach {
|
||||
val spannable = SpannableString(it.title)
|
||||
spannable.setSpan(ForegroundColorSpan(Color.WHITE), 0, spannable.length, 0)
|
||||
it.title = spannable
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import androidx.activity.compose.setContent
|
|||
import androidx.activity.viewModels
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
|
|
@ -27,6 +28,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
|
|
@ -53,14 +55,18 @@ import com.habitrpg.android.habitica.models.members.Member
|
|||
import com.habitrpg.android.habitica.ui.theme.HabiticaTheme
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.BaseViewModel
|
||||
import com.habitrpg.android.habitica.ui.views.CompletedAt
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
|
||||
import com.habitrpg.android.habitica.ui.views.UserRow
|
||||
import com.habitrpg.shared.habitica.models.tasks.TaskType
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import java.text.DateFormat
|
||||
import java.util.Date
|
||||
import javax.inject.Inject
|
||||
|
||||
class TaskSummaryViewModel(val taskId: String) : BaseViewModel() {
|
||||
@Inject
|
||||
lateinit var taskRespository: TaskRepository
|
||||
|
||||
@Inject
|
||||
lateinit var socialRepository: SocialRepository
|
||||
|
||||
|
|
@ -117,7 +123,9 @@ fun TaskSummaryView(viewModel: TaskSummaryViewModel) {
|
|||
|
||||
if (task != null) {
|
||||
val darkestColor = HabiticaTheme.colors.textPrimaryFor(task)
|
||||
val topTextColor = if ((task?.value ?: 0.0) >= -20) colorResource(task?.extraDarkTaskColor ?: R.color.white) else Color.White
|
||||
val topTextColor = if ((task?.value ?: 0.0) >= -20) colorResource(
|
||||
task?.extraDarkTaskColor ?: R.color.white
|
||||
) else Color.White
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val statusBarColor = HabiticaTheme.colors.primaryBackgroundFor(task)
|
||||
val lightestColor = HabiticaTheme.colors.contentBackgroundFor(task)
|
||||
|
|
@ -204,11 +212,40 @@ fun TaskSummaryView(viewModel: TaskSummaryViewModel) {
|
|||
fontWeight = FontWeight.Medium,
|
||||
modifier = titleModifier
|
||||
)
|
||||
Text(task?.let { taskDescriptionBuilder.describe(it) }!!.makeBoldComposable(),
|
||||
Text(
|
||||
task?.let { taskDescriptionBuilder.describe(it) }!!.makeBoldComposable(),
|
||||
fontSize = 16.sp,
|
||||
color = darkestColor,
|
||||
fontWeight = FontWeight.Normal,
|
||||
modifier = textModifier)
|
||||
modifier = textModifier
|
||||
)
|
||||
}
|
||||
if (task?.type == TaskType.REWARD) {
|
||||
Text(
|
||||
stringResource(R.string.cost),
|
||||
fontSize = 16.sp,
|
||||
color = darkestColor,
|
||||
fontWeight = FontWeight.Medium,
|
||||
modifier = titleModifier.padding(bottom = 4.dp)
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
modifier = Modifier
|
||||
.padding(vertical = 4.dp)
|
||||
.background(
|
||||
HabiticaTheme.colors.windowBackgroundFor(task),
|
||||
MaterialTheme.shapes.medium
|
||||
)
|
||||
.padding(15.dp)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Image(HabiticaIconsHelper.imageOfGold().asImageBitmap(), null)
|
||||
Text("${task?.value}",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = darkestColor
|
||||
)
|
||||
}
|
||||
}
|
||||
if (task?.checklist?.isNotEmpty() == true) {
|
||||
task?.checklist?.let { checklist ->
|
||||
|
|
@ -263,8 +300,22 @@ fun TaskSummaryView(viewModel: TaskSummaryViewModel) {
|
|||
}) else null
|
||||
)
|
||||
}
|
||||
task?.group?.assignedUsersDetail?.find { it.assignedUserID == viewModel.userViewModel.userID }?.let {
|
||||
Text("", )
|
||||
task?.group?.assignedUsersDetail?.find { it.assignedUserID == viewModel.userViewModel.userID }
|
||||
?.let {
|
||||
Text("")
|
||||
}
|
||||
task?.group?.assignedDetailsFor(viewModel.userViewModel.userID)?.let {
|
||||
val formatter = DateFormat.getDateInstance(DateFormat.SHORT)
|
||||
Text(
|
||||
stringResource(
|
||||
R.string.assigned_to_you_by,
|
||||
it.assigningUsername ?: "",
|
||||
formatter.format(it.assignedDate ?: Date())
|
||||
),
|
||||
fontSize = 14.sp,
|
||||
color = HabiticaTheme.colors.textSecondaryFor(task),
|
||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 10.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,150 @@
|
|||
package com.habitrpg.android.habitica.ui.adapter
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.CustomizationGridItemBinding
|
||||
import com.habitrpg.android.habitica.databinding.DialogPurchaseCustomizationBinding
|
||||
import com.habitrpg.android.habitica.helpers.MainNavigationController
|
||||
import com.habitrpg.android.habitica.models.inventory.CustomizationSet
|
||||
import com.habitrpg.android.habitica.models.inventory.Equipment
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
|
||||
import com.habitrpg.common.habitica.extensions.loadImage
|
||||
import com.habitrpg.common.habitica.views.PixelArtView
|
||||
|
||||
class CustomizationEquipmentRecyclerViewAdapter : androidx.recyclerview.widget.RecyclerView.Adapter<androidx.recyclerview.widget.RecyclerView.ViewHolder>() {
|
||||
|
||||
var gemBalance: Int = 0
|
||||
var equipmentList: MutableList<Equipment> =
|
||||
ArrayList()
|
||||
set(value) {
|
||||
field = value
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
var activeEquipment: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
this.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
var onSelect: ((Equipment) -> Unit)? = null
|
||||
var onUnlock: ((Equipment) -> Unit)? = null
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder {
|
||||
val viewID: Int = R.layout.customization_grid_item
|
||||
|
||||
val view = LayoutInflater.from(parent.context).inflate(viewID, parent, false)
|
||||
return EquipmentViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(
|
||||
holder: androidx.recyclerview.widget.RecyclerView.ViewHolder,
|
||||
position: Int
|
||||
) {
|
||||
(holder as EquipmentViewHolder).bind(equipmentList[position])
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return equipmentList.size
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
if (equipmentList.size <= position) return 0
|
||||
return if (this.equipmentList[position].javaClass == CustomizationSet::class.java) {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
fun setEquipment(newEquipmentList: List<Equipment>) {
|
||||
this.equipmentList = newEquipmentList.toMutableList()
|
||||
val emptyEquipment = Equipment()
|
||||
equipmentList.add(0, emptyEquipment)
|
||||
this.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
internal inner class EquipmentViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener {
|
||||
|
||||
private val binding = CustomizationGridItemBinding.bind(itemView)
|
||||
var equipment: Equipment? = null
|
||||
|
||||
init {
|
||||
itemView.setOnClickListener(this)
|
||||
}
|
||||
|
||||
fun bind(equipment: Equipment) {
|
||||
this.equipment = equipment
|
||||
binding.imageView.loadImage("shop_" + this.equipment?.key)
|
||||
if (equipment.owned == true || equipment.value == 0.0) {
|
||||
binding.buyButton.visibility = View.GONE
|
||||
} else {
|
||||
binding.buyButton.visibility = View.VISIBLE
|
||||
binding.priceLabel.currency = "gems"
|
||||
binding.priceLabel.value = if (equipment.gearSet == "animal") {
|
||||
2.0
|
||||
} else {
|
||||
equipment.value
|
||||
}
|
||||
}
|
||||
|
||||
if (activeEquipment == equipment.key || (activeEquipment?.contains("base_0") == true && equipment.key?.isNotBlank() != true)) {
|
||||
binding.wrapper.background = ContextCompat.getDrawable(itemView.context, R.drawable.layout_rounded_bg_window_tint_border)
|
||||
} else {
|
||||
binding.wrapper.background = ContextCompat.getDrawable(itemView.context, R.drawable.layout_rounded_bg_window)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
if (equipment?.owned != true && (equipment?.value ?: 0.0) > 0.0) {
|
||||
val dialogContent = LinearLayout(itemView.context)
|
||||
DialogPurchaseCustomizationBinding.inflate(LayoutInflater.from(itemView.context), dialogContent)
|
||||
|
||||
val imageView = dialogContent.findViewById<PixelArtView>(R.id.imageView)
|
||||
imageView.loadImage("shop_" + this.equipment?.key)
|
||||
|
||||
val priceLabel = dialogContent.findViewById<TextView>(R.id.priceLabel)
|
||||
priceLabel.text = if (equipment?.gearSet == "animal") {
|
||||
2.0
|
||||
} else {
|
||||
equipment?.value ?: 0
|
||||
}.toString()
|
||||
|
||||
(dialogContent.findViewById<View>(R.id.gem_icon) as? ImageView)?.setImageBitmap(
|
||||
HabiticaIconsHelper.imageOfGem())
|
||||
|
||||
val dialog = HabiticaAlertDialog(itemView.context)
|
||||
dialog.addButton(R.string.purchase_button, true) { _, _ ->
|
||||
if ((equipment?.value ?: 0.0) > gemBalance) {
|
||||
MainNavigationController.navigate(R.id.gemPurchaseActivity, bundleOf(Pair("openSubscription", false)))
|
||||
return@addButton
|
||||
}
|
||||
|
||||
equipment?.let {
|
||||
onUnlock?.invoke(it)
|
||||
}
|
||||
}
|
||||
dialog.setTitle(R.string.purchase_customization)
|
||||
dialog.setAdditionalContentView(dialogContent)
|
||||
dialog.addButton(R.string.reward_dialog_dismiss, false)
|
||||
dialog.show()
|
||||
return
|
||||
}
|
||||
|
||||
if (equipment?.key == activeEquipment) {
|
||||
return
|
||||
}
|
||||
|
||||
equipment?.let {
|
||||
onSelect?.invoke(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,12 +3,15 @@ package com.habitrpg.android.habitica.ui.adapter
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.extensions.inflate
|
||||
import com.habitrpg.android.habitica.models.promotions.HabiticaPromotion
|
||||
import com.habitrpg.android.habitica.ui.menu.HabiticaDrawerItem
|
||||
import com.habitrpg.android.habitica.ui.viewHolders.ComposableViewHolder
|
||||
import com.habitrpg.android.habitica.ui.views.promo.BirthdayBanner
|
||||
import com.habitrpg.android.habitica.ui.views.promo.PromoMenuView
|
||||
import com.habitrpg.android.habitica.ui.views.promo.PromoMenuViewHolder
|
||||
import com.habitrpg.android.habitica.ui.views.promo.SubscriptionBuyGemsPromoView
|
||||
|
|
@ -89,6 +92,11 @@ class NavigationDrawerAdapter(tintColor: Int, backgroundTintColor: Int) : Recycl
|
|||
}
|
||||
}
|
||||
}
|
||||
getItemViewType(position) == 6 -> {
|
||||
(holder.itemView as? ComposeView)?.setContent {
|
||||
BirthdayBanner()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -122,6 +130,7 @@ class NavigationDrawerAdapter(tintColor: Int, backgroundTintColor: Int) : Recycl
|
|||
)
|
||||
PromoMenuViewHolder(promoView)
|
||||
}
|
||||
6 -> ComposableViewHolder(ComposeView(parent.context))
|
||||
1 -> SectionHeaderViewHolder(parent.inflate(R.layout.drawer_main_section_header))
|
||||
else -> DrawerItemViewHolder(parent.inflate(R.layout.drawer_main_item))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -546,6 +546,12 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
item.itemViewType = 2
|
||||
items.add(item)
|
||||
}
|
||||
|
||||
if (configManager.isBirthday()) {
|
||||
val birthdayItem = HabiticaDrawerItem(R.id.birthdayActivity, SIDEBAR_BIRTHDAY)
|
||||
birthdayItem.itemViewType = 6
|
||||
items.add(0, birthdayItem)
|
||||
}
|
||||
adapter.updateItems(items)
|
||||
}
|
||||
|
||||
|
|
@ -805,6 +811,7 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
const val SIDEBAR_GEMS = "gems"
|
||||
const val SIDEBAR_SUBSCRIPTION = "subscription"
|
||||
const val SIDEBAR_SUBSCRIPTION_PROMO = "subscriptionpromo"
|
||||
const val SIDEBAR_BIRTHDAY = "birthday"
|
||||
const val SIDEBAR_PROMO = "promo"
|
||||
const val SIDEBAR_ABOUT_HEADER = "about_header"
|
||||
const val SIDEBAR_NEWS = "news"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,148 @@
|
|||
package com.habitrpg.android.habitica.ui.fragments.inventory.customization
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.components.UserComponent
|
||||
import com.habitrpg.android.habitica.data.InventoryRepository
|
||||
import com.habitrpg.android.habitica.databinding.FragmentRefreshRecyclerviewBinding
|
||||
import com.habitrpg.android.habitica.helpers.ExceptionHandler
|
||||
import com.habitrpg.android.habitica.helpers.launchCatching
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.ui.adapter.CustomizationEquipmentRecyclerViewAdapter
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
|
||||
import com.habitrpg.android.habitica.ui.helpers.MarginDecoration
|
||||
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class AvatarEquipmentFragment :
|
||||
BaseMainFragment<FragmentRefreshRecyclerviewBinding>(),
|
||||
SwipeRefreshLayout.OnRefreshListener {
|
||||
|
||||
@Inject
|
||||
lateinit var inventoryRepository: InventoryRepository
|
||||
@Inject
|
||||
lateinit var userViewModel: MainUserViewModel
|
||||
|
||||
override var binding: FragmentRefreshRecyclerviewBinding? = null
|
||||
|
||||
override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRefreshRecyclerviewBinding {
|
||||
return FragmentRefreshRecyclerviewBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
var type: String? = null
|
||||
var category: String? = null
|
||||
private var activeEquipment: String? = null
|
||||
|
||||
internal var adapter: CustomizationEquipmentRecyclerViewAdapter = CustomizationEquipmentRecyclerViewAdapter()
|
||||
internal var layoutManager: GridLayoutManager = GridLayoutManager(activity, 2)
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
showsBackButton = true
|
||||
adapter.onSelect = { equipment ->
|
||||
val key = (if (equipment.key?.isNotBlank() != true) activeEquipment else equipment.key) ?: ""
|
||||
lifecycleScope.launchCatching {
|
||||
inventoryRepository.equip(if (userViewModel.user.value?.preferences?.costume == true) "costume" else "equipped", key)
|
||||
}
|
||||
}
|
||||
adapter.onUnlock = { equipment ->
|
||||
lifecycleScope.launchCatching { }
|
||||
}
|
||||
return super.onCreateView(inflater, container, savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
showsBackButton = true
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
arguments?.let {
|
||||
val args = AvatarEquipmentFragmentArgs.fromBundle(it)
|
||||
type = args.type
|
||||
if (args.category.isNotEmpty()) {
|
||||
category = args.category
|
||||
}
|
||||
}
|
||||
binding?.refreshLayout?.setOnRefreshListener(this)
|
||||
setGridSpanCount(view.width)
|
||||
val layoutManager = GridLayoutManager(activity, 4)
|
||||
layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
return if (adapter.getItemViewType(position) == 0) {
|
||||
layoutManager.spanCount
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
binding?.recyclerView?.layoutManager = layoutManager
|
||||
binding?.recyclerView?.addItemDecoration(MarginDecoration(context))
|
||||
|
||||
binding?.recyclerView?.adapter = adapter
|
||||
binding?.recyclerView?.itemAnimator = SafeDefaultItemAnimator()
|
||||
this.loadEquipment()
|
||||
|
||||
userViewModel.user.observe(viewLifecycleOwner) { updateUser(it) }
|
||||
}
|
||||
|
||||
override fun injectFragment(component: UserComponent) {
|
||||
component.inject(this)
|
||||
}
|
||||
|
||||
private fun loadEquipment() {
|
||||
val type = this.type ?: return
|
||||
lifecycleScope.launchCatching {
|
||||
inventoryRepository.getEquipmentType(type, category ?: "").collect {
|
||||
adapter.setEquipment(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setGridSpanCount(width: Int) {
|
||||
val itemWidth = context?.resources?.getDimension(R.dimen.customization_width) ?: 0F
|
||||
var spanCount = (width / itemWidth).toInt()
|
||||
if (spanCount == 0) {
|
||||
spanCount = 1
|
||||
}
|
||||
layoutManager.spanCount = spanCount
|
||||
}
|
||||
|
||||
fun updateUser(user: User?) {
|
||||
this.updateActiveCustomization(user)
|
||||
this.adapter.gemBalance = user?.gemCount ?: 0
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
private fun updateActiveCustomization(user: User?) {
|
||||
if (this.type == null || user?.preferences == null) {
|
||||
return
|
||||
}
|
||||
val outfit = if (user.preferences?.costume == true) user.items?.gear?.costume else user.items?.gear?.equipped
|
||||
val activeEquipment = when (this.type) {
|
||||
"headAccessory" -> outfit?.headAccessory
|
||||
"back" -> outfit?.back
|
||||
"eyewear" -> outfit?.eyeWear
|
||||
else -> ""
|
||||
}
|
||||
if (activeEquipment != null) {
|
||||
this.activeEquipment = activeEquipment
|
||||
this.adapter.activeEquipment = activeEquipment
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRefresh() {
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine()) {
|
||||
userRepository.retrieveUser(true, true)
|
||||
binding?.refreshLayout?.isRefreshing = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,9 +29,9 @@ import com.habitrpg.android.habitica.helpers.launchCatching
|
|||
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
|
||||
import com.habitrpg.android.habitica.ui.theme.HabiticaTheme
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
|
||||
import com.habitrpg.android.habitica.ui.views.AvatarCustomizationOverviewView
|
||||
import com.habitrpg.android.habitica.ui.views.EquipmentOverviewView
|
||||
import com.habitrpg.android.habitica.ui.views.SegmentedControl
|
||||
import com.habitrpg.android.habitica.ui.views.equipment.AvatarCustomizationOverviewView
|
||||
import com.habitrpg.android.habitica.ui.views.equipment.EquipmentOverviewView
|
||||
import javax.inject.Inject
|
||||
|
||||
open class AvatarOverviewFragment : BaseMainFragment<FragmentComposeScrollingBinding>(),
|
||||
|
|
@ -65,7 +65,9 @@ open class AvatarOverviewFragment : BaseMainFragment<FragmentComposeScrollingBin
|
|||
showCustomization, !showCustomization,
|
||||
{ type, category ->
|
||||
displayCustomizationFragment(type, category)
|
||||
}, { type, equipped, isCostume ->
|
||||
}, { type, category ->
|
||||
displayAvatarEquipmentFragment(type, category)
|
||||
}, { type, equipped, isCostume ->
|
||||
displayEquipmentFragment(type, equipped, isCostume)
|
||||
})
|
||||
}
|
||||
|
|
@ -87,6 +89,10 @@ open class AvatarOverviewFragment : BaseMainFragment<FragmentComposeScrollingBin
|
|||
)
|
||||
}
|
||||
|
||||
private fun displayAvatarEquipmentFragment(type: String, category: String?) {
|
||||
MainNavigationController.navigate(AvatarOverviewFragmentDirections.openAvatarEquipment(type, category ?: ""))
|
||||
}
|
||||
|
||||
private fun displayEquipmentFragment(type: String, equipped: String?, isCostume: Boolean = false) {
|
||||
MainNavigationController.navigate(AvatarOverviewFragmentDirections.openEquipmentDetail(type, isCostume, equipped ?: ""))
|
||||
}
|
||||
|
|
@ -108,6 +114,7 @@ fun AvatarOverviewView(userViewModel: MainUserViewModel,
|
|||
showCustomization: Boolean = true,
|
||||
showEquipment: Boolean = true,
|
||||
onCustomizationTap: (String, String?) -> Unit,
|
||||
onAvatarEquipmentTap: (String, String?) -> Unit,
|
||||
onEquipmentTap: (String, String?, Boolean) -> Unit
|
||||
) {
|
||||
val user by userViewModel.user.observeAsState()
|
||||
|
|
@ -139,7 +146,7 @@ fun AvatarOverviewView(userViewModel: MainUserViewModel,
|
|||
)
|
||||
})
|
||||
}
|
||||
AvatarCustomizationOverviewView(user?.preferences, onCustomizationTap)
|
||||
AvatarCustomizationOverviewView(user?.preferences, user?.items?.gear?.equipped, onCustomizationTap, onAvatarEquipmentTap)
|
||||
}
|
||||
if (showEquipment) {
|
||||
Row(
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ class AccountPreferenceFragment :
|
|||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
viewModel.googleEmail = it?.data?.getStringExtra(AccountManager.KEY_ACCOUNT_NAME)
|
||||
activity?.let { it1 ->
|
||||
viewModel.handleGoogleLoginResult(it1, recoverFromPlayServicesErrorResult) { _, _ ->
|
||||
viewModel.handleGoogleLoginResult(it1, recoverFromPlayServicesErrorResult) { _ ->
|
||||
displayAuthenticationSuccess(getString(R.string.google))
|
||||
}
|
||||
}
|
||||
|
|
@ -246,7 +246,7 @@ class AccountPreferenceFragment :
|
|||
) {
|
||||
if (it.resultCode != Activity.RESULT_CANCELED) {
|
||||
activity?.let { it1 ->
|
||||
viewModel.handleGoogleLoginResult(it1, null) { _, _ ->
|
||||
viewModel.handleGoogleLoginResult(it1, null) { _ ->
|
||||
displayAuthenticationSuccess(getString(R.string.google))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -225,7 +225,9 @@ class PreferencesFragment : BasePreferencesFragment(), SharedPreferences.OnShare
|
|||
pushNotificationManager.addPushDeviceUsingStoredToken()
|
||||
}
|
||||
} else {
|
||||
pushNotificationManager.removePushDeviceUsingStoredToken()
|
||||
lifecycleScope.launchCatching {
|
||||
pushNotificationManager.removePushDeviceUsingStoredToken()
|
||||
}
|
||||
}
|
||||
}
|
||||
"useEmails" -> {
|
||||
|
|
|
|||
|
|
@ -137,19 +137,19 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
context?.let { recyclerAdapter?.taskDisplayMode = configManager.taskDisplayMode(it) }
|
||||
|
||||
recyclerAdapter?.errorButtonEvents = {
|
||||
lifecycleScope.launchCatching {
|
||||
taskRepository.syncErroredTasks()
|
||||
}
|
||||
lifecycleScope.launchCatching {
|
||||
taskRepository.syncErroredTasks()
|
||||
}
|
||||
}
|
||||
recyclerAdapter?.taskOpenEvents = { task, view ->
|
||||
openTaskForm(task)
|
||||
}
|
||||
recyclerAdapter?.taskScoreEvents = { task, direction ->
|
||||
playSound(direction)
|
||||
context?.let { it1 -> notificationsManager.dismissTaskNotification(it1, task) }
|
||||
playSound(direction)
|
||||
context?.let { it1 -> notificationsManager.dismissTaskNotification(it1, task) }
|
||||
scoreTask(task, direction)
|
||||
}
|
||||
recyclerAdapter?.checklistItemScoreEvents = { task, item ->
|
||||
}
|
||||
recyclerAdapter?.checklistItemScoreEvents = { task, item ->
|
||||
scoreChecklistItem(task, item)
|
||||
}
|
||||
recyclerAdapter?.brokenTaskEvents = { showBrokenChallengeDialog(it) }
|
||||
|
|
@ -165,6 +165,7 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
}
|
||||
lifecycleScope.launch {
|
||||
viewModel.userViewModel.user.asFlow()
|
||||
.onEach { recyclerAdapter?.user = it }
|
||||
.map { it?.preferences?.tasks?.mirrorGroupTasks }
|
||||
.distinctUntilChanged()
|
||||
.collect {
|
||||
|
|
@ -364,6 +365,7 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
recyclerAdapter?.user = it
|
||||
}
|
||||
}
|
||||
setPreferenceTaskFilters()
|
||||
}
|
||||
|
||||
private fun updateTaskSubscription(ownerID: String?) {
|
||||
|
|
@ -419,215 +421,215 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
}
|
||||
dialog.setExtraCloseButtonVisibility(View.VISIBLE)
|
||||
dialog.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setEmptyLabels() {
|
||||
binding?.recyclerView?.emptyItem = if (viewModel.filterCount(taskType) > 0) {
|
||||
when (this.taskType) {
|
||||
TaskType.HABIT -> {
|
||||
EmptyItem(
|
||||
getString(R.string.empty_title_habits_filtered),
|
||||
getString(R.string.empty_description_habits_filtered),
|
||||
R.drawable.icon_habits
|
||||
)
|
||||
}
|
||||
TaskType.DAILY -> {
|
||||
EmptyItem(
|
||||
getString(R.string.empty_title_dailies_filtered),
|
||||
getString(R.string.empty_description_dailies_filtered),
|
||||
R.drawable.icon_dailies
|
||||
)
|
||||
}
|
||||
TaskType.TODO -> {
|
||||
EmptyItem(
|
||||
getString(R.string.empty_title_todos_filtered),
|
||||
getString(R.string.empty_description_todos_filtered),
|
||||
R.drawable.icon_todos
|
||||
)
|
||||
}
|
||||
TaskType.REWARD -> {
|
||||
EmptyItem(
|
||||
getString(R.string.empty_title_rewards_filtered),
|
||||
null,
|
||||
R.drawable.icon_rewards
|
||||
)
|
||||
}
|
||||
else -> EmptyItem("")
|
||||
}
|
||||
} else {
|
||||
when (this.taskType) {
|
||||
TaskType.HABIT -> {
|
||||
EmptyItem(
|
||||
getString(R.string.empty_title_habits),
|
||||
getString(R.string.empty_description_habits),
|
||||
R.drawable.icon_habits
|
||||
)
|
||||
}
|
||||
TaskType.DAILY -> {
|
||||
EmptyItem(
|
||||
getString(R.string.empty_title_dailies),
|
||||
getString(R.string.empty_description_dailies),
|
||||
R.drawable.icon_dailies
|
||||
)
|
||||
}
|
||||
TaskType.TODO -> {
|
||||
EmptyItem(
|
||||
getString(R.string.empty_title_todos),
|
||||
getString(R.string.empty_description_todos),
|
||||
R.drawable.icon_todos
|
||||
)
|
||||
}
|
||||
TaskType.REWARD -> {
|
||||
EmptyItem(
|
||||
getString(R.string.empty_title_rewards),
|
||||
null,
|
||||
R.drawable.icon_rewards
|
||||
)
|
||||
}
|
||||
else -> EmptyItem("")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun scoreTask(task: Task, direction: TaskDirection) {
|
||||
viewModel.scoreTask(task, direction) { result, value ->
|
||||
handleTaskResult(result, value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putString(CLASS_TYPE_KEY, this.taskType.value)
|
||||
}
|
||||
|
||||
override val displayedClassName: String?
|
||||
get() = this.taskType.value + super.displayedClassName
|
||||
|
||||
override fun onRefresh() {
|
||||
binding?.refreshLayout?.isRefreshing = true
|
||||
viewModel.refreshData {
|
||||
binding?.refreshLayout?.isRefreshing = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
context?.let { recyclerAdapter?.taskDisplayMode = configManager.taskDisplayMode(it) }
|
||||
setInnerAdapter()
|
||||
}
|
||||
|
||||
fun setActiveFilter(activeFilter: String) {
|
||||
viewModel.setActiveFilter(taskType, activeFilter)
|
||||
recyclerAdapter?.filter()
|
||||
|
||||
setEmptyLabels()
|
||||
|
||||
if (activeFilter == Task.FILTER_COMPLETED) {
|
||||
lifecycleScope.launchCatching {
|
||||
taskRepository.retrieveCompletedTodos()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setPreferenceTaskFilters() {
|
||||
(activity as? MainActivity)?.viewModel?.user?.observeOnce(this) {
|
||||
if (it != null) {
|
||||
when (taskType) {
|
||||
TaskType.TODO -> viewModel.setActiveFilter(
|
||||
TaskType.TODO,
|
||||
Task.FILTER_ACTIVE
|
||||
)
|
||||
TaskType.DAILY -> {
|
||||
if (!viewModel.initialPreferenceFilterSet) {
|
||||
viewModel.initialPreferenceFilterSet = true
|
||||
if (it.isValid && it.preferences?.dailyDueDefaultView == true) {
|
||||
viewModel.setActiveFilter(TaskType.DAILY, Task.FILTER_ACTIVE)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openTaskForm(task: Task) {
|
||||
if (Date().time - (TasksFragment.lastTaskFormOpen?.time
|
||||
?: 0) < 2000 || !task.isValid
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
val bundle = Bundle()
|
||||
bundle.putString(TaskFormActivity.TASK_TYPE_KEY, task.type?.value)
|
||||
bundle.putString(TaskFormActivity.TASK_ID_KEY, task.id)
|
||||
bundle.putString(TaskFormActivity.GROUP_ID_KEY, task.group?.groupID)
|
||||
bundle.putDouble(TaskFormActivity.TASK_VALUE_KEY, task.value)
|
||||
|
||||
lifecycleScope.launchCatching {
|
||||
val id = if (viewModel.canEditTask(task)) {
|
||||
R.id.taskFormActivity
|
||||
} else {
|
||||
R.id.taskSummaryActivity
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
MainNavigationController.navigate(id, bundle)
|
||||
}
|
||||
}
|
||||
TasksFragment.lastTaskFormOpen = Date()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val CLASS_TYPE_KEY = "CLASS_TYPE_KEY"
|
||||
|
||||
fun newInstance(context: Context?, classType: TaskType): TaskRecyclerViewFragment {
|
||||
val fragment = TaskRecyclerViewFragment()
|
||||
fragment.taskType = classType
|
||||
var tutorialTexts: List<String>? = null
|
||||
if (context != null) {
|
||||
when (fragment.taskType) {
|
||||
private fun setEmptyLabels() {
|
||||
binding?.recyclerView?.emptyItem = if (viewModel.filterCount(taskType) > 0) {
|
||||
when (this.taskType) {
|
||||
TaskType.HABIT -> {
|
||||
fragment.tutorialStepIdentifier = "habits"
|
||||
tutorialTexts = listOf(
|
||||
context.getString(R.string.tutorial_overview),
|
||||
context.getString(R.string.tutorial_habits_1),
|
||||
context.getString(R.string.tutorial_habits_2),
|
||||
context.getString(R.string.tutorial_habits_3),
|
||||
context.getString(R.string.tutorial_habits_4)
|
||||
EmptyItem(
|
||||
getString(R.string.empty_title_habits_filtered),
|
||||
getString(R.string.empty_description_habits_filtered),
|
||||
R.drawable.icon_habits
|
||||
)
|
||||
}
|
||||
TaskType.DAILY -> {
|
||||
fragment.tutorialStepIdentifier = "dailies"
|
||||
tutorialTexts = listOf(
|
||||
context.getString(R.string.tutorial_dailies_1),
|
||||
context.getString(R.string.tutorial_dailies_2)
|
||||
EmptyItem(
|
||||
getString(R.string.empty_title_dailies_filtered),
|
||||
getString(R.string.empty_description_dailies_filtered),
|
||||
R.drawable.icon_dailies
|
||||
)
|
||||
}
|
||||
TaskType.TODO -> {
|
||||
fragment.tutorialStepIdentifier = "todos"
|
||||
tutorialTexts = listOf(
|
||||
context.getString(R.string.tutorial_todos_1),
|
||||
context.getString(R.string.tutorial_todos_2)
|
||||
EmptyItem(
|
||||
getString(R.string.empty_title_todos_filtered),
|
||||
getString(R.string.empty_description_todos_filtered),
|
||||
R.drawable.icon_todos
|
||||
)
|
||||
}
|
||||
TaskType.REWARD -> {
|
||||
fragment.tutorialStepIdentifier = "rewards"
|
||||
tutorialTexts = listOf(
|
||||
context.getString(R.string.tutorial_rewards_1),
|
||||
context.getString(R.string.tutorial_rewards_2)
|
||||
EmptyItem(
|
||||
getString(R.string.empty_title_rewards_filtered),
|
||||
null,
|
||||
R.drawable.icon_rewards
|
||||
)
|
||||
}
|
||||
else -> EmptyItem("")
|
||||
}
|
||||
} else {
|
||||
when (this.taskType) {
|
||||
TaskType.HABIT -> {
|
||||
EmptyItem(
|
||||
getString(R.string.empty_title_habits),
|
||||
getString(R.string.empty_description_habits),
|
||||
R.drawable.icon_habits
|
||||
)
|
||||
}
|
||||
TaskType.DAILY -> {
|
||||
EmptyItem(
|
||||
getString(R.string.empty_title_dailies),
|
||||
getString(R.string.empty_description_dailies),
|
||||
R.drawable.icon_dailies
|
||||
)
|
||||
}
|
||||
TaskType.TODO -> {
|
||||
EmptyItem(
|
||||
getString(R.string.empty_title_todos),
|
||||
getString(R.string.empty_description_todos),
|
||||
R.drawable.icon_todos
|
||||
)
|
||||
}
|
||||
TaskType.REWARD -> {
|
||||
EmptyItem(
|
||||
getString(R.string.empty_title_rewards),
|
||||
null,
|
||||
R.drawable.icon_rewards
|
||||
)
|
||||
}
|
||||
else -> EmptyItem("")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun scoreTask(task: Task, direction: TaskDirection) {
|
||||
viewModel.scoreTask(task, direction) { result, value ->
|
||||
handleTaskResult(result, value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putString(CLASS_TYPE_KEY, this.taskType.value)
|
||||
}
|
||||
|
||||
override val displayedClassName: String?
|
||||
get() = this.taskType.value + super.displayedClassName
|
||||
|
||||
override fun onRefresh() {
|
||||
binding?.refreshLayout?.isRefreshing = true
|
||||
viewModel.refreshData {
|
||||
binding?.refreshLayout?.isRefreshing = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
context?.let { recyclerAdapter?.taskDisplayMode = configManager.taskDisplayMode(it) }
|
||||
setInnerAdapter()
|
||||
}
|
||||
|
||||
fun setActiveFilter(activeFilter: String) {
|
||||
viewModel.setActiveFilter(taskType, activeFilter)
|
||||
recyclerAdapter?.filter()
|
||||
|
||||
setEmptyLabels()
|
||||
|
||||
if (activeFilter == Task.FILTER_COMPLETED) {
|
||||
lifecycleScope.launchCatching {
|
||||
taskRepository.retrieveCompletedTodos()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setPreferenceTaskFilters() {
|
||||
(activity as? MainActivity)?.viewModel?.user?.observeOnce(this) {
|
||||
if (it != null) {
|
||||
when (taskType) {
|
||||
TaskType.TODO -> viewModel.setActiveFilter(
|
||||
TaskType.TODO,
|
||||
Task.FILTER_ACTIVE
|
||||
)
|
||||
TaskType.DAILY -> {
|
||||
if (!viewModel.initialPreferenceFilterSet) {
|
||||
viewModel.initialPreferenceFilterSet = true
|
||||
if (it.isValid && it.preferences?.dailyDueDefaultView == true) {
|
||||
viewModel.setActiveFilter(TaskType.DAILY, Task.FILTER_ACTIVE)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tutorialTexts != null) {
|
||||
fragment.tutorialTexts = ArrayList(tutorialTexts)
|
||||
private fun openTaskForm(task: Task) {
|
||||
if (Date().time - (TasksFragment.lastTaskFormOpen?.time
|
||||
?: 0) < 2000 || !task.isValid
|
||||
) {
|
||||
return
|
||||
}
|
||||
fragment.tutorialCanBeDeferred = false
|
||||
|
||||
return fragment
|
||||
val bundle = Bundle()
|
||||
bundle.putString(TaskFormActivity.TASK_TYPE_KEY, task.type?.value)
|
||||
bundle.putString(TaskFormActivity.TASK_ID_KEY, task.id)
|
||||
bundle.putString(TaskFormActivity.GROUP_ID_KEY, task.group?.groupID)
|
||||
bundle.putDouble(TaskFormActivity.TASK_VALUE_KEY, task.value)
|
||||
|
||||
lifecycleScope.launchCatching {
|
||||
val id = if (viewModel.canEditTask(task)) {
|
||||
R.id.taskFormActivity
|
||||
} else {
|
||||
R.id.taskSummaryActivity
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
MainNavigationController.navigate(id, bundle)
|
||||
}
|
||||
}
|
||||
TasksFragment.lastTaskFormOpen = Date()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val CLASS_TYPE_KEY = "CLASS_TYPE_KEY"
|
||||
|
||||
fun newInstance(context: Context?, classType: TaskType): TaskRecyclerViewFragment {
|
||||
val fragment = TaskRecyclerViewFragment()
|
||||
fragment.taskType = classType
|
||||
var tutorialTexts: List<String>? = null
|
||||
if (context != null) {
|
||||
when (fragment.taskType) {
|
||||
TaskType.HABIT -> {
|
||||
fragment.tutorialStepIdentifier = "habits"
|
||||
tutorialTexts = listOf(
|
||||
context.getString(R.string.tutorial_overview),
|
||||
context.getString(R.string.tutorial_habits_1),
|
||||
context.getString(R.string.tutorial_habits_2),
|
||||
context.getString(R.string.tutorial_habits_3),
|
||||
context.getString(R.string.tutorial_habits_4)
|
||||
)
|
||||
}
|
||||
TaskType.DAILY -> {
|
||||
fragment.tutorialStepIdentifier = "dailies"
|
||||
tutorialTexts = listOf(
|
||||
context.getString(R.string.tutorial_dailies_1),
|
||||
context.getString(R.string.tutorial_dailies_2)
|
||||
)
|
||||
}
|
||||
TaskType.TODO -> {
|
||||
fragment.tutorialStepIdentifier = "todos"
|
||||
tutorialTexts = listOf(
|
||||
context.getString(R.string.tutorial_todos_1),
|
||||
context.getString(R.string.tutorial_todos_2)
|
||||
)
|
||||
}
|
||||
TaskType.REWARD -> {
|
||||
fragment.tutorialStepIdentifier = "rewards"
|
||||
tutorialTexts = listOf(
|
||||
context.getString(R.string.tutorial_rewards_1),
|
||||
context.getString(R.string.tutorial_rewards_2)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tutorialTexts != null) {
|
||||
fragment.tutorialTexts = ArrayList(tutorialTexts)
|
||||
}
|
||||
fragment.tutorialCanBeDeferred = false
|
||||
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ import androidx.appcompat.view.menu.ActionMenuItemView
|
|||
import androidx.appcompat.widget.ActionMenuView
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.common.habitica.extensions.getThemeColor
|
||||
import com.habitrpg.android.habitica.extensions.waitForLayout
|
||||
import com.habitrpg.common.habitica.extensions.getThemeColor
|
||||
|
||||
/**
|
||||
* Helper class that iterates through Toolbar views, and sets dynamically icons and texts color
|
||||
|
|
@ -42,10 +42,12 @@ object ToolbarColorHelper {
|
|||
fun colorizeToolbar(
|
||||
toolbar: Toolbar,
|
||||
activity: Activity?,
|
||||
iconColor: Int? = null,
|
||||
backgroundColor: Int? = null
|
||||
) {
|
||||
if (activity == null) return
|
||||
toolbar.setBackgroundColor(activity.getThemeColor(R.attr.headerBackgroundColor))
|
||||
val toolbarIconsColor = activity.getThemeColor(R.attr.headerTextColor)
|
||||
toolbar.setBackgroundColor(backgroundColor ?: activity.getThemeColor(R.attr.headerBackgroundColor))
|
||||
val toolbarIconsColor = iconColor ?: activity.getThemeColor(R.attr.headerTextColor)
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
activity.window.statusBarColor = activity.getThemeColor(R.attr.colorPrimaryDark)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ object HabiticaTheme {
|
|||
textPrimary = Color(context.getThemeColor(R.attr.textColorPrimary)),
|
||||
textSecondary = Color(context.getThemeColor(R.attr.textColorSecondary)),
|
||||
textTertiary = Color(ContextCompat.getColor(context, R.color.text_ternary)),
|
||||
textQuad = Color(ContextCompat.getColor(context, R.color.text_quad)),
|
||||
textDimmed = Color(ContextCompat.getColor(context, R.color.text_dimmed)),
|
||||
)
|
||||
}
|
||||
|
|
@ -151,12 +152,19 @@ class HabiticaColors(
|
|||
val textPrimary: Color,
|
||||
val textSecondary: Color,
|
||||
val textTertiary: Color,
|
||||
val textQuad: Color,
|
||||
val textDimmed: Color
|
||||
) {
|
||||
@Composable
|
||||
fun textPrimaryFor(task: Task?): Color {
|
||||
return colorResource((if (isSystemInDarkTheme()) task?.extraExtraLightTaskColor else task?.extraDarkTaskColor) ?: R.color.text_primary)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun textSecondaryFor(task: Task?): Color {
|
||||
return colorResource((if (isSystemInDarkTheme()) task?.extraLightTaskColor else task?.lowSaturationTaskColor) ?: R.color.brand_sub_text)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun primaryBackgroundFor(task: Task?): Color {
|
||||
return colorResource((if (isSystemInDarkTheme()) task?.mediumTaskColor else task?.lightTaskColor) ?: R.color.brand_400)
|
||||
|
|
|
|||
|
|
@ -211,14 +211,15 @@ class ChatRecyclerMessageViewHolder(
|
|||
binding.buttonsWrapper.visibility = View.GONE
|
||||
}
|
||||
|
||||
if ((chatMessage?.flagCount ?: 0) > 0) {
|
||||
binding.flagCountTextview.text = if (chatMessage?.flagCount == 10) {
|
||||
val flagCount = (chatMessage?.flagCount ?: 0)
|
||||
if (flagCount > 0) {
|
||||
binding.flagCountTextview.text = if (flagCount == 10) {
|
||||
context.getString(R.string.shadow_muted_hidden)
|
||||
} else {
|
||||
context.resources.getQuantityString(R.plurals.flagged_count, (chatMessage?.flagCount ?: 0))
|
||||
context.resources.getQuantityString(R.plurals.flagged_count, flagCount, flagCount)
|
||||
}
|
||||
binding.flagCountTextview.isVisible = true
|
||||
if (chatMessage?.flagCount == 1) {
|
||||
if (flagCount == 1) {
|
||||
binding.flagCountTextview.setTextColor(ContextCompat.getColor(context, R.color.text_orange))
|
||||
} else {
|
||||
binding.flagCountTextview.setTextColor(ContextCompat.getColor(context, R.color.text_red))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
package com.habitrpg.android.habitica.ui.viewHolders
|
||||
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
class ComposableViewHolder(view: ComposeView) : RecyclerView.ViewHolder(view) {
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import android.content.ActivityNotFoundException
|
|||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.core.content.edit
|
||||
import androidx.fragment.app.FragmentManager
|
||||
|
|
@ -26,7 +27,6 @@ import com.habitrpg.android.habitica.extensions.addCloseButton
|
|||
import com.habitrpg.android.habitica.helpers.SignInWithAppleResult
|
||||
import com.habitrpg.android.habitica.helpers.SignInWithAppleService
|
||||
import com.habitrpg.android.habitica.helpers.launchCatching
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.proxy.AnalyticsManager
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
|
||||
import com.habitrpg.common.habitica.api.HostConfig
|
||||
|
|
@ -107,12 +107,13 @@ class AuthenticationViewModel() {
|
|||
fun handleGoogleLoginResult(
|
||||
activity: Activity,
|
||||
recoverFromPlayServicesErrorResult: ActivityResultLauncher<Intent>?,
|
||||
onSuccess: (User, Boolean) -> Unit
|
||||
onSuccess: (Boolean) -> Unit
|
||||
) {
|
||||
val scopesString = Scopes.PROFILE + " " + Scopes.EMAIL
|
||||
val scopes = "oauth2:$scopesString"
|
||||
var newUser = false
|
||||
CoroutineScope(Dispatchers.IO).launchCatching({ throwable ->
|
||||
Log.e("Auth", throwable.localizedMessage, throwable)
|
||||
if (recoverFromPlayServicesErrorResult == null) return@launchCatching
|
||||
throwable.cause?.let {
|
||||
if (GoogleAuthException::class.java.isAssignableFrom(it.javaClass)) {
|
||||
|
|
@ -128,8 +129,7 @@ class AuthenticationViewModel() {
|
|||
val response = apiClient.connectSocial("google", googleEmail ?: "", token) ?: return@launchCatching
|
||||
newUser = response.newUser
|
||||
handleAuthResponse(response)
|
||||
val user = userRepository.retrieveUser(true, true) ?: return@launchCatching
|
||||
onSuccess(user, newUser)
|
||||
onSuccess(newUser)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import com.habitrpg.android.habitica.helpers.AmplitudeManager
|
|||
import com.habitrpg.android.habitica.helpers.AppConfigManager
|
||||
import com.habitrpg.android.habitica.helpers.ExceptionHandler
|
||||
import com.habitrpg.android.habitica.helpers.GroupPlanInfoProvider
|
||||
import com.habitrpg.android.habitica.helpers.launchCatching
|
||||
import com.habitrpg.android.habitica.models.TeamPlan
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.shared.habitica.models.responses.TaskDirection
|
||||
|
|
@ -79,9 +78,6 @@ class TasksViewModel : BaseViewModel(), GroupPlanInfoProvider {
|
|||
}
|
||||
}
|
||||
}
|
||||
viewModelScope.launchCatching {
|
||||
userRepository.retrieveTeamPlans()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun refreshData(onComplete: () -> Unit) {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import androidx.compose.material.ModalBottomSheetLayout
|
|||
import androidx.compose.material.ModalBottomSheetValue
|
||||
import androidx.compose.material.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
|
@ -30,6 +31,7 @@ import androidx.compose.ui.platform.ComposeView
|
|||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.ui.theme.HabiticaTheme
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -74,6 +76,13 @@ private fun BottomSheetWrapper(
|
|||
val modalBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
|
||||
var isSheetOpened by remember { mutableStateOf(false) }
|
||||
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val statusBarColor = colorResource(R.color.content_background).copy(alpha = 0.3f)
|
||||
DisposableEffect(systemUiController) {
|
||||
systemUiController.setStatusBarColor(statusBarColor, darkIcons = true)
|
||||
onDispose {}
|
||||
}
|
||||
|
||||
val radius = 20.dp
|
||||
ModalBottomSheetLayout(
|
||||
sheetBackgroundColor = Color.Transparent,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,17 @@ import androidx.compose.ui.unit.sp
|
|||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.common.habitica.helpers.NumberAbbreviator
|
||||
|
||||
@Composable
|
||||
fun CurrencyText(
|
||||
currency: String,
|
||||
value: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
decimals: Int = 0,
|
||||
minForAbbrevation: Int = 0,
|
||||
animated: Boolean = true
|
||||
) {
|
||||
CurrencyText(currency = currency, value = value.toDouble(), modifier, decimals, minForAbbrevation, animated)
|
||||
}
|
||||
@Composable
|
||||
fun CurrencyText(
|
||||
currency: String,
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ fun SegmentedControl(
|
|||
onItemSelection: (selectedItemIndex: Int) -> Unit
|
||||
) {
|
||||
val selectedIndex = remember { mutableStateOf(defaultSelectedItemIndex) }
|
||||
val color = MaterialTheme.colors.surface
|
||||
val color = MaterialTheme.colors.primary
|
||||
Row(
|
||||
modifier = Modifier
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package com.habitrpg.android.habitica.ui.views
|
||||
package com.habitrpg.android.habitica.ui.views.equipment
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
|
|
@ -28,6 +28,7 @@ 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
|
||||
import com.habitrpg.android.habitica.ui.views.PixelArtView
|
||||
|
||||
@Composable
|
||||
fun OverviewItem(
|
||||
|
|
@ -36,7 +37,7 @@ fun OverviewItem(
|
|||
modifier: Modifier = Modifier,
|
||||
isTwoHanded: Boolean = false
|
||||
) {
|
||||
val hasIcon = iconName?.isNotBlank() == true
|
||||
val hasIcon = iconName?.isNotBlank() == true && iconName != "shirt_"
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier
|
||||
.width(70.dp)
|
||||
|
|
@ -121,7 +122,9 @@ fun EquipmentOverviewView(
|
|||
@Composable
|
||||
fun AvatarCustomizationOverviewView(
|
||||
preferences: Preferences?,
|
||||
outfit: Outfit?,
|
||||
onCustomizationTap: (String, String?) -> Unit,
|
||||
onAvatarEquipmentTap: (String, String?) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
|
|
@ -135,25 +138,25 @@ fun AvatarCustomizationOverviewView(
|
|||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_shirt),
|
||||
preferences?.shirt.let { "${preferences?.size}_shirt$it" }, Modifier.clickable {
|
||||
preferences?.shirt.let { "icon_${preferences?.size}_shirt_$it" }, Modifier.clickable {
|
||||
onCustomizationTap("shirt", null)
|
||||
})
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_skin),
|
||||
preferences?.skin.let { "skin_$it" },
|
||||
preferences?.skin.let { "icon_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 "",
|
||||
if (preferences?.hair?.color != null && preferences.hair?.color != "") "icon_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 "",
|
||||
if (preferences?.hair?.bangs != null && preferences.hair?.bangs != 0) "icon_hair_bangs_" + preferences.hair?.bangs + "_" + preferences.hair?.color else "",
|
||||
Modifier.clickable {
|
||||
onCustomizationTap("hair", "bangs")
|
||||
}
|
||||
|
|
@ -162,28 +165,28 @@ fun AvatarCustomizationOverviewView(
|
|||
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 "",
|
||||
if (preferences?.hair?.base != null && preferences.hair?.base != 0) "icon_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 "",
|
||||
if (preferences?.hair?.mustache != null && preferences.hair?.mustache != 0) "icon_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 "",
|
||||
if (preferences?.hair?.beard != null && preferences.hair?.beard != 0) "icon_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 "",
|
||||
if (preferences?.hair?.flower != null && preferences.hair?.flower != 0) "icon_hair_flower_" + preferences.hair?.flower else "",
|
||||
Modifier.clickable {
|
||||
onCustomizationTap("hair", "flower")
|
||||
}
|
||||
|
|
@ -192,18 +195,30 @@ fun AvatarCustomizationOverviewView(
|
|||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_wheelchair),
|
||||
preferences?.chair?.let { if (it.startsWith("handleless")) "chair_$it" else it },
|
||||
preferences?.chair?.let { if (it.startsWith("handleless")) "icon_chair_$it" else "icon_$it" },
|
||||
Modifier.clickable {
|
||||
onCustomizationTap("chair", null)
|
||||
})
|
||||
OverviewItem(
|
||||
stringResource(R.string.avatar_background),
|
||||
preferences?.background.let { "background_$it" },
|
||||
preferences?.background.let { "icon_background_$it" },
|
||||
Modifier.clickable {
|
||||
onCustomizationTap("background", null)
|
||||
})
|
||||
Box(Modifier.size(70.dp))
|
||||
Box(Modifier.size(70.dp))
|
||||
OverviewItem(
|
||||
stringResource(R.string.animal_ears),
|
||||
outfit?.headAccessory.let { "shop_$it" },
|
||||
Modifier.clickable {
|
||||
onAvatarEquipmentTap("headAccessory", "animal")
|
||||
}
|
||||
)
|
||||
OverviewItem(
|
||||
stringResource(R.string.animal_tail),
|
||||
outfit?.back.let { "shop_$it" },
|
||||
Modifier.clickable {
|
||||
onAvatarEquipmentTap("back", "animal")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -218,6 +233,6 @@ fun EquipmentOverviewItemPreview() {
|
|||
OverviewItem("Armor", null)
|
||||
}
|
||||
EquipmentOverviewView(null, { _, _ -> })
|
||||
AvatarCustomizationOverviewView(null, { _, _ -> })
|
||||
AvatarCustomizationOverviewView(null, null, { _, _ -> }, { _, _ -> })
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package com.habitrpg.android.habitica.ui.views.promo
|
||||
|
||||
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.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.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
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.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.helpers.MainNavigationController
|
||||
|
||||
@Composable
|
||||
fun BirthdayBanner() {
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
MainNavigationController.navigate(R.id.birthdayActivity)
|
||||
}
|
||||
) {
|
||||
Column(Modifier.fillMaxWidth()) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp, Alignment.CenterVertically),
|
||||
modifier = Modifier
|
||||
.height(67.dp)
|
||||
.fillMaxWidth()
|
||||
.background(colorResource(R.color.brand_100))
|
||||
.padding(start = 10.dp)) {
|
||||
Image(
|
||||
painterResource(R.drawable.birthday_menu_text), null
|
||||
)
|
||||
Text(
|
||||
stringResource(R.string.exclusive_items_await),
|
||||
color = colorResource(R.color.yellow_100),
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(33.dp)
|
||||
.background(colorResource(R.color.brand_300))
|
||||
.padding(horizontal = 10.dp)
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.ends_in_x).uppercase(),
|
||||
color = colorResource(R.color.yellow_50),
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Spacer(Modifier.weight(1f))
|
||||
Text(
|
||||
stringResource(R.string.see_more).uppercase(),
|
||||
color = colorResource(R.color.white),
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,6 @@ 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
|
||||
|
|
@ -24,6 +23,8 @@ import androidx.compose.material.MaterialTheme
|
|||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
|
|
@ -33,6 +34,7 @@ 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.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.habitrpg.android.habitica.R
|
||||
|
|
@ -69,65 +71,91 @@ fun AssignSheet(
|
|||
}
|
||||
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,
|
||||
avatar = member,
|
||||
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()
|
||||
)
|
||||
AssignSheetRow(member = member, isAssigned = isAssigned, onAssignClick = onAssignClick)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AssignSheetRow(
|
||||
member: Member,
|
||||
isAssigned: Boolean,
|
||||
onAssignClick: (String) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
UserRow(
|
||||
username = member.displayName,
|
||||
avatar = member,
|
||||
color = colorResource(R.color.text_primary),
|
||||
extraContent = {
|
||||
Text(
|
||||
member.formattedUsername ?: "",
|
||||
color = colorResource(R.color.text_ternary)
|
||||
)
|
||||
}, endContent = {
|
||||
IsAssignedIndicator(isAssigned = isAssigned)
|
||||
}, modifier = modifier
|
||||
.clickable {
|
||||
member.id?.let { onAssignClick(it) }
|
||||
}
|
||||
.padding(30.dp, 12.dp)
|
||||
.heightIn(min = 24.dp)
|
||||
.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun IsAssignedIndicator(
|
||||
isAssigned: Boolean,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val transition = updateTransition(isAssigned, label = "isAssigned")
|
||||
val rotation = transition.animateFloat(
|
||||
label = "isAssigned",
|
||||
transitionSpec = { spring(Spring.DampingRatioLowBouncy, Spring.StiffnessMediumLow) }) {
|
||||
if (it) 0f else 135f
|
||||
}
|
||||
val backgroundColor = transition.animateColor(
|
||||
label = "isAssigned",
|
||||
transitionSpec = { tween(450, easing = FastOutLinearInEasing) }) {
|
||||
if (it) MaterialTheme.colors.primary else colorResource(id = R.color.transparent)
|
||||
}
|
||||
val color = transition.animateColor(
|
||||
label = "isAssigned",
|
||||
transitionSpec = { tween(450, easing = FastOutLinearInEasing) }) {
|
||||
colorResource(if (it) R.color.white else R.color.text_dimmed)
|
||||
}
|
||||
val borderColor = transition.animateColor(
|
||||
label = "isAssigned",
|
||||
transitionSpec = { tween(450, easing = FastOutLinearInEasing) }) {
|
||||
if (it) MaterialTheme.colors.primary else colorResource(id = R.color.text_dimmed)
|
||||
}
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
private fun IsAssignedIndicatorPreview() {
|
||||
val isAssigned = remember { mutableStateOf(false) }
|
||||
Column {
|
||||
IsAssignedIndicator(isAssigned = isAssigned.value)
|
||||
IsAssignedIndicator(isAssigned = !isAssigned.value)
|
||||
}
|
||||
}
|
||||
|
|
@ -79,15 +79,15 @@ private fun HabitScoringSelection(
|
|||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val selectedState = updateTransition(selected)
|
||||
val context = LocalContext.current
|
||||
val iconColor = selectedState.animateColor {
|
||||
val context = LocalContext.current
|
||||
if (it) Color(context.getThemeColor(R.attr.colorTintedBackground)) else colorResource(R.color.text_dimmed)
|
||||
}
|
||||
val textColor = selectedState.animateColor {
|
||||
if (it) MaterialTheme.colors.primary else colorResource(R.color.text_ternary)
|
||||
if (it) MaterialTheme.colors.primary else Color(context.getThemeColor(R.attr.textColorTintedSecondary))
|
||||
}
|
||||
val borderColor = selectedState.animateColor {
|
||||
if (it) MaterialTheme.colors.primary else colorResource(R.color.text_dimmed)
|
||||
if (it) MaterialTheme.colors.primary else Color(context.getThemeColor(R.attr.textColorTintedSecondary))
|
||||
}
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(12.dp), modifier = modifier) {
|
||||
Box(
|
||||
|
|
|
|||
|
|
@ -77,12 +77,12 @@ private fun TaskDifficultySelection(
|
|||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val selectedState = updateTransition(selected)
|
||||
val context = LocalContext.current
|
||||
val iconColor = selectedState.animateColor {
|
||||
val context = LocalContext.current
|
||||
if (it) Color(context.getThemeColor(R.attr.colorTintedBackground)) else MaterialTheme.colors.primary
|
||||
}
|
||||
val textColor = selectedState.animateColor {
|
||||
if (it) MaterialTheme.colors.primary else colorResource(R.color.text_ternary)
|
||||
if (it) MaterialTheme.colors.primary else Color(context.getThemeColor(R.attr.textColorTintedSecondary))
|
||||
}
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(6.dp), modifier = modifier) {
|
||||
Box(
|
||||
|
|
|
|||
|
|
@ -40,14 +40,14 @@ class TaskSerializer : JsonSerializer<Task>, JsonDeserializer<Task> {
|
|||
return intList
|
||||
}
|
||||
|
||||
private fun getMonthlyDays(e: JsonElement, task: Task) {
|
||||
val weeksOfMonth = e.asJsonObject.getAsJsonArray("weeksOfMonth")
|
||||
private fun getMonthlyDays(e: JsonObject, task: Task) {
|
||||
val weeksOfMonth = e.getAsJsonArray("weeksOfMonth")
|
||||
if (weeksOfMonth != null && weeksOfMonth.size() > 0) {
|
||||
task.setWeeksOfMonth(getIntListFromJsonArray(weeksOfMonth))
|
||||
}
|
||||
|
||||
val daysOfMonth = e.asJsonObject.getAsJsonArray("daysOfMonth")
|
||||
if (weeksOfMonth != null && weeksOfMonth.size() > 0) {
|
||||
val daysOfMonth = e.getAsJsonArray("daysOfMonth")
|
||||
if (daysOfMonth != null && daysOfMonth.size() > 0) {
|
||||
task.setDaysOfMonth(getIntListFromJsonArray(daysOfMonth))
|
||||
}
|
||||
}
|
||||
|
|
@ -134,7 +134,7 @@ class TaskSerializer : JsonSerializer<Task>, JsonDeserializer<Task> {
|
|||
task.group = group
|
||||
}
|
||||
// Work around since Realm does not support Arrays of ints
|
||||
getMonthlyDays(json, task)
|
||||
getMonthlyDays(obj, task)
|
||||
|
||||
// Workaround, since gson doesn't call setter methods
|
||||
task.id = obj.getAsString("_id")
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ buildscript {
|
|||
navigation_version = '2.5.3'
|
||||
okhttp_version = '4.10.0'
|
||||
play_wearables_version = '18.0.0'
|
||||
play_auth_version = '20.3.0'
|
||||
play_auth_version = '20.4.0'
|
||||
preferences_version = '1.2.0'
|
||||
realm_version = '1.0.2'
|
||||
retrofit_version = '2.9.0'
|
||||
|
|
|
|||
|
|
@ -9,4 +9,12 @@ val TaskDifficulty.nameRes: Int
|
|||
TaskDifficulty.EASY -> R.string.easy
|
||||
TaskDifficulty.MEDIUM -> R.string.medium
|
||||
TaskDifficulty.HARD -> R.string.hard
|
||||
}
|
||||
|
||||
val TaskDifficulty.nameSentenceRes: Int
|
||||
get() = when (this) {
|
||||
TaskDifficulty.TRIVIAL -> R.string.trivial_sentence
|
||||
TaskDifficulty.EASY -> R.string.easy_sentence
|
||||
TaskDifficulty.MEDIUM -> R.string.medium_sentence
|
||||
TaskDifficulty.HARD -> R.string.hard_sentence
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ object NumberAbbreviator {
|
|||
}
|
||||
|
||||
fun abbreviate(context: Context?, number: Double, numberOfDecimals: Int = 2, minForAbbrevation: Int = 0): String {
|
||||
val decimalCount = if (number != 0.0 && number > -1 && number < 1 && numberOfDecimals == 0) 1 else numberOfDecimals
|
||||
val decimalCount = if (number != 0.0 && number > -1 && number < 1 && numberOfDecimals == 0) 2 else numberOfDecimals
|
||||
var usedNumber = number
|
||||
var counter = 0
|
||||
while (usedNumber >= 1000 && number >= minForAbbrevation) {
|
||||
|
|
@ -21,7 +21,7 @@ object NumberAbbreviator {
|
|||
}
|
||||
var pattern = "###"
|
||||
if (decimalCount > 0) {
|
||||
pattern = ("$pattern.").padEnd(4 + numberOfDecimals, '#')
|
||||
pattern = ("$pattern.").padEnd(4 + decimalCount, '#')
|
||||
}
|
||||
val formatter = DecimalFormat(pattern + abbreviationForCounter(context, counter).replace(".", ""))
|
||||
formatter.roundingMode = RoundingMode.FLOOR
|
||||
|
|
|
|||
4
common/src/main/res/values-night/colors.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="brand_sub_text">@color/brand_500</color>
|
||||
</resources>
|
||||
|
|
@ -12,13 +12,13 @@
|
|||
<color name="brand_400">#925CF3</color>
|
||||
<color name="brand_500">#bda8ff</color>
|
||||
<color name="brand_600">#D5C8FF</color>
|
||||
<color name="brand_700">#F4F0FC</color>
|
||||
<color name="brand_700">#EEEBF8</color>
|
||||
<color name="brand_800">#FAF7FF</color>
|
||||
<color name="brand">@color/brand_100</color>
|
||||
|
||||
<!-- HabitRPG task color -->
|
||||
<color name="maroon_700">#FFF5F5</color>
|
||||
<color name="maroon_600">#FCEDED</color>
|
||||
<color name="maroon_600">#F7E9E9</color>
|
||||
<color name="maroon_500">#f19595</color>
|
||||
<color name="maroon_100">#DE3F3F</color>
|
||||
<color name="maroon_50">#C92B2B</color>
|
||||
|
|
@ -26,14 +26,15 @@
|
|||
<color name="maroon_5">#7D0C0C</color>
|
||||
|
||||
<color name="red_700">#FFF6F7</color>
|
||||
<color name="red_600">#FCEEEF</color>
|
||||
<color name="red_600">#F7E9E9</color>
|
||||
<color name="red_500">#ffb6b8</color>
|
||||
<color name="red_100">#FF6165</color>
|
||||
<color name="red_50">#F74E52</color>
|
||||
<color name="red_10">#F23035</color>
|
||||
<color name="red_5">#BF262B</color>
|
||||
|
||||
<color name="orange_700">#FFF9F5</color>
|
||||
<color name="orange_600">#FCF3ED</color>
|
||||
<color name="orange_600">#F7EDED</color>
|
||||
<color name="orange_500">#ffc8a7</color>
|
||||
<color name="orange_100">#FF944C</color>
|
||||
<color name="orange_50">#FA8537</color>
|
||||
|
|
@ -49,7 +50,7 @@
|
|||
<color name="yellow_5">#EE9109</color>
|
||||
|
||||
<color name="green_700">#F7FFFC</color>
|
||||
<color name="green_600">#EDFAF6</color>
|
||||
<color name="green_600">#EBF5F5</color>
|
||||
<color name="green_500">#77f4c7</color>
|
||||
<color name="green_100">#24CC8F</color>
|
||||
<color name="green_50">#20B780</color>
|
||||
|
|
@ -57,7 +58,7 @@
|
|||
<color name="green_5">#168059</color>
|
||||
|
||||
<color name="teal_700">#F5FEFF</color>
|
||||
<color name="teal_600">#EDFBFC</color>
|
||||
<color name="teal_600">#E5F5F5</color>
|
||||
<color name="teal_500">#8eedf6</color>
|
||||
<color name="teal_100">#3BCAD7</color>
|
||||
<color name="teal_50">#34B5C1</color>
|
||||
|
|
@ -95,7 +96,7 @@
|
|||
<color name="yellow_1">#794b00</color>
|
||||
<color name="orange_1">#7f3300</color>
|
||||
|
||||
<color name="brand_0">#30283D</color>
|
||||
<color name="brand_0">#2F283F</color>
|
||||
<color name="maroon_0">#3D2828</color>
|
||||
<color name="red_0">#3D2828</color>
|
||||
<color name="blue_0">#28373D</color>
|
||||
|
|
@ -104,7 +105,7 @@
|
|||
<color name="yellow_0">#3D3528</color>
|
||||
<color name="orange_0">#3D3028</color>
|
||||
|
||||
<color name="brand_00">#201C26</color>
|
||||
<color name="brand_00">#1A181D</color>
|
||||
<color name="maroon_00">#261C1C</color>
|
||||
<color name="red_00">#261C1C</color>
|
||||
<color name="blue_00">#191D21</color>
|
||||
|
|
@ -113,6 +114,15 @@
|
|||
<color name="yellow_00">#26221C</color>
|
||||
<color name="orange_00">#26201C</color>
|
||||
|
||||
<color name="brand_sub_text">#7C65AB</color>
|
||||
<color name="maroon_sub_text">#AB6565</color>
|
||||
<color name="red_sub_text">#AB6570</color>
|
||||
<color name="orange_sub_text">#AB8165</color>
|
||||
<color name="yellow_sub_text">#AB9065</color>
|
||||
<color name="green_sub_text">#65AB94</color>
|
||||
<color name="teal_sub_text">#65A7AB</color>
|
||||
<color name="blue_sub_text">#6594AB</color>
|
||||
|
||||
<color name="hpColor">@color/red_100</color>
|
||||
<color name="xpColor">@color/yellow_100</color>
|
||||
<color name="mpColor">@color/blue_100</color>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@
|
|||
<string name="easy">Easy</string>
|
||||
<string name="medium">Medium</string>
|
||||
<string name="hard">Hard</string>
|
||||
<string name="trivial_sentence">trivial</string>
|
||||
<string name="easy_sentence">easy</string>
|
||||
<string name="medium_sentence">medium</string>
|
||||
<string name="hard_sentence">hard</string>
|
||||
<string name="habits">Habits</string>
|
||||
<string name="dailies">Dailies</string>
|
||||
<string name="todos">To Do\'s</string>
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
NAME=4.1
|
||||
CODE=4901
|
||||
CODE=4951
|
||||
|
|
@ -125,6 +125,7 @@ dependencies {
|
|||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
|
||||
implementation "androidx.preference:preference-ktx:$preferences_version"
|
||||
implementation "androidx.navigation:navigation-fragment-ktx:2.5.2"
|
||||
|
||||
implementation "com.google.android.gms:play-services-auth:$play_auth_version"
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.habitrpg.wearos.habitica.ui.activities
|
|||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.PersistableBundle
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
|
@ -19,6 +20,10 @@ import kotlin.time.toDuration
|
|||
class SplashActivity: BaseActivity<ActivitySplashBinding, SplashViewModel>() {
|
||||
override val viewModel: SplashViewModel by viewModels()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
|
||||
super.onCreate(savedInstanceState, persistentState)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
binding = ActivitySplashBinding.inflate(layoutInflater)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
@ -52,7 +57,6 @@ class SplashActivity: BaseActivity<ActivitySplashBinding, SplashViewModel>() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onStop() {
|
||||
messageClient.removeListener(viewModel)
|
||||
super.onStop()
|
||||
|
|
|
|||