diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 781ca3b7b..cc1617cf1 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -8,7 +8,7 @@ assignees: ''
---
**Describe the bug**
-A clear and concise description of what the bug is.
+A clear and concise description of what the bug is. If your issue is not related to a specific bug, please submit it as a suggestion through our form: https://forms.gle/Fa6oztkDsbzLqSvE9
**To Reproduce**
Steps to reproduce the behavior:
diff --git a/.github/ISSUE_TEMPLATE/crash_report.md b/.github/ISSUE_TEMPLATE/crash_report.md
new file mode 100644
index 000000000..7a79469ec
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/crash_report.md
@@ -0,0 +1,26 @@
+---
+name: Crash report
+about: Create a report for app crash instances
+title: ''
+labels: 'Type: Crash'
+assignees: ''
+
+---
+
+**Describe the crash**
+What were you doing before the issue? Was there an error, did the app stall, or did it fully close?
+
+**To Reproduce**
+Can you reliably reproduce the crash? If so, list steps.
+
+**Screenshots/Video**
+If applicable, add screenshots or video to help show the problem
+
+**Smartphone (please complete the following information):**
+ - Device: [e.g. Moto G5]
+ - OS: [e.g. Android 9]
+ - App Version: [e.g. 2.2]
+ - Habitica Username:
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index 63b82974a..000000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,20 +0,0 @@
----
-name: Feature request
-about: Suggest an idea for this project
-title: ''
-labels: 'Type: Feature'
-assignees: ''
-
----
-
-**Is your feature request related to a problem? Please describe.**
-A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
-
-**Describe the solution you'd like**
-A clear and concise description of what you want to happen.
-
-**Describe alternatives you've considered**
-A clear and concise description of any alternative solutions or features you've considered.
-
-**Additional context**
-Add any other context or screenshots about the feature request here.
diff --git a/.gitignore b/.gitignore
index 83d5a2959..b535999d9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -67,3 +67,5 @@ fastlane/metadata/android/*/changelogs/
/Habitica/amazon
/fastlane/Fastfile.bkp
/fastlane/Fastfile
+*.aab
+/Habitica/alpha
diff --git a/Gemfile.lock b/Gemfile.lock
index 48719447b..e6897e404 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,103 +1,133 @@
GEM
remote: https://rubygems.org/
specs:
- CFPropertyList (3.0.0)
- addressable (2.5.2)
- public_suffix (>= 2.0.2, < 4.0)
+ CFPropertyList (3.0.2)
+ addressable (2.7.0)
+ public_suffix (>= 2.0.2, < 5.0)
atomos (0.1.3)
- babosa (1.0.2)
- claide (1.0.2)
+ aws-eventstream (1.1.0)
+ aws-partitions (1.329.0)
+ aws-sdk-core (3.99.2)
+ aws-eventstream (~> 1, >= 1.0.2)
+ aws-partitions (~> 1, >= 1.239.0)
+ aws-sigv4 (~> 1.1)
+ jmespath (~> 1.0)
+ aws-sdk-kms (1.34.1)
+ aws-sdk-core (~> 3, >= 3.99.0)
+ aws-sigv4 (~> 1.1)
+ aws-sdk-s3 (1.68.1)
+ aws-sdk-core (~> 3, >= 3.99.0)
+ aws-sdk-kms (~> 1)
+ aws-sigv4 (~> 1.1)
+ aws-sigv4 (1.1.4)
+ aws-eventstream (~> 1.0, >= 1.0.2)
+ babosa (1.0.3)
+ claide (1.0.3)
colored (1.2)
colored2 (3.1.2)
commander-fastlane (4.4.6)
highline (~> 1.7.2)
declarative (0.0.10)
declarative-option (0.1.0)
- domain_name (0.5.20180417)
+ digest-crc (0.5.1)
+ domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
- dotenv (2.5.0)
- emoji_regex (0.1.1)
- excon (0.62.0)
- faraday (0.15.3)
+ dotenv (2.7.5)
+ emoji_regex (1.0.1)
+ excon (0.74.0)
+ faraday (1.0.1)
multipart-post (>= 1.2, < 3)
faraday-cookie_jar (0.0.6)
faraday (>= 0.7.4)
http-cookie (~> 1.0.0)
- faraday_middleware (0.12.2)
- faraday (>= 0.7.4, < 1.0)
- fastimage (2.1.4)
- fastlane (2.107.0)
+ faraday_middleware (1.0.0)
+ faraday (~> 1.0)
+ fastimage (2.1.7)
+ fastlane (2.149.1)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
+ aws-sdk-s3 (~> 1.0)
babosa (>= 1.0.2, < 2.0.0)
- bundler (>= 1.12.0, < 2.0.0)
+ bundler (>= 1.12.0, < 3.0.0)
colored
commander-fastlane (>= 4.4.6, < 5.0.0)
dotenv (>= 2.1.1, < 3.0.0)
- emoji_regex (~> 0.1)
- excon (>= 0.45.0, < 1.0.0)
- faraday (~> 0.9)
+ emoji_regex (>= 0.1, < 2.0)
+ excon (>= 0.71.0, < 1.0.0)
+ faraday (>= 0.17, < 2.0)
faraday-cookie_jar (~> 0.0.6)
- faraday_middleware (~> 0.9)
+ faraday_middleware (>= 0.13.1, < 2.0)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
- google-api-client (>= 0.21.2, < 0.24.0)
+ google-api-client (>= 0.37.0, < 0.39.0)
+ google-cloud-storage (>= 1.15.0, < 2.0.0)
highline (>= 1.7.2, < 2.0.0)
json (< 3.0.0)
- mini_magick (~> 4.5.1)
- multi_json
+ jwt (~> 2.1.0)
+ mini_magick (>= 4.9.4, < 5.0.0)
multi_xml (~> 0.5)
multipart-post (~> 2.0.0)
plist (>= 3.1.0, < 4.0.0)
public_suffix (~> 2.0.0)
- rubyzip (>= 1.2.2, < 2.0.0)
+ rubyzip (>= 1.3.0, < 2.0.0)
security (= 0.1.3)
simctl (~> 1.6.3)
slack-notifier (>= 2.0.0, < 3.0.0)
- terminal-notifier (>= 1.6.2, < 2.0.0)
+ terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (>= 1.4.5, < 2.0.0)
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
- xcodeproj (>= 1.6.0, < 2.0.0)
+ xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
- fastlane-plugin-increment_version_code (0.4.3)
fastlane-plugin-versioning_android (0.1.0)
gh_inspector (1.1.3)
- google-api-client (0.23.9)
+ google-api-client (0.38.0)
addressable (~> 2.5, >= 2.5.1)
- googleauth (>= 0.5, < 0.7.0)
+ googleauth (~> 0.9)
httpclient (>= 2.8.1, < 3.0)
- mime-types (~> 3.0)
+ mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
- signet (~> 0.9)
- googleauth (0.6.7)
- faraday (~> 0.12)
+ signet (~> 0.12)
+ google-cloud-core (1.5.0)
+ google-cloud-env (~> 1.0)
+ google-cloud-errors (~> 1.0)
+ google-cloud-env (1.3.2)
+ faraday (>= 0.17.3, < 2.0)
+ google-cloud-errors (1.0.1)
+ google-cloud-storage (1.26.2)
+ addressable (~> 2.5)
+ digest-crc (~> 0.4)
+ google-api-client (~> 0.33)
+ google-cloud-core (~> 1.2)
+ googleauth (~> 0.9)
+ mini_mime (~> 1.0)
+ googleauth (0.12.0)
+ faraday (>= 0.17.3, < 2.0)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
- signet (~> 0.7)
+ signet (~> 0.14)
highline (1.7.10)
http-cookie (1.0.3)
domain_name (~> 0.5)
httpclient (2.8.3)
- json (2.1.0)
+ jmespath (1.4.0)
+ json (2.3.0)
jwt (2.1.0)
- memoist (0.16.0)
- mime-types (3.2.2)
- mime-types-data (~> 3.2015)
- mime-types-data (3.2018.0812)
- mini_magick (4.5.1)
- multi_json (1.13.1)
+ memoist (0.16.2)
+ mini_magick (4.10.1)
+ mini_mime (1.0.2)
+ multi_json (1.14.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
nanaimo (0.2.6)
naturally (2.2.0)
- os (1.0.0)
- plist (3.4.0)
+ os (1.1.0)
+ plist (3.5.0)
public_suffix (2.0.5)
representable (3.0.4)
declarative (< 0.1.0)
@@ -105,31 +135,31 @@ GEM
uber (< 0.2.0)
retriable (3.1.2)
rouge (2.0.7)
- rubyzip (1.2.2)
+ rubyzip (1.3.0)
security (0.1.3)
- signet (0.11.0)
+ signet (0.14.0)
addressable (~> 2.3)
- faraday (~> 0.9)
+ faraday (>= 0.17.3, < 2.0)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
- simctl (1.6.5)
+ simctl (1.6.8)
CFPropertyList
naturally
slack-notifier (2.3.2)
- terminal-notifier (1.8.0)
+ terminal-notifier (2.0.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
- tty-cursor (0.6.0)
- tty-screen (0.6.5)
- tty-spinner (0.8.0)
- tty-cursor (>= 0.5.0)
+ tty-cursor (0.7.1)
+ tty-screen (0.8.0)
+ tty-spinner (0.9.3)
+ tty-cursor (~> 0.7)
uber (0.1.0)
unf (0.1.4)
unf_ext
- unf_ext (0.0.7.5)
- unicode-display_width (1.4.0)
+ unf_ext (0.0.7.7)
+ unicode-display_width (1.7.0)
word_wrap (1.0.0)
- xcodeproj (1.7.0)
+ xcodeproj (1.16.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
@@ -145,8 +175,7 @@ PLATFORMS
DEPENDENCIES
fastlane
- fastlane-plugin-increment_version_code
fastlane-plugin-versioning_android
BUNDLED WITH
- 1.16.4
+ 1.17.2
diff --git a/Habitica/AndroidManifest.xml b/Habitica/AndroidManifest.xml
index 3c501e746..6bbf318fd 100644
--- a/Habitica/AndroidManifest.xml
+++ b/Habitica/AndroidManifest.xml
@@ -5,11 +5,8 @@
android:screenOrientation="portrait"
android:installLocation="auto" >
-
-
-
@@ -83,6 +80,15 @@
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize">
+
+
+
diff --git a/Habitica/build.gradle b/Habitica/build.gradle
index 5e4e1dce1..2c05a47a1 100644
--- a/Habitica/build.gradle
+++ b/Habitica/build.gradle
@@ -18,7 +18,7 @@ buildscript {
}
dependencies {
classpath 'io.fabric.tools:gradle:1.+'
- classpath 'com.android.tools.build:gradle:3.6.2'
+ classpath 'com.android.tools.build:gradle:4.0.0'
classpath('com.noveogroup.android:check:1.2.5') {
exclude module: 'checkstyle'
exclude module: 'pmd-java'
@@ -104,7 +104,7 @@ dependencies {
}
//Tests
testImplementation 'junit:junit:4.12'
- testImplementation 'androidx.test:core:1.0.0'
+ testImplementation 'androidx.test:core:1.2.0'
testImplementation "com.google.truth:truth:1.0.1"
testImplementation 'org.assertj:assertj-core:2.6.0'
testImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.2'
@@ -119,22 +119,22 @@ dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.2'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.2'
//Push Notifications
- implementation 'com.google.firebase:firebase-core:17.2.2'
- implementation 'com.google.firebase:firebase-messaging:20.1.0'
- implementation 'com.google.firebase:firebase-config:19.1.1'
- implementation 'com.google.firebase:firebase-perf:19.0.5'
- implementation 'com.google.android.gms:play-services-auth:17.0.0'
+ implementation 'com.google.firebase:firebase-core:17.4.4'
+ implementation 'com.google.firebase:firebase-messaging:20.2.3'
+ implementation 'com.google.firebase:firebase-config:19.2.0'
+ implementation 'com.google.firebase:firebase-perf:19.0.7'
+ implementation 'com.google.android.gms:play-services-auth:18.0.0'
implementation 'io.realm:android-adapters:3.1.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'com.nex3z:flow-layout:1.2.2'
- implementation 'androidx.core:core-ktx:1.2.0'
+ implementation 'androidx.core:core-ktx:1.3.0'
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"
- implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1'
- implementation 'androidx.navigation:navigation-ui-ktx:2.2.1'
- implementation "androidx.paging:paging-runtime-ktx:2.1.1"
+ implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0'
+ implementation 'androidx.navigation:navigation-ui-ktx:2.3.0'
+ implementation "androidx.paging:paging-runtime-ktx:2.1.2"
implementation 'com.plattysoft.leonids:LeonidsLib:1.3.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
@@ -145,8 +145,8 @@ dependencies {
}
android {
- compileSdkVersion 29
- buildToolsVersion '28.0.3'
+ compileSdkVersion 30
+ buildToolsVersion '29.0.3'
testOptions {
unitTests {
includeAndroidResources = true
@@ -162,8 +162,8 @@ android {
multiDexEnabled true
resConfigs "en", "bg", "de", "en-rGB", "es", "fr", "hr-rHR", "in", "it", "iw", "ja", "ko", "lt", "nl", "pl", "pt-rBR", "pt-rPT", "ru", "tr", "zh", "zh-rTW"
- versionCode 2404
- versionName "2.5"
+ versionCode 2500
+ versionName "2.8"
}
viewBinding {
@@ -213,6 +213,12 @@ android {
dimension "buildType"
}
+ staff {
+ dimension "buildType"
+ buildConfigField "String", "TESTING_LEVEL", "\"staff\""
+ resValue "string", "app_name", "Habitica Staff"
+ }
+
alpha {
dimension "buildType"
buildConfigField "String", "TESTING_LEVEL", "\"alpha\""
diff --git a/Habitica/res/color/task_form_box_stroke.xml b/Habitica/res/color/task_form_box_stroke.xml
new file mode 100644
index 000000000..f8eaeebf9
--- /dev/null
+++ b/Habitica/res/color/task_form_box_stroke.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/drawable-hdpi/adventure_guide_art.png b/Habitica/res/drawable-hdpi/adventure_guide_art.png
new file mode 100644
index 000000000..706c3b517
Binary files /dev/null and b/Habitica/res/drawable-hdpi/adventure_guide_art.png differ
diff --git a/Habitica/res/drawable-hdpi/gold_coins_left.png b/Habitica/res/drawable-hdpi/gold_coins_left.png
new file mode 100644
index 000000000..adf6d3ca1
Binary files /dev/null and b/Habitica/res/drawable-hdpi/gold_coins_left.png differ
diff --git a/Habitica/res/drawable-hdpi/gold_coins_right.png b/Habitica/res/drawable-hdpi/gold_coins_right.png
new file mode 100644
index 000000000..34d071e45
Binary files /dev/null and b/Habitica/res/drawable-hdpi/gold_coins_right.png differ
diff --git a/Habitica/res/drawable-hdpi/ic_keyboard_arrow_right.png b/Habitica/res/drawable-hdpi/ic_keyboard_arrow_right.png
new file mode 100644
index 000000000..5b4950a2e
Binary files /dev/null and b/Habitica/res/drawable-hdpi/ic_keyboard_arrow_right.png differ
diff --git a/Habitica/res/drawable-hdpi/onboarding_done_art.png b/Habitica/res/drawable-hdpi/onboarding_done_art.png
new file mode 100644
index 000000000..548d9f526
Binary files /dev/null and b/Habitica/res/drawable-hdpi/onboarding_done_art.png differ
diff --git a/Habitica/res/drawable-hdpi/party_invite_accept.png b/Habitica/res/drawable-hdpi/party_invite_accept.png
new file mode 100644
index 000000000..b4b1584d1
Binary files /dev/null and b/Habitica/res/drawable-hdpi/party_invite_accept.png differ
diff --git a/Habitica/res/drawable-hdpi/party_invite_reject.png b/Habitica/res/drawable-hdpi/party_invite_reject.png
new file mode 100644
index 000000000..3afda7ce2
Binary files /dev/null and b/Habitica/res/drawable-hdpi/party_invite_reject.png differ
diff --git a/Habitica/res/drawable-hdpi/pet_checkmark.png b/Habitica/res/drawable-hdpi/pet_checkmark.png
new file mode 100644
index 000000000..3ddfe89d1
Binary files /dev/null and b/Habitica/res/drawable-hdpi/pet_checkmark.png differ
diff --git a/Habitica/res/drawable-hdpi/pet_status_egg.png b/Habitica/res/drawable-hdpi/pet_status_egg.png
new file mode 100644
index 000000000..5dd39f87d
Binary files /dev/null and b/Habitica/res/drawable-hdpi/pet_status_egg.png differ
diff --git a/Habitica/res/drawable-hdpi/pet_status_potion.png b/Habitica/res/drawable-hdpi/pet_status_potion.png
new file mode 100644
index 000000000..dc63d40c8
Binary files /dev/null and b/Habitica/res/drawable-hdpi/pet_status_potion.png differ
diff --git a/Habitica/res/drawable-hdpi/star.png b/Habitica/res/drawable-hdpi/star.png
new file mode 100644
index 000000000..07691492f
Binary files /dev/null and b/Habitica/res/drawable-hdpi/star.png differ
diff --git a/Habitica/res/drawable-hdpi/task_broken_megaphone.png b/Habitica/res/drawable-hdpi/task_broken_megaphone.png
new file mode 100644
index 000000000..fff129368
Binary files /dev/null and b/Habitica/res/drawable-hdpi/task_broken_megaphone.png differ
diff --git a/Habitica/res/drawable-hdpi/task_icon_calendar.png b/Habitica/res/drawable-hdpi/task_icon_calendar.png
new file mode 100644
index 000000000..d55431538
Binary files /dev/null and b/Habitica/res/drawable-hdpi/task_icon_calendar.png differ
diff --git a/Habitica/res/drawable-hdpi/task_icon_calendar.webp b/Habitica/res/drawable-hdpi/task_icon_calendar.webp
deleted file mode 100644
index fbd1d22be..000000000
Binary files a/Habitica/res/drawable-hdpi/task_icon_calendar.webp and /dev/null differ
diff --git a/Habitica/res/drawable-hdpi/task_icon_challenge.webp b/Habitica/res/drawable-hdpi/task_icon_challenge.webp
deleted file mode 100644
index c812710fc..000000000
Binary files a/Habitica/res/drawable-hdpi/task_icon_challenge.webp and /dev/null differ
diff --git a/Habitica/res/drawable-hdpi/task_icon_reminder.png b/Habitica/res/drawable-hdpi/task_icon_reminder.png
new file mode 100644
index 000000000..0e6e6bbb7
Binary files /dev/null and b/Habitica/res/drawable-hdpi/task_icon_reminder.png differ
diff --git a/Habitica/res/drawable-hdpi/task_icon_reminder.webp b/Habitica/res/drawable-hdpi/task_icon_reminder.webp
deleted file mode 100644
index 078bfa750..000000000
Binary files a/Habitica/res/drawable-hdpi/task_icon_reminder.webp and /dev/null differ
diff --git a/Habitica/res/drawable-hdpi/task_icon_streak.png b/Habitica/res/drawable-hdpi/task_icon_streak.png
new file mode 100644
index 000000000..7db958b43
Binary files /dev/null and b/Habitica/res/drawable-hdpi/task_icon_streak.png differ
diff --git a/Habitica/res/drawable-hdpi/task_icon_streak.webp b/Habitica/res/drawable-hdpi/task_icon_streak.webp
deleted file mode 100644
index 051618ac4..000000000
Binary files a/Habitica/res/drawable-hdpi/task_icon_streak.webp and /dev/null differ
diff --git a/Habitica/res/drawable-hdpi/task_icon_tag.png b/Habitica/res/drawable-hdpi/task_icon_tag.png
new file mode 100644
index 000000000..16e8ced1a
Binary files /dev/null and b/Habitica/res/drawable-hdpi/task_icon_tag.png differ
diff --git a/Habitica/res/drawable-hdpi/task_icon_tag.webp b/Habitica/res/drawable-hdpi/task_icon_tag.webp
deleted file mode 100644
index 8f105e670..000000000
Binary files a/Habitica/res/drawable-hdpi/task_icon_tag.webp and /dev/null differ
diff --git a/Habitica/res/drawable-hdpi/task_megaphone.png b/Habitica/res/drawable-hdpi/task_megaphone.png
new file mode 100644
index 000000000..6db0d3b53
Binary files /dev/null and b/Habitica/res/drawable-hdpi/task_megaphone.png differ
diff --git a/Habitica/res/drawable-mdpi/adventure_guide_art.png b/Habitica/res/drawable-mdpi/adventure_guide_art.png
new file mode 100644
index 000000000..723babddd
Binary files /dev/null and b/Habitica/res/drawable-mdpi/adventure_guide_art.png differ
diff --git a/Habitica/res/drawable-mdpi/baseline_keyboard_arrow_right_black_18dp.png b/Habitica/res/drawable-mdpi/baseline_keyboard_arrow_right_black_18dp.png
new file mode 100644
index 000000000..35d43f55a
Binary files /dev/null and b/Habitica/res/drawable-mdpi/baseline_keyboard_arrow_right_black_18dp.png differ
diff --git a/Habitica/res/drawable-mdpi/gold_coins_left.png b/Habitica/res/drawable-mdpi/gold_coins_left.png
new file mode 100644
index 000000000..62b9ba544
Binary files /dev/null and b/Habitica/res/drawable-mdpi/gold_coins_left.png differ
diff --git a/Habitica/res/drawable-mdpi/gold_coins_right.png b/Habitica/res/drawable-mdpi/gold_coins_right.png
new file mode 100644
index 000000000..58b23943e
Binary files /dev/null and b/Habitica/res/drawable-mdpi/gold_coins_right.png differ
diff --git a/Habitica/res/drawable-mdpi/ic_keyboard_arrow_right.png b/Habitica/res/drawable-mdpi/ic_keyboard_arrow_right.png
new file mode 100644
index 000000000..355277b9a
Binary files /dev/null and b/Habitica/res/drawable-mdpi/ic_keyboard_arrow_right.png differ
diff --git a/Habitica/res/drawable-mdpi/onboarding_done_art.png b/Habitica/res/drawable-mdpi/onboarding_done_art.png
new file mode 100644
index 000000000..cdf546563
Binary files /dev/null and b/Habitica/res/drawable-mdpi/onboarding_done_art.png differ
diff --git a/Habitica/res/drawable-mdpi/party_invite_accept.png b/Habitica/res/drawable-mdpi/party_invite_accept.png
new file mode 100644
index 000000000..9a2c8b79b
Binary files /dev/null and b/Habitica/res/drawable-mdpi/party_invite_accept.png differ
diff --git a/Habitica/res/drawable-mdpi/party_invite_reject.png b/Habitica/res/drawable-mdpi/party_invite_reject.png
new file mode 100644
index 000000000..1767d39ab
Binary files /dev/null and b/Habitica/res/drawable-mdpi/party_invite_reject.png differ
diff --git a/Habitica/res/drawable-mdpi/pet_checkmark.png b/Habitica/res/drawable-mdpi/pet_checkmark.png
new file mode 100644
index 000000000..e31aff0ea
Binary files /dev/null and b/Habitica/res/drawable-mdpi/pet_checkmark.png differ
diff --git a/Habitica/res/drawable-mdpi/pet_status_egg.png b/Habitica/res/drawable-mdpi/pet_status_egg.png
new file mode 100644
index 000000000..cc358b246
Binary files /dev/null and b/Habitica/res/drawable-mdpi/pet_status_egg.png differ
diff --git a/Habitica/res/drawable-mdpi/pet_status_potion.png b/Habitica/res/drawable-mdpi/pet_status_potion.png
new file mode 100644
index 000000000..34a1c5c92
Binary files /dev/null and b/Habitica/res/drawable-mdpi/pet_status_potion.png differ
diff --git a/Habitica/res/drawable-mdpi/star.png b/Habitica/res/drawable-mdpi/star.png
new file mode 100644
index 000000000..23af96a47
Binary files /dev/null and b/Habitica/res/drawable-mdpi/star.png differ
diff --git a/Habitica/res/drawable-mdpi/task_broken_megaphone.png b/Habitica/res/drawable-mdpi/task_broken_megaphone.png
new file mode 100644
index 000000000..72faf39ce
Binary files /dev/null and b/Habitica/res/drawable-mdpi/task_broken_megaphone.png differ
diff --git a/Habitica/res/drawable-mdpi/task_icon_calendar.png b/Habitica/res/drawable-mdpi/task_icon_calendar.png
new file mode 100644
index 000000000..5a5efddd2
Binary files /dev/null and b/Habitica/res/drawable-mdpi/task_icon_calendar.png differ
diff --git a/Habitica/res/drawable-mdpi/task_icon_calendar.webp b/Habitica/res/drawable-mdpi/task_icon_calendar.webp
deleted file mode 100644
index 817808dc1..000000000
Binary files a/Habitica/res/drawable-mdpi/task_icon_calendar.webp and /dev/null differ
diff --git a/Habitica/res/drawable-mdpi/task_icon_challenge.webp b/Habitica/res/drawable-mdpi/task_icon_challenge.webp
deleted file mode 100644
index c8b6cb4a2..000000000
Binary files a/Habitica/res/drawable-mdpi/task_icon_challenge.webp and /dev/null differ
diff --git a/Habitica/res/drawable-mdpi/task_icon_reminder.png b/Habitica/res/drawable-mdpi/task_icon_reminder.png
new file mode 100644
index 000000000..81012e31e
Binary files /dev/null and b/Habitica/res/drawable-mdpi/task_icon_reminder.png differ
diff --git a/Habitica/res/drawable-mdpi/task_icon_reminder.webp b/Habitica/res/drawable-mdpi/task_icon_reminder.webp
deleted file mode 100644
index c4b1728d7..000000000
Binary files a/Habitica/res/drawable-mdpi/task_icon_reminder.webp and /dev/null differ
diff --git a/Habitica/res/drawable-mdpi/task_icon_streak.png b/Habitica/res/drawable-mdpi/task_icon_streak.png
new file mode 100644
index 000000000..ba6e9f5d0
Binary files /dev/null and b/Habitica/res/drawable-mdpi/task_icon_streak.png differ
diff --git a/Habitica/res/drawable-mdpi/task_icon_streak.webp b/Habitica/res/drawable-mdpi/task_icon_streak.webp
deleted file mode 100644
index 103832958..000000000
Binary files a/Habitica/res/drawable-mdpi/task_icon_streak.webp and /dev/null differ
diff --git a/Habitica/res/drawable-mdpi/task_icon_tag.png b/Habitica/res/drawable-mdpi/task_icon_tag.png
new file mode 100644
index 000000000..880bfeb2c
Binary files /dev/null and b/Habitica/res/drawable-mdpi/task_icon_tag.png differ
diff --git a/Habitica/res/drawable-mdpi/task_icon_tag.webp b/Habitica/res/drawable-mdpi/task_icon_tag.webp
deleted file mode 100644
index 2fc94900c..000000000
Binary files a/Habitica/res/drawable-mdpi/task_icon_tag.webp and /dev/null differ
diff --git a/Habitica/res/drawable-mdpi/task_megaphone.png b/Habitica/res/drawable-mdpi/task_megaphone.png
new file mode 100644
index 000000000..3f1cdac21
Binary files /dev/null and b/Habitica/res/drawable-mdpi/task_megaphone.png differ
diff --git a/Habitica/res/drawable-xhdpi/adventure_guide_art.png b/Habitica/res/drawable-xhdpi/adventure_guide_art.png
new file mode 100644
index 000000000..b80cc559c
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/adventure_guide_art.png differ
diff --git a/Habitica/res/drawable-xhdpi/gold_coins_left.png b/Habitica/res/drawable-xhdpi/gold_coins_left.png
new file mode 100644
index 000000000..ace067333
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/gold_coins_left.png differ
diff --git a/Habitica/res/drawable-xhdpi/gold_coins_right.png b/Habitica/res/drawable-xhdpi/gold_coins_right.png
new file mode 100644
index 000000000..f3b918478
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/gold_coins_right.png differ
diff --git a/Habitica/res/drawable-xhdpi/ic_keyboard_arrow_right.png b/Habitica/res/drawable-xhdpi/ic_keyboard_arrow_right.png
new file mode 100644
index 000000000..7daf30463
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/ic_keyboard_arrow_right.png differ
diff --git a/Habitica/res/drawable-xhdpi/onboarding_done_art.png b/Habitica/res/drawable-xhdpi/onboarding_done_art.png
new file mode 100644
index 000000000..3451bd631
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/onboarding_done_art.png differ
diff --git a/Habitica/res/drawable-xhdpi/party_invite_accept.png b/Habitica/res/drawable-xhdpi/party_invite_accept.png
new file mode 100644
index 000000000..648a9fea3
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/party_invite_accept.png differ
diff --git a/Habitica/res/drawable-xhdpi/party_invite_reject.png b/Habitica/res/drawable-xhdpi/party_invite_reject.png
new file mode 100644
index 000000000..4dbfde229
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/party_invite_reject.png differ
diff --git a/Habitica/res/drawable-xhdpi/pet_checkmark.png b/Habitica/res/drawable-xhdpi/pet_checkmark.png
new file mode 100644
index 000000000..d1b5d1ec3
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/pet_checkmark.png differ
diff --git a/Habitica/res/drawable-xhdpi/pet_status_egg.png b/Habitica/res/drawable-xhdpi/pet_status_egg.png
new file mode 100644
index 000000000..de8b3fdc2
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/pet_status_egg.png differ
diff --git a/Habitica/res/drawable-xhdpi/pet_status_potion.png b/Habitica/res/drawable-xhdpi/pet_status_potion.png
new file mode 100644
index 000000000..3a2ebe35e
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/pet_status_potion.png differ
diff --git a/Habitica/res/drawable-xhdpi/star.png b/Habitica/res/drawable-xhdpi/star.png
new file mode 100644
index 000000000..dc432e650
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/star.png differ
diff --git a/Habitica/res/drawable-xhdpi/task_broken_megaphone.png b/Habitica/res/drawable-xhdpi/task_broken_megaphone.png
new file mode 100644
index 000000000..3b4250dcf
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/task_broken_megaphone.png differ
diff --git a/Habitica/res/drawable-xhdpi/task_icon_calendar.png b/Habitica/res/drawable-xhdpi/task_icon_calendar.png
new file mode 100644
index 000000000..2ad0d6c6d
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/task_icon_calendar.png differ
diff --git a/Habitica/res/drawable-xhdpi/task_icon_calendar.webp b/Habitica/res/drawable-xhdpi/task_icon_calendar.webp
deleted file mode 100644
index ce9101d71..000000000
Binary files a/Habitica/res/drawable-xhdpi/task_icon_calendar.webp and /dev/null differ
diff --git a/Habitica/res/drawable-xhdpi/task_icon_challenge.webp b/Habitica/res/drawable-xhdpi/task_icon_challenge.webp
deleted file mode 100644
index c83b2daf4..000000000
Binary files a/Habitica/res/drawable-xhdpi/task_icon_challenge.webp and /dev/null differ
diff --git a/Habitica/res/drawable-xhdpi/task_icon_reminder.png b/Habitica/res/drawable-xhdpi/task_icon_reminder.png
new file mode 100644
index 000000000..785e9e17e
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/task_icon_reminder.png differ
diff --git a/Habitica/res/drawable-xhdpi/task_icon_reminder.webp b/Habitica/res/drawable-xhdpi/task_icon_reminder.webp
deleted file mode 100644
index 1b920ced7..000000000
Binary files a/Habitica/res/drawable-xhdpi/task_icon_reminder.webp and /dev/null differ
diff --git a/Habitica/res/drawable-xhdpi/task_icon_streak.png b/Habitica/res/drawable-xhdpi/task_icon_streak.png
new file mode 100644
index 000000000..6d2eac19f
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/task_icon_streak.png differ
diff --git a/Habitica/res/drawable-xhdpi/task_icon_streak.webp b/Habitica/res/drawable-xhdpi/task_icon_streak.webp
deleted file mode 100644
index 0897b1606..000000000
Binary files a/Habitica/res/drawable-xhdpi/task_icon_streak.webp and /dev/null differ
diff --git a/Habitica/res/drawable-xhdpi/task_icon_tag.png b/Habitica/res/drawable-xhdpi/task_icon_tag.png
new file mode 100644
index 000000000..28adc1085
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/task_icon_tag.png differ
diff --git a/Habitica/res/drawable-xhdpi/task_icon_tag.webp b/Habitica/res/drawable-xhdpi/task_icon_tag.webp
deleted file mode 100644
index aa1cf0f3c..000000000
Binary files a/Habitica/res/drawable-xhdpi/task_icon_tag.webp and /dev/null differ
diff --git a/Habitica/res/drawable-xhdpi/task_megaphone.png b/Habitica/res/drawable-xhdpi/task_megaphone.png
new file mode 100644
index 000000000..fa0392b70
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/task_megaphone.png differ
diff --git a/Habitica/res/drawable-xxhdpi/adventure_guide_art.png b/Habitica/res/drawable-xxhdpi/adventure_guide_art.png
new file mode 100644
index 000000000..49a4e5759
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/adventure_guide_art.png differ
diff --git a/Habitica/res/drawable-xxhdpi/gold_coins_left.png b/Habitica/res/drawable-xxhdpi/gold_coins_left.png
new file mode 100644
index 000000000..f36dd59f9
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/gold_coins_left.png differ
diff --git a/Habitica/res/drawable-xxhdpi/gold_coins_right.png b/Habitica/res/drawable-xxhdpi/gold_coins_right.png
new file mode 100644
index 000000000..a5a90e515
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/gold_coins_right.png differ
diff --git a/Habitica/res/drawable-xxhdpi/ic_keyboard_arrow_right.png b/Habitica/res/drawable-xxhdpi/ic_keyboard_arrow_right.png
new file mode 100644
index 000000000..71b4b80c0
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/ic_keyboard_arrow_right.png differ
diff --git a/Habitica/res/drawable-xxhdpi/onboarding_done_art.png b/Habitica/res/drawable-xxhdpi/onboarding_done_art.png
new file mode 100644
index 000000000..3d8dc9e0c
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/onboarding_done_art.png differ
diff --git a/Habitica/res/drawable-xxhdpi/party_invite_accept.png b/Habitica/res/drawable-xxhdpi/party_invite_accept.png
new file mode 100644
index 000000000..e1d80f210
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/party_invite_accept.png differ
diff --git a/Habitica/res/drawable-xxhdpi/party_invite_reject.png b/Habitica/res/drawable-xxhdpi/party_invite_reject.png
new file mode 100644
index 000000000..61ca79082
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/party_invite_reject.png differ
diff --git a/Habitica/res/drawable-xxhdpi/pet_checkmark.png b/Habitica/res/drawable-xxhdpi/pet_checkmark.png
new file mode 100644
index 000000000..8faedaa58
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/pet_checkmark.png differ
diff --git a/Habitica/res/drawable-xxhdpi/pet_status_egg.png b/Habitica/res/drawable-xxhdpi/pet_status_egg.png
new file mode 100644
index 000000000..b4f145ea0
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/pet_status_egg.png differ
diff --git a/Habitica/res/drawable-xxhdpi/pet_status_potion.png b/Habitica/res/drawable-xxhdpi/pet_status_potion.png
new file mode 100644
index 000000000..5edb9dab0
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/pet_status_potion.png differ
diff --git a/Habitica/res/drawable-xxhdpi/star.png b/Habitica/res/drawable-xxhdpi/star.png
new file mode 100644
index 000000000..d3f087bf7
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/star.png differ
diff --git a/Habitica/res/drawable-xxhdpi/task_broken_megaphone.png b/Habitica/res/drawable-xxhdpi/task_broken_megaphone.png
new file mode 100644
index 000000000..9f5d7cac3
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/task_broken_megaphone.png differ
diff --git a/Habitica/res/drawable-xxhdpi/task_icon_calendar.png b/Habitica/res/drawable-xxhdpi/task_icon_calendar.png
new file mode 100644
index 000000000..bd9fe16ae
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/task_icon_calendar.png differ
diff --git a/Habitica/res/drawable-xxhdpi/task_icon_calendar.webp b/Habitica/res/drawable-xxhdpi/task_icon_calendar.webp
deleted file mode 100644
index a08091cf9..000000000
Binary files a/Habitica/res/drawable-xxhdpi/task_icon_calendar.webp and /dev/null differ
diff --git a/Habitica/res/drawable-xxhdpi/task_icon_challenge.webp b/Habitica/res/drawable-xxhdpi/task_icon_challenge.webp
deleted file mode 100644
index 885461b2a..000000000
Binary files a/Habitica/res/drawable-xxhdpi/task_icon_challenge.webp and /dev/null differ
diff --git a/Habitica/res/drawable-xxhdpi/task_icon_reminder.png b/Habitica/res/drawable-xxhdpi/task_icon_reminder.png
new file mode 100644
index 000000000..72d884a7b
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/task_icon_reminder.png differ
diff --git a/Habitica/res/drawable-xxhdpi/task_icon_reminder.webp b/Habitica/res/drawable-xxhdpi/task_icon_reminder.webp
deleted file mode 100644
index ba853d98a..000000000
Binary files a/Habitica/res/drawable-xxhdpi/task_icon_reminder.webp and /dev/null differ
diff --git a/Habitica/res/drawable-xxhdpi/task_icon_streak.png b/Habitica/res/drawable-xxhdpi/task_icon_streak.png
new file mode 100644
index 000000000..87f210dd9
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/task_icon_streak.png differ
diff --git a/Habitica/res/drawable-xxhdpi/task_icon_streak.webp b/Habitica/res/drawable-xxhdpi/task_icon_streak.webp
deleted file mode 100644
index 9a3a387bf..000000000
Binary files a/Habitica/res/drawable-xxhdpi/task_icon_streak.webp and /dev/null differ
diff --git a/Habitica/res/drawable-xxhdpi/task_icon_tag.png b/Habitica/res/drawable-xxhdpi/task_icon_tag.png
new file mode 100644
index 000000000..0419931d7
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/task_icon_tag.png differ
diff --git a/Habitica/res/drawable-xxhdpi/task_icon_tag.webp b/Habitica/res/drawable-xxhdpi/task_icon_tag.webp
deleted file mode 100644
index fcb12a6b9..000000000
Binary files a/Habitica/res/drawable-xxhdpi/task_icon_tag.webp and /dev/null differ
diff --git a/Habitica/res/drawable-xxhdpi/task_megaphone.png b/Habitica/res/drawable-xxhdpi/task_megaphone.png
new file mode 100644
index 000000000..4a2813ba2
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/task_megaphone.png differ
diff --git a/Habitica/res/drawable-xxxhdpi/gold_coins_left.png b/Habitica/res/drawable-xxxhdpi/gold_coins_left.png
new file mode 100644
index 000000000..db0ab7495
Binary files /dev/null and b/Habitica/res/drawable-xxxhdpi/gold_coins_left.png differ
diff --git a/Habitica/res/drawable-xxxhdpi/gold_coins_right.png b/Habitica/res/drawable-xxxhdpi/gold_coins_right.png
new file mode 100644
index 000000000..6c71c1cd7
Binary files /dev/null and b/Habitica/res/drawable-xxxhdpi/gold_coins_right.png differ
diff --git a/Habitica/res/drawable-xxxhdpi/ic_keyboard_arrow_right.png b/Habitica/res/drawable-xxxhdpi/ic_keyboard_arrow_right.png
new file mode 100644
index 000000000..e81763f3f
Binary files /dev/null and b/Habitica/res/drawable-xxxhdpi/ic_keyboard_arrow_right.png differ
diff --git a/Habitica/res/drawable-xxxhdpi/star.png b/Habitica/res/drawable-xxxhdpi/star.png
new file mode 100644
index 000000000..ac8b6f5ca
Binary files /dev/null and b/Habitica/res/drawable-xxxhdpi/star.png differ
diff --git a/Habitica/res/drawable/adventure_guide_menu_bg.xml b/Habitica/res/drawable/adventure_guide_menu_bg.xml
new file mode 100644
index 000000000..b5877fa91
--- /dev/null
+++ b/Habitica/res/drawable/adventure_guide_menu_bg.xml
@@ -0,0 +1,19 @@
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/drawable/adventure_guide_menu_card_bg.xml b/Habitica/res/drawable/adventure_guide_menu_card_bg.xml
new file mode 100644
index 000000000..be6da8b36
--- /dev/null
+++ b/Habitica/res/drawable/adventure_guide_menu_card_bg.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
diff --git a/Habitica/res/drawable/adventure_guide_menu_card_gray.xml b/Habitica/res/drawable/adventure_guide_menu_card_gray.xml
new file mode 100644
index 000000000..4a16d5346
--- /dev/null
+++ b/Habitica/res/drawable/adventure_guide_menu_card_gray.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/drawable/layout_rounded_bg_animalitem_complete.xml b/Habitica/res/drawable/layout_rounded_bg_animalitem_complete.xml
new file mode 100644
index 000000000..a8cb4e3d1
--- /dev/null
+++ b/Habitica/res/drawable/layout_rounded_bg_animalitem_complete.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/drawable/layout_rounded_bg_gray_700_brand_border.xml b/Habitica/res/drawable/layout_rounded_bg_gray_700_brand_border.xml
index 727262a5e..995c49409 100644
--- a/Habitica/res/drawable/layout_rounded_bg_gray_700_brand_border.xml
+++ b/Habitica/res/drawable/layout_rounded_bg_gray_700_brand_border.xml
@@ -3,5 +3,5 @@
-
+
\ No newline at end of file
diff --git a/Habitica/res/drawable/layout_rounded_bg_header_bar.xml b/Habitica/res/drawable/layout_rounded_bg_header_bar.xml
new file mode 100644
index 000000000..3ab4bfac4
--- /dev/null
+++ b/Habitica/res/drawable/layout_rounded_bg_header_bar.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/drawable/party_invitation_accept.xml b/Habitica/res/drawable/party_invitation_accept.xml
new file mode 100644
index 000000000..4b0624866
--- /dev/null
+++ b/Habitica/res/drawable/party_invitation_accept.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
diff --git a/Habitica/res/drawable/party_invitation_reject.xml b/Habitica/res/drawable/party_invitation_reject.xml
new file mode 100644
index 000000000..f1025484d
--- /dev/null
+++ b/Habitica/res/drawable/party_invitation_reject.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
diff --git a/Habitica/res/drawable/pill_bg_gray.xml b/Habitica/res/drawable/pill_bg_gray.xml
index c5f636827..fc8626c84 100644
--- a/Habitica/res/drawable/pill_bg_gray.xml
+++ b/Habitica/res/drawable/pill_bg_gray.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/Habitica/res/layout/achievement_grid_item.xml b/Habitica/res/layout/achievement_grid_item.xml
index 6ed1113d8..5bdcae926 100644
--- a/Habitica/res/layout/achievement_grid_item.xml
+++ b/Habitica/res/layout/achievement_grid_item.xml
@@ -1,6 +1,7 @@
+ android:gravity="center_vertical">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/activity_fixcharacter.xml b/Habitica/res/layout/activity_fixcharacter.xml
index 359ce75ee..7dce1e80b 100644
--- a/Habitica/res/layout/activity_fixcharacter.xml
+++ b/Habitica/res/layout/activity_fixcharacter.xml
@@ -35,72 +35,266 @@
android:background="@color/gray_700"
android:layout_marginBottom="16dp"
/>
-
-
-
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+ android:layout_marginBottom="12dp" >
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/activity_login.xml b/Habitica/res/layout/activity_login.xml
index f216f1e8d..ba11b3606 100644
--- a/Habitica/res/layout/activity_login.xml
+++ b/Habitica/res/layout/activity_login.xml
@@ -220,8 +220,8 @@
android:layout_height="@dimen/diamond_button_height"
android:text="@string/login_btn_apple"
android:drawableStart="@drawable/apple_icon"
- style="@style/LoginButton"
- android:visibility="gone"/>
+ android:visibility="gone"
+ style="@style/LoginButton"/>
@@ -51,6 +51,7 @@
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:theme="@style/Toolbar"
+ android:background="?attr/colorPrimaryDark"
app:layout_collapseMode="pin"
app:popupTheme="@style/Theme.AppCompat.Light">
@@ -43,20 +42,29 @@
android:paddingEnd="@dimen/spacing_large"
android:paddingBottom="@dimen/spacing_large">
+ style="@style/TaskFormTextInputLayoutAppearance"
+ app:boxStrokeColor="?attr/colorPrimary"
+ android:backgroundTint="?attr/colorPrimary"
+ android:alpha="0.75">
+ android:layout_height="wrap_content"
+ app:boxStrokeColor="?attr/colorPrimary"
+ android:backgroundTint="?attr/colorPrimary"
+ android:alpha="0.75">
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/adventure_guide_menu_banner.xml b/Habitica/res/layout/adventure_guide_menu_banner.xml
new file mode 100644
index 000000000..f9258362e
--- /dev/null
+++ b/Habitica/res/layout/adventure_guide_menu_banner.xml
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/animal_overview_item.xml b/Habitica/res/layout/animal_overview_item.xml
deleted file mode 100644
index 68c34da9d..000000000
--- a/Habitica/res/layout/animal_overview_item.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
diff --git a/Habitica/res/layout/avatar_with_bars.xml b/Habitica/res/layout/avatar_with_bars.xml
index 6b2798b7b..eda10233c 100644
--- a/Habitica/res/layout/avatar_with_bars.xml
+++ b/Habitica/res/layout/avatar_with_bars.xml
@@ -6,7 +6,7 @@
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:adjustViewBounds="true"
- android:background="?colorPrimary"
+ android:background="?colorPrimaryDark"
android:focusable="true"
android:focusableInTouchMode="true"
android:orientation="vertical"
@@ -24,8 +24,8 @@
android:layout_width="@dimen/avatar_header_width"
android:layout_height="@dimen/avatar_header_height"
android:layout_gravity="center_vertical"
- android:layout_marginEnd="16dp"
- android:layout_marginRight="16dp"
+ android:layout_marginEnd="20dp"
+ android:layout_marginStart="2dp"
android:contentDescription="@string/sidebar_avatar"
app:showBackground="true"
app:showMount="true"
diff --git a/Habitica/res/layout/customization_section_header.xml b/Habitica/res/layout/customization_section_header.xml
index 8a7df6961..b37e6a3bb 100644
--- a/Habitica/res/layout/customization_section_header.xml
+++ b/Habitica/res/layout/customization_section_header.xml
@@ -3,18 +3,29 @@
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
- android:layout_height="65dp"
- android:paddingTop="16dp"
- android:gravity="bottom"
- android:layout_marginStart="20dp"
- android:layout_marginEnd="20dp">
+ android:layout_height="55dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:gravity="bottom">
-
+ tools:text="Test header"
+ style="@style/Overline"
+ android:textColor="@color/gray_200"/>
+
+ android:scaleType="center"
+ app:srcCompat="@drawable/task_megaphone" />
@@ -176,8 +179,7 @@
android:layout_width="@dimen/checklist_wrapper_width"
android:layout_height="match_parent"
android:gravity="center"
- android:orientation="vertical"
- tools:background="@color/md_green_300">
+ android:orientation="vertical">
+ android:layout_width="52dp"
+ android:layout_height="56dp"
+ app:actualImageScaleType="fitCenter"
+ android:layout_marginStart="20dp"
+ android:layout_marginEnd="20dp" />
+
\ No newline at end of file
diff --git a/Habitica/res/layout/dialog_first_drop.xml b/Habitica/res/layout/dialog_first_drop.xml
new file mode 100644
index 000000000..c37fb490a
--- /dev/null
+++ b/Habitica/res/layout/dialog_first_drop.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/dialog_pet_suggest_hatch.xml b/Habitica/res/layout/dialog_pet_suggest_hatch.xml
new file mode 100644
index 000000000..12ca1906c
--- /dev/null
+++ b/Habitica/res/layout/dialog_pet_suggest_hatch.xml
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/dialog_purchase_content_item.xml b/Habitica/res/layout/dialog_purchase_content_item.xml
index c61f9b15c..568e5b470 100644
--- a/Habitica/res/layout/dialog_purchase_content_item.xml
+++ b/Habitica/res/layout/dialog_purchase_content_item.xml
@@ -1,6 +1,7 @@
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/drawer_main.xml b/Habitica/res/layout/drawer_main.xml
index 60a527613..ce2b4dde3 100644
--- a/Habitica/res/layout/drawer_main.xml
+++ b/Habitica/res/layout/drawer_main.xml
@@ -68,7 +68,8 @@
android:background="@color/transparent"
android:src="@drawable/menu_notifications"
android:layout_centerVertical="true"
- android:clickable="false" />
+ android:clickable="false"
+ android:contentDescription="@string/notifications" />
+ android:clickable="false"
+ android:contentDescription="@string/inbox" />
-
+ android:layout_centerVertical="true"
+ android:contentDescription="@string/PS_settings_title" />
-
-
+
diff --git a/Habitica/res/layout/fragment_faq_overview.xml b/Habitica/res/layout/fragment_faq_overview.xml
index 4ca622466..b861b8a7a 100644
--- a/Habitica/res/layout/fragment_faq_overview.xml
+++ b/Habitica/res/layout/fragment_faq_overview.xml
@@ -130,7 +130,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
- android:textColor="@color/gray_50"
+ android:textColor="@color/gray_100"
android:gravity="center"/>
diff --git a/Habitica/res/layout/fragment_items.xml b/Habitica/res/layout/fragment_items.xml
index 1a694a4ea..b34771600 100644
--- a/Habitica/res/layout/fragment_items.xml
+++ b/Habitica/res/layout/fragment_items.xml
@@ -1,9 +1,9 @@
-
-
+ android:layout_height="match_parent"
+ android:background="@color/gray_700"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior">
+
+
+
+
+
+
+
+
+
+
diff --git a/Habitica/res/layout/fragment_support_bug_fix.xml b/Habitica/res/layout/fragment_support_bug_fix.xml
index fe09f89b7..bab44c9fc 100644
--- a/Habitica/res/layout/fragment_support_bug_fix.xml
+++ b/Habitica/res/layout/fragment_support_bug_fix.xml
@@ -1,8 +1,12 @@
-
+
+ android:background="@color/white"
+ android:layout_marginBottom="@dimen/spacing_large">
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/fragment_support_main.xml b/Habitica/res/layout/fragment_support_main.xml
index 2ccd6820d..5d98b294c 100644
--- a/Habitica/res/layout/fragment_support_main.xml
+++ b/Habitica/res/layout/fragment_support_main.xml
@@ -1,4 +1,8 @@
+
@@ -88,7 +92,7 @@
android:text="@string/bugs_fixes_description"
style="@style/Body2"
android:lineSpacingExtra="3sp"
- android:textColor="@color/gray_50"
+ android:textColor="@color/gray_100"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:gravity="center"
@@ -106,7 +110,7 @@
android:layout_marginBottom="@dimen/spacing_medium"
android:background="@drawable/support_info_lower_bg"
android:textColor="@color/white"
- android:elevation="2dp"
+ android:elevation="4dp"
android:textAllCaps="false"
style="@style/Subheader1"
/>
@@ -138,7 +142,7 @@
android:text="@string/suggestions_feedback_description"
style="@style/Body2"
android:lineSpacingExtra="3sp"
- android:textColor="@color/gray_50"
+ android:textColor="@color/gray_100"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:gravity="center"
@@ -155,7 +159,7 @@
android:layout_marginEnd="@dimen/spacing_medium"
android:layout_marginBottom="@dimen/spacing_medium"
android:background="@drawable/support_info_lower_bg"
- android:elevation="2dp"
+ android:elevation="4dp"
android:textColor="@color/white"
style="@style/Subheader1"
/>
@@ -173,4 +177,5 @@
android:textColor="@color/gray_50"
android:text="@string/reset_walkthrough"
android:layout_margin="@dimen/spacing_large"/>
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/habit_item_card.xml b/Habitica/res/layout/habit_item_card.xml
index 66632b7e8..1f588688e 100644
--- a/Habitica/res/layout/habit_item_card.xml
+++ b/Habitica/res/layout/habit_item_card.xml
@@ -122,7 +122,8 @@
android:layout_height="@dimen/task_icon_size"
android:layout_marginStart="@dimen/task_icon_space"
android:contentDescription="@string/belongs_to_challenge"
- app:srcCompat="@drawable/task_icon_challenge" />
+ android:scaleType="center"
+ app:srcCompat="@drawable/task_megaphone" />
diff --git a/Habitica/res/layout/main_navigation_view.xml b/Habitica/res/layout/main_navigation_view.xml
index 43a1d455f..ce12da747 100644
--- a/Habitica/res/layout/main_navigation_view.xml
+++ b/Habitica/res/layout/main_navigation_view.xml
@@ -5,7 +5,8 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- tools:parentTag="android.widget.RelativeLayout">
+ tools:parentTag="android.widget.RelativeLayout"
+ tools:background="@color/gray_600">
+ android:layout_height="62dp"
+ android:src="@drawable/bottom_navigation_inset"
+ android:layout_gravity="top"/>
diff --git a/Habitica/res/layout/menu_bottom_sheet.xml b/Habitica/res/layout/menu_bottom_sheet.xml
index 943ae2746..fcc654b20 100644
--- a/Habitica/res/layout/menu_bottom_sheet.xml
+++ b/Habitica/res/layout/menu_bottom_sheet.xml
@@ -1,7 +1,23 @@
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/mount_overview_item.xml b/Habitica/res/layout/mount_overview_item.xml
new file mode 100644
index 000000000..2c96f6fb1
--- /dev/null
+++ b/Habitica/res/layout/mount_overview_item.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Habitica/res/layout/pet_detail_item.xml b/Habitica/res/layout/pet_detail_item.xml
index 7dcb68812..67776ae04 100644
--- a/Habitica/res/layout/pet_detail_item.xml
+++ b/Habitica/res/layout/pet_detail_item.xml
@@ -1,28 +1,65 @@
+ android:padding="8dp">
+
-
-
+ android:layout_width="75dp"
+ android:layout_height="75dp"
+ android:layout_gravity="center"
+ app:actualImageScaleType="fitCenter"/>
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/pet_overview_item.xml b/Habitica/res/layout/pet_overview_item.xml
new file mode 100644
index 000000000..cea0dfe82
--- /dev/null
+++ b/Habitica/res/layout/pet_overview_item.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Habitica/res/layout/shop_header.xml b/Habitica/res/layout/shop_header.xml
index 84951777e..cc63e3840 100644
--- a/Habitica/res/layout/shop_header.xml
+++ b/Habitica/res/layout/shop_header.xml
@@ -9,7 +9,9 @@
+ android:layout_height="@dimen/shop_scene_height"
+ android:paddingStart="-8dp"
+ android:paddingEnd="-8dp"/>
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp" />
+ tools:text="To Do Title" />
+ android:scaleType="center"
+ app:srcCompat="@drawable/task_megaphone" />
@@ -157,8 +161,7 @@
android:layout_width="@dimen/checklist_wrapper_width"
android:layout_height="match_parent"
android:gravity="center"
- android:orientation="vertical"
- tools:background="@color/md_green_300">
+ android:orientation="vertical">
@@ -24,7 +21,6 @@
android:layout_width="match_parent"
android:layout_height="@dimen/bar_size"
android:layout_toEndOf="@id/ic_header"
- android:layout_toRightOf="@id/ic_header"
android:orientation="horizontal"/>
+ android:paddingTop="2dp">
-
-
+ style="@style/CardView.Default"
+ android:id="@+id/party_invitation_wrapper">
+
-
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/navigation/navigation.xml b/Habitica/res/navigation/navigation.xml
index 3235fa47d..a8f2bfbbc 100644
--- a/Habitica/res/navigation/navigation.xml
+++ b/Habitica/res/navigation/navigation.xml
@@ -304,8 +304,7 @@
+ android:name="com.habitrpg.android.habitica.ui.fragments.inventory.stable.PetDetailRecyclerFragment">
@@ -319,8 +318,7 @@
+ android:name="com.habitrpg.android.habitica.ui.fragments.inventory.stable.MountDetailRecyclerFragment">
@@ -429,4 +427,8 @@
android:id="@+id/bugFixFragment"
android:name="com.habitrpg.android.habitica.ui.fragments.support.BugFixFragment"
android:label="@string/bugs_fixes" />
+
\ No newline at end of file
diff --git a/Habitica/res/values-da/strings.xml b/Habitica/res/values-da/strings.xml
index f1ea13f04..5bcbabe49 100755
--- a/Habitica/res/values-da/strings.xml
+++ b/Habitica/res/values-da/strings.xml
@@ -36,7 +36,7 @@
Inviteret til Quest
Værdi
- Ny To-Do
+ Ny To Do
Ny Belønning
Ny Daglig Opgave
Ny Vane
@@ -136,7 +136,7 @@
Medlemmer
Vaner
Daglige
- To-Dos
+ To Do\'s
Belønninger
Er du sikker?
Vil du virkelig slette?
@@ -302,10 +302,10 @@
Tilføj Opgave
Tilføj Vane
Tilføj Daglig Opgave
- Tilføj To-Do
+ Tilføj To Do
Tilføj Belønning
Habitica Daglige Opgaver
- Habitica To-Do Liste
+ Habitica To Do Liste
Habitica Tilføj Opgave
Køb
@@ -334,7 +334,7 @@
Daglig
Vane
Belønning
- To-Do
+ To Do
Officiel
Deltager
Udfordring
diff --git a/Habitica/res/values-de/strings.tutorial.xml b/Habitica/res/values-de/strings.tutorial.xml
index 829e8e7e1..2025c4a05 100644
--- a/Habitica/res/values-de/strings.tutorial.xml
+++ b/Habitica/res/values-de/strings.tutorial.xml
@@ -7,8 +7,8 @@
Probier es aus! Du kannst die anderen Aufgaben-Typen kennenernen, indem du die untere Navigation benutzt.
Erstelle tägliche Aufgaben für zeitkritische Aufgaben, die regelmäßig erledigt werden müssen.
Pass auf - wenn Du eine dieser Aufgaben nicht erledigst, wird Deinem Avatar über Nacht Schaden zugefügt. Erledige Deine Aufgaben täglich und Du wirst tolle Belohnungen erhalten!
- Nutze die To-Dos, um Aufgaben zu organisieren, die du nur einmalig erledigen musst.
- Falls Dein To-Do an einem bestimmten Tag erledigt sein muss, kannst du dafür ein Fälligkeitsdatum erstellen. Es sieht so aus, als ob du jetzt auch eine Aufgabe abhaken kannst - probier es aus!
+ Nutze die To Do\'s, um Aufgaben zu organisieren, die du nur einmalig erledigen musst.
+ Falls Dein To Do an einem bestimmten Tag erledigt sein muss, kannst du dafür ein Fälligkeitsdatum erstellen. Es sieht so aus, als ob du jetzt auch eine Aufgabe abhaken kannst - probier es aus!
Kaufe dir mit dem verdienten Gold Ausrüstung für deinen Avatar!
Du kannst auch benutzerdefinierte Belohnungen erstellen, die sich auf etwas in Deinem wirklichen Leben beziehen, je nachdem was Dich motiviert.
Das war\'s für das Erste. Falls du eine Erinnerungshilfe brauchst, dann schau in die FAQs.
diff --git a/Habitica/res/values-de/strings.xml b/Habitica/res/values-de/strings.xml
index bdb8874bc..b86056cd0 100644
--- a/Habitica/res/values-de/strings.xml
+++ b/Habitica/res/values-de/strings.xml
@@ -106,7 +106,7 @@
Mitglieder
Gewohnheiten
Tagesaufgaben
- To-Dos
+ To Do\'s
Belohnungen
Ja
Nein
@@ -305,12 +305,12 @@
Neue Aufgabe hinzufügen
Neue Gewohnheit hinzufügen
Neue Tagesaufgabe hinzufügen
- Neues To-Do hinzufügen
+ Neues To Do hinzufügen
Neue Belohnung hinzufügen
Du hast alle Deine Tagesaufgaben erledigt. Gut gemacht!
Habitica: Gewohnheit
Habitica: Tagesaufgaben
- Habitica To-Do-Liste
+ Habitica To Do-Liste
Google Play Dienste konnten nicht gefunden werden.
Kaufen
Das Erwerben von Edelsteinen unterstützt die Entwickler und hilft Habitica am Laufen zu halten
@@ -378,7 +378,7 @@
Tagesaufgabe
Gewohnheit
Belohnung
- To-Do
+ To Do
Offiziell
Herausforderung
Du nimmst im Moment an keiner Herausforderung teil!
@@ -426,8 +426,8 @@
Gewohnheiten sind Aufgaben ohne festen Zeitplan. Du kannst sie mehrmals am Tag oder gar nicht abhaken.
Du hast keine Tagesaufgaben
Tagesaufgaben sind sich regelmäßig wiederholende Aufgaben. Such Dir den Zeitplan aus, der Dir am besten passt!
- Du hast keine To-Dos
- To-Dos sind Aufgaben, die nur einmal abgehakt werden. Füge Unteraufgaben zu den To-Dos hinzu, um deren Wert zu steigern.
+ Du hast keine To Do\'s
+ To Do\'s sind Aufgaben, die nur einmal abgehakt werden. Füge Unteraufgaben zu den To Do\'s hinzu, um deren Wert zu steigern.
Du hast keine Belohnungen
Setze Justins Einführung zurück
Lies unsere Community-Richtlinien durch, bevor du etwas postest
@@ -620,7 +620,7 @@ Die Quest-Schriftrolle wird an den Quest-Besitzer zurückgegeben.
Hast Du Deine Aufgaben heute abgehakt\?
Es gibt eine Menge freizuschalten und zu entdecken während Du in den Leveln aufsteigst, also bleib an Deinen Aufgaben dran und hab Spaß!
Willst Du etwas Neues ausprobieren\? Tritt einer Herausforderung bei um Deine Aufgabenliste zu erweitern und ein paar Edelsteine zu gewinnen!
- Erweitere Deine To-Dos mit Kontrolllisten um Deine Belohnungen zu vervielfachen!
+ Erweitere Deine To Do\'s mit Kontrolllisten um Deine Belohnungen zu vervielfachen!
Du kannst verändern wie oft sich eine Tagesaufgabe wiederholt. Sogar gelegentliche Aufgaben können festgelegt werden.
Du kannst auch Erinnerungen für Deine Tagesaufgaben einplanen.
Deine Aufgaben hin und wieder zu überdenken hilft Dir auf dem richtigen Weg zu bleiben.
@@ -643,7 +643,7 @@ Die Quest-Schriftrolle wird an den Quest-Besitzer zurückgegeben.
Wie sollen wir Dich nennen?
Anzeigenamen müssen zwischen 1 und 30 Zeichen lang sein
Tritt Habitica bei (Hak mich ab!)
- Du kannst dieses To-Do erledigen, bearbeiten oder entfernen.
+ Du kannst dieses To Do erledigen, bearbeiten oder entfernen.
Belohne Dich
Schau Fernsehen, spiel ein Spiel, iss etwas Süßes, es liegt ganz an Dir!
Internetseite besuchen
@@ -1020,4 +1020,4 @@ Die Quest-Schriftrolle wird an den Quest-Besitzer zurückgegeben.
\n**Wahrnehmung (PER)** erhöht die Goldmenge, die Du erhältst und die Rate Deiner Fundgegenstände. Schurken und Magier erhalten PER von Ihrer Klassenausrüstung.
\n
\nAb Level 10 verdienst Du einen Eigenschaften-Punkt für jedes Level, das Du aufsteigst. Diesen Punkt kannst Du in eine beliebige Eigenschaft stecken. Ausserdem kannst Du Ausrüstung anziehen, die unterschiedliche Eigenschaften-Boni gewährt.
-
\ No newline at end of file
+
diff --git a/Habitica/res/values-en-rGB/strings.tutorial.xml b/Habitica/res/values-en-rGB/strings.tutorial.xml
index 6a115f2c0..9745f2f18 100644
--- a/Habitica/res/values-en-rGB/strings.tutorial.xml
+++ b/Habitica/res/values-en-rGB/strings.tutorial.xml
@@ -7,8 +7,8 @@
Give it a shot! You can explore the other task types through the bottom navigation.
Make Dailies for time sensitive tasks that need to be done on a regular schedule.
Be careful — if you miss one, your avatar will take damage overnight. Checking them off consistently brings great rewards!
- Use To-dos to keep track of tasks you need to do just once.
- If your To-do has to be done by a certain time, set a due date. Looks like you can check one off — go ahead!
+ Use To Do\'s to keep track of tasks you need to do just once.
+ If your To Do has to be done by a certain time, set a due date. Looks like you can check one off — go ahead!
Buy gear for your avatar with the gold you earn!
You can also make real-world Custom Rewards based on what motivates you.
That\'s all for now. If you need a reminder, check the FAQ section.
diff --git a/Habitica/res/values-en-rGB/strings.xml b/Habitica/res/values-en-rGB/strings.xml
index d2b8a55ae..94da98eba 100644
--- a/Habitica/res/values-en-rGB/strings.xml
+++ b/Habitica/res/values-en-rGB/strings.xml
@@ -1016,4 +1016,4 @@
Selected
Off
On
-
\ No newline at end of file
+
diff --git a/Habitica/res/values-in/strings.tutorial.xml b/Habitica/res/values-in/strings.tutorial.xml
index 9624db2fa..da5d2aa6e 100644
--- a/Habitica/res/values-in/strings.tutorial.xml
+++ b/Habitica/res/values-in/strings.tutorial.xml
@@ -7,8 +7,8 @@
Coba deh! Kamu bisa mengeksplor macam-macam tugas lain melalui tombol navigasi di bawah.
Buat Keseharian untuk tugas-tugas yang terikat waktu dan butuh untuk diselesaikan secara berkala.
Hati-hati — kalau ada yang terlewat, avatar kamu akan terkena damage besoknya. Menyelesaikan Keseharian secara konsisten memberi banyak keuntungan!
- Gunakan To-do untuk mencatat tugas-tugas yang hanya perlu kamu lakukan sekali.
- Kalau To-do-mu harus diselesaikan pada waktu tertentu, tetapkan tenggat waktunya. Sepertinya ada satu yang bisa kamu centang sekarang — sana!
+ Gunakan To Do untuk mencatat tugas-tugas yang hanya perlu kamu lakukan sekali.
+ Kalau To Do-mu harus diselesaikan pada waktu tertentu, tetapkan tenggat waktunya. Sepertinya ada satu yang bisa kamu centang sekarang — sana!
Beli perlengkapan untuk avatarmu menggunakan koin emas yang kamu dapatkan!
Kamu juga bisa membuat Hadiah yang benar-benar ada di dunia nyata tergantung apa yang memotivasimu.
Itu dulu untuk sekarang. Kalau kamu butuh pengingat, lihat bagian FAQ.
diff --git a/Habitica/res/values-in/strings.xml b/Habitica/res/values-in/strings.xml
index 692ebe328..046bcc720 100644
--- a/Habitica/res/values-in/strings.xml
+++ b/Habitica/res/values-in/strings.xml
@@ -102,7 +102,7 @@
Anggota
Kebiasaan
Keseharian
- To-Do
+ To Do
Hadiah
Ya
Tidak
@@ -296,7 +296,7 @@
Anda telah menyelesaikan semua keseharianmu. Selamat!
Habitica Melakukan Kebiasaan
Keseharian Habitica
- Daftar To-Do Habitica
+ Daftar To Do Habitica
Google play services tidak dapat ditemukan.
Bayar
Membeli gem dapat mendukung pengembang dan menjaga Habitica tetap berjalan
@@ -329,7 +329,7 @@
Berlangganan
Alexander sang Saudagar akan menjual permata kepadamu masing-masing seharga 20 koin emas!\n\nPengiriman bulanannya dibatasi di 25 Permata per bulan, tapi bisa meningkat berdasarkan lama berlangganan-mu. \n\nBatasnya meningkat sebanyak 5 Permata setiap tiga bulan berturut-turut kamu berlangganan, hingga batas maksimal 50 Permata per bulan!
Setiap bulan kamu akan menerima sebuah item kosmetik unik untuk avatarmu!\n\nPlus, setiap tiga bulan berturut-turut kamu berlangganan, sang Penjelajah Waktu Misterius akan memberimu akses kepada item kosmetik bersejarah (atau futuristik!).
- Buat To-Do yang telah diselesaikan dan riwayat tugas tersedia lebih lama.
+ Buat To Do yang telah diselesaikan dan riwayat tugas tersedia lebih lama.
Dua kali lipat batas drop akan mengizinkanmu mendapat lebih banyak item untuk tugas yang terselesaikan setiap hari, membantumu melengkapi istalmu lebih cepat!
+%d Jam Pasir Mistis
Metode Pembayaran
@@ -362,7 +362,7 @@
Keseharian
Kebiasaan
Hadiah
- To-Do
+ To Do
Resmi
Tantangan
Kamu tidak mengambil bagian dalam Tantangan apapun saat ini!
@@ -407,8 +407,8 @@
Kebiasaan adalah tugas yang tidak memerlukan jadwal ketat. Kamu bisa menyelesaikannya beberapa kali sehari, atau tidak sama sekali.
Kamu tidak punya Keseharian apapun
Keseharian adalah tugas yang berulang setiap waktu tertentu secara teratur. Pilih jadwal yang bekerja untukmu!
- Kamu tidak punya To-Do apapun
- To-Do adalah tugas yang hanya perlu diselesaikan sekali. Tambahkan ceklis kepada To-Do-mu untuk meningkatkan harganya.
+ Kamu tidak punya To Do apapun
+ To Do adalah tugas yang hanya perlu diselesaikan sekali. Tambahkan ceklis kepada To Do-mu untuk meningkatkan harganya.
Kamu tidak punya Hadiah apapun
Ulang Panduan Justin
Tolong baca Pedoman Komunitas kami sebelum posting
diff --git a/Habitica/res/values-it/strings.tutorial.xml b/Habitica/res/values-it/strings.tutorial.xml
index 1c4d1cf78..98ae43513 100644
--- a/Habitica/res/values-it/strings.tutorial.xml
+++ b/Habitica/res/values-it/strings.tutorial.xml
@@ -16,4 +16,4 @@
Qui è dove tu e i tuoi amici potete motivarvi a vicenda e combattere i mostri con le vostre attività!
Benvenuto nella Taverna, una chat pubblica per tutte le età! Qui puoi chiacchierare e fare delle domande. Divertiti!
Premi sul pulsante grigio per assegnare più punti statistica in una volta, oppure usa le frecce per aggiungerne uno alla volta.
-
\ No newline at end of file
+
diff --git a/Habitica/res/values-it/strings.xml b/Habitica/res/values-it/strings.xml
index 10b641ce4..4caacec66 100644
--- a/Habitica/res/values-it/strings.xml
+++ b/Habitica/res/values-it/strings.xml
@@ -1012,4 +1012,4 @@
Nessuna Ricompensa
Non ci sono Cose da Fare visibili con i filtri specificati.
Nessuna Cosa da fare
-
\ No newline at end of file
+
diff --git a/Habitica/res/values-ja/strings.tutorial.xml b/Habitica/res/values-ja/strings.tutorial.xml
index a23fcb486..d64c512c4 100644
--- a/Habitica/res/values-ja/strings.tutorial.xml
+++ b/Habitica/res/values-ja/strings.tutorial.xml
@@ -16,4 +16,4 @@
ここは、あなたと仲間たちが目標達成のために互いに責任感を持ち続けられる場です。タスクをこなしてモンスターと戦うことができます!
キャンプ場へようこそ! ここは年齢なんか気にしないオープンなチャットルームです。ここでタスクの進め方について話したり、質問したりできます。楽しんでね!
一度にステータスを割り当てるには灰色のボタンを、1つずつ割り振るには矢印をタップして下さい。
-
\ No newline at end of file
+
diff --git a/Habitica/res/values-ja/strings.xml b/Habitica/res/values-ja/strings.xml
index 7efa5f3d5..fe2ce9735 100644
--- a/Habitica/res/values-ja/strings.xml
+++ b/Habitica/res/values-ja/strings.xml
@@ -106,7 +106,7 @@
メンバー
習慣
日課
- To-Do
+ To Do
ごほうび
はい
いいえ
@@ -305,12 +305,12 @@
タスクを追加
習慣を追加
日課を追加
- To-Do を追加
+ To Do を追加
ごほうびを追加
日課をすべて完了した。お見事!
Habitica の習慣
Habitica の日課
- Habitica のTo-Do リスト
+ Habitica のTo Do リスト
Google Play サービスが見つかりません。
購入
ジェムを購入することで、開発者を支援し、Habitica の運営を維持する手助けができます
@@ -380,7 +380,7 @@
日課
習慣
ごほうび
- To-Do
+ To Do
Habitica公式
チャレンジ
今参加しているチャレンジはありません!
@@ -620,7 +620,7 @@
今日のタスクはチェックしましたか?
あなたがレベルアップするとアンロックされたり発見したりできるものがたくさんあります。だからタスクへ取り組み続けて、楽しんでいきましょう!
何か新しいことに挑戦したいですか? あなたのタスクリストを発展させてくれるチャレンジに参加して、いくつかのジェムを獲得しましょう!
- 報酬を増やすためにTo-Doにチェックリストを加えてみましょう!
+ 報酬を増やすためにTo Doにチェックリストを加えてみましょう!
それぞれの日課をどのくらいの頻度で繰り返すかを変更することができます。たまにしなかないタスクでさえスケジュールすることができます。
日課に対して特定のリマインダーを設定することもできます。
ときどきタスクを見直して再評価することは、正しい道筋を進み続けるために役立ちます。
@@ -655,7 +655,7 @@
あなたを何と呼べばいいですか?
表示名は1~30文字以内にしてください
Habiticaに参加する(チェックして!)
- To-Doは完了したり、編集したり、削除することができます。
+ To Doは完了したり、編集したり、削除することができます。
自分自身へごほうびを与える
TVを見ましょう、ゲームで遊びましょう、おやつを食べましょう、あなた次第です!
力:
@@ -1016,4 +1016,4 @@
神秘の砂時計は極めてレアなゲーム内通貨で、Habiticaの寄付会員を3ヶ月以上継続することで受け取れます。タイムトラベラーの店で神秘の砂時計を使い、過去の装備セット・ペット・乗騎・動く背景・特別なクエストなどが購入できます。
\n
\nあなたは神秘の砂時計を年に4つ受け取れます。入手のタイミングは寄付会員の更新スケジュールに基づいています。砂時計を入手できる期間の寄付を支払った、次の月の最初の日に発送されます。詳しくは[寄付]のページをご覧ください。
-
\ No newline at end of file
+
diff --git a/Habitica/res/values-nl/strings.tutorial.xml b/Habitica/res/values-nl/strings.tutorial.xml
index 00166cd28..a85bd493b 100644
--- a/Habitica/res/values-nl/strings.tutorial.xml
+++ b/Habitica/res/values-nl/strings.tutorial.xml
@@ -7,8 +7,8 @@
Probeer het maar! Je kan de andere soorten taken doornemen met de onderstaande navigatie.
Maak dagelijkse taken voor tijdgevoelige taken die volgens een vast schema gedaan moeten worden.
Wees voorzichtig - als je er een mist, verliest je avatar gezondheid de volgende dag. Vink ze regelmatig af voor grootse beloningen!
- Gebruik to-do\'s om taken bij te houden die je slechts één keer moet doen.
- Als je to-do tegen een bepaalde dag afgewerkt moet zijn, voer dan een einddatum in. Je kan er een afvinken - doe maar!
+ Gebruik To Do\'s om taken bij te houden die je slechts één keer moet doen.
+ Als je To Do tegen een bepaalde dag afgewerkt moet zijn, voer dan een einddatum in. Je kan er een afvinken - doe maar!
Koop een uitrusting voor je avatar met het goud dat je verdient!
Je kan ook zelf beloningen maken voor in de echte wereld, gebaseerd op wat je motiveert.
Dat was het voorlopig. Als je iets vergeet, kijk dan in de FAQ sectie.
diff --git a/Habitica/res/values-nl/strings.xml b/Habitica/res/values-nl/strings.xml
index a258ec7fb..520dbd436 100644
--- a/Habitica/res/values-nl/strings.xml
+++ b/Habitica/res/values-nl/strings.xml
@@ -937,4 +937,4 @@
Niveau %1$d %2$s
Niet geselecteerd
Geselecteerd
-
\ No newline at end of file
+
diff --git a/Habitica/res/values-ro/strings.xml b/Habitica/res/values-ro/strings.xml
index e04a2426a..cca10f6b7 100755
--- a/Habitica/res/values-ro/strings.xml
+++ b/Habitica/res/values-ro/strings.xml
@@ -718,4 +718,4 @@
Ian Ghidul Expedițiilor
Vrăjitoarea Sezonului
Atac furios:
-
\ No newline at end of file
+
diff --git a/Habitica/res/values-sv/strings.xml b/Habitica/res/values-sv/strings.xml
index 09f334f22..ace35c582 100755
--- a/Habitica/res/values-sv/strings.xml
+++ b/Habitica/res/values-sv/strings.xml
@@ -701,4 +701,4 @@
Låst
Du deltar inte
Rea
-
\ No newline at end of file
+
diff --git a/Habitica/res/values-vi/strings.sidebar.xml b/Habitica/res/values-vi/strings.sidebar.xml
index ce2abad6e..2d06407b8 100644
--- a/Habitica/res/values-vi/strings.sidebar.xml
+++ b/Habitica/res/values-vi/strings.sidebar.xml
@@ -18,4 +18,4 @@
Chỉ số
Cửa hàng
Về chúng tôi
-
\ No newline at end of file
+
diff --git a/Habitica/res/values-vi/strings.xml b/Habitica/res/values-vi/strings.xml
index a7195d25d..fecececaf 100755
--- a/Habitica/res/values-vi/strings.xml
+++ b/Habitica/res/values-vi/strings.xml
@@ -1083,4 +1083,4 @@
Đôi khi ứng dụng không tự động cập nhập nội dung. Thử kéo xuống để tải lại hoặc buộc đóng ứng dụng và mở lại.
Đồng bộ hóa Thủ công hay Khởi động lại
Để xóa bộ nhớ tạm của bạn, mở ứng dụng Cài đặt trên điện thoại. Tới Lưu trữ > Ứng dụng > Habitica, sau đó chạm Xóa bộ nhớ tạm.
-
\ No newline at end of file
+
diff --git a/Habitica/res/values-zh/strings.xml b/Habitica/res/values-zh/strings.xml
index 43221fbee..bd0e5b219 100644
--- a/Habitica/res/values-zh/strings.xml
+++ b/Habitica/res/values-zh/strings.xml
@@ -624,7 +624,7 @@
分配点数的功能在10级解锁
你干的越多,奖励就会越多。溪流终将汇聚成大海。
想试试新东西?加入一个挑战来扩充你的人物列表并赢得一些宝石吧!
- 把待办事项都加到你的To-Do列表来赢取奖励!
+ 把待办事项都加到你的To Do列表来赢取奖励!
你可以修改你的每日任务的重复频率。多罕见的任务都能被安排得明明白白。
你也可以安排特定的日程提醒。
深呼吸!保持专注!你能办到!
diff --git a/Habitica/res/values/attrs.xml b/Habitica/res/values/attrs.xml
index c892eeec8..9626196e1 100644
--- a/Habitica/res/values/attrs.xml
+++ b/Habitica/res/values/attrs.xml
@@ -9,6 +9,8 @@
+
+
diff --git a/Habitica/res/values/colors.xml b/Habitica/res/values/colors.xml
index 295b8cd59..f7db6cabc 100644
--- a/Habitica/res/values/colors.xml
+++ b/Habitica/res/values/colors.xml
@@ -23,7 +23,7 @@
#F74E52
#F23035
#BF262B
- #6C0406
+ #6c0406
#ffc8a7
#FF944C
@@ -148,6 +148,12 @@
#000
#efeff4
#fafaff
+ #6ECDB2
#794b00
+ #033f5e
+ #005158
+ #005737
+ #794b00
+ #7f3300
diff --git a/Habitica/res/values/dimens.xml b/Habitica/res/values/dimens.xml
index 3b56e5900..c4ac4e1b8 100644
--- a/Habitica/res/values/dimens.xml
+++ b/Habitica/res/values/dimens.xml
@@ -13,14 +13,20 @@
16dp
6dp
- 15dp
+ 31dp
+ 5dp
+ 46dp
+ 4dp
+ 140dp
+
+ 8dp
5dp
2dp
5dp
140dp
147dp
- 108dp
- 113dp
+ 94dp
+ 98dp
219dp
@@ -50,14 +56,17 @@
16sp
14sp
2dp
- 120dp
- 28dp
- 81dp
- 99dp
+ 84dp
+ 120dp
+ 20dp
+ 68dp
+ 65dp
+ 81dp
+ 99dp
124dp
- 10dp
+ 4dp
16dp
- 7dp
+ 9dp
13dp
4dp
diff --git a/Habitica/res/values/ids.xml b/Habitica/res/values/ids.xml
deleted file mode 100644
index a6b3daec9..000000000
--- a/Habitica/res/values/ids.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/Habitica/res/values/strings.tutorial.xml b/Habitica/res/values/strings.tutorial.xml
index 42566a9d1..f1741fee0 100644
--- a/Habitica/res/values/strings.tutorial.xml
+++ b/Habitica/res/values/strings.tutorial.xml
@@ -7,8 +7,8 @@
Give it a shot! You can explore the other task types through the bottom navigation.
Make Dailies for time sensitive tasks that need to be done on a regular schedule.
Be careful — if you miss one, your avatar will take damage overnight. Checking them off consistently brings great rewards!
- Use To-dos to keep track of tasks you need to do just once.
- If your To-do has to be done by a certain time, set a due date. Looks like you can check one off — go ahead!
+ Use To Do\'s to keep track of tasks you need to do just once.
+ If your To Do has to be done by a certain time, set a due date. Looks like you can check one off — go ahead!
Buy gear for your avatar with the gold you earn!
You can also make real-world Custom Rewards based on what motivates you.
That\'s all for now. If you need a reminder, check the FAQ section.
diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml
index a716c1d69..eca3d05e5 100644
--- a/Habitica/res/values/strings.xml
+++ b/Habitica/res/values/strings.xml
@@ -138,7 +138,7 @@
Members
Habits
Dailies
- To-Do\'s
+ To Do\'s
Rewards
Yes
No
@@ -363,12 +363,12 @@
Add Task
Add Habit
Add Daily
- Add To-Do
+ Add To Do
Add Reward
You completed all your dailies. Well done!
Habitica Do Habit
Habitica Dailies
- Habitica To-Do List
+ Habitica To Do List
Google play services could not be found.
Purchase
Buying gems supports the developers and helps keep Habitica running
@@ -406,7 +406,7 @@
You’ll be able to buy Gems from the Market for 20 gold each!
Earn Mystic Hourglasses to purchase items in the Time Traveler’s Shop!
Subscribe now to get an exclusive set now and receive new items every month!
- Subscribe now to get this %s and receive new items every month!
+ Subscribe now to get this %s and receive new items every month!
Receive the Royal Purple Jackalope pet when you become a new subscriber.
Discover even more items in Habitica with a 2x bonus daily drop cap.
25 Gem cap
@@ -420,7 +420,7 @@
Due
Cancel Subscription
No longer want to subscribe? You can find the option to unsubscribe in the “My Apps” section of the Google Play Store. Any months of subscription credit will be applied after your subscription has ended.
- No longer want to subscribe? Due to constraints on mobile payments, you\'ll need to cancel via our website. To do this, tap the button below, log in to your account, tap the User icon in the top right, then go to Subscription. Any months of subscription credit will be applied after your subscription has ended. We\'ll miss you!
+ No longer want to subscribe? Due to constraints on mobile payments, you\'ll need to cancel via our website. To do this, tap the button below, log in to your account, tap the User icon in the top right, then go to Subscription. Any months of subscription credit will be applied after your subscription has ended. We\'ll miss you!
Visit Habitica Website
Current Bonuses
Months subscribed
@@ -437,16 +437,16 @@
Subscription Status
Leave Challenge
- Are you sure you want to leave the Challenge “%s”?
- Remove tasks
- Do you want to remove the tasks?
+ You can choose to keep this Challenge\'s tasks on your personal task board or delete them when you leave
+ Leave & Keep Tasks
+ Leave & Delete Tasks
Remove
Keep
My Challenges
Daily
Habit
Reward
- To-Do
+ To Do
Official
Challenge
You’re not part of any Challenges right now!
@@ -455,6 +455,7 @@
Messages
Notifications
Special
+ Special items
Because you subscribe to Habitica, you can purchase a number of Gems each month using Gold.
Gem
Mystery Item
@@ -496,15 +497,15 @@
Habits are tasks that don\'t have a rigid schedule. You can check them off many times a day, or not at all.
You don\'t have any Dailies
Dailies are tasks that repeat on a regular basis. Choose the schedule that works for you!
- You don\'t have any To-Do\'s
- To-Do\'s are tasks that only need to be completed once. Add checklists to your To-Do\'s to increase their value.
+ You don\'t have any To Do\'s
+ To Do\'s are tasks that only need to be completed once. Add checklists to your To Do\'s to increase their value.
You don\'t have any Rewards
No Habits
There aren\'t any Habits visible with your current filters.
No Dailies
There aren\'t any Dailies visible with your current filters.
- No To-Do\'s
- There aren\'t any To-Do\'s visible with your current filters.
+ No To Do\'s
+ There aren\'t any To Do\'s visible with your current filters.
No Rewards
Reset Tutorials
Review our Community Guidelines before posting
@@ -698,7 +699,7 @@
Did you check off your tasks today?
There\'s lots to unlock and discover as you level up, so keep up with your tasks and have fun!
Want to try something new? Join a Challenge to expand your task list and win some Gems!
- Add checklists to your To-Do\'s to multiply your rewards!
+ Add checklists to your To Do\'s to multiply your rewards!
You can change how often each Daily repeats. Even infrequent tasks can be scheduled.
You can schedule specific reminders for Dailies too.
Occasionally re-evaluating your tasks can help keep you on the right path.
@@ -719,8 +720,8 @@
Two-Handed
It’s time to set your username!
Login names are now unique usernames that will be visible beside your display name and used for invitations, chat @mentions, and messaging.
- If you’d like to learn more about this change, visit our wiki.
- Usernames should conform to our Terms of Service and Community Guidelines . If you didn’t previously set a login name, your username was auto-generated.
+ If you’d like to learn more about this change, visit our wiki.
+ Usernames should conform to our Terms of Service and Community Guidelines . If you didn’t previously set a login name, your username was auto-generated.
Are you sure you want to confirm your current username?
Confirming your username will make it public for invitations, @mentions and messaging. You can change your username from settings at any time.
Cancel
@@ -733,7 +734,7 @@
What should we call you?
Display names must be between 1 and 30 characters
Join Habitica (Check me off!)
- You can either complete this To-Do, edit it, or remove it.
+ You can either complete this To Do, edit it, or remove it.
Reward yourself
Watch TV, play a game, eat a treat, it’s up to you!
STR:
@@ -816,7 +817,7 @@
You joined the guild
Cost
Add a task to Habitica
- Either a Habit, a Daily or a To-Do
+ Either a Habit, a Daily or a To Do
Tap here to edit this into a bad habit you\'d like to quit
Or delete it from the edit screen
Guild Summary
@@ -909,7 +910,7 @@
Copied User ID to clipboard
Copy Username
Your password has to be at least %d characters long
- %s invited you to %s
+ %s invited you to join their group %s
Someone
Authentication Methods
local
@@ -985,7 +986,7 @@
Gold is the **main form of currency** within Habitica and allows you to buy certain gear, quests, items, or even custom rewards you make for yourself.\n\n**Earn Gold** through completing tasks or quests, or through some Class skills. Higher **Perception stats** raise the amount of Gold you earn.\n\nIf you subscribe to Habitica, you can even use Gold to purchase a number of Gems determined by the length of time you’ve been subscribed.
Mana points are unlocked with the class system at level 10 and allow you to **use Skills** once you begin learning them at level 11.\n\nSome **MP is restored** at day reset every day, but you can regain more by completing tasks or using a Mage class skill.
Stat Allocation
- All Habitica characters have four stats that affect the gameplay aspects of Habitica.\n\n**Strength (STR)** affects critical hits and raises damage done to a Quest Boss. Warriors and Rogues gain STR from their class equipment.\n\n**Constitution (CON)** raises your HP and makes you take less damage. Healers and Warriors gain CON from their class equipment.\n\n**Intelligence (INT)** raises the amount of EXP you earn and gives you more Mana. Mages and Healers gain INT from their class equipment.\n\n**Perception (PER)** increases the gold you earn and the rate of finding dropped items.Rogues and Mages gain PER from their class equipment.\n\nAfter level 10, you earn 1 Stat Point every level you gain that you can put into any stat you’d like. You can also equip gear that has different combinations of stat boosts.
+ All Habitica characters have four stats that affect the gameplay aspects of Habitica.\n\n**Strength (STR)** affects critical hits and raises damage done to a Quest Boss. Warriors and Rogues gain STR from their class equipment.\n\n**Constitution (CON)** raises your HP and makes you take less damage. Healers and Warriors gain CON from their class equipment.\n\n**Intelligence (INT)** raises the amount of EXP you earn and gives you more Mana. Mages and Healers gain INT from their class equipment.\n\n**Perception (PER)** increases the gold you earn and the rate of finding dropped items. Rogues and Mages gain PER from their class equipment.\n\nAfter level 10, you earn 1 Stat Point every level you gain that you can put into any stat you’d like. You can also equip gear that has different combinations of stat boosts.
Standard
Premium Currency
@@ -1014,5 +1015,73 @@
Read More
You are unable to buy that amount.
Still have a question?
- Delete Checklist entry
+ %1$d / %2$d
+ Task list display
+ Onboarding Tasks
+ 100 Gold !]]>
+ Delete…
+ Getting Started
+ 5 Achievements and 100 Gold once you’re done!]]>
+ Your Progress
+ Let\'s get started
+ %d%% Complete
+ Create a Task
+ Complete a Task
+ Hatch a new pet
+ Feed a pet
+ Purchase Equipment
+ Add a task for something you would like to accomplish this week
+ Check off any of your tasks to earn rewards
+ Complete tasks to get a Hatching Potion and Egg then hatch your Pet!
+ Complete tasks to get food! You can feed it to your pet from Pets & Mounts
+ Equipment is a way to customize your avatar and improve your stats
+ Created your first task
+ Completed a task
+ Hatched a Pet
+ Fed a Pet
+ Purchased Equipment
+ Keep it up! If you need help planning tasks, try thinking about what you’d like to do during a specific time of day
+ A task can be a Habit, Daily, or To Do. Continue completing them to receive all sorts of rewards!
+ There are so many Pets to collect, you’re bound to have a favorite. If you feed them, they may just grow…
+ Every Pet has a specific food they enjoy! Experiment to find out which will grow your Pet the fastest
+ Equipment can be practical or just fashionable. Raise your stats to get all sorts of benefits to your avatar
+ Completing tasks gives you a chance to find eggs, hatching potions, and pet food.
+ Head to your Items and try combining your new Egg and Hatching Potion!
+ Go to Items
+ You found new items!
+ 5 Achievements and 100 Gold for your efforts.]]>
+ If you want even more, check out Achievements and start collecting!
+ You completed your OnboardingTasks!
+ Excess Items
+ You only need %d %s to hatch all possible pets. Are you sure you want to purchase %d?
+ Purchase %d
+ You already have everything you need for all %s pets. Are you sure you want to purchase %d %ss?
+ Equip
+ View Onboarding Tasks
+ You still need a %s Egg to hatch this pet
+ You still need a %s Potion to hatch this pet
+ You need a %s and %s Potion to hatch this pet
+ You still need a %s Egg to hatch this pet again
+ You still need a %s Potion to hatch this pet again
+ You need a %s and %s Potion to hatch this pet again
+ Combine your %s Egg and %s Potion to hatch this pet!
+ Hatch Pet
+ Unhatched Pet
+ Hatch Pet again
+ Magic Potions
+ Magic Potion
+ Use Saddle
+ Hatch your Pet
+ Hatch
+ Delete Challenge Task?
+ This is one of %d tasks that are part of the “%s” Challenge. You must leave the Challenge to delete this task.
+ Leave & Delete Task
+ Leave & Delete %d Tasks
+ Broken Challenge
+ This is one of %d tasks that are part of a Challenge that no longer exists. What would you like to do with these left over tasks?
+ Keep %d Tasks
+ Delete %d Tasks
+ %s Challenge Task
+ %s Pets
+ %s Mounts
diff --git a/Habitica/res/values/styles.habitica.xml b/Habitica/res/values/styles.habitica.xml
index eeded5105..fb10e8692 100644
--- a/Habitica/res/values/styles.habitica.xml
+++ b/Habitica/res/values/styles.habitica.xml
@@ -90,7 +90,7 @@
diff --git a/Habitica/res/values/styles.xml b/Habitica/res/values/styles.xml
index 60c0d0409..f6e75cc06 100644
--- a/Habitica/res/values/styles.xml
+++ b/Habitica/res/values/styles.xml
@@ -48,12 +48,14 @@
- @color/brand_50
- @style/SearchViewStyle
+ - @color/white
+ - @color/brand_50
@@ -365,6 +387,7 @@
- @dimen/pill_horizontal_padding
- @dimen/pill_vertical_padding
- @dimen/pill_vertical_padding
+ - @color/gray_200
+
+
diff --git a/Habitica/res/values/values.xml b/Habitica/res/values/values.xml
index b6510e863..3eec99b73 100644
--- a/Habitica/res/values/values.xml
+++ b/Habitica/res/values/values.xml
@@ -33,6 +33,18 @@
- @string/avatar_size_broad
+
+ - Standard
+ - Compact
+ - Minimal
+
+
+
+ - standard
+ - compact
+ - minimal
+
+
- English
- Български
diff --git a/Habitica/res/xml/network_security_config.xml b/Habitica/res/xml/network_security_config.xml
index f808081d4..4c9e97462 100644
--- a/Habitica/res/xml/network_security_config.xml
+++ b/Habitica/res/xml/network_security_config.xml
@@ -6,6 +6,6 @@
- 192.168.178.52
+ 10.0.0.107
\ No newline at end of file
diff --git a/Habitica/res/xml/preferences_fragment.xml b/Habitica/res/xml/preferences_fragment.xml
index f88a0744a..28db3e825 100644
--- a/Habitica/res/xml/preferences_fragment.xml
+++ b/Habitica/res/xml/preferences_fragment.xml
@@ -131,11 +131,12 @@
-
+
raiseShops
false
+
+ enableTaskDisplayMode
+ true
+
+
+ reorderMenu
+ false
+
+
+ feedbackURL
+
+
+
+ enableAdventureGuide
+ false
+
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt
index fc98d19c1..463ccd96e 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt
@@ -76,6 +76,9 @@ interface ApiService {
@POST("user/buy-quest/{key}")
fun purchaseQuest(@Path("key") key: String): Flowable>
+ @POST("user/buy-special-spell/{key}")
+ fun purchaseSpecialSpell(@Path("key") key: String): Flowable>
+
@POST("user/sell/{type}/{key}")
fun sellItem(@Path("type") itemType: String, @Path("key") itemKey: String): Flowable>
@@ -380,4 +383,7 @@ interface ApiService {
@POST("members/transfer-gems")
fun transferGems(@Body data: Map): Flowable>
+
+ @POST("tasks/unlink-all/{challengeID}")
+ fun unlinkAllTasks(@Path("challengeID") challengeID: String?, @Query("keep") keepOption: String): Flowable>
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/components/UserComponent.java b/Habitica/src/main/java/com/habitrpg/android/habitica/components/UserComponent.java
index 92774ceee..91b6cab22 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/components/UserComponent.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/components/UserComponent.java
@@ -13,6 +13,7 @@ import com.habitrpg.android.habitica.receivers.NotificationPublisher;
import com.habitrpg.android.habitica.receivers.TaskAlarmBootReceiver;
import com.habitrpg.android.habitica.receivers.TaskReceiver;
import com.habitrpg.android.habitica.ui.activities.AboutActivity;
+import com.habitrpg.android.habitica.ui.activities.AdventureGuideActivity;
import com.habitrpg.android.habitica.ui.activities.ChallengeFormActivity;
import com.habitrpg.android.habitica.ui.activities.ClassSelectionActivity;
import com.habitrpg.android.habitica.ui.activities.FixCharacterValuesActivity;
@@ -340,4 +341,6 @@ public interface UserComponent {
void inject(@NotNull AvatarEquipmentFragment avatarEquipmentFragment);
void inject(@NotNull FAQDetailFragment faqDetailFragment);
+
+ void inject(@NotNull AdventureGuideActivity adventureGuideFragment);
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt
index f0437e8b6..795a9e4ce 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt
@@ -61,6 +61,7 @@ interface ApiClient {
fun purchaseMysterySet(itemKey: String): Flowable
fun purchaseQuest(key: String): Flowable
+ fun purchaseSpecialSpell(key: String): Flowable
fun validateSubscription(request: SubscriptionValidationRequest): Flowable
fun validateNoRenewSubscription(request: PurchaseValidationRequest): Flowable
fun cancelSubscription(): Flowable
@@ -257,4 +258,5 @@ interface ApiClient {
fun findUsernames(username: String, context: String?, id: String?): Flowable>
fun transferGems(giftedID: String, amount: Int): Flowable
+ fun unlinkAllTasks(challengeID: String?, keepOption: String): Flowable
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/InventoryRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/InventoryRepository.kt
index e420d130a..b75880b2a 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/InventoryRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/InventoryRepository.kt
@@ -33,16 +33,16 @@ interface InventoryRepository : BaseRepository {
fun getOwnedEquipment(type: String): Flowable>
fun getEquipmentType(type: String, set: String): Flowable>
- fun getOwnedItems(itemType: String): Flowable>
- fun getOwnedItems(): Flowable>
+ fun getOwnedItems(itemType: String, includeZero: Boolean = false): Flowable>
+ fun getOwnedItems(includeZero: Boolean = false): Flowable>
fun getEquipment(key: String): Flowable
fun openMysteryItem(user: User?): Flowable
fun saveEquipment(equipment: Equipment)
- fun getMounts(type: String, group: String, color: String?): Flowable>
- fun getPets(type: String, group: String, color: String?): Flowable>
+ fun getMounts(type: String?, group: String?, color: String?): Flowable>
+ fun getPets(type: String?, group: String?, color: String?): Flowable>
fun updateOwnedEquipment(user: User)
@@ -70,10 +70,13 @@ interface InventoryRepository : BaseRepository {
fun purchaseHourglassItem(purchaseType: String, key: String): Flowable
fun purchaseQuest(key: String): Flowable
+ fun purchaseSpecialSpell(key: String): Flowable
fun purchaseItem(purchaseType: String, key: String, purchaseQuantity: Int): Flowable
fun togglePinnedItem(item: ShopItem): Flowable>
- fun getItems(itemClass: Class, keys: Array, user: User?): Flowable>
+ fun getItems(itemClass: Class, keys: Array): Flowable>
+ fun getItems(itemClass: Class): Flowable>
fun getLatestMysteryItem(): Flowable
+ fun getItem(type: String, key: String): Flowable-
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/TaskRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/TaskRepository.kt
index 01026d5ac..05447f8ef 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/TaskRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/TaskRepository.kt
@@ -59,4 +59,6 @@ interface TaskRepository : BaseRepository {
fun retrieveDailiesFromDate(date: Date): Flowable
fun retrieveCompletedTodos(userId: String): Flowable
fun syncErroredTasks(): Single>
+ fun unlinkAllTasks(challengeID: String?, keepOption: String): Flowable
+ fun getTasksForChallenge(challengeID: String?): Flowable>
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt
index d127013e5..2e0f91d05 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt
@@ -2,6 +2,7 @@ package com.habitrpg.android.habitica.data.implementation
import android.content.Context
import com.amplitude.api.Amplitude
+import com.facebook.FacebookSdk.getCacheDir
import com.google.gson.JsonSyntaxException
import com.habitrpg.android.habitica.BuildConfig
import com.habitrpg.android.habitica.HabiticaBaseApplication
@@ -37,6 +38,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.BiFunction
import io.reactivex.functions.Consumer
import io.reactivex.schedulers.Schedulers
+import okhttp3.Cache
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.logging.HttpLoggingInterceptor
@@ -102,7 +104,12 @@ class ApiClientImpl//private OnHabitsAPIResult mResultListener;
val timeZone = calendar.timeZone
val timezoneOffset = -TimeUnit.MINUTES.convert(timeZone.getOffset(calendar.timeInMillis).toLong(), TimeUnit.MILLISECONDS)
+ val cacheSize: Long = 10 * 1024 * 1024 // 10 MB
+
+ val cache = Cache(getCacheDir(), cacheSize)
+
val client = OkHttpClient.Builder()
+ .cache(cache)
.addInterceptor(logging)
.addNetworkInterceptor { chain ->
val original = chain.request()
@@ -325,6 +332,10 @@ class ApiClientImpl//private OnHabitsAPIResult mResultListener;
return apiService.buyItem(itemKey, mapOf(Pair("quantity", purchaseQuantity))).compose(configureApiCallObserver())
}
+ override fun unlinkAllTasks(challengeID: String?, keepOption: String): Flowable {
+ return apiService.unlinkAllTasks(challengeID, keepOption).compose(configureApiCallObserver())
+ }
+
override fun purchaseItem(type: String, itemKey: String, purchaseQuantity: Int): Flowable {
return apiService.purchaseItem(type, itemKey, mapOf(Pair("quantity", purchaseQuantity))).compose(configureApiCallObserver())
}
@@ -363,12 +374,21 @@ class ApiClientImpl//private OnHabitsAPIResult mResultListener;
return apiService.purchaseQuest(key).compose(configureApiCallObserver())
}
+ override fun purchaseSpecialSpell(key: String): Flowable {
+ return apiService.purchaseSpecialSpell(key).compose(configureApiCallObserver())
+ }
+
override fun sellItem(itemType: String, itemKey: String): Flowable {
return apiService.sellItem(itemType, itemKey).compose(configureApiCallObserver())
}
override fun feedPet(petKey: String, foodKey: String): Flowable {
- return apiService.feedPet(petKey, foodKey).compose(configureApiCallObserver())
+ return apiService.feedPet(petKey, foodKey)
+ .map {
+ it.data.message = it.message
+ it
+ }
+ .compose(configureApiCallObserver())
}
override fun hatchPet(eggKey: String, hatchingPotionKey: String): Flowable {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ContentRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ContentRepositoryImpl.kt
index 9060fbf18..88949f64a 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ContentRepositoryImpl.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ContentRepositoryImpl.kt
@@ -22,7 +22,7 @@ abstract class ContentRepositoryImpl(localRepository
override fun retrieveContent(context: Context?, forced: Boolean): Flowable {
val now = Date().time
- return if (forced || now - this.lastContentSync > 3) {
+ return if (forced || now - this.lastContentSync > 300000) {
lastContentSync = now
apiClient.content.doOnNext {
context?.let {context ->
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt
index 2042fb7a2..74569cb9b 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt
@@ -50,16 +50,20 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie
return localRepository.getEquipmentType(type, set)
}
- override fun getOwnedItems(itemType: String): Flowable> {
- return localRepository.getOwnedItems(itemType, userID)
+ override fun getOwnedItems(itemType: String, includeZero: Boolean): Flowable> {
+ return localRepository.getOwnedItems(itemType, userID, includeZero)
}
- override fun getOwnedItems(): Flowable> {
- return localRepository.getOwnedItems(userID)
+ override fun getOwnedItems(includeZero: Boolean): Flowable> {
+ return localRepository.getOwnedItems(userID, includeZero)
}
- override fun getItems(itemClass: Class, keys: Array, user: User?): Flowable> {
- return localRepository.getItems(itemClass, keys, user)
+ override fun getItems(itemClass: Class, keys: Array): Flowable> {
+ return localRepository.getItems(itemClass, keys)
+ }
+
+ override fun getItems(itemClass: Class): Flowable> {
+ return localRepository.getItems(itemClass)
}
override fun getEquipment(key: String): Flowable {
@@ -82,7 +86,7 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie
return localRepository.getMounts()
}
- override fun getMounts(type: String, group: String, color: String?): Flowable> {
+ override fun getMounts(type: String?, group: String?, color: String?): Flowable> {
return localRepository.getMounts(type, group, color)
}
@@ -94,7 +98,7 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie
return localRepository.getPets()
}
- override fun getPets(type: String, group: String, color: String?): Flowable> {
+ override fun getPets(type: String?, group: String?, color: String?): Flowable> {
return localRepository.getPets(type, group, color)
}
@@ -111,7 +115,7 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie
}
override fun sellItem(user: User?, type: String, key: String): Flowable {
- return localRepository.getOwnedItem(userID, type, key)
+ return localRepository.getOwnedItem(userID, type, key, true)
.flatMap { item -> sellItem(user, item) }
}
@@ -124,6 +128,10 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie
return localRepository.getLatestMysteryItem()
}
+ override fun getItem(type: String, key: String): Flowable- {
+ return localRepository.getItem(type, key)
+ }
+
private fun sellItem(user: User?, item: Item, ownedItem: OwnedItem): Flowable
{
if (user != null && appConfigManager.enableLocalChanges()) {
localRepository.executeTransaction {
@@ -204,7 +212,7 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie
override fun feedPet(pet: Pet, food: Food): Flowable {
return apiClient.feedPet(pet.key, food.key)
.doOnNext { feedResponse ->
- localRepository.feedPet(food.key, pet.key, feedResponse.value, userID)
+ localRepository.feedPet(food.key, pet.key, feedResponse.value ?: 0, userID)
}
}
@@ -280,6 +288,10 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie
return apiClient.purchaseQuest(key)
}
+ override fun purchaseSpecialSpell(key: String): Flowable {
+ return apiClient.purchaseSpecialSpell(key)
+ }
+
override fun purchaseItem(purchaseType: String, key: String, purchaseQuantity: Int): Flowable {
return apiClient.purchaseItem(purchaseType, key, purchaseQuantity)
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt
index c8cc35aa8..b5663043c 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt
@@ -307,4 +307,12 @@ class TaskRepositoryImpl(localRepository: TaskLocalRepository, apiClient: ApiCli
}
}.toList()
}
+
+ override fun unlinkAllTasks(challengeID: String?, keepOption: String): Flowable {
+ return apiClient.unlinkAllTasks(challengeID, keepOption)
+ }
+
+ override fun getTasksForChallenge(challengeID: String?): Flowable> {
+ return localRepository.getTasksForChallenge(challengeID, userID)
+ }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt
index 165e52073..c0c320941 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt
@@ -48,6 +48,7 @@ class UserRepositoryImpl(localRepository: UserLocalRepository, apiClient: ApiCli
@Suppress("ReturnCount")
override fun retrieveUser(withTasks: Boolean, forced: Boolean): Flowable {
+ // Only retrieve again after 3 minutes or it's forced.
if (forced || this.lastSync == null || Date().time - (this.lastSync?.time ?: 0) > 180000) {
lastSync = Date()
return apiClient.retrieveUser(withTasks)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/InventoryLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/InventoryLocalRepository.kt
index 08331e26e..861e20023 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/InventoryLocalRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/InventoryLocalRepository.kt
@@ -30,14 +30,15 @@ interface InventoryLocalRepository : ContentLocalRepository {
fun getOwnedEquipment(type: String): Flowable>
- fun getItems(itemClass: Class, keys: Array, user: User?): Flowable>
- fun getOwnedItems(itemType: String, userID: String): Flowable>
- fun getOwnedItems(userID: String): Flowable>
+ fun getItems(itemClass: Class, keys: Array): Flowable>
+ fun getItems(itemClass: Class): Flowable>
+ fun getOwnedItems(itemType: String, userID: String, includeZero: Boolean): Flowable>
+ fun getOwnedItems(userID: String, includeZero: Boolean): Flowable>
fun getEquipmentType(type: String, set: String): Flowable>
fun getEquipment(key: String): Flowable
- fun getMounts(type: String, group: String, color: String?): Flowable>
- fun getPets(type: String, group: String, color: String?): Flowable>
+ fun getMounts(type: String?, group: String?, color: String?): Flowable>
+ fun getPets(type: String?, group: String?, color: String?): Flowable>
fun updateOwnedEquipment(user: User)
@@ -45,7 +46,7 @@ interface InventoryLocalRepository : ContentLocalRepository {
fun changeOwnedCount(item: OwnedItem, amountToAdd: Int?)
fun getItem(type: String, key: String): Flowable-
- fun getOwnedItem(userID: String, type: String, key: String): Flowable
+ fun getOwnedItem(userID: String, type: String, key: String, includeZero: Boolean): Flowable
fun decrementMysteryItemCount(user: User?)
fun saveInAppRewards(onlineItems: List)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/TaskLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/TaskLocalRepository.kt
index eab649202..02a96642c 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/TaskLocalRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/TaskLocalRepository.kt
@@ -35,4 +35,5 @@ interface TaskLocalRepository : BaseLocalRepository {
fun saveCompletedTodos(userId: String, tasks: MutableCollection)
fun getErroredTasks(userID: String): Flowable>
fun getUser(userID: String): Flowable
+ fun getTasksForChallenge(challengeID: String?, userID: String?): Flowable>
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt
index 2d5aeb0d6..47efab428 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt
@@ -79,10 +79,12 @@ class RealmInventoryLocalRepository(realm: Realm, private val context: Context)
.filter { it.isLoaded }
}
- override fun getOwnedItems(itemType: String, userID: String): Flowable> {
- return realm.where(OwnedItem::class.java)
- .greaterThan("numberOwned", 0)
- .equalTo("itemType", itemType)
+ override fun getOwnedItems(itemType: String, userID: String, includeZero: Boolean): Flowable> {
+ var query = realm.where(OwnedItem::class.java)
+ if (!includeZero) {
+ query = query.greaterThan("numberOwned", 0)
+ }
+ return query.equalTo("itemType", itemType)
.equalTo("userID", userID)
.sort("key")
.findAll()
@@ -90,15 +92,22 @@ class RealmInventoryLocalRepository(realm: Realm, private val context: Context)
.filter { it.isLoaded }
}
- override fun getItems(itemClass: Class, keys: Array, user: User?): Flowable> {
+ override fun getItems(itemClass: Class, keys: Array): Flowable> {
return realm.where(itemClass).`in`("key", keys).findAll().asFlowable()
.filter { it.isLoaded }
}
- override fun getOwnedItems(userID: String): Flowable> {
- return realm.where(OwnedItem::class.java)
- .greaterThan("numberOwned", 0)
- .equalTo("userID", userID)
+ override fun getItems(itemClass: Class): Flowable> {
+ return realm.where(itemClass).findAll().asFlowable()
+ .filter { it.isLoaded }
+ }
+
+ override fun getOwnedItems(userID: String, includeZero: Boolean): Flowable> {
+ var query = realm.where(OwnedItem::class.java)
+ if (!includeZero) {
+ query = query.greaterThan("numberOwned", 0)
+ }
+ return query.equalTo("userID", userID)
.findAll()
.asFlowable()
.map {
@@ -128,11 +137,15 @@ class RealmInventoryLocalRepository(realm: Realm, private val context: Context)
.filter { it.isLoaded }
}
- override fun getMounts(type: String, group: String, color: String?): Flowable> {
+ override fun getMounts(type: String?, group: String?, color: String?): Flowable> {
var query = realm.where(Mount::class.java)
- .sort("color", Sort.ASCENDING)
- .equalTo("type", group)
- .equalTo("animal", type)
+ .sort("type", Sort.ASCENDING, if (color == null) "color" else "animal", Sort.ASCENDING)
+ if (type != null) {
+ query = query.equalTo("animal", type)
+ }
+ if (group != null) {
+ query = query.equalTo("type", group)
+ }
if (color != null) {
query = query.equalTo("color", color)
}
@@ -158,11 +171,15 @@ class RealmInventoryLocalRepository(realm: Realm, private val context: Context)
.filter { it.isLoaded }
}
- override fun getPets(type: String, group: String, color: String?): Flowable> {
+ override fun getPets(type: String?, group: String?, color: String?): Flowable> {
var query = realm.where(Pet::class.java)
- .sort("color", Sort.ASCENDING)
- .equalTo("type", group)
- .equalTo("animal", type)
+ .sort("type", Sort.ASCENDING, if (color == null) "color" else "animal", Sort.ASCENDING)
+ if (type != null) {
+ query = query.equalTo("animal", type)
+ }
+ if (group != null) {
+ query = query.equalTo("type", group)
+ }
if (color != null) {
query = query.equalTo("color", color)
}
@@ -185,7 +202,7 @@ class RealmInventoryLocalRepository(realm: Realm, private val context: Context)
}
override fun changeOwnedCount(type: String, key: String, userID: String, amountToAdd: Int) {
- getOwnedItem(userID, type, key).firstElement().subscribe( Consumer { changeOwnedCount(it, amountToAdd)}, RxErrorHandler.handleEmptyError())
+ getOwnedItem(userID, type, key, true).firstElement().subscribe( Consumer { changeOwnedCount(it, amountToAdd)}, RxErrorHandler.handleEmptyError())
}
override fun changeOwnedCount(item: OwnedItem, amountToAdd: Int?) {
@@ -194,13 +211,15 @@ class RealmInventoryLocalRepository(realm: Realm, private val context: Context)
}
}
- override fun getOwnedItem(userID: String, type: String, key: String): Flowable {
- return realm.where(OwnedItem::class.java)
+ override fun getOwnedItem(userID: String, type: String, key: String, includeZero: Boolean): Flowable {
+ var query = realm.where(OwnedItem::class.java)
.equalTo("itemType", type)
.equalTo("key", key)
.equalTo("userID", userID)
- .greaterThan("numberOwned", 0)
- .findFirstAsync()
+ if (!includeZero) {
+ query = query.greaterThan("numberOwned", 0)
+ }
+ return query.findFirstAsync()
.asFlowable()
.filter { realmObject -> realmObject.isLoaded }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt
index 4f6b33ed7..007c0d2bf 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt
@@ -225,7 +225,8 @@ class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm),
.findAll()
.asFlowable()
.filter { it.isLoaded }
- .retry(1) }
+ .retry(1)
+ }
override fun getUser(userID: String): Flowable {
return realm.where(User::class.java)
@@ -235,4 +236,14 @@ class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm),
.filter { realmObject -> realmObject.isLoaded && realmObject.isValid && !realmObject.isEmpty() }
.map { users -> users.first() }
}
+
+ override fun getTasksForChallenge(challengeID: String?, userID: String?): Flowable> {
+ return realm.where(Task::class.java)
+ .equalTo("challengeID", challengeID)
+ .equalTo("userId", userID)
+ .findAll()
+ .asFlowable()
+ .filter { it.isLoaded }
+ .retry(1)
+ }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/events/ShowAchievementDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/events/ShowAchievementDialog.kt
index 96edb9c26..150a89d25 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/events/ShowAchievementDialog.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/events/ShowAchievementDialog.kt
@@ -1,3 +1,3 @@
package com.habitrpg.android.habitica.events
-class ShowAchievementDialog(var type: String, val id: String)
+class ShowAchievementDialog(var type: String, val id: String, val isLastOnboardingAchievement: Boolean = false)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/events/ShowFirstDropDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/events/ShowFirstDropDialog.kt
new file mode 100644
index 000000000..7f2accb6e
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/events/ShowFirstDropDialog.kt
@@ -0,0 +1,3 @@
+package com.habitrpg.android.habitica.events
+
+class ShowFirstDropDialog(val egg: String, val hatchingPotion: String, val id: String)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/Animal-Extensions.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/Animal-Extensions.kt
index f8cb2c6bc..1f3b97fe7 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/Animal-Extensions.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/Animal-Extensions.kt
@@ -9,15 +9,14 @@ fun Animal.getTranslatedType(c: Context?): String {
return type
}
- var currType: String = when (type) {
- "drop" -> c?.getString(R.string.standard).toString()
- "quest" -> c?.getString(R.string.quest).toString()
- "wacky" -> c?.getString(R.string.wacky).toString()
- "special" -> c?.getString(R.string.special).toString()
+ return when (type) {
+ "drop" -> c.getString(R.string.standard)
+ "quest" -> c.getString(R.string.quest)
+ "wacky" -> c.getString(R.string.wacky)
+ "special" -> c.getString(R.string.special)
+ "premium" -> c.getString(R.string.magic_potion)
else -> {
type
}
}
-
- return currType
}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/AttributeSet-Extentions.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/AttributeSet-Extentions.kt
index d5ed2213c..43d18d231 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/AttributeSet-Extentions.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/AttributeSet-Extentions.kt
@@ -4,5 +4,5 @@ import android.content.Context
import android.content.res.TypedArray
import android.util.AttributeSet
-fun AttributeSet.styledAttributes(context: Context?, style: IntArray?): TypedArray? =
+fun AttributeSet.styledAttributes(context: Context?, style: IntArray): TypedArray? =
context?.theme?.obtainStyledAttributes(this, style, 0, 0)
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AppAccessLevel.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AppAccessLevel.kt
index 71d732b18..3a34e0c9f 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AppAccessLevel.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AppAccessLevel.kt
@@ -1,9 +1,8 @@
package com.habitrpg.android.habitica.helpers
enum class AppTestingLevel(identifier: String) {
+ STAFF("staff"),
ALPHA("alpha"),
BETA("beta"),
PRODUCTION("production")
-
-
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AppConfigManager.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AppConfigManager.kt
index b8d1d85af..6a18d7bb3 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AppConfigManager.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AppConfigManager.kt
@@ -1,5 +1,7 @@
package com.habitrpg.android.habitica.helpers
+import android.content.Context
+import androidx.preference.PreferenceManager
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
@@ -79,7 +81,28 @@ class AppConfigManager {
return remoteConfig.getLong("minimumPasswordLength")
}
- fun raiseShops(): Boolean {
- return remoteConfig.getBoolean("raiseShops")
+ fun enableTaskDisplayMode(): Boolean {
+ return remoteConfig.getBoolean("enableTaskDisplayMode")
+ }
+
+ fun feedbackURL(): String {
+ return remoteConfig.getString("feedbackURL")
+ }
+
+ fun taskDisplayMode(context: Context): String {
+ return if (remoteConfig.getBoolean("enableTaskDisplayMode")) {
+ val preferences = PreferenceManager.getDefaultSharedPreferences(context)
+ preferences.getString("task_display", "standard") ?: "standard"
+ } else {
+ "standard"
+ }
+ }
+
+ fun reorderMenu(): Boolean {
+ return remoteConfig.getBoolean("reorderMenu")
+ }
+
+ fun enableAdventureGuide(): Boolean {
+ return remoteConfig.getBoolean("enableAdventureGuide")
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/Keyhelper.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/Keyhelper.kt
index 52fec9c52..3de8006b0 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/Keyhelper.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/Keyhelper.kt
@@ -12,6 +12,7 @@ import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.UnsupportedEncodingException
+import java.lang.IllegalStateException
import java.math.BigInteger
import java.security.*
import java.util.*
@@ -188,6 +189,8 @@ constructor(ctx: Context, var sharedPreferences: SharedPreferences, var keyStore
null
} catch (e: GeneralSecurityException) {
null
+ } catch (e: IllegalStateException) {
+ null
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/NotificationsManager.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/NotificationsManager.kt
index 337e8ebad..fb06fd50f 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/NotificationsManager.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/NotificationsManager.kt
@@ -1,20 +1,31 @@
package com.habitrpg.android.habitica.helpers
import android.content.Context
+import androidx.core.os.bundleOf
+import com.google.firebase.analytics.FirebaseAnalytics
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.ApiClient
import com.habitrpg.android.habitica.events.ShowAchievementDialog
import com.habitrpg.android.habitica.events.ShowCheckinDialog
+import com.habitrpg.android.habitica.events.ShowFirstDropDialog
import com.habitrpg.android.habitica.events.ShowSnackbarEvent
import com.habitrpg.android.habitica.models.Notification
+import com.habitrpg.android.habitica.models.notifications.AchievementData
+import com.habitrpg.android.habitica.models.notifications.FirstDropData
import com.habitrpg.android.habitica.models.notifications.LoginIncentiveData
+import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
+import com.habitrpg.android.habitica.ui.views.dialogs.AchievementDialog
import io.reactivex.BackpressureStrategy
+import io.reactivex.Completable
import io.reactivex.Flowable
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.functions.Action
import io.reactivex.functions.Consumer
import io.reactivex.subjects.BehaviorSubject
import org.greenrobot.eventbus.EventBus
import java.util.*
+import java.util.concurrent.TimeUnit
class NotificationsManager (private val context: Context) {
private val seenNotifications: MutableMap
@@ -49,9 +60,9 @@ class NotificationsManager (private val context: Context) {
this.apiClient = apiClient
}
- fun handlePopupNotifications(notifications: List): Boolean? {
+ private fun handlePopupNotifications(notifications: List): Boolean? {
val now = Date()
- if (now.time - (lastNotificationHandling?.time ?: 0) < 500) {
+ if (now.time - (lastNotificationHandling?.time ?: 0) < 300) {
return true
}
lastNotificationHandling = now
@@ -68,6 +79,11 @@ class NotificationsManager (private val context: Context) {
Notification.Type.ACHIEVEMENT_GUILD_JOINED.type -> displayAchievementNotification(it)
Notification.Type.ACHIEVEMENT_CHALLENGE_JOINED.type -> displayAchievementNotification(it)
Notification.Type.ACHIEVEMENT_INVITED_FRIEND.type -> displayAchievementNotification(it)
+ Notification.Type.ACHIEVEMENT_GENERIC.type -> displayAchievementNotification(it, notifications.find { notif ->
+ notif.type == Notification.Type.ACHIEVEMENT_ONBOARDING_COMPLETE.type
+ } != null)
+ Notification.Type.ACHIEVEMENT_ONBOARDING_COMPLETE.type -> displayAchievementNotification(it)
+ Notification.Type.FIRST_DROP.type -> displayFirstDropNotification(it)
else -> false
}
@@ -80,7 +96,13 @@ class NotificationsManager (private val context: Context) {
return true
}
- fun displayLoginIncentiveNotification(notification: Notification): Boolean? {
+ private fun displayFirstDropNotification(notification: Notification): Boolean {
+ val data = (notification.data as? FirstDropData)
+ EventBus.getDefault().post(ShowFirstDropDialog(data?.egg ?: "", data?.hatchingPotion ?: "", notification.id))
+ return true
+ }
+
+ private fun displayLoginIncentiveNotification(notification: Notification): Boolean? {
val notificationData = notification.data as? LoginIncentiveData
val nextUnlockText = context.getString(R.string.nextPrizeUnlocks, notificationData?.nextRewardAt)
if (notificationData?.rewardKey != null) {
@@ -100,8 +122,27 @@ class NotificationsManager (private val context: Context) {
return true
}
- private fun displayAchievementNotification(notification: Notification): Boolean {
- EventBus.getDefault().post(ShowAchievementDialog(notification.type ?: "", notification.id))
+ private fun displayAchievementNotification(notification: Notification, isLastOnboardingAchievement: Boolean = false): Boolean {
+ val achievement = (notification.data as? AchievementData)?.achievement ?: notification.type ?: ""
+ val delay: Long = if (achievement == "createdTask" || achievement == Notification.Type.ACHIEVEMENT_ONBOARDING_COMPLETE.type) {
+ 1000
+ } else {
+ 200
+ }
+ val sub = Completable.complete()
+ .delay(delay, TimeUnit.MILLISECONDS)
+ .subscribe(Action {
+ EventBus.getDefault().post(ShowAchievementDialog(achievement, notification.id, isLastOnboardingAchievement))
+ }, RxErrorHandler.handleEmptyError())
+ logOnboardingEvents(achievement)
return true
}
+
+ private fun logOnboardingEvents(type: String) {
+ if (User.ONBOARDING_ACHIEVEMENT_KEYS.contains(type)) {
+ FirebaseAnalytics.getInstance(context).logEvent(type, null)
+ } else if (type == Notification.Type.ACHIEVEMENT_ONBOARDING_COMPLETE.type) {
+ FirebaseAnalytics.getInstance(context).logEvent(type, null)
+ }
+ }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseHandler.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseHandler.kt
index ada17a27b..35713840b 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseHandler.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseHandler.kt
@@ -5,7 +5,9 @@ import android.content.Intent
import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.proxy.CrashlyticsProxy
import org.solovyev.android.checkout.*
+import java.lang.NullPointerException
import java.util.*
+import javax.annotation.Nonnull
class PurchaseHandler(activity: Activity, val crashlyticsProxy: CrashlyticsProxy) {
private val billing = HabiticaBaseApplication.getInstance(activity.applicationContext)?.billing
@@ -73,20 +75,29 @@ class PurchaseHandler(activity: Activity, val crashlyticsProxy: CrashlyticsProxy
}
private fun getProduct(type: String, identifiers: List, onSuccess: ((Inventory.Product) -> Unit)) {
- inventory?.load(Inventory.Request.create()
- .loadAllPurchases().loadSkus(type, identifiers)) { products ->
+ loadInventory(type, identifiers, Inventory.Callback { products ->
val purchases = products.get(type)
- if (!purchases.supported) return@load
+ if (!purchases.supported) return@Callback
onSuccess(purchases)
- }
+ })
}
private fun getSKU(type: String, identifier: String, onSuccess: ((Sku) -> Unit)) {
- inventory?.load(Inventory.Request.create()
- .loadAllPurchases().loadSkus(type, listOf(identifier))) { products ->
+ loadInventory(type, listOf(identifier), Inventory.Callback { products ->
val purchases = products.get(type)
- if (!purchases.supported) return@load
+ if (!purchases.supported) return@Callback
purchases.skus.firstOrNull()?.let { onSuccess(it) }
+ })
+ }
+
+ private fun loadInventory(type: String, skus: List, callback: Inventory.Callback) {
+ val request = Inventory.Request.create().loadAllPurchases().loadSkus(type, skus)
+ if (request != null) {
+ try {
+ inventory?.load(request, callback)
+ } catch (e: NullPointerException) {
+ return
+ }
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/SignInWebViewDialogFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/SignInWebViewDialogFragment.kt
index b500877b9..eb596f164 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/SignInWebViewDialogFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/SignInWebViewDialogFragment.kt
@@ -48,21 +48,23 @@ class SignInWebViewDialogFragment : DialogFragment() {
): View? {
super.onCreateView(inflater, container, savedInstanceState)
- val webView = WebView(context).apply {
- settings.apply {
- javaScriptEnabled = true
- javaScriptCanOpenWindowsAutomatically = true
+ val webView = context?.let {
+ WebView(it).apply {
+ settings.apply {
+ javaScriptEnabled = true
+ javaScriptCanOpenWindowsAutomatically = true
+ }
}
}
- webView.webViewClient = SignInWebViewClient(authenticationAttempt, ::onCallback)
+ webView?.webViewClient = SignInWebViewClient(authenticationAttempt, ::onCallback)
if (savedInstanceState != null) {
savedInstanceState.getBundle(WEB_VIEW_KEY)?.run {
- webView.restoreState(this)
+ webView?.restoreState(this)
}
} else {
- webView.loadUrl(authenticationAttempt.authenticationUri)
+ webView?.loadUrl(authenticationAttempt.authenticationUri)
}
return webView
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.java
deleted file mode 100644
index 008888d84..000000000
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.habitrpg.android.habitica.helpers.notifications;
-
-import com.google.firebase.iid.FirebaseInstanceId;
-import com.google.firebase.messaging.FirebaseMessagingService;
-import com.google.firebase.messaging.RemoteMessage;
-import com.habitrpg.android.habitica.HabiticaApplication;
-
-import java.util.Objects;
-
-import javax.inject.Inject;
-
-/**
- * Created by keithholliday on 6/24/16.
- */
-public class HabiticaFirebaseMessagingService extends FirebaseMessagingService {
-
- @Inject
- public PushNotificationManager pushNotificationManager;
-
- @Override
- public void onMessageReceived(RemoteMessage remoteMessage) {
- Objects.requireNonNull(HabiticaApplication.Companion.getUserComponent()).inject(this);
- pushNotificationManager.displayNotification(remoteMessage);
- }
-
- @Override
- public void onNewToken(String s) {
- super.onNewToken(s);
- Objects.requireNonNull(HabiticaApplication.Companion.getUserComponent()).inject(this);
- String refreshedToken = FirebaseInstanceId.getInstance().getToken();
- if (refreshedToken != null) {
- pushNotificationManager.setRefreshedToken(refreshedToken);
- }
- }
-}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.kt
new file mode 100644
index 000000000..a895ba525
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.kt
@@ -0,0 +1,35 @@
+package com.habitrpg.android.habitica.helpers.notifications
+
+import com.google.firebase.iid.FirebaseInstanceId
+import com.google.firebase.messaging.FirebaseMessagingService
+import com.google.firebase.messaging.RemoteMessage
+import com.habitrpg.android.habitica.HabiticaApplication
+import com.habitrpg.android.habitica.HabiticaBaseApplication
+import com.habitrpg.android.habitica.components.UserComponent
+import java.util.*
+import javax.inject.Inject
+
+class HabiticaFirebaseMessagingService : FirebaseMessagingService() {
+
+ private val userComponent: UserComponent?
+ get() = HabiticaBaseApplication.userComponent
+
+ @Inject
+ internal lateinit var pushNotificationManager: PushNotificationManager
+
+ override fun onMessageReceived(remoteMessage: RemoteMessage) {
+ userComponent?.inject(this)
+ if (this::pushNotificationManager.isInitialized) {
+ pushNotificationManager.displayNotification(remoteMessage)
+ }
+ }
+
+ override fun onNewToken(s: String) {
+ super.onNewToken(s)
+ userComponent?.inject(this)
+ val refreshedToken = FirebaseInstanceId.getInstance().token
+ if (refreshedToken != null && this::pushNotificationManager.isInitialized) {
+ pushNotificationManager.refreshedToken = refreshedToken
+ }
+ }
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/Notification.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/Notification.kt
index b4c16138f..95c75dc70 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/Notification.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/Notification.kt
@@ -23,6 +23,10 @@ class Notification {
ACHIEVEMENT_GUILD_JOINED("GUILD_JOINED_ACHIEVEMENT"),
ACHIEVEMENT_CHALLENGE_JOINED("CHALLENGE_JOINED_ACHIEVEMENT"),
ACHIEVEMENT_INVITED_FRIEND("INVITED_FRIEND_ACHIEVEMENT"),
+ ACHIEVEMENT_GENERIC("ACHIEVEMENT"),
+ ACHIEVEMENT_ONBOARDING_COMPLETE("ONBOARDING_COMPLETE"),
+
+ FIRST_DROP("FIRST_DROPS"),
// Custom notification types (created by this app)
GUILD_INVITATION("GUILD_INVITATION"),
@@ -49,6 +53,8 @@ class Notification {
Type.GUILD_INVITATION.type -> GuildInvitationData::class.java
Type.PARTY_INVITATION.type -> PartyInvitationData::class.java
Type.QUEST_INVITATION.type -> QuestInvitationData::class.java
+ Type.FIRST_DROP.type -> FirstDropData::class.java
+ Type.ACHIEVEMENT_GENERIC.type -> AchievementData::class.java
else -> null
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Animal.java b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Animal.java
index a2f2bbd41..96c1b7f90 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Animal.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Animal.java
@@ -29,4 +29,8 @@ public interface Animal {
Integer getNumberOwned();
void setNumberOwned(Integer numberOwned);
+
+ Integer getTotalNumber();
+
+ void setTotalNumber(Integer totalNumber);
}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Mount.java b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Mount.java
index 19326be4f..1fd23de3c 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Mount.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Mount.java
@@ -12,7 +12,10 @@ public class Mount extends RealmObject implements Animal {
boolean premium;
@Ignore
- Integer numberOwned;
+ private Integer numberOwned;
+
+ @Ignore
+ private Integer totalNumber;
public String getKey() {
return key;
@@ -82,4 +85,15 @@ public class Mount extends RealmObject implements Animal {
public void setNumberOwned(Integer numberOwned) {
this.numberOwned = numberOwned;
}
+
+ public Integer getTotalNumber() {
+ if (totalNumber == null) {
+ return 0;
+ }
+ return totalNumber;
+ }
+
+ public void setTotalNumber(Integer totalNumber) {
+ this.totalNumber = totalNumber;
+ }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Pet.java b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Pet.java
index cf8c531e6..642c2c1fc 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Pet.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Pet.java
@@ -14,6 +14,9 @@ public class Pet extends RealmObject implements Animal{
@Ignore
private Integer numberOwned;
+ @Ignore
+ private Integer totalNumber;
+
public String getKey() {
return key;
}
@@ -83,4 +86,16 @@ public class Pet extends RealmObject implements Animal{
public void setNumberOwned(Integer numberOwned) {
this.numberOwned = numberOwned;
}
+
+ public Integer getTotalNumber() {
+ if (totalNumber == null) {
+ return 0;
+ }
+
+ return totalNumber;
+ }
+
+ public void setTotalNumber(Integer totalNumber) {
+ this.totalNumber = totalNumber;
+ }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/StableSection.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/StableSection.kt
new file mode 100644
index 000000000..a8b390a9d
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/StableSection.kt
@@ -0,0 +1,7 @@
+package com.habitrpg.android.habitica.models.inventory
+
+class StableSection(val key: Any?, val text: String) {
+
+ var ownedCount = 0
+ var totalCount = 0
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/notifications/AchievementData.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/notifications/AchievementData.kt
new file mode 100644
index 000000000..17f66cceb
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/notifications/AchievementData.kt
@@ -0,0 +1,7 @@
+package com.habitrpg.android.habitica.models.notifications
+
+open class AchievementData : NotificationData {
+
+ var achievement: String? = null
+
+}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/notifications/FirstDropData.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/notifications/FirstDropData.kt
new file mode 100644
index 000000000..a9104de60
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/notifications/FirstDropData.kt
@@ -0,0 +1,8 @@
+package com.habitrpg.android.habitica.models.notifications
+
+open class FirstDropData : NotificationData {
+
+ var egg: String? = null
+ var hatchingPotion: String? = null
+
+}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/responses/FeedResponse.java b/Habitica/src/main/java/com/habitrpg/android/habitica/models/responses/FeedResponse.java
deleted file mode 100644
index 3ba84ced1..000000000
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/responses/FeedResponse.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.habitrpg.android.habitica.models.responses;
-
-public class FeedResponse {
-
- public Integer value;
-
-}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/responses/FeedResponse.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/responses/FeedResponse.kt
new file mode 100644
index 000000000..4d9461077
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/responses/FeedResponse.kt
@@ -0,0 +1,6 @@
+package com.habitrpg.android.habitica.models.responses
+
+class FeedResponse {
+ var value: Int? = null
+ var message: String? = null
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/responses/HabitResponse.java b/Habitica/src/main/java/com/habitrpg/android/habitica/models/responses/HabitResponse.java
index 478ade392..993f9d91e 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/responses/HabitResponse.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/responses/HabitResponse.java
@@ -13,6 +13,7 @@ public class HabitResponse {
public T data;
public List notifications;
private Boolean success;
+ public String message;
/**
* @return The success
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/shops/ShopItem.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/shops/ShopItem.kt
index a619c621b..5e7fa29f4 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/shops/ShopItem.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/shops/ShopItem.kt
@@ -57,7 +57,7 @@ open class ShopItem : RealmObject() {
var level: Int? = null
val isTypeItem: Boolean
- get() = "eggs" == purchaseType || "hatchingPotions" == purchaseType || "food" == purchaseType || "armoire" == purchaseType || "potion" == purchaseType
+ get() = "eggs" == purchaseType || "hatchingPotions" == purchaseType || "food" == purchaseType || "armoire" == purchaseType || "potion" == purchaseType || "debuffPotion" == purchaseType
val isTypeQuest: Boolean
get() = "quests" == purchaseType
@@ -68,10 +68,13 @@ open class ShopItem : RealmObject() {
val isTypeAnimal: Boolean
get() = "pets" == purchaseType || "mounts" == purchaseType
+ val canPurchaseBulk: Boolean
+ get() = "eggs" == purchaseType || "hatchingPotions" == purchaseType || "food" == purchaseType
+
fun canAfford(user: User?, quantity: Int): Boolean = when(currency) {
"gold" -> (value * quantity) <= user?.stats?.gp ?: 0.0
- "gems" -> true
- "hourglasses" -> true
+ "gems" -> (value * quantity) <= user?.gemCount?.toDouble() ?: 0.0
+ "hourglasses" -> (value * quantity) <= user?.hourglassCount?.toDouble() ?: 0.0
else -> false
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/Task.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/Task.kt
index 166ba9578..60af9bf85 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/Task.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/Task.kt
@@ -33,6 +33,7 @@ open class Task : RealmObject, Parcelable {
@TaskTypes
var type: String = ""
var challengeID: String? = null
+ var challengeBroken: String? = null
var attribute: String? = Stats.STRENGTH
var value: Double = 0.0
var tags: RealmList? = RealmList()
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/User.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/User.kt
index a9f9123c6..2fd86f3dc 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/User.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/User.kt
@@ -53,6 +53,9 @@ open class User : RealmObject(), Avatar, VersionedObject {
for (test in abTests ?: emptyList()) {
test.userID = id
}
+ for (achievement in achievements ?: emptyList()) {
+ achievement.userId = id
+ }
}
@SerializedName("_v")
@@ -127,6 +130,7 @@ open class User : RealmObject(), Avatar, VersionedObject {
}
var tags = RealmList()
+ var achievements = RealmList()
var questAchievements = RealmList()
set(value) {
field = value
@@ -238,4 +242,30 @@ open class User : RealmObject(), Avatar, VersionedObject {
}
return isSubscribed
}
+
+ val onboardingAchievements: List
+ get() {
+ val onboarding = mutableMapOf()
+ for (key in ONBOARDING_ACHIEVEMENT_KEYS) {
+ val achievement = UserAchievement()
+ achievement.key = key
+ onboarding[key] = achievement
+ }
+ for (achievement in achievements) {
+ if (achievement.key in ONBOARDING_ACHIEVEMENT_KEYS) {
+ onboarding[achievement.key ?: ""] = achievement
+ }
+ }
+ return onboarding.values.toList()
+ }
+
+ val hasCompletedOnboarding: Boolean
+ get() {
+ val onboarding = onboardingAchievements
+ return onboarding.count { it.earned } == onboarding.size
+ }
+
+ companion object {
+ val ONBOARDING_ACHIEVEMENT_KEYS = listOf("createdTask", "completedTask", "hatchedPet", "fedPet", "purchasedEquipment")
+ }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/UserAchievement.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/UserAchievement.kt
new file mode 100644
index 000000000..36cd0f5a4
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/UserAchievement.kt
@@ -0,0 +1,24 @@
+package com.habitrpg.android.habitica.models.user
+
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+
+open class UserAchievement : RealmObject {
+
+ @PrimaryKey
+ var combinedKey: String? = null
+ var userId: String? = null
+ set(value) {
+ field = value
+ combinedKey = field + key
+ }
+ var key: String? = null
+ set(value) {
+ field = value
+ combinedKey = userId + field
+ }
+ var earned: Boolean = false
+
+ constructor()
+}
+
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AdventureGuideActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AdventureGuideActivity.kt
new file mode 100644
index 000000000..7553900bc
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AdventureGuideActivity.kt
@@ -0,0 +1,131 @@
+package com.habitrpg.android.habitica.ui.activities
+
+import android.graphics.Paint
+import android.graphics.PorterDuff
+import android.os.Build
+import android.os.Bundle
+import android.text.Html
+import android.view.MenuItem
+import android.view.View
+import android.widget.TextView
+import androidx.core.app.NavUtils
+import androidx.core.content.ContextCompat
+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.databinding.ActivityAdventureGuideBinding
+import com.habitrpg.android.habitica.databinding.AdventureGuideItemBinding
+import com.habitrpg.android.habitica.helpers.RxErrorHandler
+import com.habitrpg.android.habitica.models.user.User
+import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
+import io.reactivex.functions.Consumer
+import javax.inject.Inject
+
+
+class AdventureGuideActivity : BaseActivity() {
+ private lateinit var binding: ActivityAdventureGuideBinding
+
+
+ private lateinit var achievementTitles: Map
+ private lateinit var achievementDescriptions: Map
+
+ @Inject
+ internal lateinit var userRepository: UserRepository
+
+ override fun getLayoutResId(): Int {
+ return R.layout.activity_main
+ }
+
+ override fun getContentView(): View {
+ binding = ActivityAdventureGuideBinding.inflate(layoutInflater)
+ return binding.root
+ }
+
+ override fun injectActivity(component: UserComponent?) {
+ component?.inject(this)
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setupToolbar(binding.toolbar)
+
+ achievementTitles = mapOf(
+ Pair("createdTask", getString(R.string.create_task_title)),
+ Pair("completedTask", getString(R.string.complete_task_title)),
+ Pair("hatchedPet", getString(R.string.hatch_pet_title)),
+ Pair("fedPet", getString(R.string.feedPet_title)),
+ Pair("purchasedEquipment", getString(R.string.purchase_equipment_title))
+ )
+ achievementDescriptions = mapOf(
+ Pair("createdTask", getString(R.string.create_task_description)),
+ Pair("completedTask", getString(R.string.complete_task_description)),
+ Pair("hatchedPet", getString(R.string.hatch_pet_description)),
+ Pair("fedPet", getString(R.string.feedPet_description)),
+ Pair("purchasedEquipment", getString(R.string.purchase_equipment_description))
+ )
+
+ val descriptionText = getString(R.string.adventure_guide_description)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ binding.descriptionView.setText(Html.fromHtml(descriptionText, Html.FROM_HTML_MODE_LEGACY), TextView.BufferType.SPANNABLE)
+ } else {
+ binding.descriptionView.setText(Html.fromHtml(descriptionText), TextView.BufferType.SPANNABLE)
+ }
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ return when (item.itemId) {
+ android.R.id.home -> {
+ NavUtils.navigateUpFromSameTask(this)
+ true
+ }
+ else -> super.onOptionsItemSelected(item)
+ }
+ }
+
+ override fun onStart() {
+ super.onStart()
+ compositeSubscription.add(userRepository.getUser().subscribe(Consumer {
+ updateUser(it)
+ }, RxErrorHandler.handleEmptyError()))
+ }
+
+ private fun updateUser(user: User) {
+ val achievements = user.onboardingAchievements
+ val completed = achievements.count { it.earned }
+ binding.progressBar.max = achievements.size
+ binding.progressBar.progress = completed
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
+ binding.progressBar.progressBackgroundTintMode = PorterDuff.Mode.SRC_OVER
+ }
+
+ if (completed > 0) {
+ binding.progressTextview.text = getString(R.string.percent_completed, ((completed / achievements.size.toFloat()) * 100).toInt())
+ binding.progressTextview.setTextColor(ContextCompat.getColor(this, R.color.yellow_5))
+ }
+
+ binding.achievementContainer.removeAllViews()
+ for (achievement in achievements) {
+ val itemBinding = AdventureGuideItemBinding.inflate(layoutInflater, binding.achievementContainer, true)
+ itemBinding.titleView.text = achievementTitles[achievement.key]
+ itemBinding.descriptionView.text = achievementDescriptions[achievement.key]
+
+ val iconName = if (achievement.earned) {
+ "achievement-" + achievement.key + "2x"
+ } else {
+ "achievement-unearned2x"
+ }
+ DataBindingUtils.loadImage(itemBinding.iconView, iconName)
+ if (achievement.earned) {
+ itemBinding.titleView.paintFlags = itemBinding.titleView.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
+ itemBinding.titleView.setTextColor(ContextCompat.getColor(this, R.color.gray_200))
+ itemBinding.descriptionView.setTextColor(ContextCompat.getColor(this, R.color.gray_200))
+ } else {
+ itemBinding.titleView.setTextColor(ContextCompat.getColor(this, R.color.gray_50))
+ itemBinding.descriptionView.setTextColor(ContextCompat.getColor(this, R.color.gray_50))
+ itemBinding.iconView.alpha = 0.5f
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ChallengeFormActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ChallengeFormActivity.kt
index 0e6392b68..a3a82706e 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ChallengeFormActivity.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ChallengeFormActivity.kt
@@ -460,6 +460,11 @@ class ChallengeFormActivity : BaseActivity() {
Task.TYPE_TODO -> addTodo
else -> addReward
}
+ if(!isExistingTask){
+ // If the task is new we create a unique id for it
+ // Doing it we solve the issue #1278
+ task.id = UUID.randomUUID().toString()
+ }
challengeTasks.addTaskUnder(task, taskAbove)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ClassSelectionActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ClassSelectionActivity.kt
index 3544a903f..1ea1ea435 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ClassSelectionActivity.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ClassSelectionActivity.kt
@@ -119,7 +119,7 @@ class ClassSelectionActivity : BaseActivity(), Consumer {
return true
}
- override fun onOptionsItemSelected(item: MenuItem?): Boolean {
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item?.itemId) {
R.id.opt_out -> optOutSelected()
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FixCharacterValuesActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FixCharacterValuesActivity.kt
index 9c7ea04d8..dbd66a8b1 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FixCharacterValuesActivity.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FixCharacterValuesActivity.kt
@@ -1,10 +1,12 @@
package com.habitrpg.android.habitica.ui.activities
import android.app.ProgressDialog
+import android.graphics.PorterDuff
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
+import android.widget.EditText
import androidx.core.content.ContextCompat
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
@@ -15,9 +17,9 @@ import com.habitrpg.android.habitica.models.user.Stats
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.modules.AppModule
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
-import com.habitrpg.android.habitica.ui.views.settings.FixValuesEditText
import io.reactivex.functions.Action
import io.reactivex.functions.Consumer
+import kotlinx.android.synthetic.main.activity_fixcharacter.*
import javax.inject.Inject
import javax.inject.Named
@@ -50,6 +52,19 @@ class FixCharacterValuesActivity: BaseActivity() {
compositeSubscription.add(repository.getUser(userId).firstElement().subscribe(Consumer {
user = it
}, RxErrorHandler.handleEmptyError()))
+
+ setIconBackground(binding.healthIconBackgroundView, ContextCompat.getColor(this, R.color.red_500))
+ setIconBackground(binding.experienceIconBackgroundView, ContextCompat.getColor(this, R.color.yellow_500))
+ setIconBackground(binding.manaIconBackgroundView, ContextCompat.getColor(this, R.color.blue_500))
+ setIconBackground(binding.goldIconBackgroundView, ContextCompat.getColor(this, R.color.yellow_500))
+ setIconBackground(binding.streakIconBackgroundView, ContextCompat.getColor(this, R.color.gray_400))
+
+ binding.healthIconView.setImageBitmap(HabiticaIconsHelper.imageOfHeartLightBg())
+ binding.experienceIconView.setImageBitmap(HabiticaIconsHelper.imageOfExperience())
+ binding.manaIconView.setImageBitmap(HabiticaIconsHelper.imageOfMagic())
+ binding.goldIconView.setImageBitmap(HabiticaIconsHelper.imageOfGold())
+ binding.levelIconView.setImageBitmap(HabiticaIconsHelper.imageOfRogueLightBg())
+ binding.streakIconView.setImageResource(R.drawable.achievement_thermometer)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
@@ -90,35 +105,42 @@ class FixCharacterValuesActivity: BaseActivity() {
private fun updateFields(user: User) {
val stats = user.stats ?: return
- binding.healthEditText.text = stats.hp.toString()
- binding.experienceEditText.text = stats.exp.toString()
- binding.goldEditText.text = stats.gp.toString()
- binding.manaEditText.text = stats.mp.toString()
- binding.levelEditText.text = stats.lvl.toString()
- binding.streakEditText.text = user.streakCount.toString()
+ binding.healthEditText.setText(stats.hp.toString())
+ binding.experienceEditText.setText(stats.exp.toString())
+ binding.goldEditText.setText(stats.gp.toString())
+ binding.manaEditText.setText(stats.mp.toString())
+ binding.levelEditText.setText(stats.lvl.toString())
+ binding.streakEditText.setText(user.streakCount.toString())
when (stats.habitClass) {
Stats.WARRIOR -> {
- binding.levelEditText.iconBackgroundColor = ContextCompat.getColor(this, R.color.red_500)
- binding.levelEditText.setIconBitmap(HabiticaIconsHelper.imageOfWarriorLightBg())
+ setIconBackground(levelIconBackgroundView, ContextCompat.getColor(this, R.color.red_500))
+ binding.levelIconView.setImageBitmap(HabiticaIconsHelper.imageOfWarriorLightBg())
}
Stats.MAGE -> {
- binding.levelEditText.iconBackgroundColor = ContextCompat.getColor(this, R.color.blue_500)
- binding.levelEditText.setIconBitmap(HabiticaIconsHelper.imageOfMageLightBg())
+ setIconBackground(levelIconBackgroundView, ContextCompat.getColor(this, R.color.blue_500))
+ binding.levelIconView.setImageBitmap(HabiticaIconsHelper.imageOfMageLightBg())
}
Stats.HEALER -> {
- binding.levelEditText.iconBackgroundColor = ContextCompat.getColor(this, R.color.yellow_500)
- binding.levelEditText.setIconBitmap(HabiticaIconsHelper.imageOfHealerLightBg())
+ setIconBackground(levelIconBackgroundView, ContextCompat.getColor(this, R.color.yellow_500))
+ binding.levelIconView.setImageBitmap(HabiticaIconsHelper.imageOfHealerLightBg())
}
Stats.ROGUE -> {
- binding.levelEditText.iconBackgroundColor = ContextCompat.getColor(this, R.color.brand_500)
- binding.levelEditText.setIconBitmap(HabiticaIconsHelper.imageOfRogueLightBg())
+ setIconBackground(levelIconBackgroundView, ContextCompat.getColor(this, R.color.brand_500))
+ binding.levelIconView.setImageBitmap(HabiticaIconsHelper.imageOfRogueLightBg())
}
}
}
- private fun FixValuesEditText.getDoubleValue(): Double {
- val stringValue = this.text
+ private fun setIconBackground(view: View, color: Int) {
+ val backgroundDrawable = ContextCompat.getDrawable(this, R.drawable.layout_rounded_bg)
+ backgroundDrawable?.setColorFilter(color, PorterDuff.Mode.MULTIPLY)
+ backgroundDrawable?.alpha = 50
+ view.background = backgroundDrawable
+ }
+
+ private fun EditText.getDoubleValue(): Double {
+ val stringValue = this.text.toString()
return try {
stringValue.toDouble()
} catch (_: NumberFormatException) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GroupInviteActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GroupInviteActivity.kt
index a66da7f3f..7bd208b61 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GroupInviteActivity.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GroupInviteActivity.kt
@@ -24,7 +24,6 @@ import com.habitrpg.android.habitica.ui.helpers.dismissKeyboard
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.Companion.showSnackbar
import io.reactivex.functions.Consumer
-import kotlinx.android.synthetic.main.activity_prefs.*
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@@ -54,7 +53,7 @@ class GroupInviteActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setupToolbar(toolbar)
+ setupToolbar(findViewById(R.id.toolbar))
viewPager.currentItem = 0
supportActionBar?.title = null
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GuidelinesActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GuidelinesActivity.kt
index 07b4a905d..a94ee1ea2 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GuidelinesActivity.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GuidelinesActivity.kt
@@ -6,7 +6,6 @@ import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.ui.helpers.setMarkdown
-import kotlinx.android.synthetic.main.activity_prefs.*
import okhttp3.*
import java.io.BufferedReader
import java.io.IOException
@@ -20,7 +19,7 @@ class GuidelinesActivity: BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setupToolbar(toolbar)
+ setupToolbar(findViewById(R.id.toolbar))
val client = OkHttpClient()
val request = Request.Builder().url("https://s3.amazonaws.com/habitica-assets/mobileApp/endpoint/community-guidelines.md").build()
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/IntroActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/IntroActivity.kt
index 9445b55e8..ac0ad6d55 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/IntroActivity.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/IntroActivity.kt
@@ -3,46 +3,47 @@ package com.habitrpg.android.habitica.ui.activities
import android.content.Intent
import android.os.Bundle
import android.view.View
-import android.widget.Button
+import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
-import androidx.fragment.app.FragmentPagerAdapter
+import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.viewpager.widget.ViewPager
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.ContentRepository
+import com.habitrpg.android.habitica.databinding.ActivityIntroBinding
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.ui.fragments.setup.IntroFragment
-import com.habitrpg.android.habitica.ui.helpers.bindView
-import com.viewpagerindicator.IconPageIndicator
import com.viewpagerindicator.IconPagerAdapter
import io.reactivex.functions.Consumer
import javax.inject.Inject
+
class IntroActivity : BaseActivity(), View.OnClickListener, ViewPager.OnPageChangeListener {
+ private lateinit var binding: ActivityIntroBinding
@Inject
lateinit var contentRepository: ContentRepository
- private val pager: ViewPager by bindView(R.id.viewPager)
- private val indicator: IconPageIndicator by bindView(R.id.view_pager_indicator)
- private val skipButton: Button by bindView(R.id.skipButton)
- private val finishButton: Button by bindView(R.id.finishButton)
-
override fun getLayoutResId(): Int {
return R.layout.activity_intro
}
+ override fun getContentView(): View {
+ binding = ActivityIntroBinding.inflate(layoutInflater)
+ return binding.root
+ }
+
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupIntro()
- indicator.setViewPager(pager)
+ //binding.viewPagerIndicator.setViewPager(binding.viewPager)
- this.skipButton.setOnClickListener(this)
- this.finishButton.setOnClickListener(this)
+ binding.skipButton.setOnClickListener(this)
+ binding.finishButton.setOnClickListener(this)
compositeSubscription.add(contentRepository.retrieveContent(this).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
}
@@ -52,11 +53,9 @@ class IntroActivity : BaseActivity(), View.OnClickListener, ViewPager.OnPageChan
}
private fun setupIntro() {
- val fragmentManager = supportFragmentManager
+ binding.viewPager.adapter = PagerAdapter(supportFragmentManager)
- pager.adapter = PagerAdapter(fragmentManager)
-
- pager.addOnPageChangeListener(this)
+ binding.viewPager.addOnPageChangeListener(this)
}
override fun onClick(v: View) {
@@ -76,43 +75,19 @@ class IntroActivity : BaseActivity(), View.OnClickListener, ViewPager.OnPageChan
override fun onPageSelected(position: Int) {
if (position == 2) {
- this.finishButton.visibility = View.VISIBLE
+ binding.finishButton.visibility = View.VISIBLE
} else {
- this.finishButton.visibility = View.GONE
+ binding.finishButton.visibility = View.GONE
}
}
override fun onPageScrollStateChanged(state: Int) { /* no-on */ }
- private inner class PagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT), IconPagerAdapter {
+ private inner class PagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT), IconPagerAdapter {
override fun getItem(position: Int): Fragment {
val fragment = IntroFragment()
-
- when (position) {
- 0 -> {
- fragment.setImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_1, null))
- fragment.setSubtitle(getString(R.string.intro_1_subtitle))
- fragment.setTitleImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_1_title, null))
- fragment.setDescription(getString(R.string.intro_1_description, getString(R.string.habitica_user_count)))
- fragment.setBackgroundColor(ContextCompat.getColor(this@IntroActivity, R.color.brand_300))
- }
- 1 -> {
- fragment.setImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_2, null))
- fragment.setSubtitle(getString(R.string.intro_2_subtitle))
- fragment.setTitle(getString(R.string.intro_2_title))
- fragment.setDescription(getString(R.string.intro_2_description))
- fragment.setBackgroundColor(ContextCompat.getColor(this@IntroActivity, R.color.blue_10))
- }
- 2 -> {
- fragment.setImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_3, null))
- fragment.setSubtitle(getString(R.string.intro_3_subtitle))
- fragment.setTitle(getString(R.string.intro_3_title))
- fragment.setDescription(getString(R.string.intro_3_description))
- fragment.setBackgroundColor(ContextCompat.getColor(this@IntroActivity, R.color.red_100))
- }
- }
-
+ configureFragment(fragment, position)
return fragment
}
@@ -123,5 +98,39 @@ class IntroActivity : BaseActivity(), View.OnClickListener, ViewPager.OnPageChan
override fun getCount(): Int {
return 3
}
+
+ override fun instantiateItem(container: ViewGroup, position: Int): Any {
+ val item = super.instantiateItem(container, position)
+ if (item is IntroFragment) {
+ configureFragment(item, position)
+ }
+ return item
+ }
+ }
+
+ private fun configureFragment(fragment: IntroFragment, position: Int) {
+ when (position) {
+ 0 -> {
+ fragment.setImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_1, null))
+ fragment.setSubtitle(getString(R.string.intro_1_subtitle))
+ fragment.setTitleImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_1_title, null))
+ fragment.setDescription(getString(R.string.intro_1_description, getString(R.string.habitica_user_count)))
+ fragment.setBackgroundColor(ContextCompat.getColor(this@IntroActivity, R.color.brand_300))
+ }
+ 1 -> {
+ fragment.setImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_2, null))
+ fragment.setSubtitle(getString(R.string.intro_2_subtitle))
+ fragment.setTitle(getString(R.string.intro_2_title))
+ fragment.setDescription(getString(R.string.intro_2_description))
+ fragment.setBackgroundColor(ContextCompat.getColor(this@IntroActivity, R.color.blue_10))
+ }
+ 2 -> {
+ fragment.setImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_3, null))
+ fragment.setSubtitle(getString(R.string.intro_3_subtitle))
+ fragment.setTitle(getString(R.string.intro_3_title))
+ fragment.setDescription(getString(R.string.intro_3_description))
+ fragment.setBackgroundColor(ContextCompat.getColor(this@IntroActivity, R.color.red_100))
+ }
+ }
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.kt
index c407e8d7e..51e65a860 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.kt
@@ -31,6 +31,7 @@ import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.common.GooglePlayServicesUtil
import com.google.android.gms.common.Scopes
+import com.google.firebase.analytics.FirebaseAnalytics
import com.habitrpg.android.habitica.BuildConfig
import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.R
@@ -369,6 +370,10 @@ class LoginActivity : BaseActivity(), Consumer {
HabiticaBaseApplication.reloadUserComponent()
+ if (isRegistering) {
+ FirebaseAnalytics.getInstance(this).logEvent("user_registered", null)
+ }
+
compositeSubscription.add(userRepository.retrieveUser(true)
.subscribe(Consumer {
if (userAuthResponse.newUser) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt
index 396cf53d3..6afef229a 100755
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt
@@ -22,6 +22,8 @@ import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.core.content.FileProvider
import androidx.core.content.edit
import androidx.lifecycle.ViewModelProviders
+import androidx.navigation.NavController
+import androidx.navigation.NavDestination
import androidx.navigation.findNavController
import com.facebook.drawee.view.SimpleDraweeView
import com.google.firebase.analytics.FirebaseAnalytics
@@ -42,6 +44,7 @@ import com.habitrpg.android.habitica.helpers.notifications.PushNotificationManag
import com.habitrpg.android.habitica.interactors.CheckClassSelectionUseCase
import com.habitrpg.android.habitica.interactors.DisplayItemDropUseCase
import com.habitrpg.android.habitica.interactors.NotifyUserUseCase
+import com.habitrpg.android.habitica.models.Notification
import com.habitrpg.android.habitica.models.TutorialStep
import com.habitrpg.android.habitica.models.inventory.Egg
import com.habitrpg.android.habitica.models.inventory.HatchingPotion
@@ -56,11 +59,13 @@ import com.habitrpg.android.habitica.ui.TutorialView
import com.habitrpg.android.habitica.ui.fragments.NavigationDrawerFragment
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
import com.habitrpg.android.habitica.ui.viewmodels.NotificationsViewModel
+import com.habitrpg.android.habitica.ui.views.AdventureGuideDrawerArrowDrawable
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.SnackbarDisplayType
import com.habitrpg.android.habitica.ui.views.ValueBar
import com.habitrpg.android.habitica.ui.views.dialogs.AchievementDialog
+import com.habitrpg.android.habitica.ui.views.dialogs.FirstDropDialog
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
import com.habitrpg.android.habitica.ui.views.dialogs.QuestCompletedDialog
import com.habitrpg.android.habitica.ui.views.yesterdailies.YesterdailyDialog
@@ -78,9 +83,12 @@ import io.realm.Realm
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import java.util.*
+import java.util.concurrent.TimeUnit
import javax.inject.Inject
open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
+ private lateinit var drawerIcon: AdventureGuideDrawerArrowDrawable
+
@Inject
internal lateinit var apiClient: ApiClient
@Inject
@@ -127,7 +135,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
private var sideAvatarView: AvatarView? = null
private var activeTutorialView: TutorialView? = null
private var drawerFragment: NavigationDrawerFragment? = null
- private var drawerToggle: ActionBarDrawerToggle? = null
+ var drawerToggle: ActionBarDrawerToggle? = null
private var resumeFromActivity = false
private var userIsOnQuest = false
@@ -153,7 +161,11 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
@SuppressLint("ObsoleteSdkInt")
public override fun onCreate(savedInstanceState: Bundle?) {
- launchTrace = FirebasePerformance.getInstance().newTrace("MainActivityLaunch")
+ try {
+ launchTrace = FirebasePerformance.getInstance().newTrace("MainActivityLaunch")
+ } catch (_: IllegalStateException) {
+
+ }
launchTrace?.start()
super.onCreate(savedInstanceState)
@@ -163,6 +175,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
}
setupToolbar(binding.toolbar)
+ drawerIcon = AdventureGuideDrawerArrowDrawable(supportActionBar?.themedContext)
avatarInHeader = AvatarWithBarsViewModel(this, binding.avatarWithBars, userRepository)
sideAvatarView = AvatarView(this, showBackground = true, showMount = false, showPet = false)
@@ -191,10 +204,8 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
findViewById(R.id.drawer_layout), /* DrawerLayout object */
R.string.navigation_drawer_open, /* "open drawer" description */
R.string.navigation_drawer_close /* "close drawer" description */
- ) {
-
- }
-
+ ) {}
+ drawerToggle?.drawerArrowDrawable = drawerIcon
// Set the drawer toggle as the DrawerListener
drawerToggle?.let { drawerLayout.addDrawerListener(it) }
@@ -202,13 +213,8 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
supportActionBar?.setHomeButtonEnabled(true)
val navigationController = findNavController(R.id.nav_host_fragment)
- navigationController.addOnDestinationChangedListener { _, destination, _ ->
- if (destination.label.isNullOrEmpty() && user?.isValid == true) {
- binding.toolbarTitle.text = user?.profile?.name
- } else if (user?.isValid == true && user?.profile != null) {
- binding.toolbarTitle.text = destination.label
- }
- drawerFragment?.setSelection(destination.id, null, false)
+ navigationController.addOnDestinationChangedListener { _, destination, arguments ->
+ updateToolbarTitle(destination, arguments)
}
MainNavigationController.setup(navigationController)
@@ -222,6 +228,33 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
}
}
+ private fun updateToolbarTitle(destination: NavDestination, arguments: Bundle?) {
+ binding.toolbarTitle.text = if (destination.id == R.id.petDetailRecyclerFragment || destination.id == R.id.mountDetailRecyclerFragment) {
+ arguments?.getString("type")
+ } else if (destination.label.isNullOrEmpty() && user?.isValid == true) {
+ user?.profile?.name
+ } else if (user?.isValid == true && user?.profile != null) {
+ destination.label
+ } else {
+ ""
+ }
+ if (destination.id == R.id.petDetailRecyclerFragment || destination.id == R.id.mountDetailRecyclerFragment) {
+ compositeSubscription.add(inventoryRepository.getItem("egg", arguments?.getString("type") ?: "").firstElement().subscribe(Consumer {
+ binding.toolbarTitle.text = if (destination.id == R.id.petDetailRecyclerFragment) {
+ (it as? Egg)?.text
+ } else {
+ (it as? Egg)?.mountText
+ }
+ }, RxErrorHandler.handleEmptyError()))
+ }
+ drawerFragment?.setSelection(destination.id, null, false)
+ }
+
+ override fun onSupportNavigateUp(): Boolean {
+ onBackPressed()
+ return true
+ }
+
private fun setupNotifications() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelId = "default"
@@ -250,7 +283,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
drawerToggle?.syncState()
}
- override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
+ override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
Log.e("RESTORED:", savedInstanceState.toString())
}
@@ -353,11 +386,21 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
val quest = user?.party?.quest
if (quest?.completed?.isNotBlank() == true) {
- compositeSubscription.add(inventoryRepository.getQuestContent(user?.party?.quest?.completed ?: "").firstElement().subscribe {
+ compositeSubscription.add(inventoryRepository.getQuestContent(user?.party?.quest?.completed ?: "").firstElement().subscribe(Consumer {
QuestCompletedDialog.showWithQuest(this, it)
userRepository.updateUser(user, "party.quest.completed", "").subscribe(Consumer {}, RxErrorHandler.handleEmptyError())
- })
+ }, RxErrorHandler.handleEmptyError()))
+ }
+
+ if (user?.flags?.welcomed == false) {
+ compositeSubscription.add(userRepository.updateUser(user, "flags.welcomed", true).subscribe(Consumer {}, RxErrorHandler.handleEmptyError()))
+ }
+
+ if (appConfigManager.enableAdventureGuide()) {
+ drawerIcon.setEnabled(user?.hasCompletedOnboarding == false)
+ } else {
+ drawerIcon.setEnabled(false)
}
}
}
@@ -386,20 +429,20 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
if (resultCode == NOTIFICATION_CLICK && data?.hasExtra("notificationId") == true) {
notificationsViewModel?.click(
- data.getStringExtra("notificationId"),
+ data.getStringExtra("notificationId") ?: "",
MainNavigationController
)
}
if (resultCode == NOTIFICATION_ACCEPT && data?.hasExtra("notificationId") == true) {
notificationsViewModel?.accept(
- data.getStringExtra("notificationId")
+ data.getStringExtra("notificationId") ?: ""
)
}
if (resultCode == NOTIFICATION_REJECT && data?.hasExtra("notificationId") == true) {
notificationsViewModel?.reject(
- data.getStringExtra("notificationId")
+ data.getStringExtra("notificationId") ?: ""
)
}
PurchaseHandler.findForActivity(this)?.onResult(requestCode, resultCode, data)
@@ -421,7 +464,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
val pet = event.usingPet
compositeSubscription.add(this.inventoryRepository.feedPet(event.usingPet, event.usingFood)
.subscribe(Consumer { feedResponse ->
- HabiticaSnackbar.showSnackbar(snackbarContainer, getString(R.string.notification_pet_fed, pet.text), SnackbarDisplayType.NORMAL)
+ HabiticaSnackbar.showSnackbar(snackbarContainer, feedResponse.message, SnackbarDisplayType.NORMAL)
if (feedResponse.value == -1) {
val mountWrapper = View.inflate(this, R.layout.pet_imageview, null) as? FrameLayout
val mountImageView = mountWrapper?.findViewById(R.id.pet_imageview) as? SimpleDraweeView
@@ -504,9 +547,9 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
return super.onKeyUp(keyCode, event)
}
- protected fun retrieveUser() {
+ protected fun retrieveUser(forced: Boolean = false) {
if (hostConfig.hasAuthentication()) {
- compositeSubscription.add(this.userRepository.retrieveUser(true)
+ compositeSubscription.add(this.userRepository.retrieveUser(true, forced)
.doOnNext { user1 ->
FirebaseAnalytics.getInstance(this).setUserProperty("has_party", if (user1.party?.id?.isNotEmpty() == true) "true" else "false")
FirebaseAnalytics.getInstance(this).setUserProperty("is_subscribed", if (user1.isSubscribed) "true" else "false")
@@ -685,10 +728,19 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
@Subscribe
fun showAchievementDialog(event: ShowAchievementDialog) {
+ if (User.ONBOARDING_ACHIEVEMENT_KEYS.contains(event.type) || event.type == Notification.Type.ACHIEVEMENT_ONBOARDING_COMPLETE.type) {
+ if (!appConfigManager.enableAdventureGuide()) {
+ apiClient.readNotification(event.id)
+ .subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
+ return
+ }
+ }
compositeSubscription.add(Completable.complete()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(Action {
+ retrieveUser(true)
val dialog = AchievementDialog(this)
+ dialog.isLastOnboardingAchievement = event.isLastOnboardingAchievement
dialog.setType(event.type)
dialog.enqueue()
apiClient.readNotification(event.id)
@@ -696,6 +748,25 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
}, RxErrorHandler.handleEmptyError()))
}
+ @Subscribe
+ fun showFirstDropDialog(event: ShowFirstDropDialog) {
+ if (!appConfigManager.enableAdventureGuide()) {
+ apiClient.readNotification(event.id)
+ .subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
+ return
+ }
+ compositeSubscription.add(Completable.complete()
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(Action {
+ retrieveUser(true)
+ val dialog = FirstDropDialog(this)
+ dialog.configure(event.egg, event.hatchingPotion)
+ dialog.enqueue()
+ apiClient.readNotification(event.id)
+ .subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
+ }, RxErrorHandler.handleEmptyError()))
+ }
+
override fun onEvent(event: ShowConnectionProblemEvent) {
if (event.title != null) {
super.onEvent(event)
@@ -721,7 +792,10 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
val dialog = HabiticaAlertDialog(this)
dialog.setTitle(getString(R.string.hatched_pet_title, potionName, eggName))
dialog.setAdditionalContentView(petWrapper)
- dialog.addButton(R.string.onwards, true) { hatchingDialog, _ -> hatchingDialog.dismiss() }
+ dialog.addButton(R.string.equip, true) { _, _ ->
+ inventoryRepository.equip(user, "pet", egg.key + "-" + potion.key)
+ .subscribe(Consumer {}, RxErrorHandler.handleEmptyError())
+ }
dialog.addButton(R.string.share, false) { hatchingDialog, _ ->
val event1 = ShareEvent()
event1.sharedMessage = getString(R.string.share_hatched, potionName, eggName)
@@ -734,6 +808,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
EventBus.getDefault().post(event1)
hatchingDialog.dismiss()
}
+ dialog.setExtraCloseButtonVisibility(View.VISIBLE)
dialog.enqueue()
}.subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/NotificationsActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/NotificationsActivity.kt
index 01c822893..158cdd770 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/NotificationsActivity.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/NotificationsActivity.kt
@@ -13,6 +13,8 @@ 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.data.SocialRepository
+import com.habitrpg.android.habitica.databinding.ActivityIntroBinding
+import com.habitrpg.android.habitica.databinding.ActivityNotificationsBinding
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.Notification
import com.habitrpg.android.habitica.models.inventory.QuestContent
@@ -20,11 +22,11 @@ import com.habitrpg.android.habitica.models.notifications.*
import com.habitrpg.android.habitica.ui.activities.MainActivity.Companion.NOTIFICATION_CLICK
import com.habitrpg.android.habitica.ui.viewmodels.NotificationsViewModel
import io.reactivex.functions.Consumer
-import kotlinx.android.synthetic.main.activity_notifications.*
import javax.inject.Inject
class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener {
+ private lateinit var binding: ActivityNotificationsBinding
@Inject
lateinit var inventoryRepository: InventoryRepository
@Inject
@@ -36,12 +38,17 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
override fun getLayoutResId(): Int = R.layout.activity_notifications
+ override fun getContentView(): View {
+ binding = ActivityNotificationsBinding.inflate(layoutInflater)
+ return binding.root
+ }
+
private var notifications: List = emptyList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setupToolbar(toolbar)
+ setupToolbar(binding.toolbar)
inflater = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as? LayoutInflater
@@ -53,7 +60,7 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
viewModel.markNotificationsAsSeen(it)
}, RxErrorHandler.handleEmptyError()))
- notifications_refresh_layout?.setOnRefreshListener(this)
+ binding.notificationsRefreshLayout.setOnRefreshListener(this)
}
override fun injectActivity(component: UserComponent?) {
@@ -69,21 +76,21 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
}
override fun onRefresh() {
- notifications_refresh_layout.isRefreshing = true
+ binding.notificationsRefreshLayout.isRefreshing = true
compositeSubscription.add(viewModel.refreshNotifications().subscribe(Consumer {
- notifications_refresh_layout.isRefreshing = false
+ binding.notificationsRefreshLayout.isRefreshing = false
}, RxErrorHandler.handleEmptyError()))
}
private fun setNotifications(notifications: List) {
this.notifications = notifications
- if (notification_items == null) {
+ if (binding.notificationItems == null) {
return
}
- notification_items.removeAllViewsInLayout()
+ binding.notificationItems.removeAllViewsInLayout()
if (notifications.isEmpty()) {
displayNoNotificationsView()
@@ -93,15 +100,15 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
}
private fun displayNoNotificationsView() {
- notification_items.showDividers = LinearLayout.SHOW_DIVIDER_NONE
+ binding.notificationItems.showDividers = LinearLayout.SHOW_DIVIDER_NONE
- notification_items.addView(inflater?.inflate(R.layout.no_notifications, notification_items, false))
+ binding.notificationItems.addView(inflater?.inflate(R.layout.no_notifications, binding.notificationItems, false))
}
private fun displayNotificationsListView(notifications: List) {
- notification_items.showDividers = LinearLayout.SHOW_DIVIDER_MIDDLE or LinearLayout.SHOW_DIVIDER_END
+ binding.notificationItems.showDividers = LinearLayout.SHOW_DIVIDER_MIDDLE or LinearLayout.SHOW_DIVIDER_END
- notification_items.addView(
+ binding.notificationItems.addView(
createNotificationsHeaderView(notifications.count())
)
@@ -121,13 +128,13 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
}
if (item != null) {
- notification_items.addView(item)
+ binding.notificationItems.addView(item)
}
}
}
private fun createNotificationsHeaderView(notificationCount: Int): View? {
- val header = inflater?.inflate(R.layout.notifications_header, notification_items, false)
+ val header = inflater?.inflate(R.layout.notifications_header, binding.notificationItems, false)
val badge = header?.findViewById(R.id.notifications_title_badge) as? TextView
badge?.text = notificationCount.toString()
@@ -237,7 +244,7 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
imageResourceId: Int? = null,
textColor: Int? = null
): View? {
- val item = inflater?.inflate(R.layout.notification_item, notification_items, false)
+ val item = inflater?.inflate(R.layout.notification_item, binding.notificationItems, false)
val container = item?.findViewById(R.id.notification_item) as? View
container?.setOnClickListener {
@@ -337,7 +344,7 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
notification: Notification,
messageText: CharSequence,
openable: Boolean = false): View? {
- val item = inflater?.inflate(R.layout.notification_item_actionable, notification_items, false)
+ val item = inflater?.inflate(R.layout.notification_item_actionable, binding.notificationItems, false)
if (openable) {
val container = item?.findViewById(R.id.notification_item) as? View
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PrefsActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PrefsActivity.kt
index 74bd3fe44..8e1d9dd5f 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PrefsActivity.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PrefsActivity.kt
@@ -6,7 +6,6 @@ import androidx.preference.PreferenceScreen
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.ui.fragments.preferences.*
-import kotlinx.android.synthetic.main.activity_prefs.*
class PrefsActivity : BaseActivity(), PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
@@ -15,7 +14,7 @@ class PrefsActivity : BaseActivity(), PreferenceFragmentCompat.OnPreferenceStart
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setupToolbar(toolbar)
+ setupToolbar(findViewById(R.id.toolbar))
supportFragmentManager.beginTransaction()
.add(R.id.fragment_container, PreferencesFragment())
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SetupActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SetupActivity.kt
index 538e5ce7e..288c93de2 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SetupActivity.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SetupActivity.kt
@@ -6,6 +6,7 @@ import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
import android.view.View
+import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.Button
import androidx.appcompat.content.res.AppCompatResources
@@ -61,6 +62,7 @@ class SetupActivity : BaseActivity(), ViewPager.OnPageChangeListener {
internal var taskSetupFragment: TaskSetupFragment? = null
internal var user: User? = null
private var completedSetup = false
+ private var createdTasks = false
private val isLastPage: Boolean
get() = this.pager.adapter == null || this.pager.currentItem == (this.pager.adapter?.count ?: 0) - 1
@@ -127,8 +129,13 @@ class SetupActivity : BaseActivity(), ViewPager.OnPageChangeListener {
if (this.taskSetupFragment == null) {
return
}
+ if (createdTasks) {
+ onUserReceived(user)
+ return
+ }
val newTasks = this.taskSetupFragment?.createSampleTasks()
this.completedSetup = true
+ createdTasks = true
newTasks?.let {
this.taskRepository.createTasks(it).subscribe(Consumer { onUserReceived(user) }, RxErrorHandler.handleEmptyError())
}
@@ -194,14 +201,14 @@ class SetupActivity : BaseActivity(), ViewPager.OnPageChangeListener {
private fun onUserReceived(user: User?) {
if (completedSetup) {
- if (!compositeSubscription.isDisposed) {
- compositeSubscription.dispose()
- }
val additionalData = HashMap()
additionalData["status"] = "completed"
AmplitudeManager.sendEvent("setup", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData)
compositeSubscription.add(userRepository.updateUser(user, "flags.welcomed", true).subscribe(Consumer {
+ if (!compositeSubscription.isDisposed) {
+ compositeSubscription.dispose()
+ }
startMainActivity()
}, RxErrorHandler.handleEmptyError()))
return
@@ -256,6 +263,29 @@ class SetupActivity : BaseActivity(), ViewPager.OnPageChangeListener {
}
}
+ override fun instantiateItem(container: ViewGroup, position: Int): Any {
+ val item = super.instantiateItem(container, position)
+ when (item) {
+ is AvatarSetupFragment -> {
+ avatarSetupFragment = item
+ item.activity = this@SetupActivity
+ item.setUser(user)
+ item.width = pager.width
+ }
+ is TaskSetupFragment -> {
+ taskSetupFragment = item
+ item.setUser(user)
+ }
+ is WelcomeFragment -> {
+ welcomeFragment = item
+ item.nameValidEvents.toFlowable(BackpressureStrategy.DROP)?.subscribe {
+ setNextButtonEnabled(it)
+ }
+ }
+ }
+ return item
+ }
+
override fun getCount(): Int {
return 3
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillMemberActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillMemberActivity.kt
index cc693b826..09d6ce46f 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillMemberActivity.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillMemberActivity.kt
@@ -13,7 +13,6 @@ import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.ui.adapter.social.PartyMemberRecyclerViewAdapter
import com.habitrpg.android.habitica.ui.helpers.bindView
import io.reactivex.functions.Consumer
-import kotlinx.android.synthetic.main.activity_prefs.*
import javax.inject.Inject
class SkillMemberActivity : BaseActivity() {
@@ -36,7 +35,7 @@ class SkillMemberActivity : BaseActivity() {
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setupToolbar(toolbar)
+ setupToolbar(findViewById(R.id.toolbar))
loadMemberList()
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillTasksActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillTasksActivity.kt
index a417f5b2b..50b1f405c 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillTasksActivity.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillTasksActivity.kt
@@ -5,6 +5,7 @@ import android.content.Intent
import android.os.Bundle
import android.util.SparseArray
import android.view.MenuItem
+import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentPagerAdapter
import androidx.viewpager.widget.ViewPager
@@ -18,7 +19,6 @@ import com.habitrpg.android.habitica.modules.AppModule
import com.habitrpg.android.habitica.ui.fragments.skills.SkillTasksRecyclerViewFragment
import com.habitrpg.android.habitica.ui.helpers.bindView
import io.reactivex.functions.Consumer
-import kotlinx.android.synthetic.main.activity_prefs.*
import javax.inject.Inject
import javax.inject.Named
@@ -40,7 +40,7 @@ class SkillTasksActivity : BaseActivity() {
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setupToolbar(toolbar)
+ setupToolbar(findViewById(R.id.toolbar))
loadTaskLists()
}
@@ -68,6 +68,21 @@ class SkillTasksActivity : BaseActivity() {
return fragment
}
+ override fun instantiateItem(container: ViewGroup, position: Int): Any {
+ val item = super.instantiateItem(container, position)
+ if (item is SkillTasksRecyclerViewFragment) {
+ item.taskType = when (position) {
+ 0 -> Task.TYPE_HABIT
+ 1 -> Task.TYPE_DAILY
+ else -> Task.TYPE_TODO
+ }
+
+ compositeSubscription.add(item.getTaskSelectionEvents().subscribe(Consumer { task -> taskSelected(task) }, RxErrorHandler.handleEmptyError()))
+ viewFragmentsDictionary.put(position, item)
+ }
+ return item
+ }
+
override fun getCount(): Int {
return 3
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.kt
index f3732d65c..f2fe79897 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.kt
@@ -4,6 +4,7 @@ import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList
+import android.graphics.Typeface
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.os.Handler
@@ -19,11 +20,10 @@ import androidx.core.content.ContextCompat
import androidx.core.view.children
import androidx.core.view.forEachIndexed
import androidx.core.widget.NestedScrollView
+import com.google.android.material.textfield.TextInputLayout
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
-import com.habitrpg.android.habitica.data.TagRepository
-import com.habitrpg.android.habitica.data.TaskRepository
-import com.habitrpg.android.habitica.data.UserRepository
+import com.habitrpg.android.habitica.data.*
import com.habitrpg.android.habitica.extensions.OnChangeTextWatcher
import com.habitrpg.android.habitica.extensions.addCancelButton
import com.habitrpg.android.habitica.extensions.dpToPx
@@ -31,6 +31,7 @@ import com.habitrpg.android.habitica.extensions.getThemeColor
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.helpers.TaskAlarmManager
import com.habitrpg.android.habitica.models.Tag
+import com.habitrpg.android.habitica.models.social.Challenge
import com.habitrpg.android.habitica.models.tasks.HabitResetOption
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.models.user.Stats
@@ -55,11 +56,15 @@ class TaskFormActivity : BaseActivity() {
lateinit var tagRepository: TagRepository
@Inject
lateinit var taskAlarmManager: TaskAlarmManager
+ @Inject
+ lateinit var challengeRepository: ChallengeRepository
private val toolbar: Toolbar by bindView(R.id.toolbar)
private val scrollView: NestedScrollView by bindView(R.id.scroll_view)
private val upperTextWrapper: LinearLayout by bindView(R.id.upper_text_wrapper)
+ private val textInputLayout: TextInputLayout by bindView(R.id.text_input_layout)
private val textEditText: EditText by bindView(R.id.text_edit_text)
+ private val notesInputLayout: TextInputLayout by bindView(R.id.notes_input_layout)
private val notesEditText: EditText by bindView(R.id.notes_edit_text)
private val habitScoringButtons: HabitScoringButtonsView by bindView(R.id.habit_scoring_buttons)
private val checklistTitleView: TextView by bindView(R.id.checklist_title)
@@ -90,6 +95,10 @@ class TaskFormActivity : BaseActivity() {
private val tagsTitleView: TextView by bindView(R.id.tags_title)
private val tagsWrapper: LinearLayout by bindView(R.id.tags_wrapper)
+ private val challengeNameView: TextView by bindView(R.id.challenge_name_view)
+
+ private var challenge: Challenge? = null
+
private var isCreating = true
private var isChallengeTask = false
private var usesTaskAttributeStats = false
@@ -109,12 +118,10 @@ class TaskFormActivity : BaseActivity() {
private var tintColor: Int = 0
set(value) {
field = value
- upperTextWrapper.setBackgroundColor(value)
taskDifficultyButtons.tintColor = value
habitScoringButtons.tintColor = value
habitResetStreakButtons.tintColor = value
taskSchedulingControls.tintColor = value
- supportActionBar?.setBackgroundDrawable(ColorDrawable(value))
updateTagViewsColors()
}
@@ -144,12 +151,22 @@ class TaskFormActivity : BaseActivity() {
} else {
"purple"
}
-
super.onCreate(savedInstanceState)
+
+ if (forcedTheme == "yellow") {
+ taskDifficultyButtons.textTintColor = ContextCompat.getColor(this, R.color.yellow_5)
+ habitScoringButtons.textTintColor = ContextCompat.getColor(this, R.color.yellow_5)
+ }
+
+
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true)
tintColor = getThemeColor(R.attr.taskFormTint)
+ val upperTintColor = getThemeColor(R.attr.colorAccent)
+ supportActionBar?.setBackgroundDrawable(ColorDrawable(upperTintColor))
+ upperTextWrapper.setBackgroundColor(upperTintColor)
+
isChallengeTask = bundle.getBoolean(IS_CHALLENGE_TASK, false)
@@ -163,7 +180,7 @@ class TaskFormActivity : BaseActivity() {
setTagViews()
}, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(userRepository.getUser().subscribe(Consumer {
- usesTaskAttributeStats = it.preferences?.allocationMode == "taskbased"
+ usesTaskAttributeStats = it.preferences?.allocationMode == "taskbased" && it.preferences?.automaticAllocation == true
configureForm()
}, RxErrorHandler.handleEmptyError()))
@@ -171,6 +188,12 @@ class TaskFormActivity : BaseActivity() {
textEditText.addTextChangedListener(OnChangeTextWatcher { _, _, _, _ ->
checkCanSave()
})
+ textEditText.onFocusChangeListener = View.OnFocusChangeListener { view, isFocused ->
+ textInputLayout.alpha = if (isFocused) 1.0f else 0.75f
+ }
+ notesEditText.onFocusChangeListener = View.OnFocusChangeListener { view, isFocused ->
+ notesInputLayout.alpha = if (isFocused) 1.0f else 0.75f
+ }
statStrengthButton.setOnClickListener { selectedStat = Stats.STRENGTH }
statIntelligenceButton.setOnClickListener { selectedStat = Stats.INTELLIGENCE }
statConstitutionButton.setOnClickListener { selectedStat = Stats.CONSTITUTION }
@@ -193,6 +216,13 @@ class TaskFormActivity : BaseActivity() {
task = it
//tintColor = ContextCompat.getColor(this, it.mediumTaskColor)
fillForm(it)
+ task?.challengeID?.let {
+ compositeSubscription.add(challengeRepository.retrieveChallenge(it).subscribe(Consumer {
+ challenge = it
+ challengeNameView.text = getString(R.string.challenge_task_name, it.name)
+ challengeNameView.visibility = View.VISIBLE
+ }, RxErrorHandler.handleEmptyError()))
+ }
}, RxErrorHandler.handleEmptyError()))
}
bundle.containsKey(PARCELABLE_TASK) -> {
@@ -210,6 +240,11 @@ class TaskFormActivity : BaseActivity() {
configureForm()
}
+ override fun onStart() {
+ super.onStart()
+ textEditText.requestFocus()
+ }
+
override fun onCreateOptionsMenu(menu: Menu): Boolean {
if (isCreating) {
menuInflater.inflate(R.menu.menu_task_create, menu)
@@ -220,7 +255,7 @@ class TaskFormActivity : BaseActivity() {
return true
}
- override fun onOptionsItemSelected(item: MenuItem?): Boolean {
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item?.itemId) {
R.id.action_save -> saveTask()
R.id.action_delete -> deleteTask()
@@ -373,6 +408,11 @@ class TaskFormActivity : BaseActivity() {
button.background.setTint(if (isSelected) tintColor else ContextCompat.getColor(this, R.color.taskform_gray))
val textColorID = if (isSelected) R.color.white else R.color.gray_100
button.setTextColor(ContextCompat.getColor(this, textColorID))
+ if (isSelected) {
+ button.typeface = Typeface.create("sans-serif-medium", Typeface.NORMAL)
+ } else {
+ button.typeface = Typeface.create("sans-serif", Typeface.NORMAL)
+ }
}
private fun updateTagViewsColors() {
@@ -464,6 +504,10 @@ class TaskFormActivity : BaseActivity() {
}
private fun deleteTask() {
+ if (task?.challengeID?.isNotBlank() == true && task?.challengeBroken?.isNotBlank() != true) {
+ showChallengeDeleteTask()
+ return
+ }
val alert = HabiticaAlertDialog(this)
alert.setTitle(R.string.are_you_sure)
alert.addButton(R.string.delete_task, true) { _, _ ->
@@ -476,6 +520,36 @@ class TaskFormActivity : BaseActivity() {
alert.show()
}
+ private fun showChallengeDeleteTask() {
+ compositeSubscription.add(taskRepository.getTasksForChallenge(task?.challengeID).firstElement().subscribe(Consumer { tasks ->
+ val taskCount = tasks.size
+ val alert = HabiticaAlertDialog(this)
+ alert.setTitle(getString(R.string.delete_challenge_task_title))
+ alert.setMessage(getString(R.string.delete_challenge_task_description, taskCount, challenge?.name ?: ""))
+ alert.addButton(R.string.leave_delete_task, true, true) { _, _ ->
+ challenge?.let {
+ compositeSubscription.add(challengeRepository.leaveChallenge(it, "keep-all")
+ .flatMap { taskRepository.deleteTask(task?.id ?: "") }
+ .flatMap { userRepository.retrieveUser(true) }
+ .subscribe(Consumer {
+ finish()
+ }, RxErrorHandler.handleEmptyError()))
+ }
+ }
+ alert.addButton(getString(R.string.leave_delete_x_tasks, taskCount), false, true) { _, _ ->
+ challenge?.let {
+ compositeSubscription.add(challengeRepository.leaveChallenge(it, "remove-all")
+ .flatMap { userRepository.retrieveUser(true) }
+ .subscribe(Consumer {
+ finish()
+ }, RxErrorHandler.handleEmptyError()))
+ }
+ }
+ alert.setExtraCloseButtonVisibility(View.VISIBLE)
+ alert.show()
+ }, RxErrorHandler.handleEmptyError()))
+ }
+
private fun dismissKeyboard() {
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
val currentFocus = currentFocus
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationEquipmentRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationEquipmentRecyclerViewAdapter.kt
index 68f6edf29..442c8fd26 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationEquipmentRecyclerViewAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationEquipmentRecyclerViewAdapter.kt
@@ -65,6 +65,8 @@ class CustomizationEquipmentRecyclerViewAdapter : androidx.recyclerview.widget.R
fun setEquipment(newEquipmentList: List) {
this.equipmentList = newEquipmentList.toMutableList()
+ val emptyEquipment = Equipment()
+ equipmentList.add(0, emptyEquipment)
this.notifyDataSetChanged()
}
@@ -104,7 +106,7 @@ class CustomizationEquipmentRecyclerViewAdapter : androidx.recyclerview.widget.R
}
}
- if (activeEquipment == equipment.key) {
+ if (activeEquipment == equipment.key || (activeEquipment?.contains("base_0") == true && equipment.key?.isNotBlank() != true)) {
binding.wrapper.background = itemView.context.getDrawable(R.drawable.layout_rounded_bg_gray_700_brand_border)
} else {
binding.wrapper.background = itemView.context.getDrawable(R.drawable.layout_rounded_bg_gray_700)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/NavigationDrawerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/NavigationDrawerAdapter.kt
index a4d8a28f7..4a36f10f9 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/NavigationDrawerAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/NavigationDrawerAdapter.kt
@@ -11,6 +11,7 @@ import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.ui.helpers.bindOptionalView
import com.habitrpg.android.habitica.ui.menu.HabiticaDrawerItem
import com.habitrpg.android.habitica.ui.viewHolders.GiftOneGetOnePromoMenuView
+import com.habitrpg.android.habitica.ui.views.adventureGuide.AdventureGuideMenuBanner
import com.habitrpg.android.habitica.ui.views.promo.SubscriptionBuyGemsPromoView
import com.habitrpg.android.habitica.ui.views.promo.SubscriptionBuyGemsPromoViewHolder
import io.reactivex.BackpressureStrategy
@@ -69,15 +70,23 @@ class NavigationDrawerAdapter(tintColor: Int, backgroundTintColor: Int): Recycle
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val drawerItem = getItem(position)
- if (getItemViewType(position) == 0) {
- val itemHolder = holder as? DrawerItemViewHolder
- itemHolder?.tintColor = tintColor
- itemHolder?.backgroundTintColor = backgroundTintColor
- itemHolder?.bind(drawerItem, drawerItem.transitionId == selectedItem)
- itemHolder?.itemView?.setOnClickListener { itemSelectedEvents.onNext(drawerItem) }
- } else if (getItemViewType(position) == 1) {
- (holder as? SectionHeaderViewHolder)?.backgroundTintColor = backgroundTintColor
- (holder as? SectionHeaderViewHolder)?.bind(drawerItem)
+ when {
+ getItemViewType(position) == 0 -> {
+ val itemHolder = holder as? DrawerItemViewHolder
+ itemHolder?.tintColor = tintColor
+ itemHolder?.backgroundTintColor = backgroundTintColor
+ itemHolder?.bind(drawerItem, drawerItem.transitionId == selectedItem)
+ itemHolder?.itemView?.setOnClickListener { itemSelectedEvents.onNext(drawerItem) }
+ }
+ getItemViewType(position) == 1 -> {
+ (holder as? SectionHeaderViewHolder)?.backgroundTintColor = backgroundTintColor
+ (holder as? SectionHeaderViewHolder)?.bind(drawerItem)
+ }
+ getItemViewType(position) == 4 -> {
+ drawerItem.user?.let { (holder.itemView as? AdventureGuideMenuBanner)?.updateData(it) }
+ holder.itemView.setOnClickListener { itemSelectedEvents.onNext(drawerItem) }
+
+ }
}
}
@@ -111,6 +120,14 @@ class NavigationDrawerAdapter(tintColor: Int, backgroundTintColor: Int): Recycle
)
SubscriptionBuyGemsPromoViewHolder(itemView)
}
+ 4 -> {
+ val itemView = AdventureGuideMenuBanner(parent.context)
+ itemView.layoutParams = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ 104.dpToPx(parent.context)
+ )
+ SubscriptionBuyGemsPromoViewHolder(itemView)
+ }
1 -> SectionHeaderViewHolder(parent.inflate(R.layout.drawer_main_section_header))
else -> DrawerItemViewHolder(parent.inflate(R.layout.drawer_main_item))
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ItemRecyclerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ItemRecyclerAdapter.kt
index 9712100a9..f2351c895 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ItemRecyclerAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ItemRecyclerAdapter.kt
@@ -96,7 +96,7 @@ class ItemRecyclerAdapter(data: OrderedRealmCollection?, autoUpdate:
} else {
hatchingItem?.key + "-" + item?.key
}
- val pet = existingPets?.where()?.equalTo("key", petKey)?.findFirst()
+ val pet = existingPets?.where()?.equalTo("key", petKey)?.notEqualTo("type", "special")?.findFirst()
return pet != null && ownedPets?.get(pet.key)?.trained ?: 0 <= 0
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/MountDetailRecyclerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/MountDetailRecyclerAdapter.kt
index 0401cee20..259eec1a0 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/MountDetailRecyclerAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/MountDetailRecyclerAdapter.kt
@@ -1,107 +1,52 @@
package com.habitrpg.android.habitica.ui.adapter.inventory
-import android.content.Context
-import android.content.res.Resources
-import android.graphics.drawable.BitmapDrawable
-import android.view.View
import android.view.ViewGroup
-import android.widget.TextView
-import com.facebook.drawee.view.SimpleDraweeView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.inflate
-import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.inventory.Mount
+import com.habitrpg.android.habitica.models.inventory.StableSection
import com.habitrpg.android.habitica.models.user.OwnedMount
-import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
-import com.habitrpg.android.habitica.ui.helpers.bindView
-import com.habitrpg.android.habitica.ui.menu.BottomSheetMenu
-import com.habitrpg.android.habitica.ui.menu.BottomSheetMenuItem
+import com.habitrpg.android.habitica.ui.viewHolders.MountViewHolder
+import com.habitrpg.android.habitica.ui.viewHolders.SectionViewHolder
import io.reactivex.BackpressureStrategy
import io.reactivex.Flowable
-import io.reactivex.Observable
-import io.reactivex.android.schedulers.AndroidSchedulers
-import io.reactivex.functions.Consumer
import io.reactivex.subjects.PublishSubject
-import io.realm.OrderedRealmCollection
-import io.realm.RealmRecyclerViewAdapter
-class MountDetailRecyclerAdapter(data: OrderedRealmCollection?, autoUpdate: Boolean) : RealmRecyclerViewAdapter(data, autoUpdate) {
-
- var itemType: String? = null
- var context: Context? = null
+class MountDetailRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter() {
private var ownedMounts: Map? = null
private val equipEvents = PublishSubject.create()
+ private var itemList: List = ArrayList()
+
+ fun setItemList(itemList: List) {
+ this.itemList = itemList
+ this.notifyDataSetChanged()
+ }
fun getEquipFlowable(): Flowable {
return equipEvents.toFlowable(BackpressureStrategy.DROP)
}
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MountViewHolder {
- return MountViewHolder(parent.inflate(R.layout.animal_overview_item))
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder =
+ when (viewType) {
+ 1 -> SectionViewHolder(parent)
+ else -> MountViewHolder(parent, equipEvents)
+ }
+
+ override fun onBindViewHolder(holder: androidx.recyclerview.widget.RecyclerView.ViewHolder, position: Int) {
+ when (val obj = this.itemList[position]) {
+ is StableSection -> (holder as? SectionViewHolder)?.bind(obj)
+ is Mount -> (holder as? MountViewHolder)?.bind(obj, ownedMounts?.get(obj.key ?: "")?.owned == true)
+ }
}
- override fun onBindViewHolder(holder: MountViewHolder, position: Int) {
- data?.let { holder.bind(it[position], ownedMounts?.get(it[position].key)) }
- }
+ override fun getItemViewType(position: Int): Int = if (itemList[position] is StableSection) 1 else 2
+
+ override fun getItemCount(): Int = itemList.size
fun setOwnedMounts(ownedMounts: Map) {
this.ownedMounts = ownedMounts
notifyDataSetChanged()
}
-
- inner class MountViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener {
- var animal: Mount? = null
- private var ownedMount: OwnedMount? = null
-
- private val imageView: SimpleDraweeView by bindView(R.id.imageView)
- private val titleView: TextView by bindView(R.id.titleTextView)
- private val ownedTextView: TextView by bindView(R.id.ownedTextView)
-
- var resources: Resources = itemView.resources
-
- init {
- itemView.setOnClickListener(this)
- }
-
- fun bind(item: Mount, ownedMount: OwnedMount?) {
- animal = item
- this.ownedMount = ownedMount
- titleView.text = when {
- item.color == "Veggie" -> context?.getString(R.string.garden)
- item.type == "special" ->item.text
- else -> item.color
- }
- ownedTextView.visibility = View.GONE
- val imageName = "Mount_Icon_" + itemType + "-" + item.color
- this.imageView.alpha = 1.0f
- if (ownedMount?.owned != true) {
- this.imageView.alpha = 0.1f
- }
- imageView.background = null
- val owned = ownedMount?.owned ?: false
- DataBindingUtils.loadImage(imageName) {
- val drawable = BitmapDrawable(context?.resources, if (owned) it else it.extractAlpha())
-
- Observable.just(drawable)
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(Consumer {
- imageView.background = drawable
- }, RxErrorHandler.handleEmptyError())
- }
- }
-
- override fun onClick(v: View) {
- if (ownedMount?.owned != true) {
- return
- }
- val menu = BottomSheetMenu(itemView.context)
- menu.addMenuItem(BottomSheetMenuItem(resources.getString(R.string.use_animal)))
- menu.setSelectionRunnable {
- animal?.let { equipEvents.onNext(it.key) }
- }
- menu.show()
- }
- }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/PetDetailRecyclerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/PetDetailRecyclerAdapter.kt
index 9ba351dd4..8ca33104d 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/PetDetailRecyclerAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/PetDetailRecyclerAdapter.kt
@@ -1,58 +1,86 @@
package com.habitrpg.android.habitica.ui.adapter.inventory
-import android.content.Context
-import android.graphics.drawable.BitmapDrawable
-import android.view.View
import android.view.ViewGroup
-import android.widget.ProgressBar
-import android.widget.TextView
-import com.facebook.drawee.view.SimpleDraweeView
import com.habitrpg.android.habitica.R
-import com.habitrpg.android.habitica.events.commands.FeedCommand
import com.habitrpg.android.habitica.extensions.inflate
-import com.habitrpg.android.habitica.helpers.RxErrorHandler
-import com.habitrpg.android.habitica.models.inventory.Mount
-import com.habitrpg.android.habitica.models.inventory.Pet
+import com.habitrpg.android.habitica.models.inventory.*
+import com.habitrpg.android.habitica.models.user.OwnedItem
import com.habitrpg.android.habitica.models.user.OwnedMount
import com.habitrpg.android.habitica.models.user.OwnedPet
-import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
-import com.habitrpg.android.habitica.ui.helpers.bindView
-import com.habitrpg.android.habitica.ui.menu.BottomSheetMenu
-import com.habitrpg.android.habitica.ui.menu.BottomSheetMenuItem
+import com.habitrpg.android.habitica.ui.viewHolders.PetViewHolder
+import com.habitrpg.android.habitica.ui.viewHolders.SectionViewHolder
import io.reactivex.BackpressureStrategy
import io.reactivex.Flowable
-import io.reactivex.Observable
-import io.reactivex.android.schedulers.AndroidSchedulers
-import io.reactivex.functions.Consumer
import io.reactivex.subjects.PublishSubject
-import io.realm.OrderedRealmCollection
-import io.realm.RealmRecyclerViewAdapter
import io.realm.RealmResults
-import org.greenrobot.eventbus.EventBus
-class PetDetailRecyclerAdapter(data: OrderedRealmCollection?, autoUpdate: Boolean) : RealmRecyclerViewAdapter(data, autoUpdate) {
-
- var itemType: String? = null
- var context: Context? = null
+class PetDetailRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter() {
private var existingMounts: RealmResults? = null
private var ownedPets: Map? = null
private var ownedMounts: Map? = null
+ private var ownedItems: Map? = null
private val equipEvents = PublishSubject.create()
+ private var ownsSaddles: Boolean = false
+
+ private var itemList: List = ArrayList()
+
+ fun setItemList(itemList: List) {
+ this.itemList = itemList
+ this.notifyDataSetChanged()
+ }
fun getEquipFlowable(): Flowable {
return equipEvents.toFlowable(BackpressureStrategy.DROP)
}
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PetViewHolder {
- return PetViewHolder(parent.inflate(R.layout.pet_detail_item))
+ var animalIngredientsRetriever: ((Animal) -> Pair)? = null
+
+ private fun canRaiseToMount(pet: Pet): Boolean {
+ for (mount in existingMounts ?: emptyList()) {
+ if (mount.key == pet.key) {
+ return !(ownedMounts?.get(mount.key)?.owned ?: false)
+ }
+ }
+ return false
}
- override fun onBindViewHolder(holder: PetViewHolder, position: Int) {
- data?.let {
- holder.bind(it[position], ownedPets?.get(it[position]?.key ?: ""))
+ private fun eggCount(pet: Pet): Int {
+ return ownedItems?.get(pet.animal + "-eggs")?.numberOwned ?: 0
+ }
+ private fun potionCount(pet: Pet): Int {
+ return ownedItems?.get(pet.color + "-hatchingPotions")?.numberOwned ?: 0
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder =
+ when (viewType) {
+ 1 -> SectionViewHolder(parent)
+ else -> PetViewHolder(parent, equipEvents, animalIngredientsRetriever)
+ }
+
+ override fun onBindViewHolder(holder: androidx.recyclerview.widget.RecyclerView.ViewHolder, position: Int) {
+ when (val obj = this.itemList[position]) {
+ is StableSection -> {
+ (holder as? SectionViewHolder)?.bind(obj)
+ }
+ is Pet -> {
+ (holder as? PetViewHolder)?.bind(obj,
+ ownedPets?.get(obj.key ?: "")?.trained ?: 0,
+ eggCount(obj),
+ potionCount(obj),
+ canRaiseToMount(obj),
+ ownsSaddles,
+ ownedItems?.get(obj.animal + "-eggs") != null,
+ ownedItems?.get(obj.color + "-hatchingPotions") != null,
+ ownedMounts?.containsKey(obj.key) == true
+ )
+ }
}
}
+ override fun getItemViewType(position: Int): Int = if (itemList[position] is StableSection) 1 else 2
+
+ override fun getItemCount(): Int = itemList.size
+
fun setExistingMounts(existingMounts: RealmResults) {
this.existingMounts = existingMounts
notifyDataSetChanged()
@@ -67,86 +95,15 @@ class PetDetailRecyclerAdapter(data: OrderedRealmCollection?, autoUpdate: B
this.ownedPets = ownedPets
notifyDataSetChanged()
}
- inner class PetViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener {
- var animal: Pet? = null
- var ownedPet: OwnedPet? = null
- private val imageView: SimpleDraweeView by bindView(R.id.imageView)
- private val titleView: TextView by bindView(R.id.titleTextView)
- private val trainedProgressbar: ProgressBar by bindView(R.id.trainedProgressBar)
+ fun setOwnedItems(ownedItems: Map) {
+ this.ownedItems = ownedItems
+ ownsSaddles = ownedItems.containsKey("Saddle-food")
+ notifyDataSetChanged()
+ }
- private val isOwned: Boolean
- get() = this.ownedPet?.trained ?: 0 > 0
-
- private val canRaiseToMount: Boolean
- get() {
- for (mount in existingMounts ?: emptyList()) {
- if (mount.key == animal?.key) {
- return !(ownedMounts?.get(mount.key)?.owned ?: false)
- }
- }
- return false
- }
-
- init {
- itemView.setOnClickListener(this)
- }
-
- fun bind(item: Pet, ownedPet: OwnedPet?) {
- this.animal = item
- this.ownedPet = ownedPet
- titleView.text = when {
- item.color == "Veggie" -> context?.getString(R.string.garden)
- item.type == "special" ->item.text
- else -> item.color
- }
- this.imageView.alpha = 1.0f
- val imageName = "Pet-$itemType-${item.color}"
- if (this.ownedPet?.trained ?: 0 > 0) {
- if (this.canRaiseToMount) {
- this.trainedProgressbar.visibility = View.VISIBLE
- this.trainedProgressbar.progress = ownedPet?.trained ?: 0
- } else {
- this.trainedProgressbar.visibility = View.GONE
- }
- } else {
- this.trainedProgressbar.visibility = View.GONE
- this.imageView.alpha = 0.1f
- }
- imageView.background = null
- val trained = ownedPet?.trained ?: 0
- DataBindingUtils.loadImage(imageName) {
- val drawable = BitmapDrawable(context?.resources, if (trained == 0) it.extractAlpha() else it)
- Observable.just(drawable)
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(Consumer {
- imageView.background = drawable
- }, RxErrorHandler.handleEmptyError())
- }
- }
-
- override fun onClick(v: View) {
- if (!this.isOwned) {
- return
- }
- val context = context ?: return
- val menu = BottomSheetMenu(context)
- menu.addMenuItem(BottomSheetMenuItem(itemView.resources.getString(R.string.use_animal)))
- if (canRaiseToMount) {
- menu.addMenuItem(BottomSheetMenuItem(itemView.resources.getString(R.string.feed)))
- }
- menu.setSelectionRunnable { index ->
- if (index == 0) {
- animal?.let {
- equipEvents.onNext(it.key)
- }
- } else if (index == 1) {
- val event = FeedCommand()
- event.usingPet = animal
- EventBus.getDefault().post(event)
- }
- }
- menu.show()
- }
+ fun setOwnsSaddles(ownsSaddles: Boolean) {
+ this.ownsSaddles = ownsSaddles
+ notifyDataSetChanged()
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt
index 9136c0c5f..01c7e6054 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt
@@ -84,18 +84,9 @@ class ShopRecyclerAdapter(private val configManager: AppConfigManager) : android
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder =
when (viewType) {
- 0 -> {
- val view = parent.inflate(R.layout.shop_header)
- ShopHeaderViewHolder(view)
- }
- 1 -> {
- val view = parent.inflate(R.layout.shop_section_header)
- SectionViewHolder(view)
- }
- 2 -> {
- val view = parent.inflate(emptyViewResource)
- EmptyStateViewHolder(view)
- }
+ 0 -> ShopHeaderViewHolder(parent)
+ 1 -> SectionViewHolder(parent.inflate(R.layout.shop_section_header))
+ 2 -> EmptyStateViewHolder(parent.inflate(emptyViewResource))
else -> {
val view = parent.inflate(R.layout.row_shopitem)
val viewHolder = ShopItemViewHolder(view)
@@ -140,8 +131,8 @@ class ShopRecyclerAdapter(private val configManager: AppConfigManager) : android
val item = obj as? ShopItem ?: return
val itemHolder = holder as? ShopItemViewHolder ?: return
itemHolder.bind(item, item.canAfford(user, 1))
- if (ownedItems.containsKey(item.key+"-"+item.pinType)) {
- itemHolder.itemCount = ownedItems[item.key+"-"+item.pinType]?.numberOwned ?: 0
+ ownedItems[item.key+"-"+item.purchaseType]?.let {
+ itemHolder.itemCount = it.numberOwned
}
itemHolder.isPinned = pinnedItemKeys.contains(item.key)
}
@@ -221,7 +212,7 @@ class ShopRecyclerAdapter(private val configManager: AppConfigManager) : android
this.notifyDataSetChanged()
}
- internal class ShopHeaderViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView) {
+ internal class ShopHeaderViewHolder(parent: ViewGroup) : androidx.recyclerview.widget.RecyclerView.ViewHolder(parent.inflate(R.layout.shop_header)) {
private val descriptionView: TextView by bindView(itemView, R.id.descriptionView)
private val npcBannerView: NPCBannerView by bindView(itemView, R.id.npcBannerView)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/StableRecyclerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/StableRecyclerAdapter.kt
index 280a3a0fe..e0b46ec18 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/StableRecyclerAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/StableRecyclerAdapter.kt
@@ -1,37 +1,39 @@
package com.habitrpg.android.habitica.ui.adapter.inventory
-import android.content.Context
-import android.graphics.drawable.BitmapDrawable
import android.view.View
import android.view.ViewGroup
-import android.widget.FrameLayout
-import android.widget.LinearLayout
-import android.widget.RelativeLayout
import android.widget.TextView
-import androidx.coordinatorlayout.widget.CoordinatorLayout
+import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.RecyclerView
import com.facebook.drawee.view.SimpleDraweeView
import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.databinding.ShopHeaderBinding
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.helpers.MainNavigationController
-import com.habitrpg.android.habitica.helpers.RxErrorHandler
-import com.habitrpg.android.habitica.models.inventory.Animal
-import com.habitrpg.android.habitica.ui.activities.MainActivity
+import com.habitrpg.android.habitica.models.inventory.*
import com.habitrpg.android.habitica.ui.fragments.inventory.stable.StableFragmentDirections
-import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
import com.habitrpg.android.habitica.ui.helpers.bindView
+import com.habitrpg.android.habitica.ui.helpers.loadImage
+import com.habitrpg.android.habitica.ui.viewHolders.MountViewHolder
+import com.habitrpg.android.habitica.ui.viewHolders.PetViewHolder
import com.habitrpg.android.habitica.ui.viewHolders.SectionViewHolder
-import com.habitrpg.android.habitica.ui.views.NPCBannerView
-import io.reactivex.Observable
-import io.reactivex.android.schedulers.AndroidSchedulers
-import io.reactivex.functions.Consumer
+import io.reactivex.BackpressureStrategy
+import io.reactivex.Flowable
+import io.reactivex.subjects.PublishSubject
-class StableRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter() {
+class StableRecyclerAdapter : RecyclerView.Adapter() {
+ var shopSpriteSuffix: String? = null
+ private var eggs: Map = mapOf()
+ var animalIngredientsRetriever: ((Animal) -> Pair)? = null
var itemType: String? = null
- var context: Context? = null
- var activity: MainActivity? = null
+ private val equipEvents = PublishSubject.create()
+
+ fun getEquipFlowable(): Flowable {
+ return equipEvents.toFlowable(BackpressureStrategy.DROP)
+ }
private var itemList: List = ArrayList()
@@ -40,65 +42,96 @@ class StableRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter<
this.notifyDataSetChanged()
}
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder =
- if (viewType == 0) {
- val view = parent.inflate(R.layout.shop_header)
- StableHeaderViewHolder(view)
- } else if (viewType == 1) {
- val view = parent.inflate(R.layout.customization_section_header)
- SectionViewHolder(view)
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
+ when (viewType) {
+ 1 -> SectionViewHolder(parent)
+ 4 -> StableViewHolder(parent.inflate(R.layout.pet_overview_item))
+ 5 -> StableViewHolder(parent.inflate(R.layout.mount_overview_item))
+ 2 -> PetViewHolder(parent, equipEvents, animalIngredientsRetriever)
+ 3 -> MountViewHolder(parent, equipEvents)
+ else -> StableHeaderViewHolder(parent)
}
- else {
- val view = parent.inflate(R.layout.animal_overview_item)
- StableViewHolder(view)
- }
-
- override fun onBindViewHolder(holder: androidx.recyclerview.widget.RecyclerView.ViewHolder, position: Int) {
- val obj = this.itemList[position]
- if (obj == "header") {
- (holder as? StableHeaderViewHolder)?.bind()
- } else if (obj.javaClass == String::class.java) {
- if (obj == "Standard") {
- var params = holder.itemView.layoutParams as GridLayoutManager.LayoutParams
- params.height = 135
- holder.itemView.layoutParams = params
- }
- (holder as? SectionViewHolder)?.bind(obj as? String ?: "")
-
- } else {
- (obj as? Animal)?.let { (holder as? StableViewHolder)?.bind(it) }
+ override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+ when (val item = this.itemList[position]) {
+ "header" -> (holder as? StableHeaderViewHolder)?.bind()
+ is StableSection -> {
+ if (item.key == "drop") {
+ val params = holder.itemView.layoutParams as GridLayoutManager.LayoutParams
+ params.topMargin = -30
+ holder.itemView.layoutParams = params
+ }
+ (holder as? SectionViewHolder)?.bind(item)
+ }
+ is Animal -> {
+ val isIndividualAnimal = item.type == "special" || item.type == "wacky"
+ if (isIndividualAnimal) {
+ if (item is Pet) {
+ (holder as? PetViewHolder)?.bind(item,
+ item.numberOwned,
+ canRaiseToMount = false,
+ eggCount = 0,
+ potionCount = 0,
+ ownsSaddles = false,
+ hasUnlockedEgg = false,
+ hasUnlockedPotion = false,
+ hasMount = false)
+ } else if (item is Mount) {
+ (holder as? MountViewHolder)?.bind(item, item.numberOwned > 0)
+ }
+ return
+ }
+ (holder as? StableViewHolder)?.bind(item)
+ }
}
}
override fun getItemViewType(position: Int): Int {
- return if (itemList[position] == "header") {
+ val item = itemList[position]
+ return if (item == "header") {
0
- }
- else if (itemList[position].javaClass == String::class.java) {
+ } else if (item is StableSection) {
1
- }
- else {
- 2
+ } else if (item is Animal) {
+ val isIndividualAnimal = item.type == "special" || item.type == "wacky"
+ if (isIndividualAnimal) {
+ if (itemType == "pets") {
+ 2
+ } else {
+ 3
+ }
+ } else {
+ if (itemType == "pets") {
+ 4
+ } else {
+ 5
+ }
+ }
+ } else {
+ 0
}
}
override fun getItemCount(): Int = itemList.size
+ fun setEggs(eggs: Map) {
+ this.eggs = eggs
+ notifyDataSetChanged()
+ }
- internal class StableHeaderViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView) {
+ internal inner class StableHeaderViewHolder(parent: ViewGroup) : RecyclerView.ViewHolder(parent.inflate(R.layout.shop_header)) {
- private val npcBannerView: NPCBannerView by bindView(itemView, R.id.npcBannerView)
- private val namePlate: TextView by bindView(itemView, R.id.namePlate)
- private val descriptionView: TextView by bindView(itemView, R.id.descriptionView)
+ private var binding: ShopHeaderBinding = ShopHeaderBinding.bind(itemView)
fun bind() {
- npcBannerView.identifier = "stable"
- namePlate.setText(R.string.stable_owner)
- descriptionView.visibility = View.GONE
+ binding.npcBannerView.shopSpriteSuffix = shopSpriteSuffix ?: ""
+ binding.npcBannerView.identifier = "stable"
+ binding.namePlate.setText(R.string.stable_owner)
+ binding.descriptionView.visibility = View.GONE
}
+
}
-
- internal inner class StableViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener {
+
+ internal inner class StableViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
private var animal: Animal? = null
private val imageView: SimpleDraweeView by bindView(itemView, R.id.imageView)
@@ -111,31 +144,35 @@ class StableRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter<
fun bind(item: Animal) {
this.animal = item
- titleView.text = if (item.type == "special") {
- item.text
- } else {
- item.animal
- }
+ val context = itemView.context
+ val egg = eggs[item.animal]
+ if (egg != null) {
+ titleView.text = if (item.type == "drop" || itemType == "mounts") egg.mountText else egg.text
+ } else item.animal
ownedTextView.visibility = View.VISIBLE
- this.imageView.alpha = 1.0f
+
val imageName = if (itemType == "pets") {
- "Pet-" + item.key
+ "Pet_Egg_" + item.animal
} else {
"Mount_Icon_" + item.key
}
- this.ownedTextView.text = animal?.numberOwned?.toString()
- ownedTextView.visibility = if (animal?.numberOwned == 0 || animal?.type == "special") View.GONE else View.VISIBLE
- imageView.background = null
- DataBindingUtils.loadImage(imageName) {
- val drawable = BitmapDrawable(context?.resources, if (item.numberOwned > 0) it else it.extractAlpha())
- Observable.just(drawable)
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(Consumer {
- imageView.background = drawable
- }, RxErrorHandler.handleEmptyError())
- }
- if (item.numberOwned <= 0) {
- this.imageView.alpha = 0.1f
+
+ this.ownedTextView.text = context.getString(R.string.pet_ownership_fraction, item.numberOwned, item.totalNumber)
+ this.ownedTextView.background = context.getDrawable(R.drawable.layout_rounded_bg_shopitem_price)
+
+ this.ownedTextView.setTextColor(ContextCompat.getColor(context, R.color.gray_200))
+
+ ownedTextView.visibility = View.VISIBLE
+ imageView.loadImage(imageName)
+
+ val alpha = if (item.numberOwned <= 0) 0.2f else 1.0f
+ this.imageView.alpha = alpha
+ this.titleView.alpha = alpha
+ this.ownedTextView.alpha = alpha
+
+ if (item.numberOwned == item.totalNumber) {
+ this.ownedTextView.background = context.getDrawable(R.drawable.layout_rounded_bg_animalitem_complete)
+ this.ownedTextView.setTextColor(ContextCompat.getColor(context, R.color.white))
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/challenges/ChallengeTasksRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/challenges/ChallengeTasksRecyclerViewAdapter.kt
index b9cf62730..07bb34cb9 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/challenges/ChallengeTasksRecyclerViewAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/challenges/ChallengeTasksRecyclerViewAdapter.kt
@@ -63,16 +63,16 @@ class ChallengeTasksRecyclerViewAdapter(taskFilterHelper: TaskFilterHelper?, lay
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindableViewHolder {
val viewHolder: BindableViewHolder = when (viewType) {
- TYPE_HABIT -> HabitViewHolder(getContentView(parent, R.layout.habit_item_card), { _, _ -> }) { task ->
+ TYPE_HABIT -> HabitViewHolder(getContentView(parent, R.layout.habit_item_card), { _, _ -> }, { }) { task ->
taskOpenEventsSubject.onNext(task)
}
- TYPE_DAILY -> DailyViewHolder(getContentView(parent, R.layout.daily_item_card), { _, _ -> }, { _, _ -> }) { task ->
+ TYPE_DAILY -> DailyViewHolder(getContentView(parent, R.layout.daily_item_card), { _, _ -> }, { _, _ -> }, { }) { task ->
taskOpenEventsSubject.onNext(task)
}
- TYPE_TODO -> TodoViewHolder(getContentView(parent, R.layout.todo_item_card), { _, _ -> }, { _, _ -> }) { task ->
+ TYPE_TODO -> TodoViewHolder(getContentView(parent, R.layout.todo_item_card), { _, _ -> }, { _, _ -> }, { }) { task ->
taskOpenEventsSubject.onNext(task)
}
- TYPE_REWARD -> RewardViewHolder(getContentView(parent, R.layout.reward_item_card), { _, _ -> }) { task ->
+ TYPE_REWARD -> RewardViewHolder(getContentView(parent, R.layout.reward_item_card), { _, _ -> }, { }) { task ->
taskOpenEventsSubject.onNext(task)
}
TYPE_ADD_ITEM -> AddItemViewHolder(getContentView(parent, R.layout.challenge_add_task_item), addItemSubject)
@@ -116,7 +116,7 @@ class ChallengeTasksRecyclerViewAdapter(taskFilterHelper: TaskFilterHelper?, lay
addBtn.setOnClickListener { newTask?.let { callback.onNext(it) } }
}
- override fun bind(data: Task, position: Int) {
+ override fun bind(data: Task, position: Int, displayMode: String) {
this.newTask = data
addBtn.text = data.text
}
@@ -126,7 +126,7 @@ class ChallengeTasksRecyclerViewAdapter(taskFilterHelper: TaskFilterHelper?, lay
private val dividerName: TextView = itemView.findViewById(R.id.divider_name)
- override fun bind(data: Task, position: Int) {
+ override fun bind(data: Task, position: Int, displayMode: String) {
dividerName.text = data.text
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/BaseTasksRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/BaseTasksRecyclerViewAdapter.kt
index 1ddbbbfd6..e471cfe80 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/BaseTasksRecyclerViewAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/BaseTasksRecyclerViewAdapter.kt
@@ -46,7 +46,7 @@ abstract class BaseTasksRecyclerViewAdapter>(var t
override fun onBindViewHolder(holder: VH, position: Int) {
val item = filteredContent?.get(position)
if (item != null) {
- holder.bind(item, position)
+ holder.bind(item, position, "normal")
}
/*if (this.displayedChecklist != null && ChecklistedViewHolder.class.isAssignableFrom(holder.getClass())) {
ChecklistedViewHolder checklistedHolder = (ChecklistedViewHolder) holder;
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/DailiesRecyclerViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/DailiesRecyclerViewHolder.kt
index 147ec2b88..9535f6986 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/DailiesRecyclerViewHolder.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/DailiesRecyclerViewHolder.kt
@@ -12,7 +12,9 @@ class DailiesRecyclerViewHolder(data: OrderedRealmCollection?, autoUpdate:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DailyViewHolder =
DailyViewHolder(getContentView(parent), { task, direction -> taskScoreEventsSubject.onNext(Pair(task, direction)) },
- { task, item -> checklistItemScoreSubject.onNext(Pair(task, item))}) {
+ { task, item -> checklistItemScoreSubject.onNext(Pair(task, item))}, {
task -> taskOpenEventsSubject.onNext(task)
+ }) {
+ task -> brokenTaskEventsSubject.onNext(task)
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/HabitsRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/HabitsRecyclerViewAdapter.kt
index 1e41f002f..715e42566 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/HabitsRecyclerViewAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/HabitsRecyclerViewAdapter.kt
@@ -10,7 +10,9 @@ class HabitsRecyclerViewAdapter(data: OrderedRealmCollection?, autoUpdate:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HabitViewHolder =
- HabitViewHolder(getContentView(parent), { task, direction -> taskScoreEventsSubject.onNext(Pair(task, direction)) }) {
+ HabitViewHolder(getContentView(parent), { task, direction -> taskScoreEventsSubject.onNext(Pair(task, direction)) }, {
task -> taskOpenEventsSubject.onNext(task)
+ }) {
+ task -> brokenTaskEventsSubject.onNext(task)
}
}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RealmBaseTasksRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RealmBaseTasksRecyclerViewAdapter.kt
index 661b72ce5..92e8926ae 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RealmBaseTasksRecyclerViewAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RealmBaseTasksRecyclerViewAdapter.kt
@@ -24,6 +24,14 @@ abstract class RealmBaseTasksRecyclerViewAdapter(
private var updateOnModification: Boolean = false
override var ignoreUpdates: Boolean = false
+ override var taskDisplayMode: String = "standard"
+ set(value) {
+ if (field != value) {
+ field = value
+ notifyDataSetChanged()
+ }
+ }
+
private val resultsListener: OrderedRealmCollectionChangeListener> by lazy {
OrderedRealmCollectionChangeListener> { _, changeSet ->
buildChangeSet(changeSet)
@@ -104,6 +112,8 @@ abstract class RealmBaseTasksRecyclerViewAdapter(
override val checklistItemScoreEvents: Flowable> = checklistItemScoreSubject.toFlowable(BackpressureStrategy.DROP)
protected var taskOpenEventsSubject = PublishSubject.create()
override val taskOpenEvents: Flowable = taskOpenEventsSubject.toFlowable(BackpressureStrategy.DROP)
+ protected var brokenTaskEventsSubject = PublishSubject.create()
+ override val brokenTaskEvents: Flowable = brokenTaskEventsSubject.toFlowable(BackpressureStrategy.DROP)
private val isDataValid: Boolean
get() = data?.isValid ?: false
@@ -128,7 +138,7 @@ abstract class RealmBaseTasksRecyclerViewAdapter(
override fun onBindViewHolder(holder: VH, position: Int) {
val item = getItem(position)
if (item != null) {
- holder.bind(item, position)
+ holder.bind(item, position, taskDisplayMode)
holder.errorButtonClicked = Action {
errorButtonEventsSubject.onNext("")
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RewardsRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RewardsRecyclerViewAdapter.kt
index 89ab638fa..304ef4c40 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RewardsRecyclerViewAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RewardsRecyclerViewAdapter.kt
@@ -30,9 +30,20 @@ class RewardsRecyclerViewAdapter(private var customRewards: OrderedRealmCollecti
override val checklistItemScoreEvents: Flowable> = checklistItemScoreSubject.toFlowable(BackpressureStrategy.DROP)
private var taskOpenEventsSubject = PublishSubject.create()
override val taskOpenEvents: Flowable = taskOpenEventsSubject.toFlowable(BackpressureStrategy.LATEST)
+ protected var brokenTaskEventsSubject = PublishSubject.create()
+ override val brokenTaskEvents: Flowable = brokenTaskEventsSubject.toFlowable(BackpressureStrategy.DROP)
private var purchaseCardSubject = PublishSubject.create()
val purchaseCardEvents: Flowable = purchaseCardSubject.toFlowable(BackpressureStrategy.LATEST)
+
+ override var taskDisplayMode: String = "standard"
+ set(value) {
+ if (field != value) {
+ field = value
+ notifyDataSetChanged()
+ }
+ }
+
private val inAppRewardCount: Int
get() {
if (inAppRewards?.isValid != true) return 0
@@ -55,8 +66,10 @@ class RewardsRecyclerViewAdapter(private var customRewards: OrderedRealmCollecti
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return if (viewType == VIEWTYPE_CUSTOM_REWARD) {
- RewardViewHolder(getContentView(parent), { task, direction -> taskScoreEventsSubject.onNext(Pair(task, direction)) }) {
+ RewardViewHolder(getContentView(parent), { task, direction -> taskScoreEventsSubject.onNext(Pair(task, direction)) }, {
task -> taskOpenEventsSubject.onNext(task)
+ }) {
+ task -> brokenTaskEventsSubject.onNext(task)
}
} else {
val viewHolder = ShopItemViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.row_shopitem, parent, false))
@@ -71,7 +84,7 @@ class RewardsRecyclerViewAdapter(private var customRewards: OrderedRealmCollecti
if (customRewards != null && position < customRewardCount) {
val reward = customRewards?.get(position) ?: return
val gold = user?.stats?.gp ?: 0.0
- (holder as? RewardViewHolder)?.bind(reward, position, reward.value < gold)
+ (holder as? RewardViewHolder)?.bind(reward, position, reward.value < gold, taskDisplayMode)
} else if (inAppRewards != null) {
val item = inAppRewards?.get(position - customRewardCount) ?: return
if (holder is ShopItemViewHolder) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TaskRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TaskRecyclerViewAdapter.kt
index 0fc57f5f3..1c2a0707e 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TaskRecyclerViewAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TaskRecyclerViewAdapter.kt
@@ -11,6 +11,8 @@ interface TaskRecyclerViewAdapter {
val errorButtonEvents: Flowable
+ var taskDisplayMode: String
+
fun updateData(tasks: OrderedRealmCollection?)
fun filter()
@@ -25,4 +27,5 @@ interface TaskRecyclerViewAdapter {
val taskScoreEvents: Flowable>
val checklistItemScoreEvents: Flowable>
val taskOpenEvents: Flowable
+ val brokenTaskEvents: Flowable
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TodosRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TodosRecyclerViewAdapter.kt
index 2afcc131d..f735ca531 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TodosRecyclerViewAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TodosRecyclerViewAdapter.kt
@@ -12,8 +12,10 @@ class TodosRecyclerViewAdapter(data: OrderedRealmCollection?, autoUpdate:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder =
TodoViewHolder(getContentView(parent), { task, direction -> taskScoreEventsSubject.onNext(Pair(task, direction)) },
- { task, item -> checklistItemScoreSubject.onNext(Pair(task, item))}) {
- task -> taskOpenEventsSubject.onNext(task)
- }
+ { task, item -> checklistItemScoreSubject.onNext(Pair(task, item))}, {
+ task -> taskOpenEventsSubject.onNext(task)
+ }) {
+ task -> brokenTaskEventsSubject.onNext(task)
+ }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/AboutFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/AboutFragment.kt
index c34f920ab..7eb5ba152 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/AboutFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/AboutFragment.kt
@@ -90,7 +90,7 @@ class AboutFragment : BaseMainFragment() {
private val versionName: String by lazy {
try {
@Suppress("DEPRECATION")
- activity?.packageManager?.getPackageInfo(activity?.packageName, 0)?.versionName ?: ""
+ activity?.packageManager?.getPackageInfo(activity?.packageName ?: "", 0)?.versionName ?: ""
} catch (e: PackageManager.NameNotFoundException) {
""
}
@@ -99,7 +99,7 @@ class AboutFragment : BaseMainFragment() {
private val versionCode: Int by lazy {
try {
@Suppress("DEPRECATION")
- activity?.packageManager?.getPackageInfo(activity?.packageName, 0)?.versionCode ?: 0
+ activity?.packageManager?.getPackageInfo(activity?.packageName ?: "", 0)?.versionCode ?: 0
} catch (e: PackageManager.NameNotFoundException) {
0
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/AchievementsFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/AchievementsFragment.kt
index 47cb45422..a21cbe23b 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/AchievementsFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/AchievementsFragment.kt
@@ -86,7 +86,15 @@ class AchievementsFragment: BaseMainFragment(), SwipeRefreshLayout.OnRefreshList
refreshLayout.setOnRefreshListener(this)
- compositeSubscription.add(userRepository.getAchievements().subscribe(Consumer> {
+ compositeSubscription.add(userRepository.getAchievements().map { achievements ->
+ achievements.sortedBy {
+ if (it.category == "onboarding") {
+ it.index
+ } else {
+ (it.category?.first()?.toInt() ?: 2) * it.index
+ }
+ }
+ }.subscribe(Consumer {
val entries = mutableListOf()
var lastCategory = ""
it.forEach { achievement ->
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt
index c66eb044f..f7c1f8ad8 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt
@@ -1,12 +1,16 @@
package com.habitrpg.android.habitica.ui.fragments
import android.content.Context
+import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.LayoutInflater
+import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
+import androidx.core.content.ContextCompat
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.tabs.TabLayout
+import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.ApiClient
import com.habitrpg.android.habitica.data.UserRepository
import com.habitrpg.android.habitica.helpers.RxErrorHandler
@@ -25,6 +29,8 @@ abstract class BaseMainFragment : BaseFragment() {
@Inject
lateinit var soundManager: SoundManager
+ protected var showsBackButton: Boolean = false
+
open val activity get() = getActivity() as? MainActivity
val tabLayout get() = activity?.binding?.detailTabs
val collapsingToolbar get() = activity?.binding?.toolbar
@@ -69,6 +75,12 @@ abstract class BaseMainFragment : BaseFragment() {
return null
}
+ override fun onResume() {
+ super.onResume()
+ activity?.drawerToggle?.isDrawerIndicatorEnabled = !showsBackButton
+ activity?.supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ }
+
private fun updateTabLayoutVisibility() {
if (this.usesTabLayout) {
tabLayout?.removeAllTabs()
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt
index b2039953a..50c0cabe0 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt
@@ -282,6 +282,14 @@ class NavigationDrawerFragment : DialogFragment() {
partyMenuItem?.transitionId = R.id.noPartyFragment
partyMenuItem?.bundle = null
}
+
+ val adventureGuideItem = getItemWithIdentifier(SIDEBAR_ADVENTURE_GUIDE)
+ if (configManager.enableAdventureGuide()) {
+ adventureGuideItem?.isVisible = !user.hasCompletedOnboarding
+ adventureGuideItem?.user = user
+ } else {
+ adventureGuideItem?.isVisible = false
+ }
}
override fun onDestroy() {
@@ -295,32 +303,54 @@ class NavigationDrawerFragment : DialogFragment() {
private fun initializeMenuItems() {
val items = ArrayList()
context?.let {context ->
+ val adventureItem = HabiticaDrawerItem(R.id.adventureGuideActivity, SIDEBAR_ADVENTURE_GUIDE)
+ adventureItem.itemViewType = 4
+ items.add(adventureItem)
items.add(HabiticaDrawerItem(R.id.tasksFragment, SIDEBAR_TASKS, context.getString(R.string.sidebar_tasks)))
items.add(HabiticaDrawerItem(R.id.skillsFragment, SIDEBAR_SKILLS, context.getString(R.string.sidebar_skills)))
items.add(HabiticaDrawerItem(R.id.statsFragment, SIDEBAR_STATS, context.getString(R.string.sidebar_stats)))
items.add(HabiticaDrawerItem(R.id.achievementsFragment, SIDEBAR_ACHIEVEMENTS, context.getString(R.string.sidebar_achievements)))
- items.add(HabiticaDrawerItem(0, SIDEBAR_SOCIAL, context.getString(R.string.sidebar_section_social), true))
- items.add(HabiticaDrawerItem(R.id.tavernFragment, SIDEBAR_TAVERN, context.getString(R.string.sidebar_tavern), isHeader = false))
- items.add(HabiticaDrawerItem(R.id.partyFragment, SIDEBAR_PARTY, context.getString(R.string.sidebar_party)))
- items.add(HabiticaDrawerItem(R.id.guildsOverviewFragment, SIDEBAR_GUILDS, context.getString(R.string.sidebar_guilds)))
- items.add(HabiticaDrawerItem(R.id.challengesOverviewFragment, SIDEBAR_CHALLENGES, context.getString(R.string.sidebar_challenges)))
- if (configManager.raiseShops()) {
+ if (configManager.reorderMenu()) {
items.add(HabiticaDrawerItem(0, SIDEBAR_INVENTORY, context.getString(R.string.sidebar_shops), true))
items.add(HabiticaDrawerItem(R.id.marketFragment, SIDEBAR_SHOPS_MARKET, context.getString(R.string.market)))
items.add(HabiticaDrawerItem(R.id.questShopFragment, SIDEBAR_SHOPS_QUEST, context.getString(R.string.questShop)))
items.add(HabiticaDrawerItem(R.id.seasonalShopFragment, SIDEBAR_SHOPS_SEASONAL, context.getString(R.string.seasonalShop)))
items.add(HabiticaDrawerItem(R.id.timeTravelersShopFragment, SIDEBAR_SHOPS_TIMETRAVEL, context.getString(R.string.timeTravelers)))
+
+ items.add(HabiticaDrawerItem(0, SIDEBAR_INVENTORY, context.getString(R.string.sidebar_section_inventory), true))
+ items.add(HabiticaDrawerItem(R.id.itemsFragment, SIDEBAR_ITEMS, context.getString(R.string.sidebar_items)))
+ items.add(HabiticaDrawerItem(R.id.equipmentOverviewFragment, SIDEBAR_EQUIPMENT, context.getString(R.string.sidebar_equipment)))
+ items.add(HabiticaDrawerItem(R.id.stableFragment, SIDEBAR_STABLE, context.getString(R.string.sidebar_stable)))
+ items.add(HabiticaDrawerItem(R.id.avatarOverviewFragment, SIDEBAR_AVATAR, context.getString(R.string.sidebar_avatar)))
+ items.add(HabiticaDrawerItem(R.id.gemPurchaseActivity, SIDEBAR_GEMS, context.getString(R.string.sidebar_gems)))
+ items.add(HabiticaDrawerItem(R.id.subscriptionPurchaseActivity, SIDEBAR_SUBSCRIPTION, context.getString(R.string.sidebar_subscription), isHeader = false))
+
+ items.add(HabiticaDrawerItem(0, SIDEBAR_SOCIAL, context.getString(R.string.sidebar_section_social), true))
+ items.add(HabiticaDrawerItem(R.id.tavernFragment, SIDEBAR_TAVERN, context.getString(R.string.sidebar_tavern), isHeader = false))
+ items.add(HabiticaDrawerItem(R.id.partyFragment, SIDEBAR_PARTY, context.getString(R.string.sidebar_party)))
+ items.add(HabiticaDrawerItem(R.id.guildsOverviewFragment, SIDEBAR_GUILDS, context.getString(R.string.sidebar_guilds)))
+ items.add(HabiticaDrawerItem(R.id.challengesOverviewFragment, SIDEBAR_CHALLENGES, context.getString(R.string.sidebar_challenges)))
+ } else {
+ items.add(HabiticaDrawerItem(0, SIDEBAR_SOCIAL, context.getString(R.string.sidebar_section_social), true))
+ items.add(HabiticaDrawerItem(R.id.tavernFragment, SIDEBAR_TAVERN, context.getString(R.string.sidebar_tavern), isHeader = false))
+ items.add(HabiticaDrawerItem(R.id.partyFragment, SIDEBAR_PARTY, context.getString(R.string.sidebar_party)))
+ items.add(HabiticaDrawerItem(R.id.guildsOverviewFragment, SIDEBAR_GUILDS, context.getString(R.string.sidebar_guilds)))
+ items.add(HabiticaDrawerItem(R.id.challengesOverviewFragment, SIDEBAR_CHALLENGES, context.getString(R.string.sidebar_challenges)))
+
+ items.add(HabiticaDrawerItem(0, SIDEBAR_INVENTORY, context.getString(R.string.sidebar_shops), true))
+ items.add(HabiticaDrawerItem(R.id.marketFragment, SIDEBAR_SHOPS_MARKET, context.getString(R.string.market)))
+ items.add(HabiticaDrawerItem(R.id.questShopFragment, SIDEBAR_SHOPS_QUEST, context.getString(R.string.questShop)))
+ items.add(HabiticaDrawerItem(R.id.seasonalShopFragment, SIDEBAR_SHOPS_SEASONAL, context.getString(R.string.seasonalShop)))
+ items.add(HabiticaDrawerItem(R.id.timeTravelersShopFragment, SIDEBAR_SHOPS_TIMETRAVEL, context.getString(R.string.timeTravelers)))
+
+ items.add(HabiticaDrawerItem(0, SIDEBAR_INVENTORY, context.getString(R.string.sidebar_section_inventory), true))
+ items.add(HabiticaDrawerItem(R.id.avatarOverviewFragment, SIDEBAR_AVATAR, context.getString(R.string.sidebar_avatar)))
+ items.add(HabiticaDrawerItem(R.id.equipmentOverviewFragment, SIDEBAR_EQUIPMENT, context.getString(R.string.sidebar_equipment)))
+ items.add(HabiticaDrawerItem(R.id.itemsFragment, SIDEBAR_ITEMS, context.getString(R.string.sidebar_items)))
+ items.add(HabiticaDrawerItem(R.id.stableFragment, SIDEBAR_STABLE, context.getString(R.string.sidebar_stable)))
+ items.add(HabiticaDrawerItem(R.id.gemPurchaseActivity, SIDEBAR_GEMS, context.getString(R.string.sidebar_gems)))
+ items.add(HabiticaDrawerItem(R.id.subscriptionPurchaseActivity, SIDEBAR_SUBSCRIPTION, context.getString(R.string.sidebar_subscription), isHeader = false))
}
- items.add(HabiticaDrawerItem(0, SIDEBAR_INVENTORY, context.getString(R.string.sidebar_section_inventory), true))
- if (!configManager.raiseShops()) {
- items.add(HabiticaDrawerItem(R.id.shopsFragment, SIDEBAR_SHOPS, context.getString(R.string.sidebar_shops)))
- }
- items.add(HabiticaDrawerItem(R.id.avatarOverviewFragment, SIDEBAR_AVATAR, context.getString(R.string.sidebar_avatar)))
- items.add(HabiticaDrawerItem(R.id.equipmentOverviewFragment, SIDEBAR_EQUIPMENT, context.getString(R.string.sidebar_equipment)))
- items.add(HabiticaDrawerItem(R.id.itemsFragment, SIDEBAR_ITEMS, context.getString(R.string.sidebar_items)))
- items.add(HabiticaDrawerItem(R.id.stableFragment, SIDEBAR_STABLE, context.getString(R.string.sidebar_stable)))
- items.add(HabiticaDrawerItem(R.id.gemPurchaseActivity, SIDEBAR_GEMS, context.getString(R.string.sidebar_gems)))
- items.add(HabiticaDrawerItem(R.id.subscriptionPurchaseActivity, SIDEBAR_SUBSCRIPTION, context.getString(R.string.sidebar_subscription), isHeader = false))
items.add(HabiticaDrawerItem(0, SIDEBAR_ABOUT_HEADER, context.getString(R.string.sidebar_about), true))
items.add(HabiticaDrawerItem(R.id.newsFragment, SIDEBAR_NEWS, context.getString(R.string.sidebar_news)))
items.add(HabiticaDrawerItem(R.id.supportMainFragment, SIDEBAR_HELP, context.getString(R.string.sidebar_help)))
@@ -498,6 +528,7 @@ class NavigationDrawerFragment : DialogFragment() {
const val SIDEBAR_SUBSCRIPTION = "subscription"
const val SIDEBAR_SUBSCRIPTION_PROMO = "subscriptionpromo"
const val SIDEBAR_G1G1_PROMO = "g1g1promo"
+ const val SIDEBAR_ADVENTURE_GUIDE = "adventureguide"
const val SIDEBAR_ABOUT_HEADER = "about_header"
const val SIDEBAR_NEWS = "news"
const val SIDEBAR_HELP = "help"
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarCustomizationFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarCustomizationFragment.kt
index c155503c9..5605a2f5d 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarCustomizationFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarCustomizationFragment.kt
@@ -49,7 +49,7 @@ class AvatarCustomizationFragment : BaseMainFragment() {
}
.subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(adapter.getUnlockCustomizationEvents()
- .flatMap { customization ->
+ .flatMap { customization ->
val user = this.user
if (user != null) {
userRepository.unlockPath(user, customization)
@@ -57,9 +57,11 @@ class AvatarCustomizationFragment : BaseMainFragment() {
Flowable.empty()
}
}
+ .flatMap { userRepository.retrieveUser(withTasks = false, forced = true) }
+ .flatMap { inventoryRepository.retrieveInAppRewards() }
.subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(adapter.getUnlockSetEvents()
- .flatMap { set ->
+ .flatMap { set ->
val user = this.user
if (user != null) {
userRepository.unlockPath(user, set)
@@ -67,6 +69,8 @@ class AvatarCustomizationFragment : BaseMainFragment() {
Flowable.empty()
}
}
+ .flatMap { userRepository.retrieveUser(withTasks = false, forced = true) }
+ .flatMap { inventoryRepository.retrieveInAppRewards() }
.subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarEquipmentFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarEquipmentFragment.kt
index 2e55d5d05..f840da8e6 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarEquipmentFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarEquipmentFragment.kt
@@ -40,7 +40,8 @@ class AvatarEquipmentFragment : BaseMainFragment() {
compositeSubscription.add(adapter.getSelectCustomizationEvents()
.flatMap { equipment ->
- inventoryRepository.equip(user, if (user?.preferences?.costume == true) "costume" else "equipped", equipment.key ?: "")
+ val key = (if (equipment.key?.isNotBlank() != true) activeEquipment else equipment.key) ?: ""
+ inventoryRepository.equip(user, if (user?.preferences?.costume == true) "costume" else "equipped", key)
}
.subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(adapter.getUnlockCustomizationEvents()
@@ -62,6 +63,7 @@ class AvatarEquipmentFragment : BaseMainFragment() {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ showsBackButton = true
super.onViewCreated(view, savedInstanceState)
arguments?.let {
val args = AvatarEquipmentFragmentArgs.fromBundle(it)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarOverviewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarOverviewFragment.kt
index 2cfb1db99..1ef9d578c 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarOverviewFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarOverviewFragment.kt
@@ -26,6 +26,7 @@ class AvatarOverviewFragment : BaseMainFragment(), AdapterView.OnItemSelectedLis
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ showsBackButton = true
super.onViewCreated(view, savedInstanceState)
binding.avatarSizeSpinner.onItemSelectedListener = this
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentDetailFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentDetailFragment.kt
index 464830b69..a2bb3eb0e 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentDetailFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentDetailFragment.kt
@@ -46,6 +46,7 @@ class EquipmentDetailFragment : BaseMainFragment() {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ showsBackButton = true
super.onViewCreated(view, savedInstanceState)
arguments?.let {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt
index 50ed093fd..389253f8f 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt
@@ -8,6 +8,7 @@ import android.view.ViewGroup
import android.view.Window
import android.widget.Button
import android.widget.TextView
+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
@@ -27,12 +28,13 @@ import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.Companion.showSna
import io.reactivex.functions.Consumer
import javax.inject.Inject
-class ItemRecyclerFragment : BaseFragment() {
+class ItemRecyclerFragment : BaseFragment(), androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener {
@Inject
lateinit var inventoryRepository: InventoryRepository
@Inject
lateinit var userRepository: UserRepository
+ val refreshLayout: SwipeRefreshLayout? by bindView(R.id.refreshLayout)
val recyclerView: RecyclerViewEmptySupport? by bindView(R.id.recyclerView)
val emptyView: View? by bindView(R.id.emptyView)
private val emptyTextView: TextView? by bindView(R.id.empty_text_view)
@@ -70,6 +72,7 @@ class ItemRecyclerFragment : BaseFragment() {
resetViews()
recyclerView?.setEmptyView(emptyView)
+ refreshLayout?.setOnRefreshListener(this)
emptyTextView?.text = getString(R.string.empty_items, itemTypeText)
val context = activity
@@ -191,6 +194,14 @@ class ItemRecyclerFragment : BaseFragment() {
outState.putString(ITEM_TYPE_KEY, this.itemType)
}
+ override fun onRefresh() {
+ refreshLayout?.isRefreshing = true
+ compositeSubscription.add(userRepository.retrieveUser(true, true)
+ .doOnTerminate {
+ refreshLayout?.isRefreshing = false
+ }.subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
+ }
+
private fun hatchPet(potion: HatchingPotion, egg: Egg) {
dismiss()
(activity as? MainActivity)?.hatchPet(potion, egg)
@@ -209,11 +220,16 @@ class ItemRecyclerFragment : BaseFragment() {
compositeSubscription.add(inventoryRepository.getOwnedItems(type)
.doOnNext { items ->
if (items.size > 0) {
- adapter?.updateData(items)
+ val filteredItems = if (isFeeding) {
+ items.where().notEqualTo("key", "Saddle").findAll()
+ } else {
+ items
+ }
+ adapter?.updateData(filteredItems)
}
}
.map { items -> items.mapNotNull { it.key } }
- .flatMap { inventoryRepository.getItems(itemClass, it.toTypedArray(), user) }
+ .flatMap { inventoryRepository.getItems(itemClass, it.toTypedArray()) }
.map {
val itemMap = mutableMapOf()
for (item in it) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemsFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemsFragment.kt
index 42ffe23b8..e49ea9a0d 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemsFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemsFragment.kt
@@ -53,30 +53,22 @@ class ItemsFragment : BaseMainFragment() {
viewPager?.adapter = object : FragmentPagerAdapter(fragmentManager) {
override fun getItem(position: Int): androidx.fragment.app.Fragment {
-
val fragment = ItemRecyclerFragment()
- when (position) {
- 0 -> {
- fragment.itemType = "eggs"
- }
- 1 -> {
- fragment.itemType = "hatchingPotions"
- }
- 2 -> {
- fragment.itemType = "food"
- }
- 3 -> {
- fragment.itemType = "quests"
- }
- 4 -> {
- fragment.itemType = "special"
- }
+ fragment.itemType = when (position) {
+ 0 -> "eggs"
+ 1 -> "hatchingPotions"
+ 2 -> "food"
+ 3 -> "quests"
+ 4 -> "special"
+ else -> ""
}
fragment.isHatching = false
fragment.isFeeding = false
- fragment.itemTypeText = this.getPageTitle(position).toString()
fragment.user = this@ItemsFragment.user
+ fragment.itemTypeText =
+ if (position == 4) getString(R.string.special_items)
+ else this.getPageTitle(position).toString()
return fragment
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopFragment.kt
index c3e3236c7..6729460b5 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopFragment.kt
@@ -234,7 +234,7 @@ class ShopFragment : BaseFragment() {
fun onItemPurchased(event: GearPurchasedEvent) {
if (Shop.MARKET == shopIdentifier) {
loadMarketGear()
- } else if (Shop.TIME_TRAVELERS_SHOP == shopIdentifier) {
+ } else {
loadShopInventory()
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopsFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopsFragment.kt
index 8280b1510..5dae10324 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopsFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopsFragment.kt
@@ -54,7 +54,6 @@ open class ShopsFragment : BaseMainFragment() {
this.usesTabLayout = false
tabLayout?.visibility = View.GONE
viewPager.currentItem = lockTab ?: 0
- viewPager.setOnTouchListener { _, _ -> true }
}
context?.let { FirebaseAnalytics.getInstance(it).logEvent("open_shop", bundleOf(Pair("shopIndex", lockTab))) }
@@ -85,7 +84,7 @@ open class ShopsFragment : BaseMainFragment() {
val fragment = ShopFragment()
- fragment.shopIdentifier = when (position) {
+ fragment.shopIdentifier = when (lockTab ?: position) {
0 -> Shop.MARKET
1 -> Shop.QUEST_SHOP
2 -> Shop.SEASONAL_SHOP
@@ -97,7 +96,7 @@ open class ShopsFragment : BaseMainFragment() {
return fragment
}
- override fun getCount(): Int = 4
+ override fun getCount(): Int = if (lockTab != null) 1 else 4
override fun getPageTitle(position: Int): CharSequence? {
return when (position) {
@@ -118,6 +117,6 @@ open class ShopsFragment : BaseMainFragment() {
private fun updateCurrencyView(user: User) {
currencyView.gold = user.stats?.gp ?: 0.0
currencyView.gems = user.gemCount.toDouble()
- currencyView.hourglasses = user.hourglassCount?.toDouble() ?: 0.0
+ currencyView.hourglasses = user.hourglassCount.toDouble()
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/MountDetailRecyclerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/MountDetailRecyclerFragment.kt
index 7da733378..988b5337f 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/MountDetailRecyclerFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/MountDetailRecyclerFragment.kt
@@ -8,14 +8,22 @@ import android.view.ViewGroup
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.extensions.getTranslatedType
import com.habitrpg.android.habitica.helpers.RxErrorHandler
+import com.habitrpg.android.habitica.models.inventory.Mount
+import com.habitrpg.android.habitica.models.inventory.Pet
+import com.habitrpg.android.habitica.models.inventory.StableSection
import com.habitrpg.android.habitica.models.user.OwnedMount
+import com.habitrpg.android.habitica.models.user.OwnedObject
+import com.habitrpg.android.habitica.models.user.OwnedPet
import com.habitrpg.android.habitica.ui.adapter.inventory.MountDetailRecyclerAdapter
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.helpers.bindView
+import io.reactivex.functions.BiFunction
import io.reactivex.functions.Consumer
+import io.realm.RealmResults
import javax.inject.Inject
@@ -47,24 +55,34 @@ class MountDetailRecyclerFragment : BaseMainFragment() {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ showsBackButton = true
super.onViewCreated(view, savedInstanceState)
arguments?.let {
val args = MountDetailRecyclerFragmentArgs.fromBundle(it)
- animalGroup = args.group
+ if (args.group != "drop") {
+ animalGroup = args.group
+ }
animalType = args.type
animalColor = args.color
}
layoutManager = androidx.recyclerview.widget.GridLayoutManager(activity, 2)
+ layoutManager?.spanSizeLookup = object : androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup() {
+ override fun getSpanSize(position: Int): Int {
+ return if (adapter?.getItemViewType(position) == 0 || adapter?.getItemViewType(position) == 1) {
+ layoutManager?.spanCount ?: 1
+ } else {
+ 1
+ }
+ }
+ }
recyclerView.layoutManager = layoutManager
recyclerView.addItemDecoration(MarginDecoration(activity))
adapter = recyclerView.adapter as? MountDetailRecyclerAdapter
if (adapter == null) {
- adapter = MountDetailRecyclerAdapter(null, true)
- adapter?.itemType = this.animalType
- adapter?.context = context
+ adapter = MountDetailRecyclerAdapter()
recyclerView.adapter = adapter
recyclerView.itemAnimator = SafeDefaultItemAnimator()
this.loadItems()
@@ -89,7 +107,7 @@ class MountDetailRecyclerFragment : BaseMainFragment() {
private fun setGridSpanCount(width: Int) {
var spanCount = 0
context?.resources?.let { resources
- val itemWidth: Float = resources.getDimension(R.dimen.pet_width)
+ val itemWidth: Float = resources.getDimension(R.dimen.mount_width)
spanCount = (width / itemWidth).toInt()
}
@@ -101,15 +119,38 @@ class MountDetailRecyclerFragment : BaseMainFragment() {
}
private fun loadItems() {
- if (animalType != null && animalGroup != null) {
- compositeSubscription.add(inventoryRepository.getOwnedMounts().firstElement()
+ if (animalType != null || animalGroup != null) {
+ compositeSubscription.add(inventoryRepository.getMounts(animalType, animalGroup, animalColor)
+ .zipWith(inventoryRepository.getOwnedMounts()
.map { ownedMounts ->
val mountMap = mutableMapOf()
ownedMounts.forEach { mountMap[it.key ?: ""] = it }
return@map mountMap
- }
- .subscribe(Consumer { adapter?.setOwnedMounts(it) }, RxErrorHandler.handleEmptyError()))
- compositeSubscription.add(inventoryRepository.getMounts(animalType!!, animalGroup!!, animalColor).firstElement().subscribe(Consumer { adapter?.updateData(it) }, RxErrorHandler.handleEmptyError()))
+ }.doOnNext {
+ adapter?.setOwnedMounts(it)
+ }, BiFunction, Map, List> { unsortedAnimals, ownedAnimals ->
+ val items = mutableListOf()
+ var lastMount: Mount? = null
+ var currentSection: StableSection? = null
+ for (mount in unsortedAnimals) {
+ if (mount.type == "wacky" || mount.type == "special") continue
+ if (mount.type != lastMount?.type) {
+ val title = context?.getString(R.string.pet_category, mount.getTranslatedType(context))
+ currentSection = StableSection(mount.type, title ?: "")
+ items.add(currentSection)
+ }
+ currentSection?.let {
+ it.totalCount += 1
+ if (ownedAnimals.containsKey(mount.key)) {
+ it.ownedCount += 1
+ }
+ }
+ items.add(mount)
+ lastMount = mount
+ }
+ items
+ })
+ .subscribe(Consumer { adapter?.setItemList(it) }, RxErrorHandler.handleEmptyError()))
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/PetDetailRecyclerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/PetDetailRecyclerFragment.kt
index 55e96dc9c..a9e9c4f2a 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/PetDetailRecyclerFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/PetDetailRecyclerFragment.kt
@@ -8,11 +8,12 @@ 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.events.commands.FeedCommand
+import com.habitrpg.android.habitica.extensions.getTranslatedType
import com.habitrpg.android.habitica.helpers.RxErrorHandler
-import com.habitrpg.android.habitica.models.inventory.Mount
-import com.habitrpg.android.habitica.models.inventory.Pet
+import com.habitrpg.android.habitica.models.inventory.*
import com.habitrpg.android.habitica.models.user.Items
import com.habitrpg.android.habitica.models.user.OwnedMount
+import com.habitrpg.android.habitica.models.user.OwnedObject
import com.habitrpg.android.habitica.models.user.OwnedPet
import com.habitrpg.android.habitica.ui.adapter.inventory.PetDetailRecyclerAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
@@ -21,9 +22,12 @@ import com.habitrpg.android.habitica.ui.helpers.MarginDecoration
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.helpers.resetViews
+import io.reactivex.functions.BiFunction
import io.reactivex.functions.Consumer
+import io.reactivex.rxkotlin.combineLatest
import io.realm.RealmResults
import org.greenrobot.eventbus.Subscribe
+import java.util.ArrayList
import javax.inject.Inject
class PetDetailRecyclerFragment : BaseMainFragment() {
@@ -33,9 +37,9 @@ class PetDetailRecyclerFragment : BaseMainFragment() {
private val recyclerView: androidx.recyclerview.widget.RecyclerView by bindView(R.id.recyclerView)
- var adapter: PetDetailRecyclerAdapter = PetDetailRecyclerAdapter(null, true)
- var animalType: String = ""
- var animalGroup: String = ""
+ var adapter: PetDetailRecyclerAdapter = PetDetailRecyclerAdapter()
+ var animalType: String? = null
+ var animalGroup: String? = null
var animalColor: String? = null
internal var layoutManager: androidx.recyclerview.widget.GridLayoutManager? = null
@@ -59,23 +63,38 @@ class PetDetailRecyclerFragment : BaseMainFragment() {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ showsBackButton = true
super.onViewCreated(view, savedInstanceState)
arguments?.let {
val args = MountDetailRecyclerFragmentArgs.fromBundle(it)
- animalGroup = args.group
+ if (args.group != "drop") {
+ animalGroup = args.group
+ }
animalType = args.type
animalColor = args.color
}
+
resetViews()
layoutManager = androidx.recyclerview.widget.GridLayoutManager(getActivity(), 2)
+ layoutManager?.spanSizeLookup = object : androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup() {
+ override fun getSpanSize(position: Int): Int {
+ return if (adapter.getItemViewType(position) == 0 || adapter.getItemViewType(position) == 1) {
+ layoutManager?.spanCount ?: 1
+ } else {
+ 1
+ }
+ }
+ }
recyclerView.layoutManager = layoutManager
recyclerView.addItemDecoration(MarginDecoration(getActivity()))
-
- adapter.context = this.getActivity()
- adapter.itemType = this.animalType
+ adapter.animalIngredientsRetriever = {
+ val egg = inventoryRepository.getItems(Egg::class.java, arrayOf(it.animal)).firstElement().blockingGet().firstOrNull()
+ val potion = inventoryRepository.getItems(HatchingPotion::class.java, arrayOf(it.color)).firstElement().blockingGet().firstOrNull()
+ Pair(egg as? Egg, potion as? HatchingPotion)
+ }
recyclerView.adapter = adapter
recyclerView.itemAnimator = SafeDefaultItemAnimator()
this.loadItems()
@@ -84,9 +103,15 @@ class PetDetailRecyclerFragment : BaseMainFragment() {
.flatMap { key -> inventoryRepository.equip(user, "pet", key) }
.subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
+
view.post { setGridSpanCount(view.width) }
}
+ override fun onResume() {
+ super.onResume()
+ activity?.title = animalType
+ }
+
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString(ANIMAL_TYPE_KEY, this.animalType)
@@ -95,7 +120,8 @@ class PetDetailRecyclerFragment : BaseMainFragment() {
private fun setGridSpanCount(width: Int) {
var spanCount = 0
if (context != null && context?.resources != null) {
- val itemWidth: Float = context?.resources?.getDimension(R.dimen.pet_width) ?: 120f
+ val animalWidth = R.dimen.pet_width
+ val itemWidth: Float = context?.resources?.getDimension(animalWidth) ?: 0.toFloat()
spanCount = (width / itemWidth).toInt()
}
@@ -103,18 +129,10 @@ class PetDetailRecyclerFragment : BaseMainFragment() {
spanCount = 1
}
layoutManager?.spanCount = spanCount
- layoutManager?.requestLayout()
}
private fun loadItems() {
- if (animalType.isNotEmpty() && animalGroup.isNotEmpty()) {
- compositeSubscription.add(inventoryRepository.getOwnedPets()
- .map { ownedMounts ->
- val mountMap = mutableMapOf()
- ownedMounts.forEach { mountMap[it.key ?: ""] = it }
- return@map mountMap
- }
- .subscribe(Consumer { adapter.setOwnedPets(it) }, RxErrorHandler.handleEmptyError()))
+ if (animalType?.isNotEmpty() == true || animalGroup?.isNotEmpty() == true) {
compositeSubscription.add(inventoryRepository.getOwnedMounts()
.map { ownedMounts ->
val mountMap = mutableMapOf()
@@ -122,11 +140,41 @@ class PetDetailRecyclerFragment : BaseMainFragment() {
return@map mountMap
}
.subscribe(Consumer { adapter.setOwnedMounts(it) }, RxErrorHandler.handleEmptyError()))
- compositeSubscription.add(inventoryRepository.getPets(animalType, animalGroup, animalColor).firstElement().subscribe(Consumer> { adapter.updateData(it) }, RxErrorHandler.handleEmptyError()))
+ compositeSubscription.add(inventoryRepository.getOwnedItems(true).subscribe(Consumer { adapter.setOwnedItems(it) }, RxErrorHandler.handleEmptyError()))
+ compositeSubscription.add(inventoryRepository.getPets(animalType, animalGroup, animalColor).combineLatest(inventoryRepository.getOwnedPets()
+ .map { ownedPets ->
+ val petMap = mutableMapOf()
+ ownedPets.forEach { petMap[it.key ?: ""] = it }
+ return@map petMap
+ }.doOnNext {
+ adapter.setOwnedPets(it)
+ }).map {
+ val items = mutableListOf()
+ var lastPet: Pet? = null
+ var currentSection: StableSection? = null
+ for (pet in it.first) {
+ if (pet.type == "wacky" || pet.type == "special") continue
+ if (pet.type != lastPet?.type) {
+ val title = context?.getString(R.string.pet_category, pet.getTranslatedType(context))
+ currentSection = StableSection(pet.type, title ?: "")
+ items.add(currentSection)
+ }
+ currentSection?.let {section ->
+ section.totalCount += 1
+ if (it.second.containsKey(pet.key)) {
+ section.ownedCount += 1
+ }
+ }
+ items.add(pet)
+ lastPet = pet
+ }
+ items
+ }.subscribe(Consumer { adapter.setItemList(it) }, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(inventoryRepository.getMounts(animalType, animalGroup, animalColor).subscribe(Consumer> { adapter.setExistingMounts(it) }, RxErrorHandler.handleEmptyError()))
}
}
+
@Subscribe
fun showFeedingDialog(event: FeedCommand) {
if (event.usingPet == null || event.usingFood == null) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableRecyclerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableRecyclerFragment.kt
index 2919a411a..0fd1eb5c7 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableRecyclerFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableRecyclerFragment.kt
@@ -10,12 +10,13 @@ import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.extensions.getTranslatedType
import com.habitrpg.android.habitica.extensions.inflate
+import com.habitrpg.android.habitica.helpers.AppConfigManager
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.inventory.Animal
-import com.habitrpg.android.habitica.models.user.OwnedMount
-import com.habitrpg.android.habitica.models.user.OwnedObject
-import com.habitrpg.android.habitica.models.user.OwnedPet
-import com.habitrpg.android.habitica.models.user.User
+import com.habitrpg.android.habitica.models.inventory.Egg
+import com.habitrpg.android.habitica.models.inventory.HatchingPotion
+import com.habitrpg.android.habitica.models.inventory.StableSection
+import com.habitrpg.android.habitica.models.user.*
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.adapter.inventory.StableRecyclerAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
@@ -31,6 +32,8 @@ class StableRecyclerFragment : BaseFragment() {
@Inject
lateinit var inventoryRepository: InventoryRepository
+ @Inject
+ lateinit var configManager: AppConfigManager
private val recyclerView: RecyclerViewEmptySupport? by bindView(R.id.recyclerView)
private val emptyView: TextView? by bindView(R.id.emptyView)
@@ -47,7 +50,7 @@ class StableRecyclerFragment : BaseFragment() {
this.itemType = savedInstanceState.getString(ITEM_TYPE_KEY, "")
}
- return container?.inflate(R.layout.fragment_recyclerview)
+ return container?.inflate(R.layout.fragment_recyclerview_stable)
}
override fun onDestroy() {
@@ -86,11 +89,21 @@ class StableRecyclerFragment : BaseFragment() {
adapter = recyclerView?.adapter as? StableRecyclerAdapter
if (adapter == null) {
adapter = StableRecyclerAdapter()
- adapter?.activity = this.activity as? MainActivity
+ adapter?.animalIngredientsRetriever = {
+ val egg = inventoryRepository.getItems(Egg::class.java, arrayOf(it.animal)).firstElement().blockingGet().firstOrNull()
+ val potion = inventoryRepository.getItems(HatchingPotion::class.java, arrayOf(it.color)).firstElement().blockingGet().firstOrNull()
+ Pair(egg as? Egg, potion as? HatchingPotion)
+ }
adapter?.itemType = this.itemType
- adapter?.context = context
+ adapter?.shopSpriteSuffix = configManager.shopSpriteSuffix()
recyclerView?.adapter = adapter
recyclerView?.itemAnimator = SafeDefaultItemAnimator()
+
+ adapter?.let {
+ compositeSubscription.add(it.getEquipFlowable()
+ .flatMap { key -> inventoryRepository.equip(user, if (itemType == "pets") "pet" else "mount", key) }
+ .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
+ }
}
this.loadItems()
@@ -106,7 +119,8 @@ class StableRecyclerFragment : BaseFragment() {
private fun setGridSpanCount(width: Int) {
var spanCount = 0
if (context != null && context?.resources != null) {
- val itemWidth: Float = context?.resources?.getDimension(R.dimen.pet_width) ?: 0.toFloat()
+ val animalWidth = if (itemType == "pets") R.dimen.pet_width else R.dimen.mount_width
+ val itemWidth: Float = context?.resources?.getDimension(animalWidth) ?: 0.toFloat()
spanCount = (width / itemWidth).toInt()
}
@@ -135,6 +149,17 @@ class StableRecyclerFragment : BaseFragment() {
animalMap
}
+ compositeSubscription.add(inventoryRepository.getItems(Egg::class.java)
+ .map {
+ val eggMap = mutableMapOf()
+ it.forEach { egg ->
+ eggMap[egg.key] = egg as Egg
+ }
+ eggMap
+ }
+ .subscribe(Consumer {
+ adapter?.setEggs(it)
+ }, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(observable.zipWith(ownedObservable, BiFunction, Map, ArrayList> { unsortedAnimals, ownedAnimals ->
mapAnimals(unsortedAnimals, ownedAnimals)
}).subscribe(Consumer { items -> adapter?.setItemList(items) }, RxErrorHandler.handleEmptyError()))
@@ -143,37 +168,52 @@ class StableRecyclerFragment : BaseFragment() {
private fun mapAnimals(unsortedAnimals: RealmResults, ownedAnimals: Map): ArrayList {
val items = ArrayList()
var lastAnimal: Animal = unsortedAnimals[0] ?: return items
- var lastSectionTitle = ""
-
+ var lastSection: StableSection? = null
for (animal in unsortedAnimals) {
- val identifier = if (animal.animal.isNotEmpty() && animal.type != "special") animal.animal else animal.key
+ val identifier = if (animal.animal.isNotEmpty() && (animal.type != "special" && animal.type != "wacky")) animal.animal else animal.key
val lastIdentifier = if (lastAnimal.animal.isNotEmpty()) lastAnimal.animal else lastAnimal.key
- if (identifier != lastIdentifier || animal === unsortedAnimals[unsortedAnimals.size - 1]) {
- if (!((lastAnimal.type == "premium" || lastAnimal.type == "special") && lastAnimal.numberOwned == 0)) {
+ if (animal.type == "premium") {
+ if (!items.contains(lastAnimal)) {
+ items.add(lastAnimal)
+ }
+ lastAnimal = items.first { (it as? Animal)?.animal == animal.animal } as Animal
+ } else if (identifier != lastIdentifier || animal === unsortedAnimals[unsortedAnimals.size - 1]) {
+ if (!((lastAnimal.type == "special") && lastAnimal.numberOwned == 0) && !items.contains(lastAnimal)) {
items.add(lastAnimal)
}
lastAnimal = animal
}
- if (animal.type != lastSectionTitle) {
- if (items.size > 0 && items[items.size - 1].javaClass == String::class.java) {
+
+
+ if (animal.type != lastSection?.key && animal.type != "premium") {
+ if (items.size > 0 && items[items.size - 1].javaClass == StableSection::class.java) {
items.removeAt(items.size - 1)
}
- items.add(animal.getTranslatedType(context))
- lastSectionTitle = animal.type
+ val title = if (itemType == "pets") {
+ context?.getString(R.string.pet_category, animal.getTranslatedType(context))
+ } else {
+ context?.getString(R.string.mount_category, animal.getTranslatedType(context))
+ }
+ val section = StableSection(animal.type, title ?: "")
+ items.add(section)
+ lastSection = section
}
- when (itemType) {
+ val isOwned = when (itemType) {
"pets" -> {
val ownedPet = ownedAnimals[animal?.key] as? OwnedPet
- if (ownedPet?.trained ?: 0 > 0) {
- lastAnimal.numberOwned += 1
- }
+ ownedPet?.trained ?: 0 > 0
}
"mounts" -> {
val ownedMount = ownedAnimals[animal?.key] as? OwnedMount
- if (ownedMount?.owned == true) {
- lastAnimal.numberOwned = lastAnimal.numberOwned + 1
- }
+ ownedMount?.owned == true
}
+ else -> false
+ }
+ lastAnimal.totalNumber += 1
+ lastSection?.totalCount = (lastSection?.totalCount ?: 0) + 1
+ if (isOwned) {
+ lastAnimal.numberOwned += 1
+ lastSection?.ownedCount = (lastSection?.ownedCount ?: 0) + 1
}
}
if (!((lastAnimal.type == "premium" || lastAnimal.type == "special") && lastAnimal.numberOwned == 0)) {
@@ -187,6 +227,5 @@ class StableRecyclerFragment : BaseFragment() {
companion object {
private const val ITEM_TYPE_KEY = "CLASS_TYPE_KEY"
private const val HEADER_VIEW_TYPE = 0
- private const val SECTION_VIEW_TYPE = 1
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PreferencesFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PreferencesFragment.kt
index 37a0ca815..bad74004c 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PreferencesFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PreferencesFragment.kt
@@ -78,6 +78,14 @@ class PreferencesFragment : BasePreferencesFragment(), SharedPreferences.OnShare
val themePreference = findPreference("theme_name") as? ListPreference
themePreference?.isVisible = configManager.testingLevel() == AppTestingLevel.ALPHA || BuildConfig.DEBUG
themePreference?.summary = themePreference?.entry
+
+
+ val taskDisplayPreference = findPreference("task_display") as? ListPreference
+ if (configManager.enableTaskDisplayMode()) {
+ taskDisplayPreference?.summary = taskDisplayPreference?.entry
+ } else {
+ taskDisplayPreference?.isVisible = false
+ }
}
override fun onResume() {
@@ -224,6 +232,10 @@ class PreferencesFragment : BasePreferencesFragment(), SharedPreferences.OnShare
apiClient.updateServerUrl(sharedPreferences.getString(key, ""))
findPreference(key).summary = sharedPreferences.getString(key, "")
}
+ "task_display" -> {
+ val preference = findPreference(key) as ListPreference
+ preference.summary = preference.entry
+ }
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/NoPartyFragmentFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/NoPartyFragmentFragment.kt
index d4bb074f8..72d4d190a 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/NoPartyFragmentFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/NoPartyFragmentFragment.kt
@@ -65,6 +65,17 @@ class NoPartyFragmentFragment : BaseMainFragment() {
invitations_view.rejectCall = {
socialRepository.rejectGroupInvite(it).subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
+ invitationWrapper.visibility = View.GONE
+ }
+
+ invitations_view.setLeader = { leader ->
+ compositeSubscription.add(
+ socialRepository.getMember(leader)
+ .subscribe(Consumer {
+ invitations_view.avatarView.setAvatar(it)
+ invitations_view.textView.text = getString(R.string.invitation_title,it.displayName,invitations_view.groupName)
+ }, RxErrorHandler.handleEmptyError())
+ )
}
username_textview.setOnClickListener {
@@ -77,7 +88,7 @@ class NoPartyFragmentFragment : BaseMainFragment() {
}
}
- craetePartyButton.setOnClickListener {
+ createPartyButton.setOnClickListener {
val bundle = Bundle()
bundle.putString("groupType", "party")
bundle.putString("leader", user?.id)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeDetailFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeDetailFragment.kt
index 77a260823..17bd3a0ae 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeDetailFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeDetailFragment.kt
@@ -144,7 +144,9 @@ class ChallengeDetailFragment: BaseMainFragment() {
}, RxErrorHandler.handleEmptyError()))
}
- joinButton?.setOnClickListener { challenge?.let { challenge -> challengeRepository.joinChallenge(challenge).subscribe(Consumer {}, RxErrorHandler.handleEmptyError()) } }
+ joinButton?.setOnClickListener { challenge?.let { challenge -> challengeRepository.joinChallenge(challenge)
+ .flatMap { userRepository.retrieveUser(true) }
+ .subscribe(Consumer {}, RxErrorHandler.handleEmptyError()) } }
leaveButton?.setOnClickListener { showChallengeLeaveDialog() }
refresh()
@@ -353,34 +355,16 @@ class ChallengeDetailFragment: BaseMainFragment() {
val context = context ?: return
val alert = HabiticaAlertDialog(context)
alert.setTitle(this.getString(R.string.challenge_leave_title))
- alert.setMessage(this.getString(R.string.challenge_leave_text, challenge?.name ?: ""))
- alert.addButton(R.string.yes, true) { dialog, _ ->
- dialog.dismiss()
- showRemoveTasksDialog(Consumer { keepTasks ->
- val challenge = challenge ?: return@Consumer
- challengeRepository.leaveChallenge(challenge, keepTasks).subscribe(Consumer {}, RxErrorHandler.handleEmptyError())
- })
+ alert.setMessage(this.getString(R.string.challenge_leave_description))
+ alert.addButton(R.string.leave_keep_tasks, true) { dialog, _ ->
+ val challenge = challenge ?: return@addButton
+ challengeRepository.leaveChallenge(challenge, "keep-all").subscribe(Consumer {}, RxErrorHandler.handleEmptyError())
}
- alert.addButton(R.string.no, false) { dialog, _ ->
- dialog.dismiss()
+ alert.addButton(R.string.leave_delte_tasks, false, true) { dialog, _ ->
+ val challenge = challenge ?: return@addButton
+ challengeRepository.leaveChallenge(challenge, "remove-all").subscribe(Consumer {}, RxErrorHandler.handleEmptyError())
}
+ alert.setExtraCloseButtonVisibility(View.VISIBLE)
alert.show()
}
-
- private fun showRemoveTasksDialog(callback: Consumer) {
- context?.let {
- val alert = HabiticaAlertDialog(it)
- alert.setTitle(this.getString(R.string.challenge_remove_tasks_title))
- alert.setMessage(this.getString(R.string.challenge_remove_tasks_text))
- alert.addButton(R.string.remove_tasks, false) { dialog, _ ->
- callback.accept("remove-all")
- dialog.dismiss()
- }
- alert.addButton(R.string.keep_tasks, false) { dialog, _ ->
- callback.accept("keep-all")
- dialog.dismiss()
- }
- alert.show()
- }
- }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyDetailFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyDetailFragment.kt
index 60c089713..9a3e076f5 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyDetailFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyDetailFragment.kt
@@ -24,6 +24,7 @@ import com.habitrpg.android.habitica.models.members.Member
import com.habitrpg.android.habitica.models.social.Group
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.modules.AppModule
+import com.habitrpg.android.habitica.ui.AvatarView
import com.habitrpg.android.habitica.ui.activities.FullProfileActivity
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
@@ -44,7 +45,7 @@ import kotlinx.coroutines.launch
import net.pherth.android.emoji_library.EmojiEditText
import javax.inject.Inject
import javax.inject.Named
-
+import kotlinx.android.synthetic.main.fragment_party_detail.invitations_view
class PartyDetailFragment : BaseFragment() {
@@ -62,6 +63,8 @@ class PartyDetailFragment : BaseFragment() {
private val refreshLayout: androidx.swiperefreshlayout.widget.SwipeRefreshLayout? by bindView(R.id.refreshLayout)
private val partyInvitationWrapper: ViewGroup? by bindView(R.id.party_invitation_wrapper)
private val invitationsView: InvitationsView? by bindView(R.id.invitations_view)
+ private val inviteLeaderAvatarView: AvatarView? by bindView(R.id.groupleader_avatar_view)
+ private val inviteLeaderTextView: TextView? by bindView(R.id.groupleader_text_view)
private val titleView: TextView? by bindView(R.id.title_view)
private val descriptionView: TextView? by bindView(R.id.description_view)
private val newQuestButton: Button? by bindView(R.id.new_quest_button)
@@ -105,6 +108,8 @@ class PartyDetailFragment : BaseFragment() {
questDetailButton?.setOnClickListener { questDetailButtonClicked() }
leaveButton?.setOnClickListener { leaveParty() }
+ invitationsView?.setLeader = null
+
invitationsView?.acceptCall = {
viewModel?.joinGroup(it) {
compositeSubscription.add(userRepository.retrieveUser(false)
@@ -188,7 +193,30 @@ class PartyDetailFragment : BaseFragment() {
if ((user.invitations?.parties?.count() ?: 0) > 0) {
partyInvitationWrapper?.visibility = View.VISIBLE
- user.invitations?.parties?.let { invitationsView?.setInvitations(it) }
+ user.invitations?.parties?.let {
+ for (invitation in it){
+ val leaderID = invitation.inviter
+ val groupName = invitation.name
+
+ leaderID.let {
+ compositeSubscription.add(
+ socialRepository.getMember(it)
+ .subscribe(Consumer {
+ inviteLeaderAvatarView?.setAvatar(it)
+ inviteLeaderTextView?.text = getString(R.string.invitation_title,it.displayName,groupName)
+ }, RxErrorHandler.handleEmptyError())
+ )
+ }
+
+ view?.findViewById(R.id.accept_button)?.setOnClickListener {
+ invitation.id?.let { it1 -> invitations_view.acceptCall?.invoke(it1) }
+ }
+
+ view?.findViewById(R.id.reject_button)?.setOnClickListener {
+ invitation.id?.let { it1 -> invitations_view.rejectCall?.invoke(it1) }
+ }
+ }
+ }
} else {
partyInvitationWrapper?.visibility = View.GONE
}
@@ -335,11 +363,13 @@ class PartyDetailFragment : BaseFragment() {
alert.addButton(R.string.keep_challenges, true) { _, _ ->
viewModel?.leaveGroup(true) {
fragmentManager?.popBackStack()
+ MainNavigationController.navigate(R.id.noPartyFragment)
}
}
alert.addButton(R.string.leave_challenges, true) { _, _ ->
viewModel?.leaveGroup(false) {
fragmentManager?.popBackStack()
+ MainNavigationController.navigate(R.id.noPartyFragment)
}
}
alert.addButton(R.string.no, false)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/BugFixFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/BugFixFragment.kt
index f5512c9ab..fa687803f 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/BugFixFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/BugFixFragment.kt
@@ -57,7 +57,7 @@ class BugFixFragment: BaseMainFragment() {
private val versionName: String by lazy {
try {
@Suppress("DEPRECATION")
- activity?.packageManager?.getPackageInfo(activity?.packageName, 0)?.versionName ?: ""
+ activity?.packageManager?.getPackageInfo(activity?.packageName ?: "", 0)?.versionName ?: ""
} catch (e: PackageManager.NameNotFoundException) {
""
}
@@ -66,7 +66,7 @@ class BugFixFragment: BaseMainFragment() {
private val versionCode: Int by lazy {
try {
@Suppress("DEPRECATION")
- activity?.packageManager?.getPackageInfo(activity?.packageName, 0)?.versionCode ?: 0
+ activity?.packageManager?.getPackageInfo(activity?.packageName ?: "", 0)?.versionCode ?: 0
} catch (e: PackageManager.NameNotFoundException) {
0
}
@@ -77,30 +77,30 @@ class BugFixFragment: BaseMainFragment() {
val deviceName = deviceInfo?.name ?: DeviceName.getDeviceName()
val manufacturer = deviceInfo?.manufacturer ?: Build.MANUFACTURER
var bodyOfEmail = "Device: $manufacturer $deviceName" +
- " \nAndroid Version: $version"+
- " \nAppVersion: " + getString(R.string.version_info, versionName, versionCode)
+ " %0AAndroid Version: $version"+
+ " %0AAppVersion: " + getString(R.string.version_info, versionName, versionCode)
if (appConfigManager.testingLevel().name != AppTestingLevel.PRODUCTION.name) {
bodyOfEmail += " ${appConfigManager.testingLevel().name}"
}
- bodyOfEmail += " \nUser ID: $userId"
+ bodyOfEmail += " %0AUser ID: $userId"
val user = this.user
if (user != null) {
- bodyOfEmail += " \nLevel: " + (user.stats?.lvl ?: 0) +
- " \nClass: " + (if (user.preferences?.disableClasses == true) "Disabled" else (user.stats?.habitClass ?: "None")) +
- " \nIs in Inn: " + (user.preferences?.sleep ?: false) +
- " \nUses Costume: " + (user.preferences?.costume ?: false) +
- " \nCustom Day Start: " + (user.preferences?.dayStart ?: 0) +
- " \nTimezone Offset: " + (user.preferences?.timezoneOffset ?: 0)
+ bodyOfEmail += " %0ALevel: " + (user.stats?.lvl ?: 0) +
+ " %0AClass: " + (if (user.preferences?.disableClasses == true) "Disabled" else (user.stats?.habitClass ?: "None")) +
+ " %0AIs in Inn: " + (user.preferences?.sleep ?: false) +
+ " %0AUses Costume: " + (user.preferences?.costume ?: false) +
+ " %0ACustom Day Start: " + (user.preferences?.dayStart ?: 0) +
+ " %0ATimezone Offset: " + (user.preferences?.timezoneOffset ?: 0)
}
- bodyOfEmail += " \nDetails:\n"
+ bodyOfEmail += " %0ADetails:%0A"
activity?.let {
val emailIntent = Intent(Intent.ACTION_SENDTO)
val mailto = "mailto:" + appConfigManager.supportEmail() +
- "&subject=" + Uri.encode(subject) +
+ "?subject=" + Uri.encode(subject) +
"&body=" + Uri.encode(bodyOfEmail)
emailIntent.data = Uri.parse(mailto);
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/SupportMainFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/SupportMainFragment.kt
index 3b43d6b69..0c64c7a48 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/SupportMainFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/SupportMainFragment.kt
@@ -8,6 +8,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.core.net.toUri
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.FAQRepository
@@ -51,7 +52,11 @@ class SupportMainFragment : BaseMainFragment() {
MainNavigationController.navigate(R.id.bugFixFragment)
}
binding.suggestionsFeedbackWrapper.setOnClickListener {
- sendEmail("[Android] Feedback")
+ if (appConfigManager.feedbackURL().isNotBlank()) {
+ val uriUrl = appConfigManager.feedbackURL().toUri()
+ val launchBrowser = Intent(Intent.ACTION_VIEW, uriUrl)
+ startActivity(launchBrowser)
+ }
}
compositeSubscription.add(Completable.fromAction {
@@ -71,58 +76,4 @@ class SupportMainFragment : BaseMainFragment() {
override fun injectFragment(component: UserComponent) {
component.inject(this)
}
-
- private val versionName: String by lazy {
- try {
- @Suppress("DEPRECATION")
- activity?.packageManager?.getPackageInfo(activity?.packageName, 0)?.versionName ?: ""
- } catch (e: PackageManager.NameNotFoundException) {
- ""
- }
- }
-
- private val versionCode: Int by lazy {
- try {
- @Suppress("DEPRECATION")
- activity?.packageManager?.getPackageInfo(activity?.packageName, 0)?.versionCode ?: 0
- } catch (e: PackageManager.NameNotFoundException) {
- 0
- }
- }
-
- private fun sendEmail(subject: String) {
- val version = Build.VERSION.SDK_INT
- val deviceName = deviceInfo?.name ?: DeviceName.getDeviceName()
- val manufacturer = deviceInfo?.manufacturer ?: Build.MANUFACTURER
- var bodyOfEmail = "Device: $manufacturer $deviceName" +
- " \nAndroid Version: $version"+
- " \nAppVersion: " + getString(R.string.version_info, versionName, versionCode)
-
- if (appConfigManager.testingLevel().name != AppTestingLevel.PRODUCTION.name) {
- bodyOfEmail += " ${appConfigManager.testingLevel().name}"
- }
- bodyOfEmail += " \nUser ID: $userId"
-
- val user = this.user
- if (user != null) {
- bodyOfEmail += " \nLevel: " + (user.stats?.lvl ?: 0) +
- " \nClass: " + (if (user.preferences?.disableClasses == true) "Disabled" else (user.stats?.habitClass ?: "None")) +
- " \nIs in Inn: " + (user.preferences?.sleep ?: false) +
- " \nUses Costume: " + (user.preferences?.costume ?: false) +
- " \nCustom Day Start: " + (user.preferences?.dayStart ?: 0) +
- " \nTimezone Offset: " + (user.preferences?.timezoneOffset ?: 0)
- }
-
- bodyOfEmail += " \nDetails:\n"
-
- activity?.let {
- val emailIntent = Intent(Intent.ACTION_SENDTO)
- val mailto = "mailto:" + appConfigManager.supportEmail() +
- "&subject=" + Uri.encode(subject) +
- "&body=" + Uri.encode(bodyOfEmail)
- emailIntent.data = Uri.parse(mailto);
-
- startActivity(Intent.createChooser(emailIntent, "Choose an Email client :"))
- }
- }
}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/RewardsRecyclerviewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/RewardsRecyclerviewFragment.kt
index 319abed3f..f8c0aa044 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/RewardsRecyclerviewFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/RewardsRecyclerviewFragment.kt
@@ -61,6 +61,7 @@ class RewardsRecyclerviewFragment : TaskRecyclerViewFragment() {
val intent = Intent(activity, SkillMemberActivity::class.java)
startActivityForResult(intent, 11)
}, RxErrorHandler.handleEmptyError())?.let { compositeSubscription.add(it) }
+ recyclerAdapter?.brokenTaskEvents?.subscribeWithErrorHandler(Consumer { showBrokenChallengeDialog(it) })?.let { compositeSubscription.add(it) }
}
override fun getLayoutManager(context: Context?): LinearLayoutManager {
@@ -99,7 +100,7 @@ class RewardsRecyclerviewFragment : TaskRecyclerViewFragment() {
userRepository.useSkill(user,
selectedCard?.key ?: "",
"member",
- data.getStringExtra("member_id"))
+ data.getStringExtra("member_id") ?: "")
.subscribeWithErrorHandler(Consumer {
val activity = (activity as? MainActivity) ?: return@Consumer
HabiticaSnackbar.showSnackbar(activity.snackbarContainer,
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt
index ef6f1a1a3..5e9e0c175 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt
@@ -38,6 +38,7 @@ import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
import com.habitrpg.android.habitica.ui.viewHolders.tasks.BaseTaskViewHolder
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
+import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.Consumer
import java.util.*
@@ -102,6 +103,8 @@ open class TaskRecyclerViewFragment : BaseFragment(), androidx.swiperefreshlayou
this.recyclerAdapter?.filter()
}, RxErrorHandler.handleEmptyError()))
}
+
+ context?.let { recyclerAdapter?.taskDisplayMode = configManager.taskDisplayMode(it) }
}
private fun handleTaskResult(result: TaskScoringResult, value: Int) {
@@ -260,6 +263,7 @@ open class TaskRecyclerViewFragment : BaseFragment(), androidx.swiperefreshlayou
recyclerAdapter?.checklistItemScoreEvents
?.flatMap { taskRepository.scoreChecklistItem(it.first.id ?: "", it.second.id ?: "")
}?.subscribeWithErrorHandler(Consumer {})?.let { compositeSubscription.add(it) }
+ recyclerAdapter?.brokenTaskEvents?.subscribeWithErrorHandler(Consumer { showBrokenChallengeDialog(it) })?.let { compositeSubscription.add(it) }
}
val bottomPadding = (binding.recyclerView.paddingBottom + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60f, resources.displayMetrics)).toInt()
@@ -285,6 +289,28 @@ open class TaskRecyclerViewFragment : BaseFragment(), androidx.swiperefreshlayou
}
}
+ protected fun showBrokenChallengeDialog(task: Task) {
+ context?.let {
+ if (!task.isValid) {
+ return
+ }
+ taskRepository.getTasksForChallenge(task.challengeID).subscribe(Consumer { tasks ->
+ val taskCount = tasks.size
+ val dialog = HabiticaAlertDialog(it)
+ dialog.setTitle(R.string.broken_challenge)
+ dialog.setMessage(it.getString(R.string.broken_challenge_description, taskCount))
+ dialog.addButton(it.getString(R.string.keep_x_tasks, taskCount), true) { _, _ ->
+ taskRepository.unlinkAllTasks(task.challengeID, "keep-all").subscribe(Consumer {}, RxErrorHandler.handleEmptyError())
+ }
+ dialog.addButton(it.getString(R.string.delete_x_tasks, taskCount), false, true) { _, _ ->
+ taskRepository.unlinkAllTasks(task.challengeID, "remove-all").subscribe(Consumer {}, RxErrorHandler.handleEmptyError())
+ }
+ dialog.setExtraCloseButtonVisibility(View.VISIBLE)
+ dialog.show()
+ }, RxErrorHandler.handleEmptyError())
+ }
+ }
+
private fun setEmptyLabels() {
if (this.classType != null) {
binding.recyclerView.setEmptyView(binding.emptyView)
@@ -359,6 +385,11 @@ open class TaskRecyclerViewFragment : BaseFragment(), androidx.swiperefreshlayou
}.subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
}
+ override fun onResume() {
+ super.onResume()
+ context?.let { recyclerAdapter?.taskDisplayMode = configManager.taskDisplayMode(it) }
+ }
+
fun setActiveFilter(activeFilter: String) {
taskFilterHelper.setActiveFilter(classType ?: "", activeFilter)
recyclerAdapter?.filter()
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/menu/BottomSheetMenu.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/menu/BottomSheetMenu.kt
index 7d72eb0de..7c6b0f99b 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/menu/BottomSheetMenu.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/menu/BottomSheetMenu.kt
@@ -5,13 +5,15 @@ import android.view.View
import android.widget.LinearLayout
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.databinding.MenuBottomSheetBinding
class BottomSheetMenu(context: Context) : BottomSheetDialog(context), View.OnClickListener {
- private var contentView = layoutInflater.inflate(R.layout.menu_bottom_sheet, null) as LinearLayout
+ private var binding = MenuBottomSheetBinding.inflate(layoutInflater)
private var runnable: ((Int) -> Unit)? = null
init {
- setContentView(contentView)
+ setContentView(binding.root)
+ binding.titleView.visibility = View.GONE
}
fun setSelectionRunnable(runnable: (Int) -> Unit) {
@@ -24,19 +26,24 @@ class BottomSheetMenu(context: Context) : BottomSheetDialog(context), View.OnCli
}
}
+ override fun setTitle(title: CharSequence?) {
+ binding.titleView.text = title
+ binding.titleView.visibility = View.VISIBLE
+ }
+
fun addMenuItem(menuItem: BottomSheetMenuItem) {
- val item = menuItem.inflate(this.context, layoutInflater, this.contentView)
+ val item = menuItem.inflate(this.context, layoutInflater, this.binding.menuItems)
item.setOnClickListener(this)
- this.contentView.addView(item)
+ this.binding.menuItems.addView(item)
}
fun removeMenuItem(index: Int) {
- this.contentView.removeViewAt(index)
+ this.binding.menuItems.removeViewAt(index)
}
override fun onClick(v: View) {
if (this.runnable != null) {
- val index = this.contentView.indexOfChild(v)
+ val index = this.binding.menuItems.indexOfChild(v)
if (index != -1) {
runnable?.let { it(index) }
this.dismiss()
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/menu/HabiticaDrawerItem.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/menu/HabiticaDrawerItem.kt
index 6bd558186..0ad901c6e 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/menu/HabiticaDrawerItem.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/menu/HabiticaDrawerItem.kt
@@ -2,6 +2,7 @@ package com.habitrpg.android.habitica.ui.menu
import android.graphics.drawable.Drawable
import android.os.Bundle
+import com.habitrpg.android.habitica.models.user.User
data class HabiticaDrawerItem(var transitionId: Int, val identifier: String, val text: String, val isHeader: Boolean = false) {
constructor(transitionId: Int, identifier: String) : this(transitionId, identifier, "")
@@ -15,4 +16,6 @@ data class HabiticaDrawerItem(var transitionId: Int, val identifier: String, val
var showBubble: Boolean = false
var isVisible: Boolean = true
var isEnabled: Boolean = true
+
+ var user: User? = null
}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/BindableViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/BindableViewHolder.kt
index a7e011a44..a4fec802e 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/BindableViewHolder.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/BindableViewHolder.kt
@@ -5,5 +5,5 @@ import androidx.recyclerview.widget.RecyclerView
abstract class BindableViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
- abstract fun bind(data: T, position: Int)
+ abstract fun bind(data: T, position: Int, displayMode: String)
}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/MountViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/MountViewHolder.kt
new file mode 100644
index 000000000..11aaeef5f
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/MountViewHolder.kt
@@ -0,0 +1,68 @@
+package com.habitrpg.android.habitica.ui.viewHolders
+
+import android.content.res.Resources
+import android.graphics.drawable.BitmapDrawable
+import android.view.View
+import android.view.ViewGroup
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.databinding.MountOverviewItemBinding
+import com.habitrpg.android.habitica.extensions.inflate
+import com.habitrpg.android.habitica.helpers.RxErrorHandler
+import com.habitrpg.android.habitica.models.inventory.Mount
+import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
+import com.habitrpg.android.habitica.ui.menu.BottomSheetMenu
+import com.habitrpg.android.habitica.ui.menu.BottomSheetMenuItem
+import io.reactivex.Observable
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.functions.Consumer
+import io.reactivex.subjects.PublishSubject
+
+class MountViewHolder(parent: ViewGroup, private val equipEvents: PublishSubject) : androidx.recyclerview.widget.RecyclerView.ViewHolder(parent.inflate(R.layout.mount_overview_item)), View.OnClickListener {
+ private var binding: MountOverviewItemBinding = MountOverviewItemBinding.bind(itemView)
+ private var owned: Boolean = false
+ var animal: Mount? = null
+
+ var resources: Resources = itemView.resources
+
+ init {
+ itemView.setOnClickListener(this)
+ }
+
+ fun bind(item: Mount, owned: Boolean) {
+ animal = item
+ this.owned = owned
+ if (item.type == "special" || item.type == "wacky") {
+ binding.titleTextView.text = item.text
+ } else {
+ binding.titleTextView.visibility = View.GONE
+ }
+ binding.ownedTextView.visibility = View.GONE
+ val imageName = "Mount_Icon_" + item.animal + "-" + item.color
+ binding.imageView.alpha = 1.0f
+ if (!owned) {
+ binding.imageView.alpha = 0.2f
+ }
+ binding.imageView.background = null
+ DataBindingUtils.loadImage(imageName) {
+ val drawable = BitmapDrawable(itemView.context.resources, if (owned) it else it.extractAlpha())
+ Observable.just(drawable)
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(Consumer {
+ binding.imageView.background = drawable
+ }, RxErrorHandler.handleEmptyError())
+ }
+ }
+
+ override fun onClick(v: View) {
+ if (!owned) {
+ return
+ }
+ val menu = BottomSheetMenu(itemView.context)
+ menu.setTitle(animal?.text)
+ menu.addMenuItem(BottomSheetMenuItem(resources.getString(R.string.equip)))
+ menu.setSelectionRunnable {
+ animal?.let { equipEvents.onNext(it.key) }
+ }
+ menu.show()
+ }
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/PetViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/PetViewHolder.kt
new file mode 100644
index 000000000..ebe9baf97
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/PetViewHolder.kt
@@ -0,0 +1,168 @@
+package com.habitrpg.android.habitica.ui.viewHolders
+
+import android.graphics.PorterDuff
+import android.graphics.drawable.BitmapDrawable
+import android.view.View
+import android.view.ViewGroup
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.databinding.PetDetailItemBinding
+import com.habitrpg.android.habitica.events.commands.FeedCommand
+import com.habitrpg.android.habitica.extensions.inflate
+import com.habitrpg.android.habitica.helpers.RxErrorHandler
+import com.habitrpg.android.habitica.models.inventory.*
+import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
+import com.habitrpg.android.habitica.ui.menu.BottomSheetMenu
+import com.habitrpg.android.habitica.ui.menu.BottomSheetMenuItem
+import com.habitrpg.android.habitica.ui.views.dialogs.PetSuggestHatchDialog
+import io.reactivex.Observable
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.functions.Consumer
+import io.reactivex.subjects.PublishSubject
+import org.greenrobot.eventbus.EventBus
+
+class PetViewHolder(parent: ViewGroup, private val equipEvents: PublishSubject, private val animalIngredientsRetriever: ((Animal) -> Pair)?) : androidx.recyclerview.widget.RecyclerView.ViewHolder(parent.inflate(R.layout.pet_detail_item)), View.OnClickListener {
+ private var hasMount: Boolean = false
+ private var hasUnlockedPotion: Boolean = false
+ private var hasUnlockedEgg: Boolean = false
+ private var eggCount: Int = 0
+ private var potionCount: Int = 0
+ private var ownsSaddles = false
+ private var animal: Pet? = null
+
+ private var binding: PetDetailItemBinding = PetDetailItemBinding.bind(itemView)
+
+ private var isOwned: Boolean = false
+
+ private var canRaiseToMount: Boolean = false
+
+ private val canHatch: Boolean
+ get() = eggCount > 0 && potionCount > 0
+
+ init {
+ itemView.setOnClickListener(this)
+ }
+
+ fun bind(item: Pet,
+ trained: Int,
+ eggCount: Int,
+ potionCount: Int,
+ canRaiseToMount: Boolean,
+ ownsSaddles: Boolean,
+ hasUnlockedEgg: Boolean,
+ hasUnlockedPotion: Boolean,
+ hasMount: Boolean) {
+ this.animal = item
+ isOwned = trained > 0
+ binding.imageView.alpha = 1.0f
+ this.canRaiseToMount = canRaiseToMount
+ this.eggCount = eggCount
+ this.potionCount = potionCount
+ this.ownsSaddles = ownsSaddles
+ this.hasUnlockedEgg = hasUnlockedEgg
+ this.hasUnlockedPotion = hasUnlockedPotion
+ this.hasMount = hasMount
+ binding.imageViewWrapper.visibility = View.VISIBLE
+ binding.itemWrapper.visibility = View.GONE
+ binding.checkmarkView.visibility = View.GONE
+
+ if (item.type == "special" || item.type == "wacky") {
+ binding.titleTextView.text = item.text
+ binding.titleTextView.visibility =View.VISIBLE
+ } else {
+ binding.titleTextView.visibility = View.GONE
+ }
+
+ val imageName = "social_Pet-${item.animal}-${item.color}"
+ itemView.setBackgroundResource(R.drawable.layout_rounded_bg_gray_700)
+ if (trained > 0) {
+ if (this.canRaiseToMount) {
+ binding.trainedProgressBar.visibility = View.VISIBLE
+ binding.trainedProgressBar.progress = trained
+ } else {
+ binding.trainedProgressBar.visibility = View.GONE
+ }
+ } else {
+ binding.trainedProgressBar.visibility = View.GONE
+ binding.imageView.alpha = 0.2f
+ if (canHatch) {
+ binding.imageViewWrapper.visibility = View.GONE
+ binding.itemWrapper.visibility = View.VISIBLE
+ binding.checkmarkView.visibility = View.VISIBLE
+ itemView.setBackgroundResource(R.drawable.layout_rounded_bg_gray_700_brand_border)
+ DataBindingUtils.loadImage(binding.eggView, "Pet_Egg_${item.animal}")
+ DataBindingUtils.loadImage(binding.hatchingPotionView, "Pet_HatchingPotion_${item.color}")
+ }
+ }
+
+ if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
+ binding.trainedProgressBar.progressBackgroundTintMode = PorterDuff.Mode.SRC_OVER
+ }
+ binding.imageView.background = null
+ DataBindingUtils.loadImage(imageName) {
+ val resources = itemView.context.resources ?: return@loadImage
+ val drawable = BitmapDrawable(resources, if (trained == 0) it.extractAlpha() else it)
+ Observable.just(drawable)
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(Consumer {
+ binding.imageView.background = drawable
+ }, RxErrorHandler.handleEmptyError())
+ }
+ }
+
+ override fun onClick(v: View) {
+ if (!isOwned) {
+ showRequirementsDialog()
+ return
+ }
+ val context = itemView.context
+ val menu = BottomSheetMenu(context)
+ menu.setTitle(animal?.text)
+ menu.addMenuItem(BottomSheetMenuItem(itemView.resources.getString(R.string.equip)))
+ if (canRaiseToMount) {
+ menu.addMenuItem(BottomSheetMenuItem(itemView.resources.getString(R.string.feed)))
+ if (ownsSaddles) {
+ menu.addMenuItem(BottomSheetMenuItem(itemView.resources.getString(R.string.use_saddle)))
+ }
+ }
+ menu.setSelectionRunnable { index ->
+ when (index) {
+ 0 -> {
+ animal?.let {
+ equipEvents.onNext(it.key)
+ }
+ }
+ 1 -> {
+ val event = FeedCommand()
+ event.usingPet = animal
+ EventBus.getDefault().post(event)
+ }
+ 2 -> {
+ val event = FeedCommand()
+ event.usingPet = animal
+ val saddle = Food()
+ saddle.key = "Saddle"
+ event.usingFood = saddle
+ EventBus.getDefault().post(event)
+ }
+ }
+ }
+ menu.show()
+ }
+
+ private fun showRequirementsDialog() {
+ val context = itemView.context
+ val dialog = PetSuggestHatchDialog(context)
+ animal?.let {
+ val ingredients = animalIngredientsRetriever?.invoke(it)
+ dialog.configure(it,
+ ingredients?.first,
+ ingredients?.second,
+ eggCount,
+ potionCount,
+ hasUnlockedEgg,
+ hasUnlockedPotion,
+ hasMount)
+ }
+ dialog.show()
+ }
+}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/SectionViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/SectionViewHolder.kt
index 59c3f0b14..e40aab43a 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/SectionViewHolder.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/SectionViewHolder.kt
@@ -2,12 +2,15 @@ package com.habitrpg.android.habitica.ui.viewHolders
import android.content.Context
import android.view.View
+import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Spinner
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.extensions.inflate
+import com.habitrpg.android.habitica.models.inventory.StableSection
import com.habitrpg.android.habitica.ui.helpers.bindView
class SectionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
@@ -15,10 +18,13 @@ class SectionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val label: TextView by bindView(itemView, R.id.label)
private val selectionSpinner: Spinner? by bindView(itemView, R.id.classSelectionSpinner)
internal val notesView: TextView? by bindView(itemView, R.id.headerNotesView)
+ private val countPill: TextView? by bindView(itemView, R.id.count_pill)
var context: Context = itemView.context
var spinnerSelectionChanged: (() -> Unit)? = null
+ constructor(parent: ViewGroup) : this(parent.inflate(R.layout.customization_section_header))
+
init {
itemView.findViewById