diff --git a/Habitica/AndroidManifest.xml b/Habitica/AndroidManifest.xml
index c4bcc8103..3c501e746 100644
--- a/Habitica/AndroidManifest.xml
+++ b/Habitica/AndroidManifest.xml
@@ -191,7 +191,11 @@
android:windowSoftInputMode="stateHidden" />
-
+
+
+
+
+
diff --git a/Habitica/build.gradle b/Habitica/build.gradle
index 0cabe7d08..240d47b2f 100644
--- a/Habitica/build.gradle
+++ b/Habitica/build.gradle
@@ -17,7 +17,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'io.fabric.tools:gradle:1.31.0'
+ classpath 'io.fabric.tools:gradle:1.+'
classpath('com.noveogroup.android:check:1.2.5') {
exclude module: 'checkstyle'
exclude module: 'pmd-java'
@@ -102,6 +102,8 @@ dependencies {
}
//Tests
testImplementation 'junit:junit:4.12'
+ testImplementation 'androidx.test:core:1.0.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'
testImplementation 'org.robolectric:robolectric:3.8'
@@ -115,26 +117,28 @@ 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.1'
+ 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.0'
- implementation 'com.google.firebase:firebase-perf:19.0.4'
+ 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 '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.1.0'
- implementation "androidx.lifecycle:lifecycle-extensions:2.1.0"
- implementation "androidx.lifecycle:lifecycle-common-java8:2.1.0"
- implementation 'androidx.navigation:navigation-fragment-ktx:2.1.0'
- implementation 'androidx.navigation:navigation-ui-ktx:2.1.0'
+ implementation 'androidx.core:core-ktx:1.2.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 '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'
+ implementation 'com.willowtreeapps:signinwithapplebutton:0.2'
+
implementation project(':shared')
}
@@ -151,8 +155,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 2346
- versionName "2.4.2"
+ versionCode 2396
+ versionName "2.5"
}
viewBinding {
diff --git a/Habitica/res/drawable-hdpi/apple_icon.png b/Habitica/res/drawable-hdpi/apple_icon.png
new file mode 100644
index 000000000..849330315
Binary files /dev/null and b/Habitica/res/drawable-hdpi/apple_icon.png differ
diff --git a/Habitica/res/drawable-hdpi/support_faq.png b/Habitica/res/drawable-hdpi/support_faq.png
new file mode 100644
index 000000000..13c292aec
Binary files /dev/null and b/Habitica/res/drawable-hdpi/support_faq.png differ
diff --git a/Habitica/res/drawable-mdpi/apple_icon.png b/Habitica/res/drawable-mdpi/apple_icon.png
new file mode 100644
index 000000000..3e05abc74
Binary files /dev/null and b/Habitica/res/drawable-mdpi/apple_icon.png differ
diff --git a/Habitica/res/drawable-mdpi/support_faq.png b/Habitica/res/drawable-mdpi/support_faq.png
new file mode 100644
index 000000000..6d5649a22
Binary files /dev/null and b/Habitica/res/drawable-mdpi/support_faq.png differ
diff --git a/Habitica/res/drawable-xhdpi/apple_icon.png b/Habitica/res/drawable-xhdpi/apple_icon.png
new file mode 100644
index 000000000..1394177cb
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/apple_icon.png differ
diff --git a/Habitica/res/drawable-xhdpi/support_faq.png b/Habitica/res/drawable-xhdpi/support_faq.png
new file mode 100644
index 000000000..100c4469c
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/support_faq.png differ
diff --git a/Habitica/res/drawable-xxhdpi/apple_icon.png b/Habitica/res/drawable-xxhdpi/apple_icon.png
new file mode 100644
index 000000000..3f4b39161
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/apple_icon.png differ
diff --git a/Habitica/res/drawable-xxhdpi/support_faq.png b/Habitica/res/drawable-xxhdpi/support_faq.png
new file mode 100644
index 000000000..60fa78af8
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/support_faq.png differ
diff --git a/Habitica/res/drawable-xxxhdpi/apple_icon.png b/Habitica/res/drawable-xxxhdpi/apple_icon.png
new file mode 100644
index 000000000..a4b1bafeb
Binary files /dev/null and b/Habitica/res/drawable-xxxhdpi/apple_icon.png differ
diff --git a/Habitica/res/drawable-xxxhdpi/support_faq.png b/Habitica/res/drawable-xxxhdpi/support_faq.png
new file mode 100644
index 000000000..31081bede
Binary files /dev/null and b/Habitica/res/drawable-xxxhdpi/support_faq.png differ
diff --git a/Habitica/res/drawable/button_background_gray_600.xml b/Habitica/res/drawable/button_background_gray_600.xml
new file mode 100644
index 000000000..9d6ccf7a2
--- /dev/null
+++ b/Habitica/res/drawable/button_background_gray_600.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/drawable/circle_gray300.xml b/Habitica/res/drawable/circle_gray300.xml
new file mode 100644
index 000000000..ba3d669da
--- /dev/null
+++ b/Habitica/res/drawable/circle_gray300.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/drawable/ic_arrow_drop_up_gray_48dp_disabled.xml b/Habitica/res/drawable/ic_arrow_drop_up_gray_48dp_disabled.xml
new file mode 100644
index 000000000..0d441d1cc
--- /dev/null
+++ b/Habitica/res/drawable/ic_arrow_drop_up_gray_48dp_disabled.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/Habitica/res/drawable/ic_arrow_drop_up_gray_48dp_states.xml b/Habitica/res/drawable/ic_arrow_drop_up_gray_48dp_states.xml
new file mode 100644
index 000000000..41b906774
--- /dev/null
+++ b/Habitica/res/drawable/ic_arrow_drop_up_gray_48dp_states.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ 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
new file mode 100644
index 000000000..727262a5e
--- /dev/null
+++ b/Habitica/res/drawable/layout_rounded_bg_gray_700_brand_border.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/drawable/rounded_purple_dark_square.xml b/Habitica/res/drawable/rounded_purple_dark_square.xml
new file mode 100644
index 000000000..eebd55800
--- /dev/null
+++ b/Habitica/res/drawable/rounded_purple_dark_square.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/drawable/support_info_lower_bg.xml b/Habitica/res/drawable/support_info_lower_bg.xml
new file mode 100644
index 000000000..26a2c3030
--- /dev/null
+++ b/Habitica/res/drawable/support_info_lower_bg.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/activity_login.xml b/Habitica/res/layout/activity_login.xml
index a7d91600d..f216f1e8d 100644
--- a/Habitica/res/layout/activity_login.xml
+++ b/Habitica/res/layout/activity_login.xml
@@ -201,7 +201,7 @@
android:layout_marginTop="@dimen/spacing_xlarge"
android:layout_height="@dimen/diamond_button_height"
android:text="@string/login_btn_fb"
- android:drawableLeft="@drawable/facebook_icon"
+ android:drawableStart="@drawable/facebook_icon"
style="@style/LoginButton"/>
+
+
+ android:background="@color/transparent" />
diff --git a/Habitica/res/layout/activity_task_form.xml b/Habitica/res/layout/activity_task_form.xml
index b377a792f..82f5f0b12 100644
--- a/Habitica/res/layout/activity_task_form.xml
+++ b/Habitica/res/layout/activity_task_form.xml
@@ -83,7 +83,7 @@
android:layout_height="wrap_content"
android:text="@string/cost"
style="@style/TaskFormSectionheader"/>
-
diff --git a/Habitica/res/layout/customization_grid_item.xml b/Habitica/res/layout/customization_grid_item.xml
index c37ca4360..5d60f1577 100644
--- a/Habitica/res/layout/customization_grid_item.xml
+++ b/Habitica/res/layout/customization_grid_item.xml
@@ -1,37 +1,41 @@
-
-
+
+
-
+ android:layout_gravity="center">
+
-
-
-
\ No newline at end of file
+ android:layout_height="76dp"
+ android:layout_gravity="start"
+ android:scaleType="fitEnd" />
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/customization_section_header.xml b/Habitica/res/layout/customization_section_header.xml
index 66ef678c9..8a7df6961 100644
--- a/Habitica/res/layout/customization_section_header.xml
+++ b/Habitica/res/layout/customization_section_header.xml
@@ -1,21 +1,42 @@
+ android:paddingTop="16dp"
+ android:gravity="bottom"
+ android:layout_marginStart="20dp"
+ android:layout_marginEnd="20dp">
+ android:textAppearance="?android:attr/textAppearanceMedium"/>
-
+ android:visibility="gone"
+ android:orientation="horizontal"
+ android:gravity="center"
+ tools:visibility="visible"
+ android:paddingStart="@dimen/spacing_medium"
+ android:paddingEnd="@dimen/spacing_medium"
+ android:background="@drawable/layout_rounded_bg_gray_700">
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/dialog_choose_message_recipient.xml b/Habitica/res/layout/dialog_choose_message_recipient.xml
index 36f4e7712..5408a9872 100644
--- a/Habitica/res/layout/dialog_choose_message_recipient.xml
+++ b/Habitica/res/layout/dialog_choose_message_recipient.xml
@@ -1,10 +1,12 @@
+ android:paddingRight="20dp"
+ android:gravity="center_horizontal">
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/dialog_completed_quest_content.xml b/Habitica/res/layout/dialog_completed_quest_content.xml
new file mode 100644
index 000000000..3c2cd58f2
--- /dev/null
+++ b/Habitica/res/layout/dialog_completed_quest_content.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/dialog_habitica_base.xml b/Habitica/res/layout/dialog_habitica_base.xml
index a61d21bad..246d9671a 100644
--- a/Habitica/res/layout/dialog_habitica_base.xml
+++ b/Habitica/res/layout/dialog_habitica_base.xml
@@ -57,6 +57,7 @@
android:paddingStart="@dimen/alert_side_padding"
android:paddingEnd="@dimen/alert_side_padding"
android:textColor="?textColorSecondary"
+ android:scrollbars = "vertical"
style="@style/Body2"
/>
+
\ 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 0235fb975..c61f9b15c 100644
--- a/Habitica/res/layout/dialog_purchase_content_item.xml
+++ b/Habitica/res/layout/dialog_purchase_content_item.xml
@@ -31,4 +31,13 @@
android:textColor="@color/black_50_alpha"
tools:text="These are the notes"
android:gravity="center"/>
+
\ No newline at end of file
diff --git a/Habitica/res/layout/dialog_purchase_content_quest.xml b/Habitica/res/layout/dialog_purchase_content_quest.xml
index 540e03a6f..be54a9fd5 100644
--- a/Habitica/res/layout/dialog_purchase_content_quest.xml
+++ b/Habitica/res/layout/dialog_purchase_content_quest.xml
@@ -179,4 +179,13 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
+
\ No newline at end of file
diff --git a/Habitica/res/layout/dialog_purchase_customization.xml b/Habitica/res/layout/dialog_purchase_customization.xml
index 5697da968..b83be398b 100644
--- a/Habitica/res/layout/dialog_purchase_customization.xml
+++ b/Habitica/res/layout/dialog_purchase_customization.xml
@@ -4,10 +4,11 @@
android:layout_height="wrap_content">
+ android:layout_gravity="center_horizontal"
+ android:scaleType="center"/>
+
\ No newline at end of file
diff --git a/Habitica/res/layout/dialog_purchase_gems.xml b/Habitica/res/layout/dialog_purchase_gems.xml
new file mode 100644
index 000000000..abb83f39d
--- /dev/null
+++ b/Habitica/res/layout/dialog_purchase_gems.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/task_form_reward_value.xml b/Habitica/res/layout/form_stepper_value.xml
similarity index 96%
rename from Habitica/res/layout/task_form_reward_value.xml
rename to Habitica/res/layout/form_stepper_value.xml
index 203e3d079..17387eadc 100644
--- a/Habitica/res/layout/task_form_reward_value.xml
+++ b/Habitica/res/layout/form_stepper_value.xml
@@ -20,7 +20,7 @@
android:layout_width="56dp"
android:layout_height="match_parent"
android:background="@color/gray_600"
- android:src="@drawable/ic_arrow_drop_up_gray_48dp"
+ android:src="@drawable/ic_arrow_drop_up_gray_48dp_states"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
/>
diff --git a/Habitica/res/layout/fragment_about.xml b/Habitica/res/layout/fragment_about.xml
index f7d922441..787574e97 100644
--- a/Habitica/res/layout/fragment_about.xml
+++ b/Habitica/res/layout/fragment_about.xml
@@ -115,18 +115,6 @@
android:textColor="@color/white"
android:textSize="@dimen/card_medium_text" />
-
-
+ android:layout_height="50dp"
+ android:gravity="center_vertical">
@@ -66,11 +66,57 @@
android:layout_height="wrap_content"
android:id="@+id/avatarSkinView"
app:equipmentTitle="@string/avatar_skin"/>
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/fragment_faq_overview.xml b/Habitica/res/layout/fragment_faq_overview.xml
new file mode 100644
index 000000000..94ec908d9
--- /dev/null
+++ b/Habitica/res/layout/fragment_faq_overview.xml
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/fragment_intro.xml b/Habitica/res/layout/fragment_intro.xml
index 0c282f84a..08df6184e 100644
--- a/Habitica/res/layout/fragment_intro.xml
+++ b/Habitica/res/layout/fragment_intro.xml
@@ -40,7 +40,7 @@
tools:text="@string/intro_2_title"
android:layout_marginBottom="28dp" />
-
+ android:background="@color/gray_700"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior">
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+ android:scrollbars="vertical" />
diff --git a/Habitica/res/layout/fragment_skills.xml b/Habitica/res/layout/fragment_skills.xml
index e10ba38a8..22d0a0906 100644
--- a/Habitica/res/layout/fragment_skills.xml
+++ b/Habitica/res/layout/fragment_skills.xml
@@ -5,5 +5,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbarSize="3dp"
+ android:background="@color/white"
android:scrollbarThumbVertical="@color/scrollbarThumb"
android:scrollbars="vertical" />
diff --git a/Habitica/res/layout/fragment_support_bug_fix.xml b/Habitica/res/layout/fragment_support_bug_fix.xml
new file mode 100644
index 000000000..8fe130ffc
--- /dev/null
+++ b/Habitica/res/layout/fragment_support_bug_fix.xml
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/fragment_support_main.xml b/Habitica/res/layout/fragment_support_main.xml
new file mode 100644
index 000000000..aa87da7f2
--- /dev/null
+++ b/Habitica/res/layout/fragment_support_main.xml
@@ -0,0 +1,171 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/item_image_row.xml b/Habitica/res/layout/item_image_row.xml
index 7e8fd90d3..7c1a85b17 100644
--- a/Habitica/res/layout/item_image_row.xml
+++ b/Habitica/res/layout/item_image_row.xml
@@ -13,7 +13,7 @@
android:layout_width="@dimen/gear_image_size"
android:layout_height="@dimen/gear_image_size"
actualImageScaleType="fitCenter"
- android:layout_marginRight="@dimen/row_padding"/>
+ android:layout_marginEnd="@dimen/row_padding"/>
diff --git a/Habitica/res/layout/skill_list_item.xml b/Habitica/res/layout/skill_list_item.xml
index 78f8dadef..9dc866268 100644
--- a/Habitica/res/layout/skill_list_item.xml
+++ b/Habitica/res/layout/skill_list_item.xml
@@ -1,17 +1,27 @@
-
+
+
-
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="8dp"/>
+ android:orientation="vertical"
+ android:layout_marginTop="12dp"
+ android:layout_marginBottom="12dp">
-
+
+
+
+
+
-
\ No newline at end of file
+ android:gravity="center"
+ />
+
\ No newline at end of file
diff --git a/Habitica/res/layout/support_collapsible_section.xml b/Habitica/res/layout/support_collapsible_section.xml
new file mode 100644
index 000000000..db70187a9
--- /dev/null
+++ b/Habitica/res/layout/support_collapsible_section.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/support_faq_item.xml b/Habitica/res/layout/support_faq_item.xml
new file mode 100644
index 000000000..8d78e746b
--- /dev/null
+++ b/Habitica/res/layout/support_faq_item.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/menu/inbox.xml b/Habitica/res/menu/inbox.xml
new file mode 100644
index 000000000..a2c69decb
--- /dev/null
+++ b/Habitica/res/menu/inbox.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/Habitica/res/menu/inbox_chat.xml b/Habitica/res/menu/inbox_chat.xml
new file mode 100644
index 000000000..0d735c0bc
--- /dev/null
+++ b/Habitica/res/menu/inbox_chat.xml
@@ -0,0 +1,6 @@
+
+
+
diff --git a/Habitica/res/navigation/navigation.xml b/Habitica/res/navigation/navigation.xml
index 88c24f4ec..3235fa47d 100644
--- a/Habitica/res/navigation/navigation.xml
+++ b/Habitica/res/navigation/navigation.xml
@@ -110,7 +110,12 @@
+ android:label="@string/sidebar_shops" >
+
+
+
+ android:name="com.habitrpg.android.habitica.ui.fragments.support.FAQOverviewFragment"
+ android:label="@string/habitica_questions">
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/values-id/strings.profile.xml b/Habitica/res/values-id/strings.profile.xml
deleted file mode 100644
index a6b3daec9..000000000
--- a/Habitica/res/values-id/strings.profile.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/Habitica/res/values-id/strings.xml b/Habitica/res/values-id/strings.xml
deleted file mode 100755
index 0e66974a6..000000000
--- a/Habitica/res/values-id/strings.xml
+++ /dev/null
@@ -1,696 +0,0 @@
-
-
- Muat Ulang
- Pengalaman
- Nyawa
- Mana
- Pengaturan tidak dapat dimuat dari barkode
-
- Pengaturan
- Server kustom anda
- ID Pengguna
- ID Penggunamu
- Token API
- Token API-mu
- Bahasa
- Ganti bahasa dalam Habitica
- Kode QR-mu
- Kontak saya
- Terjadi kesalahan...
- Akun
- Hari Pertama dalam Minggu
- Hari pertama dalam minggu di semua kalender
- Pengingat Harian
- Aktifkan Pengingat
- Atur Waktu Pengingat
- Awal Hari
- Atur Awal Hari
- Notifikasi Pengguna
- Notifikasi
- Pengaturan notifikasi
- Kamu memenangkan sebuah Tantangan!
- Menerima sebuah Pesan Pribadi
- Mendapat Hadiah Permata
- Mendapat Hadiah Langganan
- Diundang ke Party
- Diundang ke Guild
- Misimu telah Dimulai
- Diundang ke dalam Misi
-
- Nilai
- To-Do baru
- Hadiah baru
- Keseharian baru
- Kebiasaan baru
- Edit
- Hapus
- Batal
- Masuk
- Daftar
- Nama Pengguna
- Email atau Nama Pengguna
- Kata Sandi
- Alamat Email
- Konfirmasi kata sandi
- Keluar
- Keluar dari akunmu
- Detail Akun
- Selamat Datang
- Pulihkan
- Silakan hubungkan melalui aplikasi sebelum menggunakan widget
- Tentang Kami
- Pustaka
- Riwayat Versi
- Habitica tersedia sebagai perangkat lunak open source pada Github
- Nilai Aplikasi kami
- Kirimkan kami Feedback!
- Laporkan Gangguan
- Kode
- Ulangi Setiap
-
- Koneksi Gagal
- Kamu tidak terhubung ke internet.
- Terdapat masalah pada server. Cobalah beberapa saat lagi.
- Autentikasi Gagal
- Nama Pengguna dan/atau Kata Sandi salah.
- Validasi Gagal
- Kamu harus mengisi semua kolom.
- Simpan
- Salin
- Catatan
- Teks
- Tingkat Kesulitan
- Label
- Trivial
- Mudah
- Sedang
- Susah
- Tanggal Mulai
- Positif (+)
- Negatif (-)
- Daftar Cek
- Pengingat
- Aksi
- Atribut
- Kekuatan
- Kecerdasan
- Ketahanan
- Persepsi
- Frekuensi
- Pada Hari Tertentu dalam Seminggu
- Setiap X Hari
- Ringkasan
- Ulangi
- Ulangi pada
- Harian
- Mingguan
- Bulanan
- Tahunan
- Hari pada Bulan
- Hari pada Minggu
- Senin
- Selasa
- Rabu
- Kamis
- Jumat
- Sabtu
- Minggu
- Hore!
- Jangan bersedih!
- Kamu kehabisan Nyawa!
- Isi ulang Nyawa & Ulang Lagi
- Filter
- Gambar Profil
- %d MP
- Kamu menggunakan %1$s dengan %2$d mana.
- Kamu menggunakan %1$s
- item cek baru
- Tambahkan
- Gunakan Kemampuan
- Akan Datang
- Apakah kamu yakin kamu ingin melaporkan pesan ini sebagai pelanggaran?
- Laporkan Pesan
- Buka pada lvl 11
- Kamu tidak memiliki party. Untuk bergabung dengan party, silakan kunjungi situs.
- Lupa Kata Sandi
- Aktifkan kembali Keseharianmu
- Nonaktifkan Keseharianmu
- Beli
- Tutup
- Party
- Obrolan
- Anggota
- Kebiasaan
- Keseharian
- To-Do
- Hadiah
- Apakah kamu yakin?
- Apakah kamu benar-benar ingin menghapusnya?
- Ya
- Tidak
- Misi
- Tubuh
- Rambut
- Baju
- Kulit
- Telinga Hewan
- Biasa
- Warna
- Janggut
- Kumis
- Bunga
- Poni
- Tidak ada yang digunakan
- Ukuran
- Kurus
- Bidang
- Latar Belakang
- Beli Item
- Buka set seharga %d permata
- Beli
- Beli set %s
- Tenggat Waktu
- Terima
- Tolak
- Mulai Misi
- Batalkan Undangan
- Batalkan Misi
- Tinggalkan Misi
- Menunggu
- Diterima
- Ditolak
- Partisipan
- Versi %1$s (%2$d)
- Bantuan & FAQ
- Aku mengerti!
- Ingatkan aku lagi
- Selamat datang di
- Habitica
- Ini saatnya untuk bersenang-senang sambil benar-benar menyelesaikan urusan-urusanmu. Bergabunglah dengan lebih dari %s orang yang memperbaiki aspek hidup mereka satu demi satu.
- Berkembang di dalam game
- Berkembang di kehidupan nyata
- Buka fitur dalam game dengan mengerjakan tugasmu di dunia nyata. Dapatkan baju, peliharaan, dan banyak lagi sebagai hadiahmu mencapai tujuan!
- Bertarung dengan monster
- Menjadi lebih sosial
- Tetaplah berusaha meraih tujuan dengan bantuan teman-teman. Dukung satu sama lain dalam hidup dan bertarunglah sekaligus meningkatkan kehidupan bersama-sama!
- Mari mulai!
- Sebelumnya
- Berikutnya
- Lewati
- Selamat datang di Habitica, di mana meningkatkan diri dalam permainan akan meningkatkan kehidupan nyata! Seiring kamu menyelesaikan tujuan dunia-nyata, kamu akan membuka perlengkapan, peliharaan, misi, dan banyak lagi.
- Selamat Datang
- Jadi, kamu mau terlihat seperti apa? Jangan khawatir, kamu bisa mengubahnya lagi nanti.
- Bagus! Sekarang, apa yang menurutmu menarik untuk kamu kembangkan dalam pengembaraan ini?
- Pekerjaan
- Olahraga
- Kesehatan
- Sekolah
- Tim
- Pekerjaan Rumah Tangga
- Kreativitas
- Proses Email
- Tugas paling penting
- Proyek Kerja
- 10 menit cardio
- Stretching
- Atur jadwal olahraga
- Makan makanan bergizi / cepat saji
- Gunakan Benang Gigi
- Periksa Jadwal
- Belajar/Bermalas-malasan
- Mengerjakan PR
- Selesaikan tugas
- Periksa tim
- Kabari tim mengenai status terbaru
- Selesaikan proyek tim
- 10 menit bersih-bersih
- Cuci Piring
- Rapikan lemari
- Belajar menguasai kerajinan tangan
- Mengerjakan proyek kreatif
- Menyelesaikan proyek kreatif
- Ingin membantu kami menjaga Habitica tetap berjalan? Kamu dapat mendukung pengembang dengan membeli permata!\n\nDengan permata kamu dapat membeli hal-hal menarik untuk akunmu, termasuk: \n\n - Kostum keren untuk avatarmu\n - Latar belakang keren\n - Misi menarik yang memberimu hadiah telur peliharaan unik\n - Kemampuan untuk mengganti pekerjaan sebelum level 100\n\nTerima kasih banyak untuk bantuan kepada Habitca untuk menjadi yang terbaik. Dukunganmu sangat berarti bagi kami!
- Guild-ku
- Guild Publik
- Guild
- Tinggalkan
- Gabung
- Ketua
- Permata
- Salin sebagai To-Do
- Kirim PM
- Laporkan
- Hapus
- Nama
- Deskripsi
- Tambahkan Label baru
- Privasi
- Tulis Pesan
- Kirim
- Cari guild
- Tenggat: %s
- jumlah runtunan: %d
- Tugas memiliki Tenggat Waktu
- Perlengkapan Bertarung
- Kostum
- Kepala
- Aksesori Kepala
- Aksesori Mata
- Baju Perang
- Punggung
- Tubuh
- Perisai
- Senjata
- Gunakan Kostum
- Digunakan
- Apakah kamu yakin kamu ingin membatalkan misi ini? Semua undangan yang disetujui akan hilang. Pemilik misi akan mendapatkan gulungan misi kembali.
- Undangan Misi
- Apakah kamu yakin? Hanya %1$d dari %2$d anggota Party-mu yang sudah ikut misi ini! Misi dimulai secara otomatis ketika semua pemain telah ikut atau menolak undangannya.
- Kamu telah diundang untuk berpartisipasi dalam misi!
- Hari Ini
- Item
- Telur
- Ramuan Penetas
- Makanan
- Misi
- Peliharaan
- Tunggangan
- Kamu menemukan Perlengkapan langka di dalam Peti Harta Karun: %s! Keren!
- Kamu mengacak-acak Peti Harta Karun dan menemukan %2$s %1$s. Kenapa berada di dalam sini, ya?
- Kamu bergulat dengan Peti Harta Karun dan mendapatkan Pengalaman. Rasakan itu!
- Buka Peti Harta Karun untuk mendapatkan Perlengkapan spesial, Pengalaman, atau makanan secara acak! Perlengkapan yang tersisa: %d
- Kamu menemukan Perlengkapan terakhir di dalam Peti Harta Karun.
- Peti Harta Karun akan memiliki Perlengkapan baru pada minggu pertama setiap bulan. Sementara itu, tetaplah klik untuk mendapatkan Pengalaman dan Makanan!
- Jual (%d Emas)
- Tetaskan dengan ramuan
- Beri makan peliharaan
- Tetaskan dengan telur
- Undang party
- Beri makan %s dengan:
- Gunakan
- Beri makan
- Tetaskan dengan %s
- Kamu menetaskan seekor %2$s %1$s!
- Tutup
- Bagikan
- Kamu memberi makan %s!
- Kamu membeli sebuah hadiah
- Misi Dunia
- Perlu istirahat? Masuk ke dalam Penginapan Daniel untuk sementara menghentikan mekanika game Habitica yang lebih susah:\n\n
-• Keseharian yang terlewat tidak akan menyakitimu\n
-• Tugas tidak akan putus runtunannya atau berkurang warnanya\n
-• Boss tidak akan menyerangmu untuk Keseharian yang terlewat\n
-• Damage-mu kepada Boss atau item misi mengumpulkanmu akan tetap menunggu hingga kamu keluar
- Kamu tidak memiliki %s
- Lvl %d
- Lvl %1$d %2$s
- Warrior
- Rogue
- Mage
- Healer
- Penyihir belajar dengan cepat, mendapatkan Pengalaman dan Level lebih cepat daripada kelas lainnya. Mereka juga mendapatkan Mana yang besar dengan menggunakan kemampuan spesial. Mainkan penyihir jika kamu menyukai aspek taktis dari permainan Kebiasaan, atau jika kamu begitu termotivasi dengan meningkatkan level dan membuka fitur baru!
- Perampok senang mengumpulkan kekayaan, mendapatkan banyak Emas daripada yang lainnya, dan mahir menemukan item acak. Kemampuan Bersembunyi mereka yang terkenal membuat mereka mampu menghindari konsekuensi dari Keseharian yang terlewat. Mainkan Perampok jika kamu termotivasi dengan Hadiah dan Pencapaian, juga mendapatkan banyak barang jarahan dan lencana!
- Penyembuh tahan terhadap serangan, dan dapat membagi perlindungannya pada sesama. Keseharian yang terlewat dan kebiasaan buruk tidak terlalu banyak mengganggu mereka, dan mereka memiliki cara untuk mengembalikan nyawa dari kesalahan mereka. Mainkan seorang Penyembuh jika kamu senang membantu teman dalam kelompok, atau ide mencurangi kematian dengan kerja keras membuatmu terinspirasi!
- Pilih Profesi
- Matikan Fitur
- Tidak ingin diganggu dengan profesi? Ingin memilih nanti saja? Matikan fitur - kamu akan menjadi seorang Prajurit tanpa kemampuan spesial. Kamu dapat membaca mengenai sistem profesi nanti di wiki dan mengaktifkan fitur profesi kapan saja.
- Apakah kamu yakin ingin menjadi seorang %s?
- Kamu sekarang seorang %s!
- Kamu memiliki Baju Perang baru yang dapat diganti pada menu Perlengkapan!
- Pilih Kelas
- Kembali
- Apakah kamu yakin kamu ingin mematikan fitur?
- Ganti profesi
- Ubah kelasmu dan terima kembali poin atribut seharga 3 Permata.
- Aktifkan Sistem Profesi
- Ganti Profesi
- Melalui Email
- Undang Pengguna yang telah ada
- Kirim
- Undang Teman
- Jika kamu memiliki teman yang telah menggunakan Habitica, undang melalui ID Pengguna di sini.
- Jika seorang teman bergabung dengan Habitica melalui emailmu, mereka otomatis akan diundang ke dalam kelompokmu!
- Tambahkan Undangan
- ID Pengguna
- Email
- Undang sebagai Teman
- Bagikan dengan
- Aku mencapai level %d di Habitica dengan memperbaiki kebiasaanku di kehidupan nyata!
- Saya menetaskan peliharaan seekor %2$s %1$s di Habitica dengan menyelesaikan tugas kehidupan-nyata saya!
- Saya baru mendapatkan tunggangan seekor %s di Habitica dengan menyelesaikan tugas kehidupan-nyata saya!
- Buka di Play Store
- Apa kamu yakin untuk mengubah kelas? Ini akan membutuhkan 3 permata.
- Peringatan: Kamu tidak akan dapat membeli perlengkapan untuk profesi %s.
- Pesan dari %1$s
- Ketua: %1$s
- Pastikan
- Pasar
- Penjelajah Waktu
- Toko Musiman
- Kamu tidak punya pesan apapun. Kamu bisa mengirim pesan baru ke pengguna lain dari obrolan publik mereka!
- Buka dengan mengundang teman
- Buka dengan cara login ke Habitica secara teratur
- Buka dengan membuat sebuah akun
- Koin Emas tidak cukup
- Kamu tidak perlu membeli ramuan kesehatan
- %1$s telah dibeli
- Pastikan belian
- Membeli %1$s untuk %2$s %3$s
- Permata
- Jam Pasir
- Jam-jam Pasir
- Koin Emas
- Koin Emas
- Pesan telah tersalin di Papan pencatat
- Salin di Papan pencatat
- Rubah Label
- Rubah
- Selesai
- Hapus
- Kamu yakin?
- Apa kamu yakin ingin menghapus?
- Rubah Label
- Saring berdasarkan Label
-
- Pinta seseorang untuk mengundangmu melalui Kode QR
- Kode QR tersimpan pada
- Kode QR-mu
- Unduh
- Kirim pesan baru
- Pilih Penerima Pesan
- Lanjutkan
- Pindai Kode QR
- Masukan User ID Penerima
- Kamu telah diundang untuk bergabung dalam sebuah party!
- Status Habitica
- Tambah Tugas
- Tambah Kebiasaan
- Tambah Kegiatan Harian
- Tambah Untuk Dilakukan
- Tambah Hadiah
- Anda telah menyelesaikan semua keseharianmu. Selamat!
- Habitica Melakukan Kebiasaan
- Keseharian Habitica
- Daftar To-Do Habitica
- Habitica Menambahkan Tugas
- Google play services tidak dapat ditemukan.
- Bayar
- Membeli gem dapat mendukung pengembang dan menjaga Habitica tetap berjalan
- Permata membuatmu dapat membeli tambahan yang menyenangkan untuk akunmu, termasuk:
- Kostum keren untuk avatarmu
- Latar belakang yang keren
- Misi yang memberikanmu hadiah telur
- Kemampuan untuk mengganti profesi sebelum level 100
- Dukung Habitica
- gunakan
- Tidak mendapatkan item yang diinginkan? Beli di Market!
- Butuh makanan yang berbeda? Beli lebih dari pasar!
- Buka Pasar
- Keseharianmu akan reset pada saat kamu membuka Habitica di atas %1$s. Pastikan kamu telah menyelesaikan Keseharianmu sebelum jam tersebut!
- Tema Nada
- Ganti Tema Nada Habitica
-
- Sampai jumpa besok!
- Milik Tantangan
- Memiliki Pengingat
- Memiliki Label
- Berlangganan mendukung para pengembang dan membantu menjaga Habitica terus berjalan
- Jadilah seorang pelanggan dan kamu akan mendapat keuntungan berikut:
- Beli permata dengan koin emas
- Item bulanan eksklusif
- Simpan lebih banyak riwayat masukan
- Batas drop harian didobel
- Langganan
- Berulang setiap %s
- 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.
- 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
- Langganan
- Aktif
- Batalkan Langganan
- Tidak mau berlangganan lagi? Kamu bisa menemukan opsi untuk berhenti berlangganan di bagin \"Aplikasi Saya\" dari Google Play Store.
- Tidak mau berlangganan lagi? Karena metode pembayaranmu, kamu hanya bisa berhenti berlangganan dari situs Habitica. Klik tombol di bawah, untuk membuka situs Habitica di browsermu!
- Kunjungi Situs Habitica
- Bonus Sekarang
- Bulan Berlangganan
- Jam Pasir Mistis Sekarang
- Batas permata bulanan
- Tidak aktif
- 1 Bulan
- %d Bulan
- bulan
- 3 bulan
- 6 bulan
- 12 bulan
- Dengan Berlangganan kamu menerima keuntungan bermanfaat berikut:
- Status Langganan
- oleh %s
- Detail Tantangan
- Tinggalkan Tantangan
- Apakah kamu yakin ingin meninggalkan Tantangan \"%s\"?
- Hapus tugas
- Apakah kamu mau menghapus tugas ini?
- Hapus
- Simpan
- Tantangan Saya
- Publik
- Tantangan
- Keseharian
- Kebiasaan
- Hadiah
- To-Do
- Resmi
- Berpartisipasi
- Tantangan
- Pergi ke Tantangan
- Kamu tidak mengambil bagian dalam Tantangan apapun saat ini!
- Ikut sebuah tantangan untuk menambahkan sekumpulan tugas yang telah didesain secara khusus, lalu bersainglah dengan sesama Habitican untuk memenangkan sebuah pencapaian dan bahkan permata!
- Tekan tab \"Publik\" untuk menemukan Tantangan buatan pengguna yang paling cocok untukmu!
- Tambahkan Pengingat
- Peringatan
- Buka Pengaturan
- Sepertinya kamu mengaktifkan opsi PEngembang \\"Jangan menyimpan Aktifitas\\". Saat ini opsi ini menyebabkan isu dengan aplikasi Habitica, jadi kami sarankan untuk menonaktifkannya.
- Pesan
- Pertanyaan yang Sering Ditanyakan
- Spesial
- Karena kamu berlangganan Habitica, kamu bisa membeli sejumlah Permata setiap bulan menggunakan Koin Emas.
- %d tersisa
- Permata
- Item Misterius
- Setiap bulan, pelanggan akan menerima sebuah item misterius. Ini biasanya dirilis sekitar seminggu sebelum akhir bulan.
- Buka
- Kamu membuka kotak ini dan menemukan %s!
- Kamu mendapatkan %1$s sebagai hadiah untuk kesetiaanmu untuk memperbaiki hidupmu.
- Hadiahmu selanjutnya akan terbuka setelah %1$d kali Cek In
- menunggu persetujuan
- Filter
- Selesai
- Tugas grup tidak bisa disunting.
- Tidak bisa menyunting tugas ini
- Grup
- Semua
- Tidak Ada
- Dimiliki
- Tidak Dimiliki
- Permainan\nBaru
- Login dengan Facebook
- Login dengan Google
- Kembali
- Sembarang
- Tambahan
- Warna Kulit
- Warna Rambut
- Poni
- Kucir
- Kacamata
- Kursi Roda
- Lemah
- Kuat
- Abu-Abu
- Terjadwal
- Selesai
- Lainnya
- Hapus
- Kamu tidak punya Kebiasaan apapun
- 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 Hadiah apapun
- Ulang Panduan Justin
- Tolong baca Pedoman Komunitas kami sebelum posting
- Perbaikan
- Muat Ulang Isi
- Atur Keseharian secara otomatis di tab \'harus dikerjakan\'
- Dengan opsi ini dipilih, tugas Keseharianmu akan secara otomatis di tab \'harus dikerjakan\' dan bukan \'semua\'
- Diulang %1$s setiap %2$s%3$s%4$s
- Perangkatmu tidak punya metode pembayaran terdukung apapun. Silahkan gunakan situs Habitica jika kamu mau membeli permata.
- Perangkatmu tidak punya metode pembayaran terdukung apapun. Silahkan gunakan situs Habitica jika kamu mau berlangganan.
- Simpan
- Lokasi
- Tugas
- Kamu tidak punya cukup Permata untuk membuat sebuah Tantangan.
- Kamu perlu menambah setidaknya sebuah tugas untuk membuat Tantangan ini.
- Kamu perlu sebuah judul untuk membuat Tantangan ini.
- Deskripsi (tidak wajib)
- Judul tantangan baru
- Kepemilikan
- Penginapan
- Sistem
- Mulai Misi Baru
- Tinggalkan Party
- Deskripsi Party
- Apakah kamu yakin ingin membatalkan misi ini? Ini akan membatalkannya untuk semua orang di Party dan semua progress akan hilang. Gulungan misi akan dikembalikan kepada pemilik misi tersebut.
- Apakah kamu yakin ingin meninggalkan misi aktif ini? Semua progres misimu akan hilang.
- %1$d Partisipan
- Selamat Datang Kembali
- Apakah kamu melakukan Keseharian berikut ini kemarin?
- Mulai Hariku
- Apakah kamu yakin ingin meninggalkan Party ini?
- Hadiah selanjutnya setelah %d kali Cek In
- Menunggu
- Menerima
- Menolak
- Partisipan
- Undangan
- Dimulai oleh %s
- Apakah kamu yakin ingin membatalkan misi ini? Ini akan membatalkannya untuk semua orang di Party dan semua progress akan hilang. Gulungan misi akan dikembalikan kepada pemilik misi tersebut.
- Memuat Ulang Isi
- rb
- jt
- mil
- Saldomu:
- Tersedia hingga %s
- Permata Bulanan: %1$d/%2$d Tersisa
- Permata Bulanan: %d Tersisa
- Nyawa
- Kumpulkan
- Tingkat Kemarahan
- Misi Boss
- Misi Mengumpulkan
- %1$s x%2$d
- %d poin Pengalaman
- %d Koin Emas
- Hadiah Pemilik Misi
- Kek
- Per
- Kec
- Ket
- Reset Akun
- Hapus Akun
- Apakah kamu yakin? Ini akan menghapus akunmu untuk selamanya, dan tidak akan bisa dikembalikan! Kamu perlu mendaftarkan akun baru untuk menggunakan Habitica lagi. Permata di bank atau yang telah digunakan tidak akan diuangkan kembali. Jika kamu sepenuhnya yakin, ketik kata sandimu di kotak teks di bawah.
- reset akun-ku
- hapus akunku
- Zona Berbahaya
- Tidak jadi
- Me-reset Akun
- Menghapus Akun
- Kamu perlu menyelesaikan lebih banyak tugas sebelum bisa membeli item ini!
- Kamu perlu lebih banyak Permata untuk membeli item ini!
- Beli permata
- Kamu perlu lebih banyak Jam Pasir Mistis untuk membeli item ini!
- Dapatkan jam pasir
- Berlangganan untuk Jam Pasir
- Dapatkan satu Jam Pasir Mistis setiap tiga bulan berturut-turut kamu berlangganan, lalu gunakan untuk membuka item-item edisi terbatas, peliharaan, dan tunggangan dari masa lalu... dan masa depan!
- Aku mau Berlangganan
- Grand Gala diadakan dekat dengan titik balik matahari dan ekuinoks, jadi cek kembali nanti untuk menemukan sekumpulan seru item musiman yang spesial!
- Datang kembali nanti!
- Level
- Rentetan 21 Hari
- Status
- Jika kamu menemukan sebuah gangguan atau melakukan kesalahan yang mengubah karaktermu secara tidak adil, kamu bisa memperbaiki angka-angka tersebut secara manual di sini.
- Atur Angka Karakter
- Menyimpan
- Email sebuah Tautan Mengatur Ulang Kata Sandi
- Masukan alamat email yang kamu gunakan untuk mendaftarkan akun Habitica-mu.
- Jika kami punya data email-mu, instruksi untuk mengatur kata sandi baru telah dikirim ke email-mu.
- OK
- Profil
- Sunting profil publikmu.
- Nama Tampilan
- Tautan Foto
- Nama Login
- Tentang
- Pengaturan Aplikasi
- Autentikasi
- Ganti opsi autentikasi-mu.
- Ganti Kata Sandi
- Ganti Alamat Email
- Ganti Nama Login
- Ganti
- Level Karakter
- Pembagian Poin Otomatis
- Panduan Status
- Bentuk Tubuh Karakter
- Setiap level memberimu sebuah poin untuk ditaruh kepada sebuah atribut pilihanmu. Kamu bisa melakukannya secara manual, atau biarkan game ini mengaturnya untukmu menggunakan salah satu opsi Pembagian Otomatis.
- Kekuatan
- Meningkatkan bonus dari serangan kritikal dan buat itu lebih sering terjadi sewaktu menyelesaikan tugas. Juga meningkatkan damage yang kamu lakukan kepada boss.
- Kecerdasan
- Meningkatkan EXP yang dihasilkan dari menyelesaikan tugas. Juga meningkatkan batas mana-mu dan seberapa cepat mana bertambah dari waktu ke waktu.
- Ketahanan
- Mengurangi jumlah damage yang kamu terima dari tugasmu. Tidak mengurangi damage yang diterima dari boss.
- Persepsi
- Meningkatkan kecenderungan untuk menemukan item sewaktu menyelesaikan Tugas, batas drop harian, Bonus Rentetan, dan jumlah koin emas yang dihadiahkan dari Tugas.
- Buff
- Teralokasi
- Bagikan Merata
- Bagikan berdasarkan pekerjaan
- Bagikan berdasarkan aktifitas tugas
- Tidak ada Poin untuk Dibagikan
- %d Poin untuk Dibagikan
- Bagikan jumlah poin yang sama kepada setiap atribut.
- Bagikan lebih banyak poin kepada atribut yang penting untuk Pekerjaanmu.
- Bagikan poin berdasarkan kepada kategori Kekuatan, Kecerdasan, Ketahanan, dan Persepsi yang dihubungkan kepada tugas yang kamu selesaikan.
- Membagikan Poin
- Perlengkapan
- Kamu sudah punya semua perlengkapan! Lebih banyak lagi tersedia sewaktu Grand Gala, dekat titik balik matahari dan ekuinoks.
- Tanpa Pekerjaan
- Item ini hanya tersedia untuk pekerjaan tetentu. \nKamu bisa menggantu pekerjaanmu dari Pengaturan.
- Kamu hanya bisa membeli perlengkapan untuk pekerjaanmu yang sekarang
- %d poin
- Atur Emoji
- Selamat datang di Penginapan! Tarik kursi dan mari mengobrol, atau beristirahat dari tugas-tugasmu.
- Masuk ke Penginapan
- Pedoman Komunitas
- Lihat Pedoman Komunitas
- Habitica coba membuat sebuah lingkungan yang ramah untuk pengguna segala usia dan latar belakang, khususnya di tempat umum seperti Kedai Minuman. Jika kamu ada pertanyaan, silahkan periksa pedoman kami.
- Tautan Berguna
- Lihat FAQ
- Laporkan Gangguan
- Tingkatan Pemain
- Staf dan Moderator
- Buka laci navigasi
- Tutup laci navigasi
- Bos Dunia
- Seni Bos
- Seni Bos
- Deskripsi Bos Dunia
- Tingkat Kemarahan 1%d/1%d
- Serangan ini belum terjadi!\nWorld Boss akan mengamuk dan menyerang salah satu dari penjaga toko bersahabat kita sewaktu tingkat kemarahannya telah penuh. Kerjakan sebanyak mungkin Keseharian-mu untuk mencoba mencegah serangan itu!
- Serangan Tertunda
- Apakah itu Tingkatan Kemarahan?
- Ada 3 potensi Serangan peningkat Kemarahan
- Pengukur ini terisi sewaktu Habitican tidak menyelesaikan Keseharian mereka. Sewaktu terisi penuh, sang DysHeartener akan mengerahkan Shattering Heartbreak kepada salah satu dari penjaga toko Habitica, jadi pastikan kamu melakukan tugas-tugasmu!
- Serangan ini belum terjadi!
- Sang %s telah Diserang!
- %s sekarang Patah Hati!
- %s kita yang tercinta hancur sewaktu sang %s meremukkan %s. Cepat, kalahkan tugas-tugasmu untuk mengalahkan sang monster dan bantu membangun kembali!!
- Toko Misi
- Alex sang Pedagang
- Daniel sang penjaga penginapan
- Matt sang penguasa hewan buas
- Sembunyikan Seni Bos
- Bos Dunia aktif
- Selesaikan misi bersama teman-temanmu atau sendirian. Bertarung melawan monster, buat Tantangan, dan bantu dirimu untuk tetap bertanggung jawab melalui Party.
- Mainkan Habitica dalam Party
- Ingin bergabung ke Party?
- Buka website untuk membuat Party
- ID tersalin ke clipboard
- Kamu telah membeli semua Permata yang tersedia bulan ini. Lebih banyak akan ditambahkan dalam tiga hari pertama pada setiap bulan. Terima kasih sudah berlangganan!
- Batas Permata Bulanan Telah Tercapai
-
diff --git a/Habitica/res/values/attrs.xml b/Habitica/res/values/attrs.xml
index 4dd467210..c892eeec8 100644
--- a/Habitica/res/values/attrs.xml
+++ b/Habitica/res/values/attrs.xml
@@ -14,6 +14,7 @@
+
@@ -61,7 +62,7 @@
-
+
@@ -129,4 +130,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Habitica/res/values/colors.xml b/Habitica/res/values/colors.xml
index a3a799e93..295b8cd59 100644
--- a/Habitica/res/values/colors.xml
+++ b/Habitica/res/values/colors.xml
@@ -125,6 +125,7 @@
#33000000
#89000000
#331a181d
+ #3CA9DCF6
#F6F4F8
#ddc994
diff --git a/Habitica/res/values/dimens.xml b/Habitica/res/values/dimens.xml
index 2278b87ac..72cedb63e 100644
--- a/Habitica/res/values/dimens.xml
+++ b/Habitica/res/values/dimens.xml
@@ -50,7 +50,7 @@
6dp
90dp
90dp
- 100dp
+ 76dp
68dp
12dp
16sp
diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml
index 36240ef65..68bcbafe3 100644
--- a/Habitica/res/values/strings.xml
+++ b/Habitica/res/values/strings.xml
@@ -25,7 +25,7 @@
Custom Day Start
- User Push Notifications
+ Use Push Notifications
Push Notifications
Set your push notifications settings
You won a Challenge!
@@ -323,9 +323,9 @@
Add Invites
Email
Share using o
- I got to level %d in Habitica by improving my real-life habits!
- I just hatched a %1$s %2$s pet in Habitica by completing my real-life tasks!
- I just gained a %1$s mount in Habitica by completing my real-life tasks!
+ I got to level %d in #Habitica by improving my real-life habits!
+ I just hatched a %1$s %2$s pet in #Habitica by completing my real-life tasks!
+ I just gained a %1$s mount in #Habitica by completing my real-life tasks!
Open in play store
Are you sure you want to change your class? This will cost 3 gems.
Warning: You will no longer be able to buy equipment from the %s class.
@@ -336,9 +336,15 @@
You don\'t have any messages. You can send a user a new message from their public chat messages!
Unlock by inviting friends
Unlock by logging into Habitica regularly
+ Unlock by checking into Habitica %d times.
Unlock by creating an account
+ Invite Friends
+ log into Habitica regularly
+ %d Checkins
+ create an account
Purchased %1$s
gold
+ Gold
Message copied to Clipboard
Edit
Are you sure?
@@ -367,7 +373,7 @@
Quests that reward you with pet eggs
The ability to change your class before level 100
Support Habitica
- use
+ Use
Not getting the right drops? Check out the Market!
Need different food? Buy more from the market!
Open Market
@@ -461,6 +467,7 @@
Not owned
Login with Facebook
Login with Google
+ Sign in with Apple
Sign up with Facebook
Sign up with Google
Back
@@ -487,6 +494,13 @@
You don\'t have any To-Dos
To-Dos are tasks that only need to be completed once. Add checklists to your To-Dos 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-Dos
+ There aren\'t any To-Dos visible with your current filters.
+ No Rewards
Reset Tutorials
Review our Community Guidelines before posting
Maintenance
@@ -550,8 +564,8 @@
Reset Account
WARNING! This resets many parts of your account. This is highly discouraged, but some people find it useful in the beginning after playing with the site for a short time.\n\nYou will lose all your levels, gold, and experience points. All your tasks (except those from challenges) will be deleted permanently and you will lose all of their historical data. You will lose all your equipment but you will be able to buy it all back, including all limited edition equipment or subscriber Mystery items that you already own (you will need to be in the correct class to re-buy class-specific gear). You will keep your current class and your pets and mounts. You might prefer to use an Orb of Rebirth instead, which is a much safer option and which will preserve your tasks and equipment.
Delete Account
- Are you sure? This will delete your account forever, and it can never be restored! You will need to register a new account to use Habitica again. Banked or spent Gems will not be refunded. If you\'re absolutely certain, type your password into the text box below.
- Are you sure? This will delete your account forever, and it can never be restored! You will need to register a new account to use Habitica again. Banked or spent Gems will not be refunded. If you\'re absolutely certain, type DELETE into the text box below.
+ This will delete your account forever, and it can never be restored! Banked or spent Gems will not be refunded. If you’re absolutely certain, type your password into the text box below.
+ This will delete your account forever, and it can never be restored! Banked or spent Gems will not be refunded. If you’re absolutely certain, type DELETE into the text box below.
Reset my Account
Delete my Account
Danger Zone
@@ -928,4 +942,68 @@
Learn more
You can purchase this customization from the Time Travelers shop
Go shopping
+ Email Notifications
+ Use Email Notifications
+ Subscription Reminders
+ Kicked from group
+ Guidance with setting up your Habitica Account
+ Skill unlocks at level %d
+ Locked
+ Unlock by finishing Quest %d
+ Unlock by reaching level %d
+ Finish Quest %d
+ Level %d
+ You are not participating
+ Quest completed!
+ Using Habitica
+ Confused? We’ll go over the basics to get you up to speed.
+ Have input on how features could work better or an idea for something new? Tell us!
+ Contact Us
+ Get Help
+
+
+ Did something go wrong? Check for answers here or reach out to us for help.
+ Let\'s Go
+ We’re constantly trying to make our apps better based on player feedback but sometimes a few bugs pop up…
+ Common Fixes
+ Send a bug report to us and we’ll get back to you!
+ Overview
+ Not sure how to best use Habitica?
+
+ Health Points
+ This represents your avatars life. Missing a Daily or doing a negative Habit **reduces your HP**.\n\n**Regain HP** by completing tasks, using a Health Potion, or a healing ability.\n\nIf your **HP reaches 0** your avatar will pass out (lose a level, all Gold, and one piece of equipment). Lost equipment can be re-purchased.
+ Experience Points
+ Experience points represent your progress and allows you to level up. You’ll mainly **gain EXP** from completing tasks or quests.\n\nHigher difficulty or red colored tasks will give you **more EXP**. The **Intelligence** stat also raises your EXP gain.
+ Mana Points
+ 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. **Perception** raises the amount of Gold you earn. Rogue’s can also use an ability to get more Gold.\n\nIf you subscribe to Habitica, you can even use Gold to purchase Gems.
+ Mana points are unlocked with the class system at level 10 and allow you to **use Skills**.\n\nSome **MP is restored** naturally every day, but you can regain more by completing tasks or using a Mage ability.
+ 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. Mainly for Warriors and Rogues.\n\n**Constitution (CON)** raises your HP and makes you take less damage. Mainly for Healers and Warriors.\n\n**Intelligence (INT)** raises the amount of EXP you earn and gives you more Mana. Mainly for Mages and Healers.\n\n**Perception (PER)** increases the gold you earn and the rate of finding items. Mainly for Rogues and Mages.\nnAfter 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
+ Currency
+ Gems are a currency purchased with real money that allow you to buy extra content within Habitica and are one of the main source of financial support for the Habitica team alongside subscriptions.\n\nAll content purchased through Gems is purely cosmetic or can be obtained for free with time.\n\nYou can also receive Gems through gifts, Challenge prizes, contributing work to Habitica, or subscribing.
+ Common Questions
+ Game Mechanics
+ Subscriber Currency
+ Mystic Hourglasses
+ Mystic Hourglasses are an extremely rare form of currency you can only receive for subscribing to Habitica. They are used in the Time Traveler’s shop to buy past gear sets, pets, mounts, animated backgrounds, or even special quests.\n\nYou can receive up to four Mystic Hourglasses a year. The time they are rewarded is based on your subscription renewal schedule. They are sent out on the first day of a new month after your last subscription payment that qualified you for an hourglass. See the [Subscription] page for more details.
+ Clear your Cache
+ Apps > Habitica, then tap Clear Cache.]]>
+ Manual Sync or Restart
+ Sometimes the app won’t automatically update content. Try pulling to refresh or force closing the app and reopening it.
+ Update the App
+ We’re constantly pushing out new fixes, so be sure to check the Play Store to see if there are any updates available.
+ Support
+ Wacky
+ Habitica Questions
+ Could not find user
+ Animal Ears
+ Animal Tail
+ Headband
+ Accent
+ Buy All
+ Read More
+ You are unable to buy that amount.
diff --git a/Habitica/res/values/styles.xml b/Habitica/res/values/styles.xml
index 8e66f75c9..08a53f02a 100644
--- a/Habitica/res/values/styles.xml
+++ b/Habitica/res/values/styles.xml
@@ -272,11 +272,11 @@
+
+
diff --git a/Habitica/res/values/values.xml b/Habitica/res/values/values.xml
index 2572ea591..b6510e863 100644
--- a/Habitica/res/values/values.xml
+++ b/Habitica/res/values/values.xml
@@ -89,6 +89,14 @@
- Rosstavo\'s Theme
- Dewin\'s Theme
- Airu\'s Theme
+ - Beatscribe\'s NES Theme
+ - Arashi\'s Theme
+ - Triumph Theme
+ - Lunasol Theme
+ - SpacePenguin\'s Theme
+ - MAFL Theme
+ - Pizilden\'s Theme
+ - Farvoid Theme
@@ -100,6 +108,14 @@
- rosstavoTheme
- dewinTheme
- airuTheme
+ - beatscribeNesTheme
+ - arashiTheme
+ - triumphTheme
+ - lunasolTheme
+ - spacePenguinTheme
+ - maflTheme
+ - pizildenTheme
+ - farvoidTheme
diff --git a/Habitica/res/xml/preferences_fragment.xml b/Habitica/res/xml/preferences_fragment.xml
index 1adcef4e7..f88a0744a 100644
--- a/Habitica/res/xml/preferences_fragment.xml
+++ b/Habitica/res/xml/preferences_fragment.xml
@@ -273,9 +273,88 @@
android:title="@string/preference_push_unjoined_guild_mention"
android:layout="@layout/preference_child_summary"/>
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
insufficientGemPurchaseAdjust
false
+
+ raiseShops
+ 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 b166b4556..fc98d19c1 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
@@ -62,10 +62,10 @@ interface ApiService {
fun equipItem(@Path("type") type: String, @Path("key") itemKey: String): Flowable>
@POST("user/buy/{key}")
- fun buyItem(@Path("key") itemKey: String): Flowable>
+ fun buyItem(@Path("key") itemKey: String, @Body quantity: Map): Flowable>
@POST("user/purchase/{type}/{key}")
- fun purchaseItem(@Path("type") type: String, @Path("key") itemKey: String): Flowable>
+ fun purchaseItem(@Path("type") type: String, @Path("key") itemKey: String, @Body quantity: Map): Flowable>
@POST("user/purchase-hourglass/{type}/{key}")
fun purchaseHourglassItem(@Path("type") type: String, @Path("key") itemKey: String): Flowable>
@@ -138,6 +138,8 @@ interface ApiService {
@POST("user/auth/social")
fun connectSocial(@Body auth: UserAuthSocial): Flowable>
+ @POST("user/auth/apple")
+ fun loginApple(@Body auth: Map): Flowable>
@POST("user/sleep")
fun sleep(): Flowable>
@@ -248,6 +250,9 @@ interface ApiService {
@POST("/iap/android/subscribe")
fun validateSubscription(@Body request: SubscriptionValidationRequest): Flowable>
+ @GET("/iap/android/subscribe/cancel")
+ fun cancelSubscription(): Flowable>
+
@POST("/iap/android/norenew-subscribe")
fun validateNoRenewSubscription(@Body request: PurchaseValidationRequest): 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 5ed847c18..92774ceee 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
@@ -42,16 +42,11 @@ import com.habitrpg.android.habitica.ui.adapter.tasks.RewardsRecyclerViewAdapter
import com.habitrpg.android.habitica.ui.adapter.tasks.TodosRecyclerViewAdapter;
import com.habitrpg.android.habitica.ui.fragments.AboutFragment;
import com.habitrpg.android.habitica.ui.fragments.AchievementsFragment;
-import com.habitrpg.android.habitica.ui.fragments.purchases.GemsPurchaseFragment;
import com.habitrpg.android.habitica.ui.fragments.NavigationDrawerFragment;
import com.habitrpg.android.habitica.ui.fragments.NewsFragment;
import com.habitrpg.android.habitica.ui.fragments.StatsFragment;
-import com.habitrpg.android.habitica.ui.fragments.purchases.GiftBalanceGemsFragment;
-import com.habitrpg.android.habitica.ui.fragments.purchases.GiftPurchaseGemsFragment;
-import com.habitrpg.android.habitica.ui.fragments.purchases.SubscriptionFragment;
-import com.habitrpg.android.habitica.ui.fragments.faq.FAQDetailFragment;
-import com.habitrpg.android.habitica.ui.fragments.faq.FAQOverviewFragment;
import com.habitrpg.android.habitica.ui.fragments.inventory.customization.AvatarCustomizationFragment;
+import com.habitrpg.android.habitica.ui.fragments.inventory.customization.AvatarEquipmentFragment;
import com.habitrpg.android.habitica.ui.fragments.inventory.customization.AvatarOverviewFragment;
import com.habitrpg.android.habitica.ui.fragments.inventory.equipment.EquipmentDetailFragment;
import com.habitrpg.android.habitica.ui.fragments.inventory.equipment.EquipmentOverviewFragment;
@@ -65,9 +60,14 @@ import com.habitrpg.android.habitica.ui.fragments.inventory.stable.StableFragmen
import com.habitrpg.android.habitica.ui.fragments.inventory.stable.StableRecyclerFragment;
import com.habitrpg.android.habitica.ui.fragments.preferences.APIPreferenceFragment;
import com.habitrpg.android.habitica.ui.fragments.preferences.AuthenticationPreferenceFragment;
+import com.habitrpg.android.habitica.ui.fragments.preferences.EmailNotificationsPreferencesFragment;
import com.habitrpg.android.habitica.ui.fragments.preferences.PreferencesFragment;
import com.habitrpg.android.habitica.ui.fragments.preferences.ProfilePreferencesFragment;
import com.habitrpg.android.habitica.ui.fragments.preferences.PushNotificationsPreferencesFragment;
+import com.habitrpg.android.habitica.ui.fragments.purchases.GemsPurchaseFragment;
+import com.habitrpg.android.habitica.ui.fragments.purchases.GiftBalanceGemsFragment;
+import com.habitrpg.android.habitica.ui.fragments.purchases.GiftPurchaseGemsFragment;
+import com.habitrpg.android.habitica.ui.fragments.purchases.SubscriptionFragment;
import com.habitrpg.android.habitica.ui.fragments.setup.AvatarSetupFragment;
import com.habitrpg.android.habitica.ui.fragments.setup.IntroFragment;
import com.habitrpg.android.habitica.ui.fragments.setup.TaskSetupFragment;
@@ -76,12 +76,12 @@ import com.habitrpg.android.habitica.ui.fragments.skills.SkillTasksRecyclerViewF
import com.habitrpg.android.habitica.ui.fragments.skills.SkillsFragment;
import com.habitrpg.android.habitica.ui.fragments.social.ChatFragment;
import com.habitrpg.android.habitica.ui.fragments.social.ChatListFragment;
-import com.habitrpg.android.habitica.ui.fragments.social.NoPartyFragmentFragment;
import com.habitrpg.android.habitica.ui.fragments.social.GuildDetailFragment;
import com.habitrpg.android.habitica.ui.fragments.social.GuildFragment;
import com.habitrpg.android.habitica.ui.fragments.social.GuildsOverviewFragment;
-import com.habitrpg.android.habitica.ui.fragments.social.InboxOverviewFragment;
import com.habitrpg.android.habitica.ui.fragments.social.InboxMessageListFragment;
+import com.habitrpg.android.habitica.ui.fragments.social.InboxOverviewFragment;
+import com.habitrpg.android.habitica.ui.fragments.social.NoPartyFragmentFragment;
import com.habitrpg.android.habitica.ui.fragments.social.PublicGuildsFragment;
import com.habitrpg.android.habitica.ui.fragments.social.QuestDetailFragment;
import com.habitrpg.android.habitica.ui.fragments.social.TavernDetailFragment;
@@ -92,6 +92,10 @@ import com.habitrpg.android.habitica.ui.fragments.social.challenges.ChallengesOv
import com.habitrpg.android.habitica.ui.fragments.social.party.PartyDetailFragment;
import com.habitrpg.android.habitica.ui.fragments.social.party.PartyFragment;
import com.habitrpg.android.habitica.ui.fragments.social.party.PartyInviteFragment;
+import com.habitrpg.android.habitica.ui.fragments.support.BugFixFragment;
+import com.habitrpg.android.habitica.ui.fragments.support.FAQDetailFragment;
+import com.habitrpg.android.habitica.ui.fragments.support.FAQOverviewFragment;
+import com.habitrpg.android.habitica.ui.fragments.support.SupportMainFragment;
import com.habitrpg.android.habitica.ui.fragments.tasks.TaskRecyclerViewFragment;
import com.habitrpg.android.habitica.ui.fragments.tasks.TasksFragment;
import com.habitrpg.android.habitica.ui.viewmodels.GroupViewModel;
@@ -145,8 +149,6 @@ public interface UserComponent {
void inject(TasksFragment tasksFragment);
- void inject(FAQDetailFragment faqDetailFragment);
-
void inject(FAQOverviewFragment faqOverviewFragment);
void inject(AvatarCustomizationFragment avatarCustomizationFragment);
@@ -328,4 +330,14 @@ public interface UserComponent {
void inject(@NotNull GiftPurchaseGemsFragment giftPurchaseGemsFragment);
void inject(@NotNull GiftBalanceGemsFragment giftBalanceGemsFragment);
+
+ void inject(@NotNull EmailNotificationsPreferencesFragment emailNotificationsPreferencesFragment);
+
+ void inject(@NotNull SupportMainFragment supportMainFragment);
+
+ void inject(@NotNull BugFixFragment bugFixFragment);
+
+ void inject(@NotNull AvatarEquipmentFragment avatarEquipmentFragment);
+
+ void inject(@NotNull FAQDetailFragment faqDetailFragment);
}
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 3ecd884f3..f0437e8b6 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
@@ -52,9 +52,9 @@ interface ApiClient {
fun equipItem(type: String, itemKey: String): Flowable
- fun buyItem(itemKey: String): Flowable
+ fun buyItem(itemKey: String, purchaseQuantity: Int): Flowable
- fun purchaseItem(type: String, itemKey: String): Flowable
+ fun purchaseItem(type: String, itemKey: String, purchaseQuantity: Int): Flowable
fun purchaseHourglassItem(type: String, itemKey: String): Flowable
@@ -63,6 +63,7 @@ interface ApiClient {
fun purchaseQuest(key: String): Flowable
fun validateSubscription(request: SubscriptionValidationRequest): Flowable
fun validateNoRenewSubscription(request: PurchaseValidationRequest): Flowable
+ fun cancelSubscription(): Flowable
fun sellItem(itemType: String, itemKey: String): Flowable
@@ -103,6 +104,8 @@ interface ApiClient {
fun connectUser(username: String, password: String): Flowable
fun connectSocial(network: String, userId: String, accessToken: String): Flowable
+ fun loginApple(authToken: String): Flowable
+
fun sleep(): Flowable
fun revive(): 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 310a24668..e420d130a 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
@@ -31,6 +31,7 @@ interface InventoryRepository : BaseRepository {
fun retrieveInAppRewards(): Flowable>
fun getOwnedEquipment(type: String): Flowable>
+ fun getEquipmentType(type: String, set: String): Flowable>
fun getOwnedItems(itemType: String): Flowable>
fun getOwnedItems(): Flowable>
@@ -59,7 +60,7 @@ interface InventoryRepository : BaseRepository {
fun inviteToQuest(quest: QuestContent): Flowable
- fun buyItem(user: User?, id: String, value: Double): Flowable
+ fun buyItem(user: User?, id: String, value: Double, purchaseQuantity: Int): Flowable
fun retrieveShopInventory(identifier: String): Flowable
fun retrieveMarketGear(): Flowable
@@ -70,7 +71,7 @@ interface InventoryRepository : BaseRepository {
fun purchaseQuest(key: String): Flowable
- fun purchaseItem(purchaseType: String, 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>
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 d9763c4ad..d127013e5 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
@@ -174,6 +174,10 @@ class ApiClientImpl//private OnHabitsAPIResult mResultListener;
return this.apiService.connectSocial(auth).compose(configureApiCallObserver())
}
+ override fun loginApple(authToken: String): Flowable {
+ return apiService.loginApple(mapOf(Pair("code", authToken))).compose(configureApiCallObserver())
+ }
+
override fun accept(throwable: Throwable) {
val throwableClass = throwable.javaClass
if (SocketTimeoutException::class.java.isAssignableFrom(throwableClass)) {
@@ -317,12 +321,12 @@ class ApiClientImpl//private OnHabitsAPIResult mResultListener;
return apiService.equipItem(type, itemKey).compose(configureApiCallObserver())
}
- override fun buyItem(itemKey: String): Flowable {
- return apiService.buyItem(itemKey).compose(configureApiCallObserver())
+ override fun buyItem(itemKey: String, purchaseQuantity: Int): Flowable {
+ return apiService.buyItem(itemKey, mapOf(Pair("quantity", purchaseQuantity))).compose(configureApiCallObserver())
}
- override fun purchaseItem(type: String, itemKey: String): Flowable {
- return apiService.purchaseItem(type, itemKey).compose(configureApiCallObserver())
+ override fun purchaseItem(type: String, itemKey: String, purchaseQuantity: Int): Flowable {
+ return apiService.purchaseItem(type, itemKey, mapOf(Pair("quantity", purchaseQuantity))).compose(configureApiCallObserver())
}
override fun validateSubscription(request: SubscriptionValidationRequest): Flowable {
@@ -343,6 +347,10 @@ class ApiClientImpl//private OnHabitsAPIResult mResultListener;
}
}
+ override fun cancelSubscription(): Flowable {
+ return apiService.cancelSubscription().compose(configureApiCallObserver())
+ }
+
override fun purchaseHourglassItem(type: String, itemKey: String): Flowable {
return apiService.purchaseHourglassItem(type, itemKey).compose(configureApiCallObserver())
}
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 81fcc8f4a..8a8b91bd4 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
@@ -46,6 +46,10 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie
return localRepository.getOwnedEquipment()
}
+ override fun getEquipmentType(type: String, set: String): Flowable> {
+ return localRepository.getEquipmentType(type, set)
+ }
+
override fun getOwnedItems(itemType: String): Flowable> {
return localRepository.getOwnedItems(itemType, userID)
}
@@ -224,8 +228,8 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie
.doOnNext { localRepository.changeOwnedCount("quests", quest.key, userID, -1) }
}
- override fun buyItem(user: User?, id: String, value: Double): Flowable {
- return apiClient.buyItem(id)
+ override fun buyItem(user: User?, id: String, value: Double, purchaseQuantity: Int): Flowable {
+ return apiClient.buyItem(id, purchaseQuantity)
.doOnNext { buyResponse ->
if (user == null) {
return@doOnNext
@@ -247,7 +251,7 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie
if (buyResponse.gp != null) {
copiedUser.stats?.gp = buyResponse.gp
} else {
- copiedUser.stats?.gp = copiedUser.stats?.gp ?: 0 - value
+ copiedUser.stats?.gp = copiedUser.stats?.gp ?: 0 - (value * purchaseQuantity)
}
if (buyResponse.lvl != null) {
copiedUser.stats?.lvl = buyResponse.lvl
@@ -276,8 +280,8 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie
return apiClient.purchaseQuest(key)
}
- override fun purchaseItem(purchaseType: String, key: String): Flowable {
- return apiClient.purchaseItem(purchaseType, key)
+ override fun purchaseItem(purchaseType: String, key: String, purchaseQuantity: Int): Flowable {
+ return apiClient.purchaseItem(purchaseType, key, purchaseQuantity)
}
override fun togglePinnedItem(item: ShopItem): Flowable> {
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 cd7f125d6..c8cc35aa8 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
@@ -10,6 +10,7 @@ import com.habitrpg.android.habitica.models.responses.TaskDirection
import com.habitrpg.android.habitica.models.responses.TaskDirectionData
import com.habitrpg.android.habitica.models.responses.TaskScoringResult
import com.habitrpg.android.habitica.models.tasks.*
+import com.habitrpg.android.habitica.models.user.OwnedItem
import com.habitrpg.android.habitica.models.user.User
import io.reactivex.Flowable
import io.reactivex.Maybe
@@ -137,6 +138,23 @@ class TaskRepositoryImpl(localRepository: TaskLocalRepository, apiClient: ApiCli
}
}
}
+ res._tmp?.drop?.key?.let { key ->
+ val type = when(res._tmp?.drop?.type?.toLowerCase(Locale.US)) {
+ "hatchingpotion" -> "hatchingPotions"
+ "egg" -> "eggs"
+ else -> res._tmp?.drop?.type?.toLowerCase(Locale.US)
+ }
+ var item = it.where(OwnedItem::class.java).equalTo("itemType", type).equalTo("key", key).findFirst()
+ if (item == null) {
+ item = OwnedItem()
+ item.key = key
+ item.itemType = type
+ item.userID = user.id
+ }
+ item.numberOwned += 1
+ it.insertOrUpdate(item)
+ }
+
val stats = bgUser.stats
stats?.hp = res.hp
stats?.exp = res.exp
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 a453b1aae..165e52073 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
@@ -141,7 +141,7 @@ class UserRepositoryImpl(localRepository: UserLocalRepository, apiClient: ApiCli
copiedUser.preferences = unlockResponse.preferences
copiedUser.purchased = unlockResponse.purchased
copiedUser.items = unlockResponse.items
- copiedUser.balance = copiedUser.balance - customization.price / 4.0
+ copiedUser.balance = copiedUser.balance - (customization.price ?: 0) / 4.0
localRepository.saveUser(copiedUser)
}
}
@@ -352,6 +352,10 @@ class UserRepositoryImpl(localRepository: UserLocalRepository, apiClient: ApiCli
if (newUser.profile != null) {
copiedUser.profile = newUser.profile
}
+ if (newUser.party != null) {
+ copiedUser.party = newUser.party
+ }
+ copiedUser.needsCron = newUser.needsCron
copiedUser.versionNumber = newUser.versionNumber
localRepository.saveUser(copiedUser)
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 be2942e53..08331e26e 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
@@ -33,6 +33,7 @@ interface InventoryLocalRepository : ContentLocalRepository {
fun getItems(itemClass: Class, keys: Array, user: User?): Flowable>
fun getOwnedItems(itemType: String, userID: String): Flowable>
fun getOwnedItems(userID: String): Flowable>
+ fun getEquipmentType(type: String, set: String): Flowable>
fun getEquipment(key: String): Flowable
fun getMounts(type: String, group: String, color: String?): Flowable>
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmCustomizationLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmCustomizationLocalRepository.kt
index 70da32eea..786ff3480 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmCustomizationLocalRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmCustomizationLocalRepository.kt
@@ -27,12 +27,10 @@ class RealmCustomizationLocalRepository(realm: Realm) : RealmContentLocalReposit
.isNull("availableFrom")
.isNull("availableUntil")
.endGroup()
- .or()
- .equalTo("purchased", true)
.endGroup()
}
return query
- .sort("customizationSet")
+ .sort("customizationSetName")
.findAll()
.asFlowable()
.filter { it.isLoaded }
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 d831b2f65..2d5aeb0d6 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
@@ -70,6 +70,15 @@ class RealmInventoryLocalRepository(realm: Realm, private val context: Context)
.filter { it.isLoaded }
}
+ override fun getEquipmentType(type: String, set: String): Flowable> {
+ return realm.where(Equipment::class.java)
+ .equalTo("type", type)
+ .equalTo("gearSet", set)
+ .findAll()
+ .asFlowable()
+ .filter { it.isLoaded }
+ }
+
override fun getOwnedItems(itemType: String, userID: String): Flowable> {
return realm.where(OwnedItem::class.java)
.greaterThan("numberOwned", 0)
@@ -316,7 +325,7 @@ class RealmInventoryLocalRepository(realm: Realm, private val context: Context)
override fun getLatestMysteryItem(): Flowable {
return realm.where(Equipment::class.java)
.contains("key", "mystery_2")
- .sort("key", Sort.DESCENDING)
+ .sort("mystery", Sort.DESCENDING)
.findAll()
.asFlowable()
.filter { it.isLoaded && it.size > 0}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmUserLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmUserLocalRepository.kt
index 648f0e195..75ec17204 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmUserLocalRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmUserLocalRepository.kt
@@ -132,7 +132,6 @@ class RealmUserLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm),
val habitClass = if (user.preferences?.disableClasses == true) "none" else user.stats?.habitClass
return realm.where(Skill::class.java)
.equalTo("habitClass", habitClass)
- .lessThanOrEqualTo("lvl", user.stats?.lvl ?: 0)
.findAll()
.asFlowable()
.filter { it.isLoaded }
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
new file mode 100644
index 000000000..f8cb2c6bc
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/Animal-Extensions.kt
@@ -0,0 +1,23 @@
+package com.habitrpg.android.habitica.extensions
+
+import android.content.Context
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.models.inventory.Animal
+
+fun Animal.getTranslatedType(c: Context?): String {
+ if (c == null) {
+ 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()
+ else -> {
+ type
+ }
+ }
+
+ return currType
+}
\ No newline at end of file
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 d12edf8cd..b8d1d85af 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
@@ -79,7 +79,7 @@ class AppConfigManager {
return remoteConfig.getLong("minimumPasswordLength")
}
- fun useNewMysteryBenefits(): Boolean {
- return remoteConfig.getBoolean("useNewMysteryBenefits")
+ fun raiseShops(): Boolean {
+ return remoteConfig.getBoolean("raiseShops")
}
}
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 548cf315d..ada17a27b 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
@@ -115,6 +115,27 @@ class PurchaseHandler(activity: Activity, val crashlyticsProxy: CrashlyticsProxy
}
}
+ fun checkForSubscription(onSubscriptionFound: ((Purchase) -> Unit)) {
+ billingRequests?.getPurchases(ProductTypes.SUBSCRIPTION, null, object : RequestListener {
+ override fun onSuccess(result: Purchases) {
+ var lastPurchase: Purchase? = null
+ for (purchase in result.list) {
+ if (lastPurchase != null && lastPurchase.time > purchase.time) {
+ continue
+ } else {
+ lastPurchase = purchase
+ }
+ }
+ if (lastPurchase != null) {
+ onSubscriptionFound(lastPurchase)
+ }
+ }
+
+ override fun onError(response: Int, e: java.lang.Exception) {
+ }
+ })
+ }
+
private fun checkIfPendingPurchases() {
billingRequests?.getAllPurchases(ProductTypes.IN_APP, object : RequestListener {
override fun onSuccess(purchases: Purchases) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/SignInWebViewClient.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/SignInWebViewClient.kt
new file mode 100644
index 000000000..5e8297b52
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/SignInWebViewClient.kt
@@ -0,0 +1,71 @@
+package com.habitrpg.android.habitica.helpers
+
+import android.net.Uri
+import android.os.Build
+import android.util.Log
+import android.webkit.WebResourceRequest
+import android.webkit.WebResourceResponse
+import android.webkit.WebView
+import android.webkit.WebViewClient
+import androidx.annotation.RequiresApi
+import com.habitrpg.android.habitica.BuildConfig
+
+class SignInWebViewClient(
+ private val attempt: SignInWithAppleService.AuthenticationAttempt,
+ private val callback: (SignInWithAppleResult) -> Unit
+) : WebViewClient() {
+
+ // for API levels < 24
+ override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
+ return isUrlOverridden(view, Uri.parse(url))
+ }
+
+ @RequiresApi(Build.VERSION_CODES.N)
+ override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
+ return isUrlOverridden(view, request?.url)
+ }
+
+ override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? {
+ request?.requestHeaders?.let { it["Authorization"] = "Basic " + BuildConfig.STAGING_KEY }
+ return super.shouldInterceptRequest(view, request)
+ }
+
+ private fun isUrlOverridden(view: WebView?, url: Uri?): Boolean {
+ return when {
+ url == null -> {
+ false
+ }
+ url.toString().contains("appleid.apple.com") -> {
+ view?.loadUrl(url.toString())
+ true
+ }
+ (url.toString().contains(attempt.redirectUri) || url.toString().contains("userID")) -> {
+ Log.d("Apple Sign in", "Web view was forwarded to redirect URI")
+
+ val userID = url.getQueryParameter("userID")
+ val apiKey = url.getQueryParameter("key")
+
+ when {
+ userID == null || apiKey == null -> {
+ callback(SignInWithAppleResult.Failure(IllegalArgumentException("data not returned")))
+ }
+ else -> {
+ callback(SignInWithAppleResult.Success(userID, apiKey, url.getQueryParameter("newUser") == "true"))
+ }
+ }
+
+ true
+ }
+ else -> {
+ false
+ }
+ }
+ }
+
+}
+
+sealed class SignInWithAppleResult {
+ data class Success(val userID: String, val apiKey: String, val newUser: Boolean) : SignInWithAppleResult()
+ data class Failure(val error: Throwable) : SignInWithAppleResult()
+ object Cancel : SignInWithAppleResult()
+}
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
new file mode 100644
index 000000000..b500877b9
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/SignInWebViewDialogFragment.kt
@@ -0,0 +1,103 @@
+package com.habitrpg.android.habitica.helpers
+
+import android.content.DialogInterface
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.webkit.WebView
+import androidx.fragment.app.DialogFragment
+import com.habitrpg.android.habitica.R
+
+class SignInWebViewDialogFragment : DialogFragment() {
+
+ companion object {
+ private const val AUTHENTICATION_ATTEMPT_KEY = "authenticationAttempt"
+ private const val WEB_VIEW_KEY = "webView"
+
+ fun newInstance(authenticationAttempt: SignInWithAppleService.AuthenticationAttempt): SignInWebViewDialogFragment {
+ val fragment = SignInWebViewDialogFragment()
+ fragment.arguments = Bundle().apply {
+ putParcelable(AUTHENTICATION_ATTEMPT_KEY, authenticationAttempt)
+ }
+ return fragment
+ }
+ }
+
+ private lateinit var authenticationAttempt: SignInWithAppleService.AuthenticationAttempt
+ private var callback: ((SignInWithAppleResult) -> Unit)? = null
+
+ private val webViewIfCreated: WebView?
+ get() = view as? WebView
+
+ fun configure(callback: (SignInWithAppleResult) -> Unit) {
+ this.callback = callback
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ authenticationAttempt = arguments?.getParcelable(AUTHENTICATION_ATTEMPT_KEY)!!
+ setStyle(STYLE_NORMAL, R.style.sign_in_with_apple_button_DialogTheme)
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ super.onCreateView(inflater, container, savedInstanceState)
+
+ val webView = WebView(context).apply {
+ settings.apply {
+ javaScriptEnabled = true
+ javaScriptCanOpenWindowsAutomatically = true
+ }
+ }
+
+ webView.webViewClient = SignInWebViewClient(authenticationAttempt, ::onCallback)
+
+ if (savedInstanceState != null) {
+ savedInstanceState.getBundle(WEB_VIEW_KEY)?.run {
+ webView.restoreState(this)
+ }
+ } else {
+ webView.loadUrl(authenticationAttempt.authenticationUri)
+ }
+
+ return webView
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+ outState.putBundle(
+ WEB_VIEW_KEY,
+ Bundle().apply {
+ webViewIfCreated?.saveState(this)
+ }
+ )
+ }
+
+ override fun onStart() {
+ super.onStart()
+
+ dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
+ }
+
+ override fun onCancel(dialog: DialogInterface) {
+ super.onCancel(dialog)
+ onCallback(SignInWithAppleResult.Cancel)
+ }
+
+ // SignInWithAppleCallback
+
+ private fun onCallback(result: SignInWithAppleResult) {
+ dialog?.dismiss()
+ val callback = callback
+ if (callback == null) {
+ Log.e("Apple Sign in", "Callback is not configured")
+ return
+ }
+ callback(result)
+ }
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/SignInWithAppleService.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/SignInWithAppleService.kt
new file mode 100644
index 000000000..4cb72216d
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/SignInWithAppleService.kt
@@ -0,0 +1,89 @@
+package com.habitrpg.android.habitica.helpers
+
+import android.net.Uri
+import android.os.Parcel
+import android.os.Parcelable
+import androidx.fragment.app.FragmentManager
+import com.willowtreeapps.signinwithapplebutton.SignInWithAppleConfiguration
+import java.util.*
+
+class SignInWithAppleService(
+ private val fragmentManager: FragmentManager,
+ private val fragmentTag: String,
+ private val configuration: SignInWithAppleConfiguration,
+ private val callback: (SignInWithAppleResult) -> Unit
+) {
+
+ init {
+ val fragmentIfShown =
+ fragmentManager.findFragmentByTag(fragmentTag) as? SignInWebViewDialogFragment
+ fragmentIfShown?.configure(callback)
+ }
+
+ data class AuthenticationAttempt(
+ val authenticationUri: String,
+ val redirectUri: String,
+ val state: String
+ ) : Parcelable {
+ constructor(parcel: Parcel) : this(
+ parcel.readString() ?: "invalid",
+ parcel.readString() ?: "invalid",
+ parcel.readString() ?: "invalid"
+ )
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeString(authenticationUri)
+ parcel.writeString(redirectUri)
+ parcel.writeString(state)
+ }
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
+ companion object CREATOR : Parcelable.Creator {
+
+ override fun createFromParcel(parcel: Parcel) = AuthenticationAttempt(parcel)
+
+ override fun newArray(size: Int): Array = arrayOfNulls(size)
+
+ /*
+ The authentication page URI we're creating is based off the URI constructed by Apple's JavaScript SDK,
+ which is why certain fields (like the version, v) are included in the URI construction.
+
+ We have to build this URI ourselves because Apple's behavior in JavaScript is to POST the response,
+ while we need a GET so we can retrieve the authentication code and verify the state
+ merely by intercepting the URL.
+
+ See the Sign In With Apple Javascript SDK for comparison:
+ https://developer.apple.com/documentation/signinwithapplejs/configuring_your_webpage_for_sign_in_with_apple
+ */
+ fun create(
+ configuration: SignInWithAppleConfiguration,
+ state: String = UUID.randomUUID().toString()
+ ): AuthenticationAttempt {
+ val authenticationUri = Uri
+ .parse("https://appleid.apple.com/auth/authorize")
+ .buildUpon().apply {
+ appendQueryParameter("response_type", "code")
+ appendQueryParameter("v", "1.1.6")
+ appendQueryParameter("client_id", configuration.clientId)
+ appendQueryParameter("redirect_uri", configuration.redirectUri)
+ appendQueryParameter("scope", configuration.scope)
+ appendQueryParameter("state", state)
+ appendQueryParameter("response_mode", "form_post")
+ }
+ .build()
+ .toString()
+
+ return AuthenticationAttempt(authenticationUri, configuration.redirectUri, state)
+ }
+ }
+ }
+
+ fun show() {
+ val fragment = SignInWebViewDialogFragment.newInstance(AuthenticationAttempt.create(configuration))
+ fragment.configure(callback)
+ fragment.show(fragmentManager, fragmentTag)
+ }
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskAlarmManager.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskAlarmManager.kt
index c4e9a51ff..4f4094b80 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskAlarmManager.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskAlarmManager.kt
@@ -6,13 +6,14 @@ import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Build.VERSION.SDK_INT
-import androidx.core.content.edit
import androidx.preference.PreferenceManager
import com.habitrpg.android.habitica.data.TaskRepository
import com.habitrpg.android.habitica.models.tasks.RemindersItem
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.receivers.NotificationPublisher
import com.habitrpg.android.habitica.receivers.TaskReceiver
+import com.habitrpg.shared.habitica.HLogger
+import com.habitrpg.shared.habitica.LogLevel
import io.reactivex.Flowable
import io.reactivex.functions.Consumer
import java.util.*
@@ -61,9 +62,6 @@ class TaskAlarmManager(private var context: Context, private var taskRepository:
if (!preventDailyReminder) {
scheduleDailyReminder(context)
}
- PreferenceManager.getDefaultSharedPreferences(context).edit {
- putLong("lastReminderSchedule", Date().time)
- }
}
fun scheduleAlarmsForTask(task: Task) {
@@ -168,6 +166,7 @@ class TaskAlarmManager(private var context: Context, private var taskRepository:
}
private fun setAlarm(context: Context, time: Long, pendingIntent: PendingIntent?) {
+ HLogger.log(LogLevel.INFO, "TaskAlarmManager", "Scheduling for $time")
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
if (pendingIntent == null) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskFilterHelper.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskFilterHelper.kt
index d29ae2a96..7b9c6db69 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskFilterHelper.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskFilterHelper.kt
@@ -4,6 +4,7 @@ import com.habitrpg.android.habitica.models.tasks.Task
import io.realm.Case
import io.realm.OrderedRealmCollection
import io.realm.RealmQuery
+import io.realm.Sort
import java.util.*
class TaskFilterHelper {
@@ -99,7 +100,12 @@ class TaskFilterHelper {
query = query.`in`("tags.id", tagsId.toTypedArray())
}
if (searchQuery?.isNotEmpty() == true) {
- query = query.beginsWith("text", searchQuery ?: "", Case.INSENSITIVE)
+ query = query
+ .beginGroup()
+ .contains("text", searchQuery ?: "", Case.INSENSITIVE)
+ .or()
+ .contains("notes", searchQuery ?: "", Case.INSENSITIVE)
+ .endGroup()
}
if (activeFilter != null && activeFilter != Task.FILTER_ALL) {
when (activeFilter) {
@@ -115,6 +121,9 @@ class TaskFilterHelper {
Task.FILTER_COMPLETED -> query = query.equalTo("completed", true)
}
}
+ if (activeFilter != Task.FILTER_DATED) {
+ query = query.sort("position", Sort.ASCENDING, "dateCreated", Sort.DESCENDING)
+ }
}
return query
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/LevelUpUseCase.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/LevelUpUseCase.kt
index 94ef9d2b7..ebe88862a 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/LevelUpUseCase.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/LevelUpUseCase.kt
@@ -61,7 +61,7 @@ constructor(private val soundManager: SoundManager, threadExecutor: ThreadExecut
}
val event = ShareEvent()
- event.sharedMessage = requestValues.activity.getString(R.string.share_levelup, requestValues.newLevel) + " https://habitica.com/social/level-UP"
+ event.sharedMessage = requestValues.activity.getString(R.string.share_levelup, requestValues.newLevel)
val avatarView = AvatarView(requestValues.activity, showBackground = true, showMount = true, showPet = true)
avatarView.setAvatar(requestValues.user)
avatarView.onAvatarImageReady(Consumer { t -> event.shareImage = t })
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 3f5b11177..a2f2bbd41 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
@@ -1,6 +1,5 @@
package com.habitrpg.android.habitica.models.inventory;
-
public interface Animal {
String getKey();
@@ -30,4 +29,4 @@ public interface Animal {
Integer getNumberOwned();
void setNumberOwned(Integer numberOwned);
-}
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Customization.java b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Customization.java
deleted file mode 100644
index d8dad2205..000000000
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Customization.java
+++ /dev/null
@@ -1,200 +0,0 @@
-package com.habitrpg.android.habitica.models.inventory;
-
-import androidx.annotation.Nullable;
-
-import java.util.Date;
-
-import io.realm.RealmObject;
-import io.realm.annotations.PrimaryKey;
-
-public class Customization extends RealmObject {
-
- @PrimaryKey
- private String id;
- private String identifier, category, type, notes, customizationSet, customizationSetName, text;
- private boolean purchased, isBuyable;
- private Integer price, setPrice;
- private Date availableFrom, availableUntil;
-
- private void updateID() {
- this.id = this.identifier + "_" + this.type + "_" + this.category;
- }
-
- public String getId() {
- return this.id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- @Nullable
- public String getIdentifier() {
- return this.identifier;
- }
-
- public void setIdentifier(String identifier) {
- this.identifier = identifier;
- this.updateID();
- }
-
- @Nullable
- public String getCategory() {
- return this.category;
- }
-
- public void setCategory(String category) {
- this.category = category;
- this.updateID();
- }
-
- @Nullable
- public String getType() {
- return this.type;
- }
-
- public void setType(String type) {
- this.type = type;
- this.updateID();
- }
-
- public String getNotes() {
- return this.notes;
- }
-
- public void setNotes(String notes) {
- this.notes = notes;
- }
-
- public String getCustomizationSet() {
- return this.customizationSet;
- }
-
- public void setCustomizationSet(String customizationSet) {
- this.customizationSet = customizationSet;
- }
-
- public String getCustomizationSetName() {
- return this.customizationSetName;
- }
-
- public void setCustomizationSetName(@Nullable String customizationSetName) {
- this.customizationSetName = customizationSetName;
- }
-
- public String getText() {
- return this.text;
- }
-
- public void setText(String text) {
- this.text = text;
- }
-
- @SuppressWarnings("RedundantIfStatement")
- public boolean getPurchasable() {
- Date today = new Date();
- if (this.availableFrom != null && !this.availableFrom.before(today)) {
- //Not released yet
- return false;
- }
-
- if (this.availableUntil != null && !this.availableUntil.after(today)) {
- //Discontinued
- return false;
- }
-
- return true;
- }
-
- public boolean getPurchased() {
- return this.purchased;
- }
-
- public void setPurchased(boolean purchased) {
- this.purchased = purchased;
- }
-
- public Integer getPrice() {
- return this.price;
- }
-
- public void setPrice(Integer price) {
- this.price = price;
- }
-
- public Integer getSetPrice() {
- return this.setPrice;
- }
-
- public void setSetPrice(Integer setPrice) {
- this.setPrice = setPrice;
- }
-
- public Date getAvailableFrom() {
- return this.availableFrom;
- }
-
- public void setAvailableFrom(Date availableFrom) {
- this.availableFrom = availableFrom;
- }
-
- public Date getAvailableUntil() {
- return this.availableUntil;
- }
-
- public void setAvailableUntil(Date availableUntil) {
- this.availableUntil = availableUntil;
- }
-
- public String getImageName(String userSize, String hairColor) {
- switch (this.type) {
- case "skin":
- return "skin_" + this.identifier;
- case "shirt":
- return userSize + "_shirt_" + this.identifier;
- case "hair":
- if (this.identifier.equals("0")) {
- return "head_0";
- }
-
- switch (this.category) {
- case "color":
- return "hair_bangs_1_" + this.identifier;
- case "flower":
- return "hair_flower_" + this.identifier;
- default:
- return "hair_" + this.category + "_" + this.identifier + "_" + hairColor;
- }
- case "background":
- return "background_" + this.identifier;
- case "chair":
- return "chair_" + identifier;
- }
- return "";
- }
-
- public boolean isUsable() {
- return this.price == null || this.price == 0 || this.purchased;
- }
-
- public String getPath() {
- String path = this.type;
-
- if (this.category != null) {
- path = path + "." + this.category;
- }
-
- path = path + "." + this.identifier;
-
- return path;
-
- }
-
- public boolean getIsBuyable() {
- return isBuyable;
- }
-
- public void setIsBuyable(boolean buyable) {
- isBuyable = buyable;
- }
-}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Customization.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Customization.kt
new file mode 100644
index 000000000..93d5d0c61
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Customization.kt
@@ -0,0 +1,88 @@
+package com.habitrpg.android.habitica.models.inventory
+
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+import java.util.*
+
+open class Customization : RealmObject() {
+ @PrimaryKey
+ var id: String? = null
+ var identifier: String? = null
+ set(value) {
+ field = value
+ updateID()
+ }
+ var category: String? = null
+ set(value) {
+ field = value
+ updateID()
+ }
+ var type: String? = null
+ set(value) {
+ field = value
+ updateID()
+ }
+ var notes: String? = null
+ var customizationSet: String? = null
+ var customizationSetName: String? = null
+ var text: String? = null
+ var isBuyable = false
+ var price: Int? = null
+ var setPrice: Int? = null
+ var availableFrom: Date? = null
+ var availableUntil: Date? = null
+ private fun updateID() {
+ id = identifier + "_" + type + "_" + this.category
+ }
+
+ //Not released yet
+ val purchasable: Boolean
+ get() {
+ val today = Date()
+ if (availableFrom != null && !availableFrom!!.before(today)) { //Not released yet
+ return false
+ }
+ return !(availableUntil != null && !availableUntil!!.after(today))
+ }
+
+ fun getIconName(userSize: String?, hairColor: String?): String {
+ return if (type == "background") {
+ "icon_background_$identifier"
+ } else {
+ getImageName(userSize, hairColor)
+ }
+ }
+
+ fun getImageName(userSize: String?, hairColor: String?): String {
+ when (type) {
+ "skin" -> return "skin_$identifier"
+ "shirt" -> return userSize + "_shirt_" + identifier
+ "hair" -> {
+ return if (identifier == "0") {
+ "head_0"
+ } else when (this.category) {
+ "color" -> "hair_bangs_1_$identifier"
+ "flower" -> "hair_flower_$identifier"
+ else -> "hair_" + this.category + "_" + identifier + "_" + hairColor
+ }
+ }
+ "background" -> return "background_$identifier"
+ "chair" -> return "chair_$identifier"
+ }
+ return ""
+ }
+
+ fun isUsable(purchased: Boolean): Boolean {
+ return price == null || price == 0 || purchased
+ }
+
+ val path: String
+ get() {
+ var path = type
+ if (this.category != null) {
+ path = path + "." + this.category
+ }
+ path = "$path.$identifier"
+ return path
+ }
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Equipment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Equipment.kt
index e28d7c761..7213096d8 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Equipment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Equipment.kt
@@ -23,4 +23,6 @@ open class Equipment : RealmObject() {
var _int: Int = 0
var owned: Boolean? = null
var twoHanded = false
+ var mystery = ""
+ var gearSet = ""
}
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 f962c9d5e..19326be4f 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
@@ -1,7 +1,5 @@
package com.habitrpg.android.habitica.models.inventory;
-import androidx.annotation.Nullable;
-
import io.realm.RealmObject;
import io.realm.annotations.Ignore;
import io.realm.annotations.PrimaryKey;
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 19bbeed45..cf8c531e6 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
@@ -1,6 +1,5 @@
package com.habitrpg.android.habitica.models.inventory;
-
import io.realm.RealmObject;
import io.realm.annotations.Ignore;
import io.realm.annotations.PrimaryKey;
@@ -28,6 +27,7 @@ public class Pet extends RealmObject implements Animal{
return text;
}
+
@Override
public void setText(String text) {
this.text = text;
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Quest.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Quest.kt
index f4d683f64..7b23534ef 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Quest.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/Quest.kt
@@ -28,6 +28,8 @@ open class Quest : RealmObject() {
var participants: RealmList? = null
var rageStrikes: RealmList? = null
+ var completed: String? = null
+
fun hasRageStrikes(): Boolean {
return rageStrikes?.isNotEmpty() ?: false
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/QuestContent.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/QuestContent.kt
index 9f46f58d5..a5d84d7a6 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/QuestContent.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/QuestContent.kt
@@ -16,6 +16,7 @@ open class QuestContent : RealmObject(), Item {
}
internal var text: String = ""
var notes: String = ""
+ var completion: String = ""
internal var value: Int = 0
var previous: String? = null
var lvl: Int = 0
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 620e7a549..543fc0994 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
@@ -1,5 +1,6 @@
package com.habitrpg.android.habitica.models.shops
+import android.content.Context
import android.content.res.Resources
import com.google.gson.annotations.SerializedName
@@ -12,6 +13,10 @@ import io.realm.annotations.PrimaryKey
open class ShopItem : RealmObject() {
@PrimaryKey
var key: String = ""
+ set(value) {
+ field = value
+ unlockCondition?.questKey = key
+ }
var text: String? = ""
var notes: String? = ""
@SerializedName("class")
@@ -36,11 +41,20 @@ open class ShopItem : RealmObject() {
var categoryIdentifier: String = ""
var limitedNumberLeft: Int? = null
var unlockCondition: ShopItemUnlockCondition? = null
+ set(value) {
+ field = value
+ if (key.isNotEmpty()) {
+ field?.questKey = key
+ }
+ }
var path: String? = null
var isSuggested: String? = null
var pinType: String? = null
@SerializedName("klass")
var habitClass: String? = null
+ var previous: String? = null
+ @SerializedName("lvl")
+ var level: Int? = null
val isTypeItem: Boolean
get() = "eggs" == purchaseType || "hatchingPotions" == purchaseType || "food" == purchaseType || "armoire" == purchaseType || "potion" == purchaseType
@@ -54,10 +68,10 @@ open class ShopItem : RealmObject() {
val isTypeAnimal: Boolean
get() = "pets" == purchaseType || "mounts" == purchaseType
- fun canAfford(user: User?, canAlwaysAffordSpecial: Boolean): Boolean = when(currency) {
- "gold" -> value <= user?.stats?.gp ?: 0.0
- "gems" -> if (canAlwaysAffordSpecial) true else value <= user?.gemCount ?: 0
- "hourglasses" -> if (canAlwaysAffordSpecial) true else value <= user?.purchased?.plan?.consecutive?.trinkets ?: 0
+ fun canAfford(user: User?, quantity: Int): Boolean = when(currency) {
+ "gold" -> (value * quantity) <= user?.stats?.gp ?: 0.0
+ "gems" -> true
+ "hourglasses" -> true
else -> false
}
@@ -73,6 +87,46 @@ open class ShopItem : RealmObject() {
return this.key.hashCode()
}
+ fun shortLockedReason(context: Context): String? {
+ return when {
+ unlockCondition != null -> {
+ unlockCondition?.shortReadableUnlockCondition(context)
+ }
+ previous != null -> {
+ try {
+ val thisNumber = Character.getNumericValue(key.last())
+ context.getString(R.string.unlock_previous_short, thisNumber - 1)
+ } catch (e: NumberFormatException) {
+ null
+ }
+ }
+ level != null -> {
+ context.getString(R.string.unlock_level_short, level ?: 0)
+ }
+ else -> null
+ }
+ }
+
+ fun lockedReason(context: Context): String? {
+ return when {
+ unlockCondition != null -> {
+ unlockCondition?.readableUnlockCondition(context)
+ }
+ previous != null -> {
+ try {
+ val thisNumber = Character.getNumericValue(key.last())
+ context.getString(R.string.unlock_previous, thisNumber - 1)
+ } catch (e: NumberFormatException) {
+ null
+ }
+ }
+ level != null -> {
+ context.getString(R.string.unlock_level, level ?: 0)
+ }
+ else -> null
+ }
+ }
+
companion object {
private const val GEM_FOR_GOLD = "gem"
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/shops/ShopItemUnlockCondition.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/shops/ShopItemUnlockCondition.kt
index d53172005..bc79daefb 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/shops/ShopItemUnlockCondition.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/shops/ShopItemUnlockCondition.kt
@@ -1,5 +1,6 @@
package com.habitrpg.android.habitica.models.shops
+import android.content.Context
import com.habitrpg.android.habitica.R
import io.realm.RealmObject
@@ -8,12 +9,21 @@ import io.realm.annotations.PrimaryKey
open class ShopItemUnlockCondition : RealmObject() {
@PrimaryKey
- internal var condition: String? = null
+ var questKey: String? = null
+ private var condition: String? = null
+ var incentiveThreshold: Int? = null
- fun readableUnlockConditionId(): Int = when (this.condition) {
- "party invite" -> R.string.party_invite
- "login incentive" -> R.string.login_incentive
- "create account" -> R.string.create_account
- else -> R.string.empty
+ fun readableUnlockCondition(context: Context): String = when (this.condition) {
+ "party invite" -> context.getString(R.string.party_invite)
+ "login reward" -> if (incentiveThreshold != null) context.getString(R.string.login_incentive_count, incentiveThreshold) else context.getString(R.string.login_incentive)
+ "create account" -> context.getString(R.string.create_account)
+ else -> ""
+ }
+
+ fun shortReadableUnlockCondition(context: Context): String = when (this.condition) {
+ "party invite" -> context.getString(R.string.party_invite_short)
+ "login reward" -> if (incentiveThreshold != null) context.getString(R.string.login_incentive_short_count, incentiveThreshold) else context.getString(R.string.login_incentive_short)
+ "create account" -> context.getString(R.string.create_account_short)
+ else -> ""
}
}
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 9175fdbba..166ba9578 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
@@ -32,6 +32,7 @@ open class Task : RealmObject, Parcelable {
var notes: String? = null
@TaskTypes
var type: String = ""
+ var challengeID: 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/EmailNotificationsPreference.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/EmailNotificationsPreference.kt
new file mode 100644
index 000000000..f12b85dc9
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/EmailNotificationsPreference.kt
@@ -0,0 +1,19 @@
+package com.habitrpg.android.habitica.models.user
+
+import io.realm.RealmObject
+
+open class EmailNotificationsPreference : RealmObject() {
+ var unsubscribeFromAll: Boolean = false
+ var invitedParty: Boolean = false
+ var invitedQuest: Boolean = false
+ var majorUpdates: Boolean = false
+ var wonChallenge: Boolean = false
+ var invitedGuild: Boolean = false
+ var newPM: Boolean = false
+ var questStarted: Boolean = false
+ var giftedGems: Boolean = false
+ var giftedSubscription: Boolean = false
+ var subscriptionReminders: Boolean = false
+ var onboarding: Boolean = false
+ var kickedGroup: Boolean = false
+}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/OwnedCustomization.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/OwnedCustomization.kt
new file mode 100644
index 000000000..1d793e136
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/OwnedCustomization.kt
@@ -0,0 +1,28 @@
+package com.habitrpg.android.habitica.models.user
+
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+
+open class OwnedCustomization : RealmObject(), OwnedObject {
+
+ @PrimaryKey
+ override var combinedKey: String? = null
+ override var userID: String? = null
+ set(value) {
+ field = value
+ combinedKey = field + type + key
+ }
+ override var key: String? = null
+ set(value) {
+ field = value
+ combinedKey = userID + type + field
+ }
+
+ var type: String? = null
+ set(value) {
+ field = value
+ combinedKey = userID + field + key
+ }
+ var category: String? = null
+ var purchased = false
+}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Preferences.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Preferences.kt
index 968232525..8d450cfc4 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Preferences.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Preferences.kt
@@ -6,6 +6,7 @@ import com.habitrpg.android.habitica.models.AvatarPreferences
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
+import java.util.*
open class Preferences : RealmObject(), AvatarPreferences {
@@ -33,6 +34,7 @@ open class Preferences : RealmObject(), AvatarPreferences {
var timezoneOffset: Int = 0
var timezoneOffsetAtLastCron: Int = 0
var pushNotifications: PushNotificationsPreference? = null
+ var emailNotifications: EmailNotificationsPreference? = null
var autoEquip: Boolean = true
override fun getBackground(): String? {
@@ -93,7 +95,7 @@ open class Preferences : RealmObject(), AvatarPreferences {
override fun getChair(): String? {
return if (chair != null && chair != "none") {
- if (chair!!.length > 5 && chair!!.substring(0, 6) != "chair_") {
+ if (chair?.contains("chair_") == true) {
chair
} else {
"chair_" + chair!!
@@ -120,6 +122,6 @@ open class Preferences : RealmObject(), AvatarPreferences {
}
fun hasTaskBasedAllocation(): Boolean {
- return allocationMode?.toLowerCase() == "taskbased" && automaticAllocation
+ return allocationMode?.toLowerCase(Locale.ROOT) == "taskbased" && automaticAllocation
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Purchases.java b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Purchases.java
index f1df16051..89f35152e 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Purchases.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Purchases.java
@@ -1,7 +1,5 @@
package com.habitrpg.android.habitica.models.user;
-import com.habitrpg.android.habitica.models.inventory.Customization;
-
import java.util.List;
import io.realm.RealmList;
@@ -13,15 +11,15 @@ public class Purchases extends RealmObject {
@PrimaryKey
private String userId;
- public RealmList customizations;
+ public RealmList customizations;
User user;
private SubscriptionPlan plan;
- public List getCustomizations() {
+ public List getCustomizations() {
return customizations;
}
- public void setCustomizations(RealmList customizations) {
+ public void setCustomizations(RealmList customizations) {
this.customizations = customizations;
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskAlarmBootReceiver.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskAlarmBootReceiver.kt
index 23d20d914..37e82fc7b 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskAlarmBootReceiver.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskAlarmBootReceiver.kt
@@ -6,6 +6,8 @@ import android.content.Intent
import android.content.SharedPreferences
import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.helpers.TaskAlarmManager
+import com.habitrpg.shared.habitica.HLogger
+import com.habitrpg.shared.habitica.LogLevel
import javax.inject.Inject
class TaskAlarmBootReceiver : BroadcastReceiver() {
@@ -18,6 +20,7 @@ class TaskAlarmBootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, arg1: Intent) {
HabiticaBaseApplication.userComponent?.inject(this)
taskAlarmManager.scheduleAllSavedAlarms(sharedPreferences.getBoolean("preventDailyReminder", false))
+ HLogger.log(LogLevel.INFO, this::javaClass.name, "onReceive")
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskReceiver.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskReceiver.kt
index 85150d901..d0ed936d1 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskReceiver.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskReceiver.kt
@@ -15,6 +15,8 @@ import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.helpers.TaskAlarmManager
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.ui.activities.MainActivity
+import com.habitrpg.shared.habitica.HLogger
+import com.habitrpg.shared.habitica.LogLevel
import io.reactivex.functions.Consumer
import java.util.*
import javax.inject.Inject
@@ -31,6 +33,7 @@ class TaskReceiver : BroadcastReceiver() {
lateinit var taskRepository: TaskRepository
override fun onReceive(context: Context, intent: Intent) {
+ HLogger.log(LogLevel.INFO, this::javaClass.name, "onReceive")
HabiticaBaseApplication.userComponent?.inject(this)
val extras = intent.extras
if (extras != null) {
@@ -58,6 +61,7 @@ class TaskReceiver : BroadcastReceiver() {
private fun createNotification(context: Context, task: Task) {
val intent = Intent(context, MainActivity::class.java)
+ HLogger.log(LogLevel.INFO, this::javaClass.name, "Create Notification")
intent.putExtra("notificationIdentifier", "task_reminder")
val pendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), intent, 0)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/AvatarView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/AvatarView.kt
index 7d017fd07..396edfe86 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/AvatarView.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/AvatarView.kt
@@ -18,6 +18,7 @@ import com.facebook.imagepipeline.image.ImageInfo
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.helpers.AppConfigManager
import com.habitrpg.android.habitica.models.Avatar
+import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
import io.reactivex.functions.Consumer
import java.util.*
import java.util.concurrent.atomic.AtomicInteger
@@ -121,7 +122,7 @@ class AvatarView : View {
multiDraweeHolder.add(draweeHolder)
val controller = Fresco.newDraweeControllerBuilder()
- .setUri(IMAGE_URI_ROOT + getFileName(layerName))
+ .setUri(IMAGE_URI_ROOT + DataBindingUtils.getFullFilename(layerName, null))
.setControllerListener(object : BaseControllerListener() {
override fun onFinalImageSet(
id: String?,
@@ -382,18 +383,7 @@ class AvatarView : View {
return bounds
}
- private fun getFileName(imageName: String): String {
- val name = when {
- FILENAME_MAP.containsKey(imageName) -> FILENAME_MAP[imageName]
- imageName.startsWith("handleless") -> "chair_$imageName"
- else -> imageName
- }
- return name + if (FILEFORMAT_MAP.containsKey(imageName)) {
- "." + FILEFORMAT_MAP[imageName]
- } else {
- ".png"
- }
- }
+
private fun onLayerComplete() {
if (numberLayersInProcess.decrementAndGet() == 0) {
@@ -528,50 +518,9 @@ class AvatarView : View {
companion object {
const val IMAGE_URI_ROOT = "https://habitica-assets.s3.amazonaws.com/mobileApp/images/"
private const val TAG = "AvatarView"
- val FILEFORMAT_MAP: Map
- val FILENAME_MAP: Map
private val FULL_HERO_RECT = Rect(0, 0, 140, 147)
private val COMPACT_HERO_RECT = Rect(0, 0, 114, 114)
private val HERO_ONLY_RECT = Rect(0, 0, 90, 90)
- init {
- val tempMap = HashMap()
- tempMap["head_special_1"] = "gif"
- tempMap["broad_armor_special_1"] = "gif"
- tempMap["slim_armor_special_1"] = "gif"
- tempMap["head_special_0"] = "gif"
- tempMap["slim_armor_special_0"] = "gif"
- tempMap["broad_armor_special_0"] = "gif"
- tempMap["weapon_special_critical"] = "gif"
- tempMap["weapon_special_0"] = "gif"
- tempMap["shield_special_0"] = "gif"
- tempMap["Pet-Wolf-Cerberus"] = "gif"
- tempMap["armor_special_ks2019"] = "gif"
- tempMap["slim_armor_special_ks2019"] = "gif"
- tempMap["broad_armor_special_ks2019"] = "gif"
- tempMap["eyewear_special_ks2019"] = "gif"
- tempMap["head_special_ks2019"] = "gif"
- tempMap["shield_special_ks2019"] = "gif"
- tempMap["weapon_special_ks2019"] = "gif"
- tempMap["Pet-Gryphon-Gryphatrice"] = "gif"
- tempMap["Mount_Head_Gryphon-Gryphatrice"] = "gif"
- tempMap["Mount_Body_Gryphon-Gryphatrice"] = "gif"
- tempMap["background_clocktower"] = "gif"
- tempMap["background_airship"] = "gif"
- tempMap["background_steamworks"] = "gif"
- FILEFORMAT_MAP = Collections.unmodifiableMap(tempMap)
-
-
- val tempNameMap = HashMap()
- tempNameMap["head_special_1"] = "ContributorOnly-Equip-CrystalHelmet"
- tempNameMap["armor_special_1"] = "ContributorOnly-Equip-CrystalArmor"
- tempNameMap["head_special_0"] = "BackerOnly-Equip-ShadeHelmet"
- tempNameMap["armor_special_0"] = "BackerOnly-Equip-ShadeArmor"
- tempNameMap["shield_special_0"] = "BackerOnly-Shield-TormentedSkull"
- tempNameMap["weapon_special_0"] = "BackerOnly-Weapon-DarkSoulsBlade"
- tempNameMap["weapon_special_critical"] = "weapon_special_critical"
- tempNameMap["Pet-Wolf-Cerberus"] = "Pet-Wolf-Cerberus"
- FILENAME_MAP = Collections.unmodifiableMap(tempNameMap)
- }
}
}
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 9d0186081..9c7ea04d8 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
@@ -47,9 +47,9 @@ class FixCharacterValuesActivity: BaseActivity() {
setTitle(R.string.fix_character_values)
setupToolbar(binding.toolbar)
- repository.getUser(userId).firstElement().subscribe(Consumer {
+ compositeSubscription.add(repository.getUser(userId).firstElement().subscribe(Consumer {
user = it
- }, RxErrorHandler.handleEmptyError())
+ }, RxErrorHandler.handleEmptyError()))
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
@@ -57,8 +57,8 @@ class FixCharacterValuesActivity: BaseActivity() {
return true
}
- override fun onOptionsItemSelected(item: MenuItem?): Boolean {
- val id = item?.itemId
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ val id = item.itemId
if (id == R.id.action_save_changes) {
@Suppress("DEPRECATION")
@@ -70,10 +70,10 @@ class FixCharacterValuesActivity: BaseActivity() {
userInfo["stats.mp"] = binding.manaEditText.getDoubleValue()
userInfo["stats.lvl"] = binding.levelEditText.getDoubleValue().toInt()
userInfo["achievements.streak"] = binding.streakEditText.getDoubleValue().toInt()
- repository.updateUser(user, userInfo).subscribe(Consumer {}, RxErrorHandler.handleEmptyError(), Action {
+ compositeSubscription.add(repository.updateUser(user, userInfo).subscribe(Consumer {}, RxErrorHandler.handleEmptyError(), Action {
progressDialog.dismiss()
finish()
- })
+ }))
return true
}
@@ -89,14 +89,15 @@ class FixCharacterValuesActivity: BaseActivity() {
}
private fun updateFields(user: User) {
- binding.healthEditText.text = user.stats?.hp.toString()
- binding.experienceEditText.text = user.stats?.exp.toString()
- binding.goldEditText.text = user.stats?.gp.toString()
- binding.manaEditText.text = user.stats?.mp.toString()
- binding.levelEditText.text = user.stats?.lvl.toString()
+ 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()
- when (user.stats?.habitClass) {
+ when (stats.habitClass) {
Stats.WARRIOR -> {
binding.levelEditText.iconBackgroundColor = ContextCompat.getColor(this, R.color.red_500)
binding.levelEditText.setIconBitmap(HabiticaIconsHelper.imageOfWarriorLightBg())
@@ -116,7 +117,7 @@ class FixCharacterValuesActivity: BaseActivity() {
}
}
- fun FixValuesEditText.getDoubleValue(): Double {
+ private fun FixValuesEditText.getDoubleValue(): Double {
val stringValue = this.text
return try {
stringValue.toDouble()
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 6cb9e185b..500603e13 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.habitrpg.android.habitica.BuildConfig
import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.api.HostConfig
@@ -40,10 +41,7 @@ import com.habitrpg.android.habitica.data.UserRepository
import com.habitrpg.android.habitica.extensions.addCancelButton
import com.habitrpg.android.habitica.extensions.addCloseButton
import com.habitrpg.android.habitica.extensions.addOkButton
-import com.habitrpg.android.habitica.helpers.AmplitudeManager
-import com.habitrpg.android.habitica.helpers.AppConfigManager
-import com.habitrpg.android.habitica.helpers.KeyHelper
-import com.habitrpg.android.habitica.helpers.RxErrorHandler
+import com.habitrpg.android.habitica.helpers.*
import com.habitrpg.android.habitica.models.auth.UserAuthResponse
import com.habitrpg.android.habitica.proxy.CrashlyticsProxy
import com.habitrpg.android.habitica.ui.helpers.bindView
@@ -51,6 +49,7 @@ import com.habitrpg.android.habitica.ui.helpers.dismissKeyboard
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
import com.habitrpg.android.habitica.ui.views.login.LockableScrollView
import com.habitrpg.android.habitica.ui.views.login.LoginBackgroundView
+import com.willowtreeapps.signinwithapplebutton.SignInWithAppleConfiguration
import io.reactivex.Flowable
import io.reactivex.exceptions.Exceptions
import io.reactivex.functions.Consumer
@@ -96,6 +95,7 @@ class LoginActivity : BaseActivity(), Consumer {
private val forgotPasswordButton: Button by bindView(R.id.forgot_password)
private val facebookLoginButton: Button by bindView(R.id.fb_login_button)
private val googleLoginButton: Button by bindView(R.id.google_login_button)
+ private val appleLoginButton: Button by bindView(R.id.apple_login_button)
private var callbackManager = CallbackManager.Factory.create()
private var googleEmail: String? = null
@@ -177,6 +177,25 @@ class LoginActivity : BaseActivity(), Consumer {
forgotPasswordButton.setOnClickListener { onForgotPasswordClicked() }
facebookLoginButton.setOnClickListener { handleFacebookLogin() }
googleLoginButton.setOnClickListener { handleGoogleLogin() }
+ appleLoginButton.setOnClickListener {
+ val configuration = SignInWithAppleConfiguration(
+ clientId = BuildConfig.APPLE_AUTH_CLIENT_ID,
+ redirectUri = "${hostConfig.address}/api/v4/user/auth/apple",
+ scope = "name email"
+ )
+ val fragmentTag = "SignInWithAppleButton-SignInWebViewDialogFragment"
+
+ SignInWithAppleService(supportFragmentManager, fragmentTag, configuration) { result ->
+ when (result) {
+ is SignInWithAppleResult.Success -> {
+ val response = UserAuthResponse()
+ response.id = result.userID
+ response.apiToken = result.apiKey
+ response.newUser = result.newUser
+ }
+ }
+ }.show()
+ }
}
private fun setupFacebookLogin() {
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 b73849d1c..396cf53d3 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
@@ -35,7 +35,6 @@ import com.habitrpg.android.habitica.data.*
import com.habitrpg.android.habitica.databinding.ActivityMainBinding
import com.habitrpg.android.habitica.events.*
import com.habitrpg.android.habitica.events.commands.FeedCommand
-import com.habitrpg.android.habitica.extensions.DateUtils
import com.habitrpg.android.habitica.extensions.dpToPx
import com.habitrpg.android.habitica.extensions.subscribeWithErrorHandler
import com.habitrpg.android.habitica.helpers.*
@@ -63,6 +62,7 @@ import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.SnackbarDisplayTy
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.HabiticaAlertDialog
+import com.habitrpg.android.habitica.ui.views.dialogs.QuestCompletedDialog
import com.habitrpg.android.habitica.ui.views.yesterdailies.YesterdailyDialog
import com.habitrpg.android.habitica.userpicture.BitmapUtils
import com.habitrpg.android.habitica.widget.AvatarStatsWidgetProvider
@@ -214,6 +214,12 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
setupNotifications()
setupBottomnavigationLayoutListener()
+
+ try {
+ taskAlarmManager.scheduleAllSavedAlarms(sharedPreferences.getBoolean("preventDailyReminder", false))
+ } catch (e: Exception) {
+ crashlyticsProxy.logException(e)
+ }
}
private fun setupNotifications() {
@@ -273,15 +279,6 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
}
resumeFromActivity = false
-
- if (this.sharedPreferences.getLong("lastReminderSchedule", 0) < Date().time - DateUtils.hoursInMs(2)) {
- try {
- taskAlarmManager.scheduleAllSavedAlarms(sharedPreferences.getBoolean("preventDailyReminder", false))
- } catch (e: Exception) {
- crashlyticsProxy.logException(e)
- }
- }
-
//Track when the app was last opened, so that we can use this to send out special reminders after a week of inactivity
sharedPreferences.edit {
putLong("lastAppLaunch", Date().time)
@@ -353,6 +350,15 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
val intent = Intent(this, VerifyUsernameActivity::class.java)
startActivity(intent)
}
+
+ val quest = user?.party?.quest
+ if (quest?.completed?.isNotBlank() == true) {
+ compositeSubscription.add(inventoryRepository.getQuestContent(user?.party?.quest?.completed ?: "").firstElement().subscribe {
+ QuestCompletedDialog.showWithQuest(this, it)
+
+ userRepository.updateUser(user, "party.quest.completed", "").subscribe(Consumer {}, RxErrorHandler.handleEmptyError())
+ })
+ }
}
}
@@ -427,7 +433,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
dialog.addButton(R.string.onwards, true)
dialog.addButton(R.string.share, false) { hatchingDialog, _ ->
val event1 = ShareEvent()
- event1.sharedMessage = getString(R.string.share_raised, pet.text) + " https://habitica.com/social/raise-pet"
+ event1.sharedMessage = getString(R.string.share_raised, pet.text)
val mountImageSideLength = 99
val sharedImage = Bitmap.createBitmap(mountImageSideLength, mountImageSideLength, Bitmap.Config.ARGB_8888)
val canvas = Canvas(sharedImage)
@@ -578,13 +584,16 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
val sharingIntent = Intent(Intent.ACTION_SEND)
sharingIntent.type = "*/*"
sharingIntent.putExtra(Intent.EXTRA_TEXT, event.sharedMessage)
- val f = BitmapUtils.saveToShareableFile("$filesDir/shared_images", "share.png", event.shareImage)
+ BitmapUtils.clearDirectoryContent("$filesDir/shared_images")
+ val f = BitmapUtils.saveToShareableFile("$filesDir/shared_images", "${Date().toString()}.png", event.shareImage)
val fileUri = f?.let { FileProvider.getUriForFile(this, getString(R.string.content_provider), it) }
- sharingIntent.putExtra(Intent.EXTRA_STREAM, fileUri)
- val resInfoList = this.packageManager.queryIntentActivities(sharingIntent, PackageManager.MATCH_DEFAULT_ONLY)
- for (resolveInfo in resInfoList) {
- val packageName = resolveInfo.activityInfo.packageName
- this.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ if (fileUri != null) {
+ sharingIntent.putExtra(Intent.EXTRA_STREAM, fileUri)
+ val resInfoList = this.packageManager.queryIntentActivities(sharingIntent, PackageManager.MATCH_DEFAULT_ONLY)
+ for (resolveInfo in resInfoList) {
+ val packageName = resolveInfo.activityInfo.packageName
+ this.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ }
}
startActivity(Intent.createChooser(sharingIntent, getString(R.string.share_using)))
}
@@ -715,7 +724,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
dialog.addButton(R.string.onwards, true) { hatchingDialog, _ -> hatchingDialog.dismiss() }
dialog.addButton(R.string.share, false) { hatchingDialog, _ ->
val event1 = ShareEvent()
- event1.sharedMessage = getString(R.string.share_hatched, potionName, eggName) + " https://habitica.com/social/hatch-pet"
+ event1.sharedMessage = getString(R.string.share_hatched, potionName, eggName)
val petImageSideLength = 140
val sharedImage = Bitmap.createBitmap(petImageSideLength, petImageSideLength, Bitmap.Config.ARGB_8888)
val canvas = Canvas(sharedImage)
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 a068a6d64..74bd3fe44 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
@@ -5,11 +5,7 @@ import androidx.preference.PreferenceFragmentCompat
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.APIPreferenceFragment
-import com.habitrpg.android.habitica.ui.fragments.preferences.AuthenticationPreferenceFragment
-import com.habitrpg.android.habitica.ui.fragments.preferences.PreferencesFragment
-import com.habitrpg.android.habitica.ui.fragments.preferences.ProfilePreferencesFragment
-import com.habitrpg.android.habitica.ui.fragments.preferences.PushNotificationsPreferencesFragment
+import com.habitrpg.android.habitica.ui.fragments.preferences.*
import kotlinx.android.synthetic.main.activity_prefs.*
class PrefsActivity : BaseActivity(), PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
@@ -60,6 +56,7 @@ class PrefsActivity : BaseActivity(), PreferenceFragmentCompat.OnPreferenceStart
"authentication" -> AuthenticationPreferenceFragment()
"api" -> APIPreferenceFragment()
"pushNotifications" -> PushNotificationsPreferencesFragment()
+ "emailNotifications" -> EmailNotificationsPreferencesFragment()
else -> null
}
}
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 b5cc6a7af..d5e1e085f 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
@@ -23,7 +23,6 @@ import com.habitrpg.android.habitica.data.ApiClient
import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.data.TaskRepository
import com.habitrpg.android.habitica.data.UserRepository
-import com.habitrpg.android.habitica.extensions.subscribeWithErrorHandler
import com.habitrpg.android.habitica.helpers.AmplitudeManager
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.user.User
@@ -72,8 +71,8 @@ class SetupActivity : BaseActivity(), ViewPager.OnPageChangeListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- compositeSubscription.add(userRepository.getUser().subscribeWithErrorHandler(Consumer { this.onUserReceived(it) }))
- compositeSubscription.add(userRepository.retrieveUser().subscribeWithErrorHandler(Consumer {}))
+ compositeSubscription.add(userRepository.getUser().subscribe(Consumer { this.onUserReceived(it) }, RxErrorHandler.handleEmptyError()))
+ compositeSubscription.add(userRepository.retrieveUser().subscribe(Consumer {}, RxErrorHandler.handleEmptyError()))
val additionalData = HashMap()
additionalData["status"] = "displayed"
AmplitudeManager.sendEvent("setup", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData)
@@ -202,16 +201,15 @@ class SetupActivity : BaseActivity(), ViewPager.OnPageChangeListener {
additionalData["status"] = "completed"
AmplitudeManager.sendEvent("setup", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData)
- this.startMainActivity()
+ startMainActivity()
return
}
this.user = user
- if (this.pager.adapter == null) {
- this.setupViewpager()
- } else {
- this.avatarSetupFragment?.setUser(user)
- this.taskSetupFragment?.setUser(user)
+ if (pager.adapter == null) {
+ setupViewpager()
}
+ avatarSetupFragment?.setUser(user)
+ taskSetupFragment?.setUser(user)
}
private fun startMainActivity() {
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 8f74022b8..b602e61dd 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
@@ -85,7 +85,7 @@ class TaskFormActivity : BaseActivity() {
private val statPerceptionButton: TextView by bindView(R.id.stat_perception_button)
private val rewardValueTitleView: TextView by bindView(R.id.reward_value_title)
- private val rewardValueFormView: RewardValueFormView by bindView(R.id.reward_value)
+ private val stepperValueFormView: StepperValueFormView by bindView(R.id.reward_value)
private val tagsTitleView: TextView by bindView(R.id.tags_title)
private val tagsWrapper: LinearLayout by bindView(R.id.tags_wrapper)
@@ -271,7 +271,7 @@ class TaskFormActivity : BaseActivity() {
val rewardViewsVisibility = if (taskType == Task.TYPE_REWARD) View.VISIBLE else View.GONE
rewardValueTitleView.visibility = rewardViewsVisibility
- rewardValueFormView.visibility = rewardViewsVisibility
+ stepperValueFormView.visibility = rewardViewsVisibility
tagsTitleView.visibility = if (isChallengeTask) View.GONE else View.VISIBLE
tagsWrapper.visibility = if (isChallengeTask) View.GONE else View.VISIBLE
@@ -346,7 +346,7 @@ class TaskFormActivity : BaseActivity() {
taskSchedulingControls.frequency = task.frequency ?: Task.FREQUENCY_DAILY
}
Task.TYPE_TODO -> taskSchedulingControls.dueDate = task.dueDate
- Task.TYPE_REWARD -> rewardValueFormView.value = task.value
+ Task.TYPE_REWARD -> stepperValueFormView.value = task.value
}
if (taskType == Task.TYPE_DAILY || taskType == Task.TYPE_TODO) {
task.checklist?.let { checklistContainer.checklistItems = it }
@@ -420,7 +420,7 @@ class TaskFormActivity : BaseActivity() {
} else if (taskType == Task.TYPE_TODO) {
thisTask.dueDate = taskSchedulingControls.dueDate
} else if (taskType == Task.TYPE_REWARD) {
- thisTask.value = rewardValueFormView.value
+ thisTask.value = stepperValueFormView.value
}
val resultIntent = Intent()
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
new file mode 100644
index 000000000..68f6edf29
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationEquipmentRecyclerViewAdapter.kt
@@ -0,0 +1,157 @@
+package com.habitrpg.android.habitica.ui.adapter
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.core.os.bundleOf
+import com.facebook.drawee.view.SimpleDraweeView
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.databinding.CustomizationGridItemBinding
+import com.habitrpg.android.habitica.helpers.MainNavigationController
+import com.habitrpg.android.habitica.models.inventory.CustomizationSet
+import com.habitrpg.android.habitica.models.inventory.Equipment
+import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
+import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
+import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
+import io.reactivex.BackpressureStrategy
+import io.reactivex.Flowable
+import io.reactivex.subjects.PublishSubject
+import java.util.*
+
+class CustomizationEquipmentRecyclerViewAdapter : androidx.recyclerview.widget.RecyclerView.Adapter() {
+
+ var gemBalance: Int = 0
+ var equipmentList
+ : MutableList = ArrayList()
+ set(value) {
+ field = value
+ notifyDataSetChanged()
+ }
+ var activeEquipment: String? = null
+ set(value) {
+ field = value
+ this.notifyDataSetChanged()
+ }
+
+ private val selectCustomizationEvents = PublishSubject.create()
+ private val unlockCustomizationEvents = PublishSubject.create()
+ private val unlockSetEvents = PublishSubject.create()
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder {
+ val viewID: Int = R.layout.customization_grid_item
+
+ val view = LayoutInflater.from(parent.context).inflate(viewID, parent, false)
+ return EquipmentViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: androidx.recyclerview.widget.RecyclerView.ViewHolder, position: Int) {
+ (holder as EquipmentViewHolder).bind(equipmentList[position])
+ }
+
+ override fun getItemCount(): Int {
+ return equipmentList.size
+ }
+
+ override fun getItemViewType(position: Int): Int {
+ return if (this.equipmentList[position].javaClass == CustomizationSet::class.java) {
+ 0
+ } else {
+ 1
+ }
+ }
+
+ fun setEquipment(newEquipmentList: List) {
+ this.equipmentList = newEquipmentList.toMutableList()
+ this.notifyDataSetChanged()
+ }
+
+ fun getSelectCustomizationEvents(): Flowable {
+ return selectCustomizationEvents.toFlowable(BackpressureStrategy.DROP)
+ }
+
+ fun getUnlockCustomizationEvents(): Flowable {
+ return unlockCustomizationEvents.toFlowable(BackpressureStrategy.DROP)
+ }
+
+ fun getUnlockSetEvents(): Flowable {
+ return unlockSetEvents.toFlowable(BackpressureStrategy.DROP)
+ }
+
+ internal inner class EquipmentViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener {
+
+ private val binding = CustomizationGridItemBinding.bind(itemView)
+ var equipment: Equipment? = null
+
+ init {
+ itemView.setOnClickListener(this)
+ }
+
+ fun bind(equipment: Equipment) {
+ this.equipment = equipment
+ DataBindingUtils.loadImage(binding.imageView, "shop_" + this.equipment?.key)
+ if (equipment.owned == true || equipment.value == 0.0) {
+ binding.buyButton.visibility = View.GONE
+ } else {
+ binding.buyButton.visibility = View.VISIBLE
+ binding.priceLabel.currency = "gems"
+ binding.priceLabel.value = if (equipment.gearSet == "animal") {
+ 2.0
+ } else {
+ equipment.value
+ }
+ }
+
+ if (activeEquipment == equipment.key) {
+ 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)
+ }
+ }
+
+ override fun onClick(v: View) {
+ if (equipment?.owned != true && (equipment?.value ?: 0.0) > 0.0) {
+ val dialogContent = LayoutInflater.from(itemView.context).inflate(R.layout.dialog_purchase_customization, null) as LinearLayout
+
+ val imageView = dialogContent.findViewById(R.id.imageView)
+ DataBindingUtils.loadImage(imageView, "shop_" + this.equipment?.key)
+
+ val priceLabel = dialogContent.findViewById(R.id.priceLabel)
+ priceLabel.text = if (equipment?.gearSet == "animal") {
+ 2.0
+ } else {
+ equipment?.value ?: 0
+ }.toString()
+
+ (dialogContent.findViewById(R.id.gem_icon) as? ImageView)?.setImageBitmap(HabiticaIconsHelper.imageOfGem())
+
+ val dialog = HabiticaAlertDialog(itemView.context)
+ dialog.addButton(R.string.purchase_button, true) { _, _ ->
+ if (equipment?.value ?: 0.0 > gemBalance) {
+ MainNavigationController.navigate(R.id.gemPurchaseActivity, bundleOf(Pair("openSubscription", false)))
+ return@addButton
+ }
+
+ equipment?.let {
+ unlockCustomizationEvents.onNext(it)
+ }
+ }
+ dialog.setTitle(R.string.purchase_customization)
+ dialog.setAdditionalContentView(dialogContent)
+ dialog.addButton(R.string.reward_dialog_dismiss, false)
+ dialog.show()
+ return
+ }
+
+ if (equipment?.key == activeEquipment) {
+ return
+ }
+
+ equipment?.let {
+ selectCustomizationEvents.onNext(it)
+ }
+ }
+ }
+}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationRecyclerViewAdapter.kt
index 2443010bf..cbf9704f0 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationRecyclerViewAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationRecyclerViewAdapter.kt
@@ -1,14 +1,18 @@
package com.habitrpg.android.habitica.ui.adapter
import android.content.Context
+import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.widget.*
-import androidx.core.content.ContextCompat
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
import androidx.core.os.bundleOf
import com.facebook.drawee.view.SimpleDraweeView
import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.databinding.CustomizationGridItemBinding
+import com.habitrpg.android.habitica.databinding.CustomizationSectionHeaderBinding
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.models.inventory.Customization
import com.habitrpg.android.habitica.models.inventory.CustomizationSet
@@ -38,20 +42,15 @@ class CustomizationRecyclerViewAdapter : androidx.recyclerview.widget.RecyclerVi
this.notifyDataSetChanged()
}
+ var ownedCustomiztations: List = listOf()
+
private val selectCustomizationEvents = PublishSubject.create()
private val unlockCustomizationEvents = PublishSubject.create()
private val unlockSetEvents = PublishSubject.create()
fun updateOwnership(ownedCustomizations: List) {
- for ((position, obj) in customizationList.withIndex()) {
- if (obj.javaClass == Customization::class.java) {
- val customization = obj as? Customization ?: return
- if (customization.purchased != ownedCustomizations.contains(customization.id)) {
- customization.purchased = ownedCustomizations.contains(customization.id)
- notifyItemChanged(position)
- }
- }
- }
+ this.ownedCustomiztations = ownedCustomizations
+ notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder {
@@ -99,12 +98,12 @@ class CustomizationRecyclerViewAdapter : androidx.recyclerview.widget.RecyclerVi
set.identifier = customization.customizationSet
set.text = customization.customizationSetName
set.price = customization.setPrice
- set.hasPurchasable = !customization.isUsable
+ set.hasPurchasable = !customization.isUsable(ownedCustomiztations.contains(customization.identifier))
lastSet = set
customizationList.add(set)
}
customizationList.add(customization)
- if (!customization.isUsable && !lastSet.hasPurchasable) {
+ if (!customization.isUsable(ownedCustomiztations.contains(customization.identifier)) && !lastSet.hasPurchasable) {
lastSet.hasPurchasable = true
}
}
@@ -125,45 +124,51 @@ class CustomizationRecyclerViewAdapter : androidx.recyclerview.widget.RecyclerVi
internal inner class CustomizationViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener {
- private val cardView: androidx.cardview.widget.CardView by bindView(itemView, R.id.card_view)
- private val linearLayout: RelativeLayout by bindView(itemView, R.id.linearLayout)
- private val imageView: SimpleDraweeView by bindView(itemView, R.id.imageView)
- private val purchaseOverlay: View by bindView(itemView, R.id.purchaseOverlay)
-
+ private val binding = CustomizationGridItemBinding.bind(itemView)
var customization: Customization? = null
init {
- linearLayout.setOnClickListener(this)
+ itemView.setOnClickListener(this)
}
fun bind(customization: Customization) {
this.customization = customization
- if (customization.customizationSet?.contains("timeTravel") == true) {
- DataBindingUtils.loadImage(this.imageView, customization.getImageName(userSize, hairColor), imageFormat = "gif")
- } else {
- DataBindingUtils.loadImage(this.imageView, customization.getImageName(userSize, hairColor))
- }
- cardView.setCardBackgroundColor(ContextCompat.getColor(itemView.context, android.R.color.white))
- if (customization.isUsable) {
- imageView.alpha = 1.0f
- purchaseOverlay.alpha = 0.0f
- if (customization.identifier == activeCustomization) {
- cardView.setCardBackgroundColor(ContextCompat.getColor(itemView.context, R.color.brand_500))
+ DataBindingUtils.loadImage(binding.imageView, customization.getIconName(userSize, hairColor))
+
+ if (customization.type == "background") {
+ val params = (binding.imageView.layoutParams as? LinearLayout.LayoutParams)?.apply {
+ gravity = Gravity.CENTER
}
+ binding.imageView.layoutParams = params
+ }
+
+ if (customization.isUsable(ownedCustomiztations.contains(customization.identifier))) {
+ binding.buyButton.visibility = View.GONE
} else {
- imageView.alpha = 0.3f
- purchaseOverlay.alpha = 0.8f
+ binding.buyButton.visibility = View.VISIBLE
+ if (customization.customizationSet?.contains("timeTravel") == true) {
+ binding.priceLabel.currency = "hourglasses"
+ } else {
+ binding.priceLabel.currency = "gems"
+ }
+ binding.priceLabel.value = customization.price?.toDouble() ?: 0.0
+ }
+
+ if (activeCustomization == customization.identifier) {
+ 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)
}
}
override fun onClick(v: View) {
- if (customization?.isUsable == false) {
+ if (customization?.isUsable(ownedCustomiztations.contains(customization?.identifier)) == false) {
if (customization?.customizationSet?.contains("timeTravel") == true) {
val dialog = HabiticaAlertDialog(itemView.context)
dialog.setMessage(R.string.purchase_from_timetravel_shop)
dialog.addButton(R.string.go_shopping, true) { _, _ ->
- MainNavigationController.navigate(R.id.shopsFragment)
+ MainNavigationController.navigate(R.id.shopsFragment, bundleOf(Pair("selectedTab", 3)))
}
dialog.addButton(R.string.reward_dialog_dismiss, false)
dialog.show()
@@ -209,23 +214,24 @@ class CustomizationRecyclerViewAdapter : androidx.recyclerview.widget.RecyclerVi
internal inner class SectionViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener {
+ private val binding = CustomizationSectionHeaderBinding.bind(itemView)
private val label: TextView by bindView(itemView, R.id.label)
- private val purchaseSetButton: Button by bindView(itemView, R.id.purchaseSetButton)
var context: Context = itemView.context
private var set: CustomizationSet? = null
init {
- purchaseSetButton.setOnClickListener(this)
+ binding.purchaseSetButton.setOnClickListener(this)
}
fun bind(set: CustomizationSet) {
this.set = set
this.label.text = set.text
if (set.hasPurchasable && !set.identifier.contains("timeTravel")) {
- this.purchaseSetButton.visibility = View.VISIBLE
- this.purchaseSetButton.text = context.getString(R.string.purchase_set_button, set.price)
+ binding.purchaseSetButton.visibility = View.VISIBLE
+ binding.setPriceLabel.value = set.price.toDouble()
+ binding.setPriceLabel.currency = "gems"
} else {
- this.purchaseSetButton.visibility = View.GONE
+ binding.purchaseSetButton.visibility = View.GONE
}
}
@@ -245,11 +251,11 @@ class CustomizationRecyclerViewAdapter : androidx.recyclerview.widget.RecyclerVi
customizationList
.filter { Customization::class.java.isAssignableFrom(it.javaClass) }
.map { it as Customization }
- .filter { !it.isUsable && it.customizationSet != null && it.customizationSet == set?.identifier }
+ .filter { !it.isUsable(ownedCustomiztations.contains(it.identifier)) && it.customizationSet != null && it.customizationSet == set?.identifier }
.forEach { set?.customizations?.add(it) }
if (additionalSetItems.isNotEmpty()) {
additionalSetItems
- .filter { !it.isUsable && it.customizationSet != null && it.customizationSet == set?.identifier }
+ .filter { !it.isUsable(ownedCustomiztations.contains(it.identifier)) && it.customizationSet != null && it.customizationSet == set?.identifier }
.forEach { set?.customizations?.add(it) }
}
set?.let {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/FAQOverviewRecyclerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/FAQOverviewRecyclerAdapter.kt
deleted file mode 100644
index c09cc09b6..000000000
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/FAQOverviewRecyclerAdapter.kt
+++ /dev/null
@@ -1,120 +0,0 @@
-package com.habitrpg.android.habitica.ui.adapter
-
-import android.text.method.LinkMovementMethod
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.Button
-import android.widget.TextView
-import androidx.core.os.bundleOf
-import com.habitrpg.android.habitica.R
-import com.habitrpg.android.habitica.helpers.MainNavigationController
-import com.habitrpg.android.habitica.models.FAQArticle
-import com.habitrpg.android.habitica.ui.activities.MainActivity
-import com.habitrpg.android.habitica.ui.fragments.faq.FAQOverviewFragmentDirections
-import com.habitrpg.android.habitica.ui.helpers.bindView
-import com.habitrpg.android.habitica.ui.helpers.setMarkdown
-import io.reactivex.BackpressureStrategy
-import io.reactivex.Flowable
-import io.reactivex.subjects.PublishSubject
-
-class FAQOverviewRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter() {
-
- var activity: MainActivity? = null
- private var articles: List = emptyList()
-
- private val resetWalkthroughEvents = PublishSubject.create()
-
- fun setArticles(articles: List) {
- this.articles = articles
- this.notifyDataSetChanged()
- }
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder {
- return when (viewType) {
- VIEW_TYPE_JUSTIN -> {
- val view = LayoutInflater.from(parent.context).inflate(R.layout.button_list_item, parent, false)
- ResetWalkthroughViewHolder(view)
- }
- VIEW_TYPE_HEADER -> {
- val view = LayoutInflater.from(parent.context).inflate(R.layout.help_header, parent, false)
- HeaderViewHolder(view)
- }
- else -> {
- val view = LayoutInflater.from(parent.context)
- .inflate(R.layout.plain_list_item, parent, false)
- FAQArticleViewHolder(view)
- }
- }
- }
-
- override fun onBindViewHolder(holder: androidx.recyclerview.widget.RecyclerView.ViewHolder, position: Int) {
- if (getItemViewType(position) == VIEW_TYPE_FAQ) {
- (holder as? FAQArticleViewHolder)?.bind(articles[position - 1])
- }
- }
-
- override fun getItemViewType(position: Int): Int {
- return when (position) {
- 0 -> VIEW_TYPE_HEADER
- articles.size+1 -> VIEW_TYPE_JUSTIN
- else -> VIEW_TYPE_FAQ
- }
- }
-
- override fun getItemCount(): Int {
- return this.articles.size + 2
- }
-
- fun getResetWalkthroughEvents(): Flowable {
- return resetWalkthroughEvents.toFlowable(BackpressureStrategy.DROP)
- }
-
- internal inner class FAQArticleViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener {
-
- private val textView: TextView by bindView(itemView, R.id.textView)
-
- private var article: FAQArticle? = null
-
- init {
- textView.setOnClickListener(this)
- }
-
- fun bind(article: FAQArticle) {
- this.article = article
- this.textView.text = this.article?.question
- }
-
- override fun onClick(v: View) {
- article?.position?.let {
- MainNavigationController.navigate(FAQOverviewFragmentDirections.openFAQDetail(it))
- }
- }
- }
-
- private inner class ResetWalkthroughViewHolder internal constructor(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView) {
-
- init {
- val button = itemView as? Button
- button?.text = itemView.context.getString(R.string.reset_walkthrough)
- button?.setOnClickListener { resetWalkthroughEvents.onNext("") }
- }
- }
-
- private inner class HeaderViewHolder internal constructor(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView) {
-
- init {
- val textView = itemView.findViewById(R.id.text_view)
- textView.setMarkdown(itemView.context.getString(R.string.need_help_header_description, "[Habitica Help Guild](https://habitica.com/groups/guild/5481ccf3-5d2d-48a9-a871-70a7380cee5a)"))
- textView.setOnClickListener { MainNavigationController.navigate(R.id.guildFragment, bundleOf("groupID" to "5481ccf3-5d2d-48a9-a871-70a7380cee5a")) }
- textView.movementMethod = LinkMovementMethod.getInstance()
- }
- }
-
- companion object {
-
- private const val VIEW_TYPE_JUSTIN = 0
- private const val VIEW_TYPE_HEADER = 1
- private const val VIEW_TYPE_FAQ = 2
- }
-}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/SkillsRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/SkillsRecyclerViewAdapter.kt
index 80cfdc999..7f3334777 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/SkillsRecyclerViewAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/SkillsRecyclerViewAdapter.kt
@@ -5,7 +5,7 @@ import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.view.View
import android.view.ViewGroup
-import android.widget.Button
+import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
@@ -13,6 +13,7 @@ import com.facebook.drawee.view.SimpleDraweeView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.models.Skill
+import com.habitrpg.android.habitica.models.user.SpecialItems
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
@@ -24,11 +25,21 @@ class SkillsRecyclerViewAdapter : RecyclerView.Adapter()
val useSkillEvents = useSkillSubject.toFlowable(BackpressureStrategy.DROP)
- var mana: Double = 0.toDouble()
- set(value) {
- field = value
- notifyDataSetChanged()
- }
+ var mana: Double = 0.0
+ set(value) {
+ field = value
+ notifyDataSetChanged()
+ }
+ var level: Int = 0
+ set(value) {
+ field = value
+ notifyDataSetChanged()
+ }
+ var specialItems: SpecialItems? = null
+ set(value) {
+ field = value
+ notifyDataSetChanged()
+ }
private var skillList: List = emptyList()
fun setSkillList(skillList: List) {
@@ -51,18 +62,23 @@ class SkillsRecyclerViewAdapter : RecyclerView.Adapter mana) {
+ buttonWrapper.setBackgroundColor(ContextCompat.getColor(context, R.color.gray_600))
+ buttonIconView.alpha = 0.3f
+ priceLabel.alpha = 0.3f
+ } else {
+ buttonWrapper.setBackgroundColor(ContextCompat.getColor(context, R.color.blue_500_24))
+ buttonIconView.alpha = 1.0f
+ priceLabel.alpha = 1.0f
+ }
+ if ((skill.lvl ?: 0) > level) {
+ buttonWrapper.setBackgroundColor(ContextCompat.getColor(context, R.color.gray_600))
+ skillNameTextView.setTextColor(ContextCompat.getColor(context, R.color.task_gray))
+ skillNameTextView.text = context.getString(R.string.skill_unlocks_at, skill.lvl)
+ skillNotesTextView.visibility = View.GONE
+ buttonIconView.setImageDrawable(lockDrawable)
+ priceLabel.visibility = View.GONE
+ }
}
DataBindingUtils.loadImage(skillImageView, "shop_" + skill.key)
- if (skill.mana ?: 0 > mana) {
- priceButton.isEnabled = false
- priceButton.setBackgroundResource(R.color.task_gray)
- skillNameTextView.setTextColor(ContextCompat.getColor(context, R.color.task_gray))
- skillNotesTextView.setTextColor(ContextCompat.getColor(context, R.color.task_gray))
- } else {
- skillNameTextView.setTextColor(ContextCompat.getColor(context, android.R.color.black))
- skillNotesTextView.setTextColor(ContextCompat.getColor(context, android.R.color.black))
- priceButton.isEnabled = true
- }
}
override fun onClick(v: View) {
- skill?.let { useSkillSubject.onNext(it) }
+ if ((skill?.lvl ?: 0) <= level) {
+ skill?.let { useSkillSubject.onNext(it) }
+ }
+ }
+
+ private fun getOwnedCount(key: String): Int {
+ return when (key) {
+ "snowball" -> specialItems?.snowball
+ "shinySeed" -> specialItems?.shinySeed
+ "seafoam" -> specialItems?.seafoam
+ "spookySparkles" -> specialItems?.spookySparkles
+ else -> 0
+ } ?: 0
}
}
}
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 0d98050d8..9136c0c5f 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
@@ -139,7 +139,7 @@ class ShopRecyclerAdapter(private val configManager: AppConfigManager) : android
ShopItem::class.java -> {
val item = obj as? ShopItem ?: return
val itemHolder = holder as? ShopItemViewHolder ?: return
- itemHolder.bind(item, item.canAfford(user, configManager.insufficientGemPurchase()))
+ itemHolder.bind(item, item.canAfford(user, 1))
if (ownedItems.containsKey(item.key+"-"+item.pinType)) {
itemHolder.itemCount = ownedItems[item.key+"-"+item.pinType]?.numberOwned ?: 0
}
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 3e47a9a54..280a3a0fe 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
@@ -2,12 +2,17 @@ package com.habitrpg.android.habitica.ui.adapter.inventory
import android.content.Context
import android.graphics.drawable.BitmapDrawable
-import android.view.LayoutInflater
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.recyclerview.widget.GridLayoutManager
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.MainNavigationController
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.inventory.Animal
@@ -16,15 +21,18 @@ import com.habitrpg.android.habitica.ui.fragments.inventory.stable.StableFragmen
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
import com.habitrpg.android.habitica.ui.helpers.bindView
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
+
class StableRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter() {
var itemType: String? = null
var context: Context? = null
var activity: MainActivity? = null
+
private var itemList: List = ArrayList()
fun setItemList(itemList: List) {
@@ -34,17 +42,29 @@ class StableRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter<
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder =
if (viewType == 0) {
- val view = LayoutInflater.from(parent.context).inflate(R.layout.customization_section_header, parent, false)
+ 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)
- } else {
- val view = LayoutInflater.from(parent.context).inflate(R.layout.animal_overview_item, parent, false)
+ }
+ 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.javaClass == String::class.java) {
+ 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) }
@@ -52,15 +72,32 @@ class StableRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter<
}
override fun getItemViewType(position: Int): Int {
- return if (this.itemList[position].javaClass == String::class.java) {
+ return if (itemList[position] == "header") {
0
- } else {
+ }
+ else if (itemList[position].javaClass == String::class.java) {
1
}
+ else {
+ 2
+ }
}
override fun getItemCount(): Int = itemList.size
+ internal class StableHeaderViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView) {
+
+ 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)
+
+ fun bind() {
+ npcBannerView.identifier = "stable"
+ namePlate.setText(R.string.stable_owner)
+ descriptionView.visibility = View.GONE
+ }
+ }
+
internal inner class StableViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener {
private var animal: Animal? = null
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/PublicGuildsRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/PublicGuildsRecyclerViewAdapter.kt
index 106016e3d..b6f927827 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/PublicGuildsRecyclerViewAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/PublicGuildsRecyclerViewAdapter.kt
@@ -96,7 +96,11 @@ class PublicGuildsRecyclerViewAdapter(data: OrderedRealmCollection?, auto
unfilteredData?.let {
if (constraint.isNotEmpty()) {
updateData(it.where()
+ .beginGroup()
.contains("name", constraint.toString(), Case.INSENSITIVE)
+ .or()
+ .contains("summary", constraint.toString(), Case.INSENSITIVE)
+ .endGroup()
.findAll())
}
}
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 e3fcafabe..661b72ce5 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
@@ -146,7 +146,7 @@ abstract class RealmBaseTasksRecyclerViewAdapter(
if (taskFilterHelper != null) {
val query = taskFilterHelper.createQuery(unfilteredData)
if (query != null) {
- updateData(query.sort("position").findAll())
+ updateData(query.findAll())
}
}
}
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 a409ce8d3..89ab638fa 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
@@ -75,7 +75,7 @@ class RewardsRecyclerViewAdapter(private var customRewards: OrderedRealmCollecti
} else if (inAppRewards != null) {
val item = inAppRewards?.get(position - customRewardCount) ?: return
if (holder is ShopItemViewHolder) {
- holder.bind(item, item.canAfford(user, configManager.insufficientGemPurchase()))
+ holder.bind(item, item.canAfford(user, 1))
holder.isPinned = true
holder.hidePinIndicator()
}
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 1e6f540b8..c34f920ab 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
@@ -2,7 +2,6 @@ package com.habitrpg.android.habitica.ui.fragments
import android.content.Intent
import android.content.pm.PackageManager
-import android.os.Build
import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater
@@ -11,14 +10,13 @@ import android.view.ViewGroup
import android.view.animation.AccelerateInterpolator
import android.widget.TextView
import android.widget.Toast
-import androidx.core.app.ShareCompat
import androidx.core.net.toUri
import com.google.firebase.analytics.FirebaseAnalytics
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.helpers.AppConfigManager
-import com.habitrpg.android.habitica.helpers.AppTestingLevel
import com.habitrpg.android.habitica.helpers.DeviceName
+import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.modules.AppModule
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
import com.habitrpg.android.habitica.ui.helpers.bindView
@@ -122,8 +120,7 @@ class AboutFragment : BaseMainFragment() {
sourceCodeLink.setOnClickListener { openBrowserLink(androidSourceCodeLink) }
twitter.setOnClickListener { openBrowserLink(twitterLink) }
sourceCodeButton.setOnClickListener { openBrowserLink(androidSourceCodeLink) }
- reportBug.setOnClickListener { sendEmail("[Android] Bugreport") }
- sendFeedback.setOnClickListener { sendEmail("[Android] Feedback") }
+ reportBug.setOnClickListener { MainNavigationController.navigate(R.id.bugFixFragment) }
googlePlayStoreButton.setOnClickListener { openGooglePlay() }
updateAvailableWrapper.setOnClickListener { openGooglePlay() }
}
@@ -134,40 +131,6 @@ class AboutFragment : BaseMainFragment() {
startActivity(launchBrowser)
}
- 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"
-
- ShareCompat.IntentBuilder.from(activity)
- .setType("message/rfc822")
- .addEmailTo(appConfigManager.supportEmail())
- .setSubject(subject)
- .setText(bodyOfEmail)
- .setChooserTitle("Send email...")
- .startChooser()
- }
-
private fun doTheThing() {
context?.let { FirebaseAnalytics.getInstance(it).logEvent("found_easter_egg", null) }
DataBindingUtils.loadImage("Pet-Sabretooth-Base") {bitmap ->
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 0d0f17630..b2039953a 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
@@ -253,6 +253,8 @@ class NavigationDrawerFragment : DialogFragment() {
}
}
}
+ } else if (user.isSubscribed) {
+ subscriptionItem?.subtitle = null
} else {
subscriptionItem?.subtitle = context?.getString(R.string.more_out_of_habitica)
}
@@ -302,8 +304,17 @@ class NavigationDrawerFragment : DialogFragment() {
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()) {
+ 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.shopsFragment, SIDEBAR_SHOPS, context.getString(R.string.sidebar_shops)))
+ 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)))
@@ -312,7 +323,7 @@ class NavigationDrawerFragment : DialogFragment() {
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.FAQOverviewFragment, SIDEBAR_HELP, context.getString(R.string.sidebar_help)))
+ items.add(HabiticaDrawerItem(R.id.supportMainFragment, SIDEBAR_HELP, context.getString(R.string.sidebar_help)))
items.add(HabiticaDrawerItem(R.id.aboutFragment, SIDEBAR_ABOUT, context.getString(R.string.sidebar_about)))
}
if (configManager.enableGiftOneGetOne()) {
@@ -475,6 +486,10 @@ class NavigationDrawerFragment : DialogFragment() {
const val SIDEBAR_CHALLENGES = "challenges"
const val SIDEBAR_INVENTORY = "inventory"
const val SIDEBAR_SHOPS = "shops"
+ const val SIDEBAR_SHOPS_MARKET = "market"
+ const val SIDEBAR_SHOPS_QUEST = "questShop"
+ const val SIDEBAR_SHOPS_SEASONAL = "seasonalShop"
+ const val SIDEBAR_SHOPS_TIMETRAVEL = "timeTravelersShop"
const val SIDEBAR_AVATAR = "avatar"
const val SIDEBAR_EQUIPMENT = "equipment"
const val SIDEBAR_ITEMS = "items"
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/faq/FAQOverviewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/faq/FAQOverviewFragment.kt
deleted file mode 100644
index d33515826..000000000
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/faq/FAQOverviewFragment.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-package com.habitrpg.android.habitica.ui.fragments.faq
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import com.habitrpg.android.habitica.R
-import com.habitrpg.android.habitica.components.UserComponent
-import com.habitrpg.android.habitica.data.FAQRepository
-import com.habitrpg.android.habitica.extensions.inflate
-import com.habitrpg.android.habitica.helpers.RxErrorHandler
-import com.habitrpg.android.habitica.ui.adapter.FAQOverviewRecyclerAdapter
-import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
-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.Consumer
-import javax.inject.Inject
-
-class FAQOverviewFragment : BaseMainFragment() {
- @Inject
- lateinit var faqRepository: FAQRepository
-
- private val recyclerView: androidx.recyclerview.widget.RecyclerView? by bindView(R.id.recyclerView)
-
- internal var adapter: FAQOverviewRecyclerAdapter? = null
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?): View? {
- super.onCreateView(inflater, container, savedInstanceState)
- return container?.inflate(R.layout.fragment_recyclerview)
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- resetViews()
-
- adapter = FAQOverviewRecyclerAdapter()
- adapter?.getResetWalkthroughEvents()?.subscribe(Consumer { this.userRepository.resetTutorial(user) }, RxErrorHandler.handleEmptyError())?.let { compositeSubscription.add(it) }
- adapter?.activity = activity
- recyclerView?.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(activity)
- activity?.let { recyclerView?.addItemDecoration(androidx.recyclerview.widget.DividerItemDecoration(it, androidx.recyclerview.widget.DividerItemDecoration.VERTICAL)) }
- recyclerView?.adapter = adapter
- recyclerView?.itemAnimator = SafeDefaultItemAnimator()
- this.loadArticles()
- }
-
- override fun onDestroy() {
- faqRepository.close()
- super.onDestroy()
- }
-
- override fun injectFragment(component: UserComponent) {
- component.inject(this)
- }
-
- private fun loadArticles() {
- if (adapter == null) {
- return
- }
- compositeSubscription.add(faqRepository.getArticles().subscribe(Consumer { adapter?.setArticles(it) }, RxErrorHandler.handleEmptyError()))
- }
-
-}
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 9f59a5ce1..0df324546 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
@@ -8,6 +8,7 @@ import androidx.recyclerview.widget.GridLayoutManager
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.CustomizationRepository
+import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.extensions.subscribeWithErrorHandler
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.inventory.Customization
@@ -27,6 +28,8 @@ class AvatarCustomizationFragment : BaseMainFragment() {
@Inject
lateinit var customizationRepository: CustomizationRepository
+ @Inject
+ lateinit var inventoryRepository: InventoryRepository
var type: String? = null
var category: String? = null
@@ -80,8 +83,7 @@ class AvatarCustomizationFragment : BaseMainFragment() {
}
}
- setGridSpanCount(view.width)
- val layoutManager = GridLayoutManager(activity, 2)
+ val layoutManager = GridLayoutManager(activity, 4)
layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return if (adapter.getItemViewType(position) == 0) {
@@ -91,7 +93,9 @@ class AvatarCustomizationFragment : BaseMainFragment() {
}
}
}
+ setGridSpanCount(view.width)
recyclerView.layoutManager = layoutManager
+
recyclerView.addItemDecoration(MarginDecoration(context))
recyclerView.adapter = adapter
@@ -134,7 +138,7 @@ class AvatarCustomizationFragment : BaseMainFragment() {
this.updateActiveCustomization(user)
if (adapter.customizationList.size != 0) {
val ownedCustomizations = ArrayList()
- user.purchased?.customizations?.filter { it.type == this.type }?.mapTo(ownedCustomizations) { it.id }
+ user.purchased?.customizations?.filter { it.type == this.type && it.purchased }?.mapTo(ownedCustomizations) { it.key ?: "" }
adapter.updateOwnership(ownedCustomizations)
} else {
this.loadCustomizations()
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
new file mode 100644
index 000000000..2e55d5d05
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarEquipmentFragment.kt
@@ -0,0 +1,139 @@
+package com.habitrpg.android.habitica.ui.fragments.inventory.customization
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.GridLayoutManager
+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.subscribeWithErrorHandler
+import com.habitrpg.android.habitica.helpers.RxErrorHandler
+import com.habitrpg.android.habitica.models.responses.UnlockResponse
+import com.habitrpg.android.habitica.models.user.User
+import com.habitrpg.android.habitica.ui.adapter.CustomizationEquipmentRecyclerViewAdapter
+import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
+import com.habitrpg.android.habitica.ui.helpers.MarginDecoration
+import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
+import io.reactivex.Flowable
+import io.reactivex.functions.Consumer
+import kotlinx.android.synthetic.main.fragment_recyclerview.*
+import javax.inject.Inject
+
+class AvatarEquipmentFragment : BaseMainFragment() {
+
+ @Inject
+ lateinit var inventoryRepository: InventoryRepository
+
+ var type: String? = null
+ var category: String? = null
+ private var activeEquipment: String? = null
+
+ internal var adapter: CustomizationEquipmentRecyclerViewAdapter = CustomizationEquipmentRecyclerViewAdapter()
+ internal var layoutManager: GridLayoutManager = GridLayoutManager(activity, 2)
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ super.onCreateView(inflater, container, savedInstanceState)
+ val view = inflater.inflate(R.layout.fragment_recyclerview, container, false)
+
+ compositeSubscription.add(adapter.getSelectCustomizationEvents()
+ .flatMap { equipment ->
+ inventoryRepository.equip(user, if (user?.preferences?.costume == true) "costume" else "equipped", equipment.key ?: "")
+ }
+ .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
+ compositeSubscription.add(adapter.getUnlockCustomizationEvents()
+ .flatMap {
+ Flowable.empty()
+ }
+ .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
+ compositeSubscription.add(adapter.getUnlockSetEvents()
+ .flatMap { set ->
+ val user = this.user
+ if (user != null) {
+ userRepository.unlockPath(user, set)
+ } else {
+ Flowable.empty()
+ }
+ }
+ .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
+ return view
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ arguments?.let {
+ val args = AvatarEquipmentFragmentArgs.fromBundle(it)
+ type = args.type
+ if (args.category.isNotEmpty()) {
+ category = args.category
+ }
+ }
+
+ setGridSpanCount(view.width)
+ val layoutManager = GridLayoutManager(activity, 4)
+ layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
+ override fun getSpanSize(position: Int): Int {
+ return if (adapter.getItemViewType(position) == 0) {
+ layoutManager.spanCount
+ } else {
+ 1
+ }
+ }
+ }
+ recyclerView.layoutManager = layoutManager
+ recyclerView.addItemDecoration(MarginDecoration(context))
+
+ recyclerView.adapter = adapter
+ recyclerView.itemAnimator = SafeDefaultItemAnimator()
+ this.loadEquipment()
+
+ compositeSubscription.add(userRepository.getUser().subscribeWithErrorHandler(Consumer {
+ updateUser(it)
+ }))
+ }
+
+ override fun injectFragment(component: UserComponent) {
+ component.inject(this)
+ }
+
+ private fun loadEquipment() {
+ val type = this.type ?: return
+ inventoryRepository.getEquipmentType(type, category ?: "").subscribe(Consumer {
+ adapter.setEquipment(it)
+ }, RxErrorHandler.handleEmptyError())
+ }
+
+ private fun setGridSpanCount(width: Int) {
+ val itemWidth = context?.resources?.getDimension(R.dimen.customization_width) ?: 0F
+ var spanCount = (width / itemWidth).toInt()
+ if (spanCount == 0) {
+ spanCount = 1
+ }
+ layoutManager.spanCount = spanCount
+ }
+
+ fun updateUser(user: User) {
+ this.updateActiveCustomization(user)
+ this.adapter.gemBalance = user.gemCount
+ adapter.notifyDataSetChanged()
+ }
+
+ private fun updateActiveCustomization(user: User) {
+ if (this.type == null || user.preferences == null) {
+ return
+ }
+ val outfit = if (user.preferences?.costume == true) this.user?.items?.gear?.costume else this.user?.items?.gear?.equipped
+ val activeEquipment = when (this.type) {
+ "headAccessory" -> outfit?.headAccessory
+ "back" -> outfit?.back
+ "eyewear" -> outfit?.eyeWear
+ else -> ""
+ }
+ if (activeEquipment != null) {
+ this.activeEquipment = activeEquipment
+ this.adapter.activeEquipment = activeEquipment
+ }
+ }
+}
\ No newline at end of file
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 082512a2a..2cfb1db99 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
@@ -5,38 +5,44 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
-import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
+import com.habitrpg.android.habitica.databinding.FragmentAvatarOverviewBinding
import com.habitrpg.android.habitica.extensions.subscribeWithErrorHandler
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
import io.reactivex.functions.Consumer
-import kotlinx.android.synthetic.main.fragment_avatar_overview.*
class AvatarOverviewFragment : BaseMainFragment(), AdapterView.OnItemSelectedListener {
+ private lateinit var binding: FragmentAvatarOverviewBinding
+
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
- return inflater.inflate(R.layout.fragment_avatar_overview, container, false)
+ binding = FragmentAvatarOverviewBinding.inflate(inflater, container, false)
+ return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- avatarSizeSpinner.onItemSelectedListener = this
+ binding.avatarSizeSpinner.onItemSelectedListener = this
- avatarShirtView.setOnClickListener { displayCustomizationFragment("shirt", null) }
- avatarSkinView.setOnClickListener { displayCustomizationFragment("skin", null) }
- avatarChairView.setOnClickListener { displayCustomizationFragment("chair", null) }
- avatarHairColorView.setOnClickListener { displayCustomizationFragment("hair", "color") }
- avatarHairBangsView.setOnClickListener { displayCustomizationFragment("hair", "bangs") }
- avatarHairBaseView.setOnClickListener { displayCustomizationFragment("hair", "base") }
- avatarHairFlowerView.setOnClickListener { displayCustomizationFragment("hair", "flower") }
- avatarHairBeardView.setOnClickListener { displayCustomizationFragment("hair", "beard") }
- avatarHairMustacheView.setOnClickListener { displayCustomizationFragment("hair", "mustache") }
- avatarBackgroundView.setOnClickListener { displayCustomizationFragment("background", null) }
+ binding.avatarShirtView.setOnClickListener { displayCustomizationFragment("shirt", null) }
+ binding.avatarSkinView.setOnClickListener { displayCustomizationFragment("skin", null) }
+ binding.avatarChairView.setOnClickListener { displayCustomizationFragment("chair", null) }
+ binding.avatarGlassesView.setOnClickListener { displayEquipmentFragment("eyewear", "glasses") }
+ binding.avatarAnimalEarsView.setOnClickListener { displayEquipmentFragment("headAccessory", "animal") }
+ binding.avatarAnimalTailView.setOnClickListener { displayEquipmentFragment("back", "animal") }
+ binding.avatarHeadbandView.setOnClickListener { displayEquipmentFragment("headAccessory", "headband") }
+ binding.avatarHairColorView.setOnClickListener { displayCustomizationFragment("hair", "color") }
+ binding.avatarHairBangsView.setOnClickListener { displayCustomizationFragment("hair", "bangs") }
+ binding.avatarHairBaseView.setOnClickListener { displayCustomizationFragment("hair", "base") }
+ binding.avatarAccentView.setOnClickListener { displayCustomizationFragment("hair", "flower") }
+ binding.avatarHairBeardView.setOnClickListener { displayCustomizationFragment("hair", "beard") }
+ binding.avatarHairMustacheView.setOnClickListener { displayCustomizationFragment("hair", "mustache") }
+ binding.avatarBackgroundView.setOnClickListener { displayCustomizationFragment("background", null) }
compositeSubscription.add(userRepository.getUser().subscribeWithErrorHandler(Consumer {
updateUser(it)
@@ -51,43 +57,41 @@ class AvatarOverviewFragment : BaseMainFragment(), AdapterView.OnItemSelectedLis
MainNavigationController.navigate(AvatarOverviewFragmentDirections.openAvatarDetail(type, category ?: ""))
}
+ private fun displayEquipmentFragment(type: String, category: String?) {
+ MainNavigationController.navigate(AvatarOverviewFragmentDirections.openAvatarEquipment(type, category ?: ""))
+ }
+
fun updateUser(user: User) {
this.setSize(user.preferences?.size)
setCustomizations(user)
}
private fun setCustomizations(user: User) {
- avatarShirtView.customizationIdentifier = user.preferences?.size + "_shirt_" + user.preferences?.shirt
- avatarShirtView.equipmentName = user.preferences?.shirt
- avatarSkinView.customizationIdentifier = "skin_" + user.preferences?.skin
- avatarSkinView.equipmentName = user.preferences?.skin
+ binding.avatarShirtView.customizationIdentifier = user.preferences?.size + "_shirt_" + user.preferences?.shirt
+ binding.avatarSkinView.customizationIdentifier = "skin_" + user.preferences?.skin
val chair = user.preferences?.chair
- avatarChairView.customizationIdentifier = if (chair?.startsWith("handleless") == true) "chair_$chair" else chair
- avatarChairView.equipmentName = chair?.removePrefix("chair_")
- avatarHairColorView.customizationIdentifier = if (user.preferences?.hair?.color != null && user.preferences?.hair?.color != "") "hair_bangs_1_" + user.preferences?.hair?.color else ""
- avatarHairColorView.equipmentName = user.preferences?.hair?.color
- avatarHairBangsView.customizationIdentifier = if (user.preferences?.hair?.bangs != null && user.preferences?.hair?.bangs != 0) "hair_bangs_" + user.preferences?.hair?.bangs + "_" + user.preferences?.hair?.color else ""
- avatarHairBangsView.equipmentName = user.preferences?.hair?.bangs.toString()
- avatarHairBaseView.customizationIdentifier = if (user.preferences?.hair?.base != null && user.preferences?.hair?.base != 0) "hair_base_" + user.preferences?.hair?.base + "_" + user.preferences?.hair?.color else ""
- avatarHairBaseView.equipmentName = user.preferences?.hair?.base.toString()
- avatarHairFlowerView.customizationIdentifier = if (user.preferences?.hair?.flower != null && user.preferences?.hair?.flower != 0) "hair_flower_" + user.preferences?.hair?.flower else ""
- avatarHairFlowerView.equipmentName = user.preferences?.hair?.bangs.toString()
- avatarHairBeardView.customizationIdentifier = if (user.preferences?.hair?.beard != null && user.preferences?.hair?.beard != 0) "hair_beard_" + user.preferences?.hair?.beard + "_" + user.preferences?.hair?.color else ""
- avatarHairBeardView.equipmentName = user.preferences?.hair?.beard.toString()
- avatarHairMustacheView.customizationIdentifier = if (user.preferences?.hair?.mustache != null && user.preferences?.hair?.mustache != 0) "hair_mustache_" + user.preferences?.hair?.mustache + "_" + user.preferences?.hair?.color else ""
- avatarHairMustacheView.equipmentName = user.preferences?.hair?.mustache.toString()
- avatarBackgroundView.customizationIdentifier = "background_" + user.preferences?.background
- avatarBackgroundView.equipmentName = user.preferences?.background
+ binding.avatarChairView.customizationIdentifier = if (chair?.startsWith("handleless") == true) "chair_$chair" else chair
+ binding.avatarGlassesView.equipmentIdentifier = user.equipped?.eyeWear
+ binding.avatarAnimalEarsView.equipmentIdentifier = user.equipped?.headAccessory
+ binding.avatarHeadbandView.equipmentIdentifier = user.equipped?.headAccessory
+ binding.avatarAnimalTailView.equipmentIdentifier = user.equipped?.back
+ binding.avatarHairColorView.customizationIdentifier = if (user.preferences?.hair?.color != null && user.preferences?.hair?.color != "") "hair_bangs_1_" + user.preferences?.hair?.color else ""
+ binding.avatarHairBangsView.customizationIdentifier = if (user.preferences?.hair?.bangs != null && user.preferences?.hair?.bangs != 0) "hair_bangs_" + user.preferences?.hair?.bangs + "_" + user.preferences?.hair?.color else ""
+ binding.avatarHairBaseView.customizationIdentifier = if (user.preferences?.hair?.base != null && user.preferences?.hair?.base != 0) "hair_base_" + user.preferences?.hair?.base + "_" + user.preferences?.hair?.color else ""
+ binding.avatarAccentView.customizationIdentifier = if (user.preferences?.hair?.flower != null && user.preferences?.hair?.flower != 0) "hair_flower_" + user.preferences?.hair?.flower else ""
+ binding.avatarHairBeardView.customizationIdentifier = if (user.preferences?.hair?.beard != null && user.preferences?.hair?.beard != 0) "hair_beard_" + user.preferences?.hair?.beard + "_" + user.preferences?.hair?.color else ""
+ binding.avatarHairMustacheView.customizationIdentifier = if (user.preferences?.hair?.mustache != null && user.preferences?.hair?.mustache != 0) "hair_mustache_" + user.preferences?.hair?.mustache + "_" + user.preferences?.hair?.color else ""
+ binding.avatarBackgroundView.customizationIdentifier = "background_" + user.preferences?.background
}
private fun setSize(size: String?) {
- if (avatarSizeSpinner == null || size == null) {
+ if (size == null) {
return
}
if (size == "slim") {
- avatarSizeSpinner.setSelection(0, false)
+ binding.avatarSizeSpinner.setSelection(0, false)
} else {
- avatarSizeSpinner.setSelection(1, false)
+ binding.avatarSizeSpinner.setSelection(1, false)
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/MarketFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/MarketFragment.kt
new file mode 100644
index 000000000..9a7d42a13
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/MarketFragment.kt
@@ -0,0 +1,13 @@
+package com.habitrpg.android.habitica.ui.fragments.inventory.shops
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+
+class MarketFragment: ShopsFragment() {
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ lockTab = 0
+ return super.onCreateView(inflater, container, savedInstanceState)
+ }
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/QuestShopFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/QuestShopFragment.kt
new file mode 100644
index 000000000..1850f81a1
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/QuestShopFragment.kt
@@ -0,0 +1,13 @@
+package com.habitrpg.android.habitica.ui.fragments.inventory.shops
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+
+class QuestShopFragment: ShopsFragment() {
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ lockTab = 1
+ return super.onCreateView(inflater, container, savedInstanceState)
+ }
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/SeasonalShopFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/SeasonalShopFragment.kt
new file mode 100644
index 000000000..695b6a64f
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/SeasonalShopFragment.kt
@@ -0,0 +1,13 @@
+package com.habitrpg.android.habitica.ui.fragments.inventory.shops
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+
+class SeasonalShopFragment: ShopsFragment() {
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ lockTab = 2
+ return super.onCreateView(inflater, container, savedInstanceState)
+ }
+}
\ No newline at end of file
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 1b9b08c8d..8280b1510 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
@@ -4,7 +4,9 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.core.os.bundleOf
import androidx.fragment.app.FragmentPagerAdapter
+import com.google.firebase.analytics.FirebaseAnalytics
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.InventoryRepository
@@ -17,8 +19,10 @@ import io.reactivex.functions.Consumer
import kotlinx.android.synthetic.main.fragment_viewpager.*
import javax.inject.Inject
-class ShopsFragment : BaseMainFragment() {
+open class ShopsFragment : BaseMainFragment() {
+
+ protected var lockTab: Int? = null
@Inject
lateinit var inventoryRepository: InventoryRepository
@@ -39,6 +43,22 @@ class ShopsFragment : BaseMainFragment() {
setViewPagerAdapter()
toolbarAccessoryContainer?.addView(currencyView)
+ if (lockTab == null) {
+ arguments?.let {
+ val args = ShopsFragmentArgs.fromBundle(it)
+ if (args.selectedTab > 0) {
+ viewPager.currentItem = args.selectedTab
+ }
+ }
+ } else {
+ 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))) }
+
compositeSubscription.add(userRepository.getUser().subscribe(Consumer { updateCurrencyView(it) }, RxErrorHandler.handleEmptyError()))
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/TimeTravelersShopFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/TimeTravelersShopFragment.kt
new file mode 100644
index 000000000..2bcc6b3a8
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/TimeTravelersShopFragment.kt
@@ -0,0 +1,13 @@
+package com.habitrpg.android.habitica.ui.fragments.inventory.shops
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+
+class TimeTravelersShopFragment: ShopsFragment() {
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ lockTab = 3
+ return super.onCreateView(inflater, container, savedInstanceState)
+ }
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableFragment.kt
index ad59ba89d..c4149332d 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableFragment.kt
@@ -19,6 +19,7 @@ class StableFragment : BaseMainFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
this.usesTabLayout = true
+ this.hidesToolbar = true
super.onCreateView(inflater, container, savedInstanceState)
return inflater.inflate(R.layout.fragment_viewpager, container, false)
}
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 8a445a63d..2919a411a 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
@@ -8,6 +8,7 @@ import android.widget.TextView
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.extensions.inflate
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.inventory.Animal
@@ -69,19 +70,19 @@ class StableRecyclerFragment : BaseFragment() {
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) {
+ return if (adapter?.getItemViewType(position) == 0 || adapter?.getItemViewType(position) == 1) {
layoutManager?.spanCount ?: 1
} else {
1
}
}
}
+
recyclerView?.layoutManager = layoutManager
activity?.let {
- recyclerView?.addItemDecoration(MarginDecoration(it))
+ recyclerView?.addItemDecoration(MarginDecoration(it, setOf(HEADER_VIEW_TYPE)))
}
-
adapter = recyclerView?.adapter as? StableRecyclerAdapter
if (adapter == null) {
adapter = StableRecyclerAdapter()
@@ -157,7 +158,7 @@ class StableRecyclerFragment : BaseFragment() {
if (items.size > 0 && items[items.size - 1].javaClass == String::class.java) {
items.removeAt(items.size - 1)
}
- items.add(animal.type)
+ items.add(animal.getTranslatedType(context))
lastSectionTitle = animal.type
}
when (itemType) {
@@ -178,10 +179,14 @@ class StableRecyclerFragment : BaseFragment() {
if (!((lastAnimal.type == "premium" || lastAnimal.type == "special") && lastAnimal.numberOwned == 0)) {
items.add(lastAnimal)
}
+
+ items.add(0, "header")
return items
}
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/AuthenticationPreferenceFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/AuthenticationPreferenceFragment.kt
index 3901a5717..ec01980f0 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/AuthenticationPreferenceFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/AuthenticationPreferenceFragment.kt
@@ -175,7 +175,7 @@ class AuthenticationPreferenceFragment: BasePreferencesFragment() {
val view = context?.layoutInflater?.inflate(R.layout.dialog_edittext, null)
var deleteMessage = getString(R.string.delete_account_description)
val editText = view?.findViewById(R.id.editText)
- if (user?.authentication?.localAuthentication != null) {
+ if (user?.authentication?.localAuthentication?.email != null) {
editText?.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
} else {
deleteMessage = getString(R.string.delete_oauth_account_description)
@@ -186,7 +186,7 @@ class AuthenticationPreferenceFragment: BasePreferencesFragment() {
val dialog = HabiticaAlertDialog(context)
dialog.setTitle(R.string.delete_account)
dialog.setMessage(deleteMessage)
- dialog.addButton(R.string.delete_account_confirmation, true, true) { _, _ ->
+ dialog.addButton(R.string.delete_account_confirmation, isPrimary = true, isDestructive = true) { _, _ ->
deleteAccount(editText?.text?.toString() ?: "")
}
dialog.addCancelButton()
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/EmailNotificationsPreferencesFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/EmailNotificationsPreferencesFragment.kt
new file mode 100644
index 000000000..41048a0a0
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/EmailNotificationsPreferencesFragment.kt
@@ -0,0 +1,80 @@
+package com.habitrpg.android.habitica.ui.fragments.preferences
+
+import android.content.SharedPreferences
+import android.os.Bundle
+import androidx.preference.CheckBoxPreference
+import com.habitrpg.android.habitica.HabiticaBaseApplication
+import com.habitrpg.android.habitica.helpers.RxErrorHandler
+import com.habitrpg.android.habitica.models.user.User
+import io.reactivex.functions.Consumer
+
+class EmailNotificationsPreferencesFragment : BasePreferencesFragment(), SharedPreferences.OnSharedPreferenceChangeListener {
+
+ private var isInitialSet: Boolean = true
+ private var isSettingUser: Boolean = false
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ HabiticaBaseApplication.userComponent?.inject(this)
+ super.onCreate(savedInstanceState)
+ }
+
+ override fun onResume() {
+ super.onResume()
+ preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
+ }
+
+ override fun onPause() {
+ super.onPause()
+ preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
+ }
+
+ override fun setupPreferences() { /* no-on */ }
+
+ override fun setUser(user: User?) {
+ super.setUser(user)
+ isSettingUser = !isInitialSet
+ updatePreference("preference_email_you_won_challenge", user?.preferences?.emailNotifications?.wonChallenge)
+ updatePreference("preference_email_received_a_private_message", user?.preferences?.emailNotifications?.newPM)
+ updatePreference("preference_email_gifted_gems", user?.preferences?.emailNotifications?.giftedGems)
+ updatePreference("preference_email_gifted_subscription", user?.preferences?.emailNotifications?.giftedSubscription)
+ updatePreference("preference_email_invited_to_party", user?.preferences?.emailNotifications?.invitedParty)
+ updatePreference("preference_email_invited_to_guild", user?.preferences?.emailNotifications?.invitedGuild)
+ updatePreference("preference_email_your_quest_has_begun", user?.preferences?.emailNotifications?.questStarted)
+ updatePreference("preference_email_invited_to_quest", user?.preferences?.emailNotifications?.invitedQuest)
+ updatePreference("preference_email_important_announcements", user?.preferences?.emailNotifications?.majorUpdates)
+ updatePreference("preference_email_kicked_group", user?.preferences?.emailNotifications?.kickedGroup)
+ updatePreference("preference_email_onboarding", user?.preferences?.emailNotifications?.onboarding)
+ updatePreference("preference_email_subscription_reminders", user?.preferences?.emailNotifications?.subscriptionReminders)
+ isSettingUser = false
+ isInitialSet = false
+ }
+
+ private fun updatePreference(key: String, isChecked: Boolean?) {
+ val preference = (findPreference(key) as? CheckBoxPreference)
+ preference?.isChecked = isChecked == true
+ }
+
+ override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
+ if (isSettingUser) {
+ return
+ }
+ val pathKey = when (key) {
+ "preference_email_you_won_challenge" -> "wonChallenge"
+ "preference_email_received_a_private_message" -> "newPM"
+ "preference_email_gifted_gems" -> "giftedGems"
+ "preference_email_gifted_subscription" -> "giftedSubscription"
+ "preference_email_invited_to_party" -> "invitedParty"
+ "preference_email_invited_to_guild" -> "invitedGuild"
+ "preference_email_your_quest_has_begun" -> "questStarted"
+ "preference_email_invited_to_quest" -> "invitedQuest"
+ "preference_email_important_announcements" -> "majorUpdates"
+ "preference_email_kicked_group" -> "kickedGroup"
+ "preference_email_onboarding" -> "onboarding"
+ "preference_email_subscription_reminders" -> "subscriptionReminders"
+ else -> null
+ }
+ if (pathKey != null) {
+ compositeSubscription.add(userRepository.updateUser(user, "preferences.emailNotifications.$pathKey", sharedPreferences.getBoolean(key, false)).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
+ }
+ }
+}
\ No newline at end of file
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 634185767..37a0ca815 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
@@ -44,6 +44,7 @@ class PreferencesFragment : BasePreferencesFragment(), SharedPreferences.OnShare
private var timePreference: TimePreference? = null
private var pushNotificationsPreference: PreferenceScreen? = null
+ private var emailNotificationsPreference: PreferenceScreen? = null
private var classSelectionPreference: Preference? = null
private var serverUrlPreference: ListPreference? = null
@@ -60,8 +61,12 @@ class PreferencesFragment : BasePreferencesFragment(), SharedPreferences.OnShare
pushNotificationsPreference = findPreference("pushNotifications") as? PreferenceScreen
- val userPushNotifications = preferenceManager.sharedPreferences.getBoolean("usePushNotifications", true)
- pushNotificationsPreference?.isEnabled = userPushNotifications
+ val usePushNotifications = preferenceManager.sharedPreferences.getBoolean("usePushNotifications", true)
+ pushNotificationsPreference?.isEnabled = usePushNotifications
+
+ emailNotificationsPreference = findPreference("emailNotifications") as? PreferenceScreen
+ val useEmailNotifications = preferenceManager.sharedPreferences.getBoolean("useEmailNotifications", true)
+ emailNotificationsPreference?.isEnabled = useEmailNotifications
classSelectionPreference = findPreference("choose_class")
@@ -161,6 +166,10 @@ class PreferencesFragment : BasePreferencesFragment(), SharedPreferences.OnShare
pushNotificationManager.removePushDeviceUsingStoredToken()
}
}
+ "useEmailNotifications" -> {
+ val useEmailNotifications = sharedPreferences.getBoolean(key, false)
+ emailNotificationsPreference?.isEnabled = useEmailNotifications
+ }
"cds_time" -> {
val timeval = sharedPreferences.getString("cds_time", "00:00")
val pieces = timeval?.split(":".toRegex())?.dropLastWhile { it.isEmpty() }?.toTypedArray()
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/ProfilePreferencesFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/ProfilePreferencesFragment.kt
index 661322830..95a5da80d 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/ProfilePreferencesFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/ProfilePreferencesFragment.kt
@@ -55,7 +55,7 @@ class ProfilePreferencesFragment: BasePreferencesFragment(), SharedPreferences.O
val profileCategory = findPreference("profile") as? PreferenceCategory
configurePreference(profileCategory?.findPreference(key), sharedPreferences?.getString(key, ""))
if (sharedPreferences != null) {
- val newValue = sharedPreferences.getString(key, "") ?: ""
+ val newValue = sharedPreferences.getString(key, "") ?: return
val observable: Flowable? = when (key) {
"display_name" -> {
if (newValue != user?.profile?.name) {
@@ -65,16 +65,14 @@ class ProfilePreferencesFragment: BasePreferencesFragment(), SharedPreferences.O
}
}
"photo_url" -> {
- val newName = sharedPreferences.getString(key, "") ?: ""
- if (newName != user?.profile?.imageUrl) {
+ if (newValue != user?.profile?.imageUrl) {
userRepository.updateUser(user, "profile.imageUrl", newValue)
} else {
null
}
}
"about" -> {
- val newName = sharedPreferences.getString(key, "") ?: ""
- if (newName != user?.profile?.blurb) {
+ if (newValue != user?.profile?.blurb) {
userRepository.updateUser(user, "profile.blurb", newValue)
} else {
null
@@ -85,5 +83,4 @@ class ProfilePreferencesFragment: BasePreferencesFragment(), SharedPreferences.O
observable?.subscribe(Consumer {}, RxErrorHandler.handleEmptyError())?.let { compositeSubscription.add(it) }
}
}
-
}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/purchases/SubscriptionFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/purchases/SubscriptionFragment.kt
index 13f67e6e1..9b62e3135 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/purchases/SubscriptionFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/purchases/SubscriptionFragment.kt
@@ -2,16 +2,20 @@ package com.habitrpg.android.habitica.ui.fragments.purchases
import android.content.Intent
import android.os.Bundle
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.EditText
import androidx.core.view.isVisible
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
+import com.habitrpg.android.habitica.data.ApiClient
import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.data.UserRepository
import com.habitrpg.android.habitica.databinding.FragmentSubscriptionBinding
import com.habitrpg.android.habitica.events.UserSubscribedEvent
+import com.habitrpg.android.habitica.extensions.addCancelButton
import com.habitrpg.android.habitica.helpers.AppConfigManager
import com.habitrpg.android.habitica.helpers.PurchaseHandler
import com.habitrpg.android.habitica.helpers.PurchaseTypes
@@ -20,12 +24,16 @@ import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.proxy.CrashlyticsProxy
import com.habitrpg.android.habitica.ui.activities.GemPurchaseActivity
import com.habitrpg.android.habitica.ui.activities.GiftOneGetOneInfoActivity
+import com.habitrpg.android.habitica.ui.activities.GiftSubscriptionActivity
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
+import com.habitrpg.android.habitica.ui.helpers.dismissKeyboard
+import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
import com.habitrpg.android.habitica.ui.views.subscriptions.SubscriptionOptionView
import io.reactivex.functions.Consumer
import org.greenrobot.eventbus.Subscribe
import org.solovyev.android.checkout.Inventory
+import org.solovyev.android.checkout.Purchase
import org.solovyev.android.checkout.Sku
import javax.inject.Inject
@@ -40,6 +48,8 @@ class SubscriptionFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen
lateinit var appConfigManager: AppConfigManager
@Inject
lateinit var inventoryRepository: InventoryRepository
+ @Inject
+ lateinit var apiClient: ApiClient
private var selectedSubscriptionSku: Sku? = null
private var skus: List = emptyList()
@@ -48,6 +58,7 @@ class SubscriptionFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen
private var user: User? = null
private var hasLoadedSubscriptionOptions: Boolean = false
+ private var purchasedSubscription: Purchase? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
@@ -82,17 +93,15 @@ class SubscriptionFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen
binding.refreshLayout.setOnRefreshListener { refresh() }
- if (appConfigManager.useNewMysteryBenefits()) {
- compositeSubscription.add(inventoryRepository.getLatestMysteryItem().subscribe(Consumer {
- DataBindingUtils.loadImage(binding.subBenefitsMysteryItemIcon, "shop_set_mystery_${it.key?.split("_")?.last()}")
- binding.subBenefitsMysteryItemText.text = context?.getString(R.string.subscribe_listitem3_description_new, it.text)
- }, RxErrorHandler.handleEmptyError()))
- }
+ compositeSubscription.add(inventoryRepository.getLatestMysteryItem().subscribe(Consumer {
+ DataBindingUtils.loadImage(binding.subBenefitsMysteryItemIcon, "shop_set_mystery_${it.key?.split("_")?.last()}")
+ binding.subBenefitsMysteryItemText.text = context?.getString(R.string.subscribe_listitem3_description_new, it.text)
+ }, RxErrorHandler.handleEmptyError()))
}
override fun onResume() {
super.onResume()
- refresh();
+ refresh()
}
private fun refresh() {
@@ -165,12 +174,18 @@ class SubscriptionFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen
override fun setPurchaseHandler(handler: PurchaseHandler?) {
this.purchaseHandler = handler
+
+ handler?.checkForSubscription {
+ purchasedSubscription = it
+ checkIfNeedsCancellation()
+ }
}
private fun purchaseSubscription() {
selectedSubscriptionSku?.let { sku ->
purchaseHandler?.purchaseSubscription(sku) {
fetchUser(null)
+ binding.scrollView.smoothScrollTo(0, 0)
}
}
}
@@ -178,6 +193,10 @@ class SubscriptionFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen
fun setUser(newUser: User) {
user = newUser
this.updateSubscriptionInfo()
+ purchaseHandler?.checkForSubscription {
+ purchasedSubscription = it
+ checkIfNeedsCancellation()
+ }
}
private fun updateSubscriptionInfo() {
@@ -208,10 +227,20 @@ class SubscriptionFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen
}
}
+ private fun checkIfNeedsCancellation() {
+ if (user?.purchased?.plan?.paymentMethod == "Google" &&
+ user?.purchased?.plan?.isActive == true &&
+ (purchasedSubscription?.autoRenewing == false ||purchasedSubscription == null)) {
+ compositeSubscription.add(apiClient.cancelSubscription().subscribe(Consumer {
+ refresh()
+ }, RxErrorHandler.handleEmptyError()))
+ }
+ }
+
private fun showSubscriptionOptions() {
binding.subscriptionOptions.visibility = View.VISIBLE
binding.subscriptionOptions.postDelayed({
- binding.scrollView.smoothScrollTo(0, binding.subscriptionOptions.top ?: 0)
+ binding.scrollView.smoothScrollTo(0, binding.subscriptionOptions.top)
}, 500)
}
@@ -220,7 +249,28 @@ class SubscriptionFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen
}
private fun showGiftSubscriptionDialog() {
- val intent = Intent(context, GiftOneGetOneInfoActivity::class.java)
- context?.startActivity(intent)
+ if (appConfigManager.enableGiftOneGetOne()) {
+ val intent = Intent(context, GiftOneGetOneInfoActivity::class.java)
+ context?.startActivity(intent)
+ } else {
+ val chooseRecipientDialogView = this.activity?.layoutInflater?.inflate(R.layout.dialog_choose_message_recipient, null)
+
+ this.activity?.let { thisActivity ->
+ val alert = HabiticaAlertDialog(thisActivity)
+ alert.setTitle(getString(R.string.gift_title))
+ alert.addButton(getString(R.string.action_continue), true) { _, _ ->
+ val usernameEditText = chooseRecipientDialogView?.findViewById(R.id.uuidEditText) as? EditText
+ val intent = Intent(thisActivity, GiftSubscriptionActivity::class.java).apply {
+ putExtra("username", usernameEditText?.text.toString())
+ }
+ startActivity(intent)
+ }
+ alert.addCancelButton { _, _ ->
+ thisActivity.dismissKeyboard()
+ }
+ alert.setAdditionalContentView(chooseRecipientDialogView)
+ alert.show()
+ }
+ }
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/setup/AvatarSetupFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/setup/AvatarSetupFragment.kt
index 75d6d7e43..eceacea2f 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/setup/AvatarSetupFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/setup/AvatarSetupFragment.kt
@@ -146,6 +146,7 @@ class AvatarSetupFragment : BaseFragment() {
if (this.adapter != null) {
this.adapter?.user = user
this.adapter?.notifyDataSetChanged()
+ loadCustomizations()
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/setup/IntroFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/setup/IntroFragment.kt
index 070ecc20c..b29bbd90a 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/setup/IntroFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/setup/IntroFragment.kt
@@ -5,24 +5,13 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.widget.ImageView
-import android.widget.TextView
-import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
-import com.habitrpg.android.habitica.extensions.inflate
+import com.habitrpg.android.habitica.databinding.FragmentIntroBinding
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
-import com.habitrpg.android.habitica.ui.helpers.bindOptionalView
-import com.habitrpg.android.habitica.ui.helpers.resetViews
class IntroFragment : BaseFragment() {
- private val subtitleTextView: TextView? by bindOptionalView(R.id.subtitleTextView)
- private val titleTextView: TextView? by bindOptionalView(R.id.titleTextView)
- private val titleImageView: ImageView? by bindOptionalView(R.id.titleImageView)
- private val descriptionTextView: TextView? by bindOptionalView(R.id.descriptionTextView)
- private val imageView: ImageView? by bindOptionalView(R.id.imageView)
- private val containerView: ViewGroup? by bindOptionalView(R.id.container_view)
-
+ private var binding: FragmentIntroBinding? = null
private var image: Drawable? = null
private var titleImage: Drawable? = null
private var subtitle: String? = null
@@ -32,36 +21,35 @@ class IntroFragment : BaseFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
- return container?.inflate(R.layout.fragment_intro)
+ binding = FragmentIntroBinding.inflate(layoutInflater, container, false)
+ return binding?.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- resetViews()
-
if (this.image != null) {
- this.imageView?.setImageDrawable(this.image)
+ binding?.imageView?.setImageDrawable(this.image)
}
if (this.titleImage != null) {
- this.titleImageView?.setImageDrawable(this.titleImage)
+ binding?.titleImageView?.setImageDrawable(this.titleImage)
}
if (this.subtitle != null) {
- this.subtitleTextView?.text = this.subtitle
+ binding?.subtitleTextView?.text = this.subtitle
}
if (this.title != null) {
- this.titleTextView?.text = this.title
+ binding?.titleTextView?.text = this.title
}
if (this.description != null) {
- this.descriptionTextView?.text = this.description
+ binding?.descriptionTextView?.text = this.description
}
backgroundColor?.let {
- this.containerView?.setBackgroundColor(it)
+ binding?.containerView?.setBackgroundColor(it)
}
}
@@ -72,33 +60,33 @@ class IntroFragment : BaseFragment() {
fun setImage(image: Drawable?) {
this.image = image
if (image != null) {
- this.imageView?.setImageDrawable(image)
+ binding?.imageView?.setImageDrawable(image)
}
}
fun setTitleImage(image: Drawable?) {
this.titleImage = image
- this.titleImageView?.setImageDrawable(image)
+ binding?.titleImageView?.setImageDrawable(image)
}
fun setSubtitle(text: String?) {
this.subtitle = text
- subtitleTextView?.text = text
+ binding?.subtitleTextView?.text = text
}
fun setTitle(text: String?) {
this.title = text
- titleTextView?.text = text
+ binding?.titleTextView?.text = text
}
fun setDescription(text: String?) {
this.description = text
- descriptionTextView?.text = text
+ binding?.descriptionTextView?.text = text
}
fun setBackgroundColor(color: Int) {
this.backgroundColor = color
- containerView?.setBackgroundColor(color)
+ binding?.containerView?.setBackgroundColor(color)
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillsFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillsFragment.kt
index b2ab0e10b..9e881f079 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillsFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillsFragment.kt
@@ -67,8 +67,6 @@ class SkillsFragment : BaseMainFragment() {
resetViews()
- recyclerView.invalidateItemDecorations()
- recyclerView.addItemDecoration(androidx.recyclerview.widget.DividerItemDecoration(getActivity(), androidx.recyclerview.widget.DividerItemDecoration.VERTICAL))
recyclerView.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(activity)
recyclerView.adapter = adapter
recyclerView.itemAnimator = SafeDefaultItemAnimator()
@@ -78,9 +76,12 @@ class SkillsFragment : BaseMainFragment() {
if (user == null || adapter == null) {
return
}
- adapter?.mana = this.user?.stats?.mp ?: 0.toDouble()
+ adapter?.mana = this.user?.stats?.mp ?: 0.0
+ adapter?.level = this.user?.stats?.lvl ?: 0
+ adapter?.specialItems = this.user?.items?.special
user?.let { user ->
- Observable.concat(userRepository.getSkills(user).firstElement().toObservable().flatMap { Observable.fromIterable(it) }, userRepository.getSpecialItems(user).firstElement().toObservable().flatMap { Observable.fromIterable(it) })
+ Observable.concat(userRepository.getSkills(user).firstElement().toObservable().flatMap { Observable.fromIterable(it) },
+ userRepository.getSpecialItems(user).firstElement().toObservable().flatMap { Observable.fromIterable(it) })
.toList()
.subscribe(Consumer { skills -> adapter?.setSkillList(skills) }, RxErrorHandler.handleEmptyError())
}
@@ -103,7 +104,6 @@ class SkillsFragment : BaseMainFragment() {
}
private fun displaySkillResult(usedSkill: Skill?, response: SkillResponse) {
- removeProgressDialog()
adapter?.mana = response.user.stats?.mp ?: 0.0
val activity = activity ?: return
if ("special" == usedSkill?.habitClass) {
@@ -121,7 +121,6 @@ class SkillsFragment : BaseMainFragment() {
}
-
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (data != null) {
@@ -144,23 +143,12 @@ class SkillsFragment : BaseMainFragment() {
if (skill == null) {
return
}
- displayProgressDialog()
val observable: Flowable = if (taskId != null) {
userRepository.useSkill(user, skill.key, skill.target, taskId)
} else {
userRepository.useSkill(user, skill.key, skill.target)
}
- compositeSubscription.add(observable.subscribe({ skillResponse -> this.displaySkillResult(skill, skillResponse) }) { removeProgressDialog() })
+ compositeSubscription.add(observable.subscribe(Consumer { skillResponse -> this.displaySkillResult(skill, skillResponse) },
+ RxErrorHandler.handleEmptyError()))
}
-
- private fun displayProgressDialog() {
- progressDialog?.dismiss()
- @Suppress("DEPRECATION")
- progressDialog = ProgressDialog.show(activity, context?.getString(R.string.skill_progress_title), null, true)
- }
-
- private fun removeProgressDialog() {
- progressDialog?.dismiss()
- }
-
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatFragment.kt
index 5592c7786..30799ee45 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatFragment.kt
@@ -209,8 +209,10 @@ class ChatFragment : BaseFragment() {
}
private fun sendChatMessage(chatText: String) {
- viewModel?.postGroupChat(chatText) {
+ viewModel?.postGroupChat(chatText, {
recyclerView?.scrollToPosition(0)
+ }) {
+ chatBarView.message = chatText
}
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxMessageListFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxMessageListFragment.kt
index a494408e6..0ddcc3387 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxMessageListFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxMessageListFragment.kt
@@ -4,9 +4,7 @@ import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
+import android.view.*
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
@@ -101,6 +99,21 @@ class InboxMessageListFragment : BaseMainFragment(), androidx.swiperefreshlayout
super.onDestroy()
}
+ override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+ this.activity?.menuInflater?.inflate(R.menu.inbox_chat, menu)
+ super.onCreateOptionsMenu(menu, inflater)
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ when (item.itemId) {
+ R.id.open_profile -> {
+ openProfile()
+ return true
+ }
+ }
+ return super.onOptionsItemSelected(item)
+ }
+
override fun injectFragment(component: UserComponent) {
component.inject(this)
}
@@ -123,9 +136,12 @@ class InboxMessageListFragment : BaseMainFragment(), androidx.swiperefreshlayout
socialRepository.postPrivateMessage(userID, chatText)
.delay(200, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(Consumer {
+ .subscribe({
recyclerView?.scrollToPosition(0)
- }, RxErrorHandler.handleEmptyError())
+ }, { error ->
+ RxErrorHandler.reportError(error)
+ chatBarView.message = chatText
+ })
KeyboardUtil.dismissKeyboard(getActivity())
}
}
@@ -133,6 +149,7 @@ class InboxMessageListFragment : BaseMainFragment(), androidx.swiperefreshlayout
private fun setReceivingUser(chatRoomUser: String?, replyToUserUUID: String) {
this.chatRoomUser = chatRoomUser
this.replyToUserUUID = replyToUserUUID
+ activity?.title = chatRoomUser
}
private fun copyMessageToClipboard(chatMessage: ChatMessage) {
@@ -162,4 +179,7 @@ class InboxMessageListFragment : BaseMainFragment(), androidx.swiperefreshlayout
}
}
+ private fun openProfile() {
+ replyToUserUUID?.let { FullProfileActivity.open(it) }
+ }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxOverviewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxOverviewFragment.kt
index 204e61d1d..21ba46114 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxOverviewFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxOverviewFragment.kt
@@ -2,15 +2,12 @@ package com.habitrpg.android.habitica.ui.fragments.social
import android.content.Context
import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.MenuItem
-import android.view.View
-import android.view.ViewGroup
-import android.widget.EditText
+import android.view.*
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.SocialRepository
+import com.habitrpg.android.habitica.databinding.DialogChooseMessageRecipientBinding
import com.habitrpg.android.habitica.extensions.getAgoString
import com.habitrpg.android.habitica.helpers.AppConfigManager
import com.habitrpg.android.habitica.helpers.MainNavigationController
@@ -37,8 +34,6 @@ class InboxOverviewFragment : BaseMainFragment(), androidx.swiperefreshlayout.wi
@Inject
lateinit var configManager: AppConfigManager
- private var chooseRecipientDialogView: View? = null
-
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
this.hidesToolbar = true
@@ -69,6 +64,11 @@ class InboxOverviewFragment : BaseMainFragment(), androidx.swiperefreshlayout.wi
super.onDestroy()
}
+ override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+ this.activity?.menuInflater?.inflate(R.menu.inbox, menu)
+ super.onCreateOptionsMenu(menu, inflater)
+ }
+
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.send_message -> {
@@ -81,19 +81,34 @@ class InboxOverviewFragment : BaseMainFragment(), androidx.swiperefreshlayout.wi
private fun openNewMessageDialog() {
assert(this.activity != null)
- this.chooseRecipientDialogView = this.activity?.layoutInflater?.inflate(R.layout.dialog_choose_message_recipient, null)
+ val binding = DialogChooseMessageRecipientBinding.inflate(layoutInflater)
this.activity?.let { thisActivity ->
val alert = HabiticaAlertDialog(thisActivity)
alert.setTitle(getString(R.string.choose_recipient_title))
- alert.addButton(getString(R.string.action_continue), true) { _, _ ->
- val uuidEditText = chooseRecipientDialogView?.findViewById(R.id.uuidEditText) as? EditText
- openInboxMessages(uuidEditText?.text?.toString() ?: "", "")
- }
+ alert.addButton(
+ getString(R.string.action_continue),
+ true,
+ false,
+ false
+ ) { _, _ ->
+ binding.errorTextView.visibility = View.GONE
+ binding.progressCircular.visibility = View.VISIBLE
+ val username = binding.uuidEditText.text?.toString() ?: ""
+ socialRepository.getMemberWithUsername(username)
+ .subscribe({
+ alert.dismiss()
+ openInboxMessages("", username)
+ binding.progressCircular.visibility = View.GONE
+ }, {
+ binding.errorTextView.visibility = View.VISIBLE
+ binding.progressCircular.visibility = View.GONE
+ })
+ }
alert.addButton(getString(R.string.action_cancel), false) { dialog, _ ->
- thisActivity.dismissKeyboard()
- }
- alert.setAdditionalContentView(chooseRecipientDialogView)
+ thisActivity.dismissKeyboard()
+ }
+ alert.setAdditionalContentView(binding.root)
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 83554931a..5b45c88a2 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
@@ -247,11 +247,20 @@ class PartyDetailFragment : BaseFragment() {
} else {
DataBindingUtils.loadImage(questImageView, "quest_" + questContent.key)
}
+ questImageWrapper?.alpha = 1.0f
+ questProgressView?.alpha = 1.0f
if (viewModel?.isQuestActive == true) {
questProgressView?.visibility = View.VISIBLE
questProgressView?.setData(questContent, viewModel?.getGroupData()?.value?.quest?.progress)
- questParticipationView?.text = context?.getString(R.string.number_participants, viewModel?.getGroupData()?.value?.quest?.members?.size)
+ val questParticipants = viewModel?.getGroupData()?.value?.quest?.members
+ if (questParticipants?.find { it.key == userId } != null) {
+ questParticipationView?.text = context?.getString(R.string.number_participants, questParticipants.size)
+ } else {
+ questParticipationView?.text = context?.getString(R.string.not_participating)
+ questImageWrapper?.alpha = 0.5f
+ questProgressView?.alpha = 0.5f
+ }
} else {
questProgressView?.visibility = View.GONE
}
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
new file mode 100644
index 000000000..38b437def
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/BugFixFragment.kt
@@ -0,0 +1,109 @@
+package com.habitrpg.android.habitica.ui.fragments.support
+
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.app.ShareCompat
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.components.UserComponent
+import com.habitrpg.android.habitica.databinding.FragmentSupportBugFixBinding
+import com.habitrpg.android.habitica.helpers.AppConfigManager
+import com.habitrpg.android.habitica.helpers.AppTestingLevel
+import com.habitrpg.android.habitica.helpers.DeviceName
+import com.habitrpg.android.habitica.modules.AppModule
+import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
+import io.reactivex.Completable
+import javax.inject.Inject
+import javax.inject.Named
+
+class BugFixFragment: BaseMainFragment() {
+ private var deviceInfo: DeviceName.DeviceInfo? = null
+
+ private lateinit var binding: FragmentSupportBugFixBinding
+
+ @field:[Inject Named(AppModule.NAMED_USER_ID)]
+ lateinit var userId: String
+ @Inject
+ lateinit var appConfigManager: AppConfigManager
+
+ override fun injectFragment(component: UserComponent) {
+ component.inject(this)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ hidesToolbar = true
+ super.onCreateView(inflater, container, savedInstanceState)
+ binding = FragmentSupportBugFixBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ compositeSubscription.add(Completable.fromAction {
+ deviceInfo = DeviceName.getDeviceInfo(context)
+ }.subscribe())
+
+ binding.reportBugButton.setOnClickListener {
+ sendEmail("[Android] Bugreport")
+ }
+ }
+
+ 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 {
+ ShareCompat.IntentBuilder.from(it)
+ .setType("message/rfc822")
+ .addEmailTo(appConfigManager.supportEmail())
+ .setSubject(subject)
+ .setText(bodyOfEmail)
+ .setChooserTitle("Send email...")
+ .startChooser()
+ }
+ }
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/faq/FAQDetailFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/FAQDetailFragment.kt
similarity index 94%
rename from Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/faq/FAQDetailFragment.kt
rename to Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/FAQDetailFragment.kt
index c5add4974..e73f034d9 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/faq/FAQDetailFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/FAQDetailFragment.kt
@@ -1,52 +1,53 @@
-package com.habitrpg.android.habitica.ui.fragments.faq
-
-import android.os.Bundle
-import android.text.method.LinkMovementMethod
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import com.habitrpg.android.habitica.R
-import com.habitrpg.android.habitica.components.UserComponent
-import com.habitrpg.android.habitica.data.FAQRepository
-import com.habitrpg.android.habitica.extensions.inflate
-import com.habitrpg.android.habitica.helpers.RxErrorHandler
-import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
-import com.habitrpg.android.habitica.ui.helpers.MarkdownParser
-import com.habitrpg.android.habitica.ui.helpers.bindOptionalView
-import com.habitrpg.android.habitica.ui.helpers.resetViews
-import io.reactivex.functions.Consumer
-import javax.inject.Inject
-
-class FAQDetailFragment : BaseMainFragment() {
- @Inject
- lateinit var faqRepository: FAQRepository
-
- private val questionTextView: TextView? by bindOptionalView(R.id.questionTextView)
- private val answerTextView: TextView? by bindOptionalView(R.id.answerTextView)
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- super.onCreateView(inflater, container, savedInstanceState)
- return container?.inflate(R.layout.fragment_faq_detail)
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- resetViews()
-
- arguments?.let {
- val args = FAQDetailFragmentArgs.fromBundle(it)
- compositeSubscription.add(faqRepository.getArticle(args.position).subscribe(Consumer { faq ->
- this.questionTextView?.text = faq.question
- this.answerTextView?.text = MarkdownParser.parseMarkdown(faq.answer)
- }, RxErrorHandler.handleEmptyError()))
- }
-
- this.answerTextView?.movementMethod = LinkMovementMethod.getInstance()
- }
-
- override fun injectFragment(component: UserComponent) {
- component.inject(this)
- }
-}
+package com.habitrpg.android.habitica.ui.fragments.support
+
+import android.os.Bundle
+import android.text.method.LinkMovementMethod
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.components.UserComponent
+import com.habitrpg.android.habitica.data.FAQRepository
+import com.habitrpg.android.habitica.extensions.inflate
+import com.habitrpg.android.habitica.helpers.RxErrorHandler
+import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
+import com.habitrpg.android.habitica.ui.helpers.MarkdownParser
+import com.habitrpg.android.habitica.ui.helpers.bindOptionalView
+import com.habitrpg.android.habitica.ui.helpers.resetViews
+import io.reactivex.functions.Consumer
+import javax.inject.Inject
+
+class FAQDetailFragment : BaseMainFragment() {
+ @Inject
+ lateinit var faqRepository: FAQRepository
+
+ private val questionTextView: TextView? by bindOptionalView(R.id.questionTextView)
+ private val answerTextView: TextView? by bindOptionalView(R.id.answerTextView)
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ hidesToolbar = true
+ super.onCreateView(inflater, container, savedInstanceState)
+ return container?.inflate(R.layout.fragment_faq_detail)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ resetViews()
+
+ arguments?.let {
+ val args = FAQDetailFragmentArgs.fromBundle(it)
+ compositeSubscription.add(faqRepository.getArticle(args.position).subscribe(Consumer { faq ->
+ this.questionTextView?.text = faq.question
+ this.answerTextView?.text = MarkdownParser.parseMarkdown(faq.answer)
+ }, RxErrorHandler.handleEmptyError()))
+ }
+
+ this.answerTextView?.movementMethod = LinkMovementMethod.getInstance()
+ }
+
+ override fun injectFragment(component: UserComponent) {
+ component.inject(this)
+ }
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/FAQOverviewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/FAQOverviewFragment.kt
new file mode 100644
index 000000000..cdfa5d49b
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/FAQOverviewFragment.kt
@@ -0,0 +1,78 @@
+package com.habitrpg.android.habitica.ui.fragments.support
+
+import android.os.Bundle
+import android.text.method.LinkMovementMethod
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import androidx.core.os.bundleOf
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.components.UserComponent
+import com.habitrpg.android.habitica.data.FAQRepository
+import com.habitrpg.android.habitica.databinding.FragmentFaqOverviewBinding
+import com.habitrpg.android.habitica.databinding.SupportFaqItemBinding
+import com.habitrpg.android.habitica.extensions.layoutInflater
+import com.habitrpg.android.habitica.helpers.MainNavigationController
+import com.habitrpg.android.habitica.helpers.RxErrorHandler
+import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
+import com.habitrpg.android.habitica.ui.helpers.setMarkdown
+import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
+import io.reactivex.functions.Consumer
+import javax.inject.Inject
+
+class FAQOverviewFragment : BaseMainFragment() {
+
+ private lateinit var binding: FragmentFaqOverviewBinding
+
+ @Inject
+ lateinit var faqRepository: FAQRepository
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ hidesToolbar = true
+ super.onCreateView(inflater, container, savedInstanceState)
+ binding = FragmentFaqOverviewBinding.inflate(inflater, container, false)
+ binding.healthSection.findViewById(R.id.icon_view).setImageBitmap(HabiticaIconsHelper.imageOfHeartLarge())
+ binding.experienceSection.findViewById(R.id.icon_view).setImageBitmap(HabiticaIconsHelper.imageOfExperienceReward())
+ binding.manaSection.findViewById(R.id.icon_view).setImageBitmap(HabiticaIconsHelper.imageOfMagicLarge())
+ binding.goldSection.findViewById(R.id.icon_view).setImageBitmap(HabiticaIconsHelper.imageOfGoldReward())
+ binding.gemsSection.findViewById(R.id.icon_view).setImageBitmap(HabiticaIconsHelper.imageOfGem())
+ binding.hourglassesSection.findViewById(R.id.icon_view).setImageBitmap(HabiticaIconsHelper.imageOfHourglassLarge())
+ binding.statsSection.findViewById(R.id.icon_view).setImageBitmap(HabiticaIconsHelper.imageOfStats())
+
+ binding.moreHelpTextView.setMarkdown(context?.getString(R.string.need_help_header_description, "[Habitica Help Guild](https://habitica.com/groups/guild/5481ccf3-5d2d-48a9-a871-70a7380cee5a)"))
+ binding.moreHelpTextView.setOnClickListener { MainNavigationController.navigate(R.id.guildFragment, bundleOf("groupID" to "5481ccf3-5d2d-48a9-a871-70a7380cee5a")) }
+ binding.moreHelpTextView.movementMethod = LinkMovementMethod.getInstance()
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ this.loadArticles()
+ }
+
+ override fun onDestroy() {
+ faqRepository.close()
+ super.onDestroy()
+ }
+
+ override fun injectFragment(component: UserComponent) {
+ component.inject(this)
+ }
+
+ private fun loadArticles() {
+ compositeSubscription.add(faqRepository.getArticles().subscribe(Consumer {
+ val context = context ?: return@Consumer
+ for (article in it) {
+ val binding = SupportFaqItemBinding.inflate(context.layoutInflater, binding.faqLinearLayout, true)
+ binding.textView.text = article.question
+ binding.root.setOnClickListener {
+ MainNavigationController.navigate(FAQOverviewFragmentDirections.openFAQDetail(article.position ?: 0))
+ }
+ }
+ }, RxErrorHandler.handleEmptyError()))
+ }
+
+}
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
new file mode 100644
index 000000000..28be1802e
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/SupportMainFragment.kt
@@ -0,0 +1,126 @@
+package com.habitrpg.android.habitica.ui.fragments.support
+
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.app.ShareCompat
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.components.UserComponent
+import com.habitrpg.android.habitica.data.FAQRepository
+import com.habitrpg.android.habitica.databinding.FragmentSupportMainBinding
+import com.habitrpg.android.habitica.helpers.AppConfigManager
+import com.habitrpg.android.habitica.helpers.AppTestingLevel
+import com.habitrpg.android.habitica.helpers.DeviceName
+import com.habitrpg.android.habitica.helpers.MainNavigationController
+import com.habitrpg.android.habitica.modules.AppModule
+import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
+import io.reactivex.Completable
+import javax.inject.Inject
+import javax.inject.Named
+
+class SupportMainFragment : BaseMainFragment() {
+ private var deviceInfo: DeviceName.DeviceInfo? = null
+
+ private lateinit var binding: FragmentSupportMainBinding
+ @field:[Inject Named(AppModule.NAMED_USER_ID)]
+ lateinit var userId: String
+ @Inject
+ lateinit var faqRepository: FAQRepository
+ @Inject
+ lateinit var appConfigManager: AppConfigManager
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ hidesToolbar = true
+ super.onCreateView(inflater, container, savedInstanceState)
+ binding = FragmentSupportMainBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ binding.usingHabiticaWrapper.setOnClickListener {
+ MainNavigationController.navigate(R.id.FAQOverviewFragment)
+ }
+ binding.bugsFixesWrapper.setOnClickListener {
+ MainNavigationController.navigate(R.id.bugFixFragment)
+ }
+ binding.suggestionsFeedbackWrapper.setOnClickListener {
+ sendEmail("[Android] Feedback")
+ }
+
+ compositeSubscription.add(Completable.fromAction {
+ deviceInfo = DeviceName.getDeviceInfo(context)
+ }.subscribe())
+
+ binding.resetTutorialButton.setOnClickListener {
+ userRepository.resetTutorial(user)
+ }
+ }
+
+ override fun onDestroy() {
+ faqRepository.close()
+ super.onDestroy()
+ }
+
+ 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 {
+ ShareCompat.IntentBuilder.from(it)
+ .setType("message/rfc822")
+ .addEmailTo(appConfigManager.supportEmail())
+ .setSubject(subject)
+ .setText(bodyOfEmail)
+ .setChooserTitle("Send email...")
+ .startChooser()
+ }
+ }
+}
\ No newline at end of file
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 adc5213e2..ef6f1a1a3 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
@@ -277,26 +277,7 @@ open class TaskRecyclerViewFragment : BaseFragment(), androidx.swiperefreshlayou
}
})
- if (this.classType != null) {
- binding.recyclerView.setEmptyView(binding.emptyView)
- when (this.classType) {
- Task.TYPE_HABIT -> {
- binding.emptyViewTitle.setText(R.string.empty_title_habits)
- binding.emptyViewDescription.setText(R.string.empty_description_habits)
- }
- Task.TYPE_DAILY -> {
- binding.emptyViewTitle.setText(R.string.empty_title_dailies)
- binding.emptyViewDescription.setText(R.string.empty_description_dailies)
- }
- Task.TYPE_TODO -> {
- binding.emptyViewTitle.setText(R.string.empty_title_todos)
- binding.emptyViewDescription.setText(R.string.empty_description_todos)
- }
- Task.TYPE_REWARD -> {
- binding.emptyViewTitle.setText(R.string.empty_title_rewards)
- }
- }
- }
+ setEmptyLabels()
if (Task.TYPE_REWARD == className) {
compositeSubscription.add(taskRepository.getTasks(this.className, userID)
@@ -304,6 +285,58 @@ open class TaskRecyclerViewFragment : BaseFragment(), androidx.swiperefreshlayou
}
}
+ private fun setEmptyLabels() {
+ if (this.classType != null) {
+ binding.recyclerView.setEmptyView(binding.emptyView)
+ context?.let { binding.emptyIconView.setColorFilter(ContextCompat.getColor(it, R.color.gray_400), android.graphics.PorterDuff.Mode.MULTIPLY) }
+ if (taskFilterHelper.howMany(classType) > 0) {
+ when (this.classType) {
+ Task.TYPE_HABIT -> {
+ binding.emptyIconView.setImageResource(R.drawable.icon_habits_selected)
+ binding.emptyViewTitle.setText(R.string.empty_title_habits_filtered)
+ binding.emptyViewDescription.setText(R.string.empty_description_habits_filtered)
+ }
+ Task.TYPE_DAILY -> {
+ binding.emptyIconView.setImageResource(R.drawable.icon_dailies_selected)
+ binding.emptyViewTitle.setText(R.string.empty_title_dailies_filtered)
+ binding.emptyViewDescription.setText(R.string.empty_description_dailies_filtered)
+ }
+ Task.TYPE_TODO -> {
+ binding.emptyIconView.setImageResource(R.drawable.icon_todos_selected)
+ binding.emptyViewTitle.setText(R.string.empty_title_todos_filtered)
+ binding.emptyViewDescription.setText(R.string.empty_description_todos_filtered)
+ }
+ Task.TYPE_REWARD -> {
+ binding.emptyIconView.setImageResource(R.drawable.icon_rewards_selected)
+ binding.emptyViewTitle.setText(R.string.empty_title_rewards)
+ }
+ }
+ } else {
+ when (this.classType) {
+ Task.TYPE_HABIT -> {
+ binding.emptyIconView.setImageResource(R.drawable.icon_habits_selected)
+ binding.emptyViewTitle.setText(R.string.empty_title_habits)
+ binding.emptyViewDescription.setText(R.string.empty_description_habits)
+ }
+ Task.TYPE_DAILY -> {
+ binding.emptyIconView.setImageResource(R.drawable.icon_dailies_selected)
+ binding.emptyViewTitle.setText(R.string.empty_title_dailies)
+ binding.emptyViewDescription.setText(R.string.empty_description_dailies)
+ }
+ Task.TYPE_TODO -> {
+ binding.emptyIconView.setImageResource(R.drawable.icon_todos_selected)
+ binding.emptyViewTitle.setText(R.string.empty_title_todos)
+ binding.emptyViewDescription.setText(R.string.empty_description_todos)
+ }
+ Task.TYPE_REWARD -> {
+ binding.emptyIconView.setImageResource(R.drawable.icon_rewards_selected)
+ binding.emptyViewTitle.setText(R.string.empty_title_rewards)
+ }
+ }
+ }
+ }
+ }
+
private fun scoreTask(task: Task, direction: TaskDirection) {
compositeSubscription.add(taskRepository.taskChecked(user, task, direction == TaskDirection.UP, false) { result ->
handleTaskResult(result, task.value.toInt())
@@ -330,6 +363,8 @@ open class TaskRecyclerViewFragment : BaseFragment(), androidx.swiperefreshlayou
taskFilterHelper.setActiveFilter(classType ?: "", activeFilter)
recyclerAdapter?.filter()
+ setEmptyLabels()
+
if (activeFilter == Task.FILTER_COMPLETED) {
compositeSubscription.add(taskRepository.retrieveCompletedTodos(userID).subscribe(Consumer {}, RxErrorHandler.handleEmptyError()))
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt
index 9fa6bc7c7..997a00be3 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt
@@ -21,6 +21,7 @@ import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.activities.TaskFormActivity
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
import com.habitrpg.android.habitica.ui.views.tasks.TaskFilterDialog
+import io.reactivex.disposables.Disposable
import io.reactivex.functions.Consumer
import java.util.*
import javax.inject.Inject
@@ -158,9 +159,11 @@ class TasksFragment : BaseMainFragment(), SearchView.OnQueryTextListener {
private fun showFilterDialog() {
context?.let {
+ var disposable: Disposable? = null
val dialog = TaskFilterDialog(it, HabiticaBaseApplication.userComponent)
if (user != null) {
dialog.setTags(user?.tags?.createSnapshot() ?: emptyList())
+ disposable = tagRepository.getTags(user?.id ?: "").subscribe(Consumer {tagsList -> dialog.setTags(tagsList)}, RxErrorHandler.handleEmptyError())
}
dialog.setActiveTags(taskFilterHelper.tags)
if (activeFragment != null) {
@@ -182,6 +185,11 @@ class TasksFragment : BaseMainFragment(), SearchView.OnQueryTextListener {
updateFilterIcon()
}
})
+ dialog.setOnDismissListener {
+ if (disposable?.isDisposed == false) {
+ disposable?.dispose()
+ }
+ }
dialog.show()
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/DataBindingUtils.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/DataBindingUtils.kt
index 44998cad1..ee1c80e90 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/DataBindingUtils.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/DataBindingUtils.kt
@@ -14,41 +14,50 @@ import com.facebook.common.executors.CallerThreadExecutor
import com.facebook.common.references.CloseableReference
import com.facebook.datasource.DataSource
import com.facebook.drawee.backends.pipeline.Fresco
+import com.facebook.drawee.interfaces.DraweeController
import com.facebook.drawee.view.SimpleDraweeView
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber
import com.facebook.imagepipeline.image.CloseableImage
import com.facebook.imagepipeline.request.ImageRequestBuilder
import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.helpers.AppConfigManager
+import java.util.*
+import kotlin.collections.HashMap
-fun SimpleDraweeView.loadImage(imageName: String, imageFormat: String = "png") {
+fun SimpleDraweeView.loadImage(imageName: String, imageFormat: String? = null) {
DataBindingUtils.loadImage(this, imageName, imageFormat)
}
object DataBindingUtils {
fun loadImage(view: SimpleDraweeView?, imageName: String) {
- loadImage(view, imageName, "png")
+ loadImage(view, imageName, null)
}
- fun loadImage(view: SimpleDraweeView?, imageName: String?, imageFormat: String = "png") {
+ fun loadImage(view: SimpleDraweeView?, imageName: String?, imageFormat: String? = null) {
if (view != null && imageName != null && view.visibility == View.VISIBLE) {
- val fullname = "$imageName.$imageFormat"
+ val fullname = getFullFilename(imageName, imageFormat)
if (view.tag == fullname) {
return
}
view.tag = fullname
- view.setImageURI("https://habitica-assets.s3.amazonaws.com/mobileApp/images/$fullname")
+ val controller: DraweeController = Fresco.newDraweeControllerBuilder()
+ .setUri("https://habitica-assets.s3.amazonaws.com/mobileApp/images/$fullname")
+ .setAutoPlayAnimations(true)
+ .setOldController(view.controller)
+ .build()
+ view.controller = controller
}
}
fun loadImage(imageName: String, imageResult: (Bitmap) -> Unit) {
- loadImage(imageName, "png", imageResult)
+ loadImage(imageName, null, imageResult)
}
- fun loadImage(imageName: String, imageFormat: String = "png", imageResult: (Bitmap) -> Unit) {
+ fun loadImage(imageName: String, imageFormat: String?, imageResult: (Bitmap) -> Unit) {
val imageRequest = ImageRequestBuilder
- .newBuilderWithSource("https://habitica-assets.s3.amazonaws.com/mobileApp/images/$imageName.$imageFormat".toUri())
+ .newBuilderWithSource("https://habitica-assets.s3.amazonaws.com/mobileApp/images/${getFullFilename(imageName, imageFormat)}".toUri())
.build()
val imagePipeline = Fresco.getImagePipeline()
@@ -68,6 +77,20 @@ object DataBindingUtils {
}, CallerThreadExecutor.getInstance())
}
+ fun getFullFilename(imageName: String, imageFormat: String?): String {
+ val name = when {
+ spriteSubstitutions.containsKey(imageName) -> spriteSubstitutions[imageName]
+ FILENAME_MAP.containsKey(imageName) -> FILENAME_MAP[imageName]
+ imageName.startsWith("handleless") -> "chair_$imageName"
+ else -> imageName
+ }
+ return name + if (imageFormat == null && FILEFORMAT_MAP.containsKey(imageName)) {
+ "." + FILEFORMAT_MAP[imageName]
+ } else {
+ ".${imageFormat ?: "png"}"
+ }
+ }
+
fun setForegroundTintColor(view: TextView, color: Int) {
var thisColor = color
if (thisColor > 0) {
@@ -105,4 +128,60 @@ object DataBindingUtils {
override fun willChangeBounds(): Boolean = true
}
+
+ private val FILEFORMAT_MAP: Map
+ private val FILENAME_MAP: Map
+
+ private var spriteSubstitutions: Map = HashMap()
+ get() {
+ if (Date().time - (lastSubstitutionCheck?.time ?: 0) > 180000) {
+ field = AppConfigManager().spriteSubstitutions().get("generic") ?: HashMap()
+ lastSubstitutionCheck = Date()
+ }
+ return field
+ }
+ private var lastSubstitutionCheck: Date? = null
+
+ init {
+ val tempMap = HashMap()
+ tempMap["head_special_1"] = "gif"
+ tempMap["broad_armor_special_1"] = "gif"
+ tempMap["slim_armor_special_1"] = "gif"
+ tempMap["head_special_0"] = "gif"
+ tempMap["slim_armor_special_0"] = "gif"
+ tempMap["broad_armor_special_0"] = "gif"
+ tempMap["weapon_special_critical"] = "gif"
+ tempMap["weapon_special_0"] = "gif"
+ tempMap["shield_special_0"] = "gif"
+ tempMap["Pet-Wolf-Cerberus"] = "gif"
+ tempMap["armor_special_ks2019"] = "gif"
+ tempMap["slim_armor_special_ks2019"] = "gif"
+ tempMap["broad_armor_special_ks2019"] = "gif"
+ tempMap["eyewear_special_ks2019"] = "gif"
+ tempMap["head_special_ks2019"] = "gif"
+ tempMap["shield_special_ks2019"] = "gif"
+ tempMap["weapon_special_ks2019"] = "gif"
+ tempMap["Pet-Gryphon-Gryphatrice"] = "gif"
+ tempMap["Mount_Head_Gryphon-Gryphatrice"] = "gif"
+ tempMap["Mount_Body_Gryphon-Gryphatrice"] = "gif"
+ tempMap["background_clocktower"] = "gif"
+ tempMap["background_airship"] = "gif"
+ tempMap["background_steamwork"] = "gif"
+ tempMap["Pet_HatchingPotion_Veggie"] = "gif"
+ tempMap["Pet_HatchingPotion_Dessert"] = "gif"
+ tempMap["Pet-HatchingPotion-Dessert"] = "gif"
+ FILEFORMAT_MAP = Collections.unmodifiableMap(tempMap)
+
+
+ val tempNameMap = HashMap()
+ tempNameMap["head_special_1"] = "ContributorOnly-Equip-CrystalHelmet"
+ tempNameMap["armor_special_1"] = "ContributorOnly-Equip-CrystalArmor"
+ tempNameMap["head_special_0"] = "BackerOnly-Equip-ShadeHelmet"
+ tempNameMap["armor_special_0"] = "BackerOnly-Equip-ShadeArmor"
+ tempNameMap["shield_special_0"] = "BackerOnly-Shield-TormentedSkull"
+ tempNameMap["weapon_special_0"] = "BackerOnly-Weapon-DarkSoulsBlade"
+ tempNameMap["weapon_special_critical"] = "weapon_special_critical"
+ tempNameMap["Pet-Wolf-Cerberus"] = "Pet-Wolf-Cerberus"
+ FILENAME_MAP = Collections.unmodifiableMap(tempNameMap)
+ }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/MarginDecoration.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/MarginDecoration.kt
index 46302b14a..57408da6a 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/MarginDecoration.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/MarginDecoration.kt
@@ -6,10 +6,17 @@ import android.view.View
import androidx.recyclerview.widget.RecyclerView
import com.habitrpg.android.habitica.R
-class MarginDecoration(context: Context?) : RecyclerView.ItemDecoration() {
+class MarginDecoration(context: Context?, noMarginViewTypes: Set = setOf()) : RecyclerView.ItemDecoration() {
private val margin: Int = context?.resources?.getDimensionPixelSize(R.dimen.grid_item_margin) ?: 0
+ private var noMarginViewTypes = noMarginViewTypes
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
- outRect.set(margin, margin, margin, margin)
+ val position = parent.getChildAdapterPosition(view)
+ val viewType: Int? = parent.adapter?.getItemViewType(position)
+ if (noMarginViewTypes.contains(viewType)) {
+ outRect.setEmpty();
+ } else {
+ outRect.set(margin, margin, margin, margin);
+ }
}
}
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 9a74a2b70..59c3f0b14 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
@@ -1,20 +1,18 @@
package com.habitrpg.android.habitica.ui.viewHolders
import android.content.Context
-import androidx.recyclerview.widget.RecyclerView
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
-import android.widget.Button
import android.widget.Spinner
import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.ui.helpers.bindView
class SectionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val label: TextView by bindView(itemView, R.id.label)
- private val purchaseSetButton: Button? by bindView(itemView, R.id.purchaseSetButton)
private val selectionSpinner: Spinner? by bindView(itemView, R.id.classSelectionSpinner)
internal val notesView: TextView? by bindView(itemView, R.id.headerNotesView)
var context: Context = itemView.context
@@ -22,7 +20,7 @@ class SectionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var spinnerSelectionChanged: (() -> Unit)? = null
init {
- this.purchaseSetButton?.visibility = View.GONE
+ itemView.findViewById(R.id.purchaseSetButton)?.visibility = View.GONE
selectionSpinner?.onItemSelectedListener = object: AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
spinnerSelectionChanged?.invoke()
@@ -32,7 +30,6 @@ class SectionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
spinnerSelectionChanged?.invoke()
}
}
-
}
fun bind(title: String) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/ShopItemViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/ShopItemViewHolder.kt
index 1424da989..3484348c4 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/ShopItemViewHolder.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/ShopItemViewHolder.kt
@@ -62,9 +62,13 @@ class ShopItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), Vi
this.item = item
buyButton.visibility = View.VISIBLE
- DataBindingUtils.loadImage(this.imageView, item.imageName)
+ DataBindingUtils.loadImage(this.imageView, item.imageName?.replace("_locked", ""))
- if (item.unlockCondition == null || !item.locked) {
+ itemDetailIndicator.text = null
+ itemDetailIndicator.visibility = View.GONE
+
+ val lockedReason = item.shortLockedReason(context)
+ if (!item.locked || lockedReason == null) {
priceLabel.text = item.value.toString()
priceLabel.currency = item.currency
if (item.currency == null) {
@@ -72,24 +76,24 @@ class ShopItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), Vi
}
priceLabel.visibility = View.VISIBLE
unlockLabel.visibility = View.GONE
+ if (item.locked) {
+ itemDetailIndicator.background = lockedDrawable
+ itemDetailIndicator.visibility = View.VISIBLE
+ }
} else {
- unlockLabel.setText(item.unlockCondition?.readableUnlockConditionId() ?: 0)
+ unlockLabel.text = lockedReason
priceLabel.visibility = View.GONE
unlockLabel.visibility = View.VISIBLE
+ itemDetailIndicator.background = lockedDrawable
+ itemDetailIndicator.visibility = View.VISIBLE
}
- itemDetailIndicator.text = null
- itemDetailIndicator.visibility = View.GONE
if (item.isLimited) {
itemDetailIndicator.background = limitedDrawable
itemDetailIndicator.visibility = View.VISIBLE
}
priceLabel.isLocked = item.locked || !canBuy
- if (item.locked) {
- itemDetailIndicator.background = lockedDrawable
- itemDetailIndicator.visibility = View.VISIBLE
- }
}
override fun onClick(view: View) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/BaseTaskViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/BaseTaskViewHolder.kt
index 24ba2e679..0913d78ec 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/BaseTaskViewHolder.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/BaseTaskViewHolder.kt
@@ -83,6 +83,9 @@ abstract class BaseTaskViewHolder constructor(itemView: View, var scoreTaskFunc:
notesTextView?.addEllipsesListener(object : EllipsisTextView.EllipsisListener {
override fun ellipsisStateChanged(ellipses: Boolean) {
GlobalScope.launch(Dispatchers.Main.immediate) {
+ if (ellipses) {
+ notesTextView?.maxLines = 3
+ }
expandNotesButton?.visibility = if (ellipses || notesExpanded) View.VISIBLE else View.GONE
}
}
@@ -96,7 +99,7 @@ abstract class BaseTaskViewHolder constructor(itemView: View, var scoreTaskFunc:
notesTextView?.maxLines = 100
expandNotesButton?.text = context.getString(R.string.collapse_notes)
} else {
- notesTextView?.maxLines = 3
+ notesTextView?.maxLines = 5
expandNotesButton?.text = context.getString(R.string.expand_notes)
}
}
@@ -106,6 +109,8 @@ abstract class BaseTaskViewHolder constructor(itemView: View, var scoreTaskFunc:
itemView.setBackgroundResource(R.color.white)
expandNotesButton?.visibility = View.GONE
+ notesExpanded = false
+ notesTextView?.maxLines = 5
if (data.notes?.isNotEmpty() == true) {
notesTextView?.visibility = View.VISIBLE
//expandNotesButton.visibility = if (notesTextView.hadEllipses() || notesExpanded) View.VISIBLE else View.GONE
@@ -158,7 +163,7 @@ abstract class BaseTaskViewHolder constructor(itemView: View, var scoreTaskFunc:
iconViewReminder?.visibility = if (data.reminders?.size ?: 0 > 0) View.VISIBLE else View.GONE
iconViewTag?.visibility = if (data.tags?.size ?: 0 > 0) View.VISIBLE else View.GONE
- iconViewChallenge?.visibility = View.GONE
+ iconViewChallenge?.visibility = if (task?.challengeID != null) View.VISIBLE else View.GONE
configureSpecialTaskTextView(data)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/GroupViewModel.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/GroupViewModel.kt
index c7c187c6a..61e07998b 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/GroupViewModel.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/GroupViewModel.kt
@@ -200,11 +200,14 @@ open class GroupViewModel : BaseViewModel() {
disposable.add(socialRepository.deleteMessage(chatMessage).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
}
- fun postGroupChat(chatText: String, onComplete: () -> Unit?) {
+ fun postGroupChat(chatText: String, onComplete: () -> Unit, onError: () -> Unit) {
groupIDSubject.value?.value?.let {
- socialRepository.postGroupChat(it, chatText).subscribe(Consumer {
+ socialRepository.postGroupChat(it, chatText).subscribe({
onComplete()
- }, RxErrorHandler.handleEmptyError())
+ }, { error ->
+ RxErrorHandler.reportError(error)
+ onError()
+ })
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/PartyViewModel.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/PartyViewModel.kt
index ef0546baf..3540b9e52 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/PartyViewModel.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/PartyViewModel.kt
@@ -44,13 +44,17 @@ class PartyViewModel: GroupViewModel() {
fun acceptQuest() {
groupIDSubject.value?.value?.let {
- disposable.add(socialRepository.acceptQuest(null, it).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
+ disposable.add(socialRepository.acceptQuest(null, it)
+ .flatMap { userRepository.retrieveUser() }
+ .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
}
}
fun rejectQuest() {
groupIDSubject.value?.value?.let {
- disposable.add(socialRepository.rejectQuest(null, it).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
+ disposable.add(socialRepository.rejectQuest(null, it)
+ .flatMap { userRepository.retrieveUser() }
+ .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/EllipsisTextView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/EllipsisTextView.kt
index afbbb7a5b..2842b3002 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/EllipsisTextView.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/EllipsisTextView.kt
@@ -16,7 +16,6 @@ package com.habitrpg.android.habitica.ui.views
import android.content.Context
import android.util.AttributeSet
-import com.habitrpg.android.habitica.R
class EllipsisTextView : HabiticaEmojiTextView {
@@ -56,10 +55,8 @@ class EllipsisTextView : HabiticaEmojiTextView {
val layout = layout
if (layout != null) {
val lines = layout.lineCount
- if (lines > 0) {
- if (layout.getEllipsisCount(lines - 1) > 0) {
- ellipses = true
- }
+ if (lines >= maxLines) {
+ ellipses = true
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/EquipmentItemRow.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/EquipmentItemRow.kt
index 8e50fe6bd..f53dda804 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/EquipmentItemRow.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/EquipmentItemRow.kt
@@ -5,40 +5,37 @@ import android.util.AttributeSet
import android.view.View
import android.widget.LinearLayout
import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.databinding.ItemImageRowBinding
+import com.habitrpg.android.habitica.extensions.layoutInflater
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
-import kotlinx.android.synthetic.main.item_image_row.view.*
-class EquipmentItemRow(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) {
+class EquipmentItemRow(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) {
+ private val binding: ItemImageRowBinding = ItemImageRowBinding.inflate(context.layoutInflater, this)
var equipmentIdentifier: String? = null
set(value) {
field = value
val imageName = if (equipmentIdentifier?.isNotEmpty() == true && equipmentIdentifier?.endsWith("base_0") == false) "shop_$equipmentIdentifier" else "head_0"
- DataBindingUtils.loadImage(imageView, imageName)
+ DataBindingUtils.loadImage(binding.imageView, imageName)
}
var customizationIdentifier: String? = null
set(value) {
field = value
val imageName = if (customizationIdentifier?.isNotEmpty() == true) customizationIdentifier else "head_0"
- DataBindingUtils.loadImage(imageView, imageName)
- }
-
- var equipmentName: String? = ""
- set(value) {
- field = value
- valueTextView.text = equipmentName
+ DataBindingUtils.loadImage(binding.imageView, imageName)
}
init {
View.inflate(context, R.layout.item_image_row, this)
isClickable = true
- val attributes = context?.theme?.obtainStyledAttributes(
+ val attributes = context.theme?.obtainStyledAttributes(
attrs,
R.styleable.EquipmentItemRow,
0, 0)
- titleTextView.text = attributes?.getString(R.styleable.EquipmentItemRow_equipmentTitle)
+ binding.titleTextView.text = attributes?.getString(R.styleable.EquipmentItemRow_equipmentTitle)
+ binding.valueTextView.visibility = View.GONE
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaIcons.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaIcons.java
index 664ed1fec..7907c31ab 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaIcons.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaIcons.java
@@ -1,6 +1,7 @@
package com.habitrpg.android.habitica.ui.views;
import android.content.Context;
+import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
@@ -16,6 +17,7 @@ import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.Shader;
+import android.graphics.Typeface;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
@@ -26,8 +28,8 @@ import java.util.Stack;
/**
- * Created by Phillip Thelen on 12. Oct 2018.
- * Copyright © 2018 HabitRPG Inc.. All rights reserved.
+ * Created by Phillip Thelen on 18. Mar 2020.
+ * Copyright © 2020 HabitRPG Inc.. All rights reserved.
*
* Generated by PaintCode
* http://www.paintcodeapp.com
@@ -38,6 +40,13 @@ public class HabiticaIcons {
private static class GlobalCache {
static PaintCodeBitmap rageStrikeNPC = new PaintCodeBitmap();
static PorterDuffXfermode blendModeSourceIn = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
+ static Typeface.Builder helveticaNeue = null;
+
+ private static void initializeTypefaceBuilders(AssetManager assetManager) {
+ if (GlobalCache.helveticaNeue == null) {
+ GlobalCache.helveticaNeue = new Typeface.Builder(assetManager, "HelveticaNeue.ttc");
+ }
+ }
}
// Resizing Behavior
@@ -88,13 +97,13 @@ public class HabiticaIcons {
Paint paint = CacheForHeart.paint;
// Local Colors
- int fillColor6 = Color.argb(128, 181, 36, 40);
- int heartDarkBackground = Color.argb(178, 255, 255, 255);
- int fillColor3 = Color.argb(128, 255, 255, 255);
- int fillColor5 = Color.argb(64, 255, 255, 255);
- int heartLightBackground = Color.argb(255, 247, 78, 82);
- int fillColor4 = Color.argb(89, 181, 36, 40);
int fillColor2 = Color.argb(255, 255, 97, 101);
+ int fillColor5 = Color.argb(64, 255, 255, 255);
+ int fillColor3 = Color.argb(128, 255, 255, 255);
+ int fillColor4 = Color.argb(89, 181, 36, 40);
+ int fillColor6 = Color.argb(128, 181, 36, 40);
+ int heartLightBackground = Color.argb(255, 247, 78, 82);
+ int heartDarkBackground = Color.argb(178, 255, 255, 255);
// Local Variables
int expression = darkBackground ? heartDarkBackground : heartLightBackground;
@@ -368,11 +377,11 @@ public class HabiticaIcons {
Paint paint = CacheForExperience.paint;
// Local Colors
- int fillColor3 = Color.argb(128, 255, 255, 255);
int fillColor12 = Color.argb(128, 191, 125, 26);
int fillColor11 = Color.argb(64, 191, 125, 26);
int fillColor5 = Color.argb(64, 255, 255, 255);
int fillColor10 = Color.argb(255, 255, 166, 35);
+ int fillColor3 = Color.argb(128, 255, 255, 255);
// Resize to Target Frame
canvas.save();
@@ -599,11 +608,11 @@ public class HabiticaIcons {
Paint paint = CacheForMagic.paint;
// Local Colors
- int fillColor3 = Color.argb(128, 255, 255, 255);
int fillColor5 = Color.argb(64, 255, 255, 255);
int fillColor9 = Color.argb(64, 31, 112, 154);
int fillColor8 = Color.argb(255, 80, 181, 233);
int fillColor7 = Color.argb(255, 41, 149, 205);
+ int fillColor3 = Color.argb(128, 255, 255, 255);
// Resize to Target Frame
canvas.save();
@@ -750,11 +759,11 @@ public class HabiticaIcons {
Paint paint = CacheForGold.paint;
// Local Colors
- int fillColor13 = Color.argb(191, 191, 125, 26);
int fillColor12 = Color.argb(128, 191, 125, 26);
int fillColor5 = Color.argb(64, 255, 255, 255);
int fillColor3 = Color.argb(128, 255, 255, 255);
int fillColor10 = Color.argb(255, 255, 166, 35);
+ int fillColor13 = Color.argb(191, 191, 125, 26);
// Resize to Target Frame
canvas.save();
@@ -905,9 +914,9 @@ public class HabiticaIcons {
// Local Colors
int fillColor3 = Color.argb(128, 255, 255, 255);
- int fillColor5 = Color.argb(64, 255, 255, 255);
int fillColor15 = Color.argb(89, 27, 153, 107);
int fillColor14 = Color.argb(255, 36, 204, 143);
+ int fillColor5 = Color.argb(64, 255, 255, 255);
// Resize to Target Frame
canvas.save();
@@ -1531,14 +1540,14 @@ public class HabiticaIcons {
Paint paint = CacheForWarrior.paint;
// Local Colors
- int fillColor41 = Color.argb(255, 255, 149, 152);
- int fillColor43 = Color.argb(255, 200, 43, 43);
- int fillColor22 = Color.argb(255, 242, 123, 134);
- int fillColor20 = Color.argb(255, 255, 181, 183);
- int fillColor21 = Color.argb(255, 255, 182, 184);
int fillColor42 = Color.argb(255, 255, 149, 152);
- int fillColor23 = Color.argb(255, 229, 65, 77);
int fillColor = Color.argb(255, 240, 97, 102);
+ int fillColor43 = Color.argb(255, 200, 43, 43);
+ int fillColor21 = Color.argb(255, 255, 182, 184);
+ int fillColor20 = Color.argb(255, 255, 181, 183);
+ int fillColor23 = Color.argb(255, 229, 65, 77);
+ int fillColor41 = Color.argb(255, 255, 149, 152);
+ int fillColor22 = Color.argb(255, 242, 123, 134);
// Local Variables
boolean lightBackground = !darkBackground;
@@ -2705,9 +2714,9 @@ public class HabiticaIcons {
public static void drawDifficultyStars(Canvas canvas, RectF targetFrame, ResizingBehavior resizing, float difficulty) {
// Local Variables
+ float _2StarDifficulty = difficulty >= 2f ? 1f : (difficulty >= 1.5f ? 0.5f : 0f);
float _3StarDifficulty = difficulty >= 3f ? 1f : (difficulty >= 2.5f ? 0.5f : 0f);
float _4StarDifficulty = difficulty >= 4f ? 1f : (difficulty >= 3.5f ? 0.5f : 0f);
- float _2StarDifficulty = difficulty >= 2f ? 1f : (difficulty >= 1.5f ? 0.5f : 0f);
float _1StarDifficulty = difficulty >= 1f ? 1f : (difficulty >= 0.5f ? 0.5f : 0f);
// Resize to Target Frame
@@ -2785,12 +2794,12 @@ public class HabiticaIcons {
Paint paint = CacheForDifficultyStar.paint;
// Local Colors
- int fillColor24 = Color.argb(255, 255, 166, 36);
int fillColor25 = Color.argb(255, 225, 224, 227);
+ int fillColor24 = Color.argb(255, 255, 166, 36);
// Local Variables
- boolean isHalf = singleDifficulty == 0.5f;
boolean isFull = singleDifficulty == 1f;
+ boolean isHalf = singleDifficulty == 0.5f;
// Resize to Target Frame
canvas.save();
@@ -3038,11 +3047,11 @@ public class HabiticaIcons {
Paint paint = CacheForStarLarge.paint;
// Local Colors
- int fillColor28 = Color.argb(64, 229, 220, 255);
- int fillColor31 = Color.argb(191, 229, 220, 255);
int fillColor30 = Color.argb(166, 229, 220, 255);
+ int fillColor31 = Color.argb(191, 229, 220, 255);
int fillColor29 = Color.argb(128, 229, 220, 255);
int fillColor27 = Color.argb(255, 229, 220, 255);
+ int fillColor28 = Color.argb(64, 229, 220, 255);
// Resize to Target Frame
canvas.save();
@@ -3442,10 +3451,10 @@ public class HabiticaIcons {
Paint paint = CacheForStarMedium.paint;
// Local Colors
- int fillColor27 = Color.argb(255, 229, 220, 255);
int fillColor34 = Color.argb(38, 229, 220, 255);
int fillColor33 = Color.argb(77, 229, 220, 255);
int fillColor32 = Color.argb(153, 229, 220, 255);
+ int fillColor27 = Color.argb(255, 229, 220, 255);
// Resize to Target Frame
canvas.save();
@@ -3772,8 +3781,8 @@ public class HabiticaIcons {
Paint paint = CacheForAddRemovePin.paint;
// Local Colors
- int fillColor36 = Color.argb(255, 97, 51, 180);
int fillColor2 = Color.argb(255, 255, 97, 101);
+ int fillColor36 = Color.argb(255, 97, 51, 180);
// Local Variables
int pinColor = addPin ? fillColor36 : fillColor2;
@@ -3963,27 +3972,28 @@ public class HabiticaIcons {
private static RectF symbolTargetRect = new RectF();
}
- public static void drawItemIndicator(Canvas canvas, int lockColor, boolean isLocked, boolean isLimited) {
- HabiticaIcons.drawItemIndicator(canvas, new RectF(0f, 0f, 28f, 28f), ResizingBehavior.AspectFit, lockColor, isLocked, isLimited);
+ public static void drawItemIndicator(Canvas canvas, int lockColor, boolean isLocked, boolean isLimited, boolean isDark) {
+ HabiticaIcons.drawItemIndicator(canvas, new RectF(0f, 0f, 28f, 28f), ResizingBehavior.AspectFit, lockColor, isLocked, isLimited, isDark);
}
- public static void drawItemIndicator(Canvas canvas, RectF targetFrame, ResizingBehavior resizing, int lockColor, boolean isLocked, boolean isLimited) {
+ public static void drawItemIndicator(Canvas canvas, RectF targetFrame, ResizingBehavior resizing, int lockColor, boolean isLocked, boolean isLimited, boolean isDark) {
// General Declarations
Paint paint = CacheForItemIndicator.paint;
// Local Colors
int fillColor38 = Color.argb(255, 165, 161, 172);
+ int fillColor39 = Color.argb(255, 189, 168, 255);
int strokeColor2 = Color.argb(255, 189, 168, 255);
+ int strokeColor = Color.argb(255, 97, 51, 180);
+ int indicatorLockedDark = Color.argb(255, 82, 82, 82);
int shadowTint = Color.argb(255, 26, 24, 29);
int indicatorLocked = Color.argb(255, 237, 236, 238);
- int strokeColor = Color.argb(255, 97, 51, 180);
- int fillColor39 = Color.argb(255, 189, 168, 255);
// Local Shadows
PaintCodeShadow shadow = CacheForItemIndicator.shadow.get(PaintCodeColor.colorByChangingAlpha(shadowTint, (int) (Color.alpha(shadowTint) * 0.12f * 255f)), 0f, 1f, 1f);
// Local Variables
- int itemIndicatorColor = isLocked ? indicatorLocked : (isLimited ? strokeColor : fillColor38);
+ int itemIndicatorColor = isLocked ? (isDark ? indicatorLockedDark : indicatorLocked) : (isLimited ? strokeColor : fillColor38);
// Resize to Target Frame
canvas.save();
@@ -4112,7 +4122,7 @@ public class HabiticaIcons {
canvas.translate(symbolRect.left, symbolRect.top);
RectF symbolTargetRect = CacheForItemIndicatorNumber.symbolTargetRect;
symbolTargetRect.set(0f, 0f, symbolRect.width(), symbolRect.height());
- HabiticaIcons.drawItemIndicator(canvas, symbolTargetRect, ResizingBehavior.Stretch, lockColor, false, false);
+ HabiticaIcons.drawItemIndicator(canvas, symbolTargetRect, ResizingBehavior.Stretch, lockColor, false, false, true);
canvas.restore();
canvas.restore();
@@ -4145,7 +4155,7 @@ public class HabiticaIcons {
canvas.translate(symbolRect.left, symbolRect.top);
RectF symbolTargetRect = CacheForItemIndicatorLocked.symbolTargetRect;
symbolTargetRect.set(0f, 0f, symbolRect.width(), symbolRect.height());
- HabiticaIcons.drawItemIndicator(canvas, symbolTargetRect, ResizingBehavior.Stretch, lockColor, true, false);
+ HabiticaIcons.drawItemIndicator(canvas, symbolTargetRect, ResizingBehavior.Stretch, lockColor, true, false, false);
canvas.restore();
canvas.restore();
@@ -4178,7 +4188,7 @@ public class HabiticaIcons {
canvas.translate(symbolRect.left, symbolRect.top);
RectF symbolTargetRect = CacheForItemIndicatorLimited.symbolTargetRect;
symbolTargetRect.set(0f, 0f, symbolRect.width(), symbolRect.height());
- HabiticaIcons.drawItemIndicator(canvas, symbolTargetRect, ResizingBehavior.Stretch, lockColor, false, true);
+ HabiticaIcons.drawItemIndicator(canvas, symbolTargetRect, ResizingBehavior.Stretch, lockColor, false, true, false);
canvas.restore();
canvas.restore();
@@ -4360,13 +4370,13 @@ public class HabiticaIcons {
Paint paint = CacheForRogue.paint;
// Local Colors
- int fillColor46 = Color.argb(255, 122, 84, 192);
int fillColor48 = Color.argb(255, 137, 102, 199);
- int fillColor45 = Color.argb(255, 155, 129, 226);
int fillColor36 = Color.argb(255, 97, 51, 180);
+ int fillColor45 = Color.argb(255, 155, 129, 226);
int fillColor47 = Color.argb(255, 198, 182, 228);
int fillColor19 = Color.argb(255, 79, 42, 147);
int fillColor44 = Color.argb(255, 204, 190, 237);
+ int fillColor46 = Color.argb(255, 122, 84, 192);
// Local Variables
boolean lightBackground = !darkBackground;
@@ -5157,18 +5167,18 @@ public class HabiticaIcons {
Paint paint = CacheForHealer.paint;
// Local Colors
- int fillColor58 = Color.argb(255, 229, 144, 37);
- int fillColor50 = Color.argb(255, 226, 158, 69);
- int fillColor57 = Color.argb(255, 255, 221, 181);
- int fillColor49 = Color.argb(255, 255, 215, 168);
- int fillColor56 = Color.argb(255, 255, 166, 35);
- int fillColor55 = Color.argb(255, 253, 198, 126);
- int fillColor54 = Color.argb(255, 207, 130, 41);
int fillColor53 = Color.argb(255, 206, 129, 41);
- int fillColor10 = Color.argb(255, 255, 166, 35);
- int fillColor52 = Color.argb(255, 253, 198, 126);
- int fillColor59 = Color.argb(255, 255, 228, 201);
+ int fillColor57 = Color.argb(255, 255, 221, 181);
+ int fillColor58 = Color.argb(255, 229, 144, 37);
+ int fillColor54 = Color.argb(255, 207, 130, 41);
int fillColor51 = Color.argb(255, 229, 144, 37);
+ int fillColor50 = Color.argb(255, 226, 158, 69);
+ int fillColor59 = Color.argb(255, 255, 228, 201);
+ int fillColor55 = Color.argb(255, 253, 198, 126);
+ int fillColor56 = Color.argb(255, 255, 166, 35);
+ int fillColor49 = Color.argb(255, 255, 215, 168);
+ int fillColor52 = Color.argb(255, 253, 198, 126);
+ int fillColor10 = Color.argb(255, 255, 166, 35);
// Local Variables
boolean lightBackground = !darkBackground;
@@ -5995,16 +6005,16 @@ public class HabiticaIcons {
// Local Colors
int fillColor60 = Color.argb(255, 182, 225, 247);
- int fillColor64 = Color.argb(255, 77, 178, 214);
- int fillColor69 = Color.argb(255, 169, 219, 245);
- int fillColor61 = Color.argb(255, 39, 138, 191);
- int fillColor66 = Color.argb(255, 107, 196, 233);
- int fillColor63 = Color.argb(255, 42, 160, 207);
+ int fillColor62 = Color.argb(255, 83, 180, 229);
int fillColor68 = Color.argb(255, 31, 110, 162);
int fillColor65 = Color.argb(255, 107, 196, 233);
int fillColor70 = Color.argb(255, 132, 207, 242);
- int fillColor62 = Color.argb(255, 83, 180, 229);
+ int fillColor63 = Color.argb(255, 42, 160, 207);
+ int fillColor64 = Color.argb(255, 77, 178, 214);
int fillColor67 = Color.argb(255, 83, 180, 229);
+ int fillColor69 = Color.argb(255, 169, 219, 245);
+ int fillColor61 = Color.argb(255, 39, 138, 191);
+ int fillColor66 = Color.argb(255, 107, 196, 233);
// Local Variables
boolean lightBackground = !darkBackground;
@@ -6815,10 +6825,10 @@ public class HabiticaIcons {
Paint paint = CacheForAttributeSparklesLeft.paint;
// Local Colors
- int fillColor73 = Color.argb(255, 255, 180, 69);
int fillColor72 = Color.argb(255, 255, 97, 101);
int fillColor18 = Color.argb(255, 154, 98, 255);
int fillColor71 = Color.argb(255, 80, 181, 233);
+ int fillColor73 = Color.argb(255, 255, 180, 69);
// Resize to Target Frame
canvas.save();
@@ -6941,10 +6951,10 @@ public class HabiticaIcons {
Paint paint = CacheForAttributeSparklesRight.paint;
// Local Colors
- int fillColor18 = Color.argb(255, 154, 98, 255);
- int fillColor71 = Color.argb(255, 80, 181, 233);
int fillColor74 = Color.argb(255, 255, 190, 93);
+ int fillColor18 = Color.argb(255, 154, 98, 255);
int fillColor72 = Color.argb(255, 255, 97, 101);
+ int fillColor71 = Color.argb(255, 80, 181, 233);
// Resize to Target Frame
canvas.save();
@@ -7182,48 +7192,48 @@ public class HabiticaIcons {
private static Path bezier4Path = new Path();
}
- public static void drawContributorBadge(Canvas canvas, float contributorTier, boolean isNPC) {
- HabiticaIcons.drawContributorBadge(canvas, new RectF(0f, 0f, 16f, 16f), ResizingBehavior.AspectFit, contributorTier, isNPC);
+ public static void drawContributorBadge(Canvas canvas, float _1, boolean isNPC) {
+ HabiticaIcons.drawContributorBadge(canvas, new RectF(0f, 0f, 16f, 16f), ResizingBehavior.AspectFit, _1, isNPC);
}
- public static void drawContributorBadge(Canvas canvas, RectF targetFrame, ResizingBehavior resizing, float contributorTier, boolean isNPC) {
+ public static void drawContributorBadge(Canvas canvas, RectF targetFrame, ResizingBehavior resizing, float _1, boolean isNPC) {
// General Declarations
Stack currentTransformation = new Stack();
currentTransformation.push(new Matrix());
Paint paint = CacheForContributorBadge.paint;
// Local Colors
+ int fillColor81 = Color.argb(255, 94, 221, 233);
+ int fillColor79 = Color.argb(255, 255, 190, 93);
int strokeColor4 = Color.argb(255, 136, 10, 10);
int fillColor82 = Color.argb(255, 154, 98, 255);
int strokeColor8 = Color.argb(255, 28, 163, 114);
int strokeColor6 = Color.argb(255, 236, 109, 23);
int strokeColor12 = Color.argb(255, 0, 87, 55);
- int fillColor2 = Color.argb(255, 255, 97, 101);
int fillColor77 = Color.argb(255, 201, 43, 43);
- int fillColor7 = Color.argb(255, 41, 149, 205);
int strokeColor10 = Color.argb(255, 5, 112, 168);
int fillColor80 = Color.argb(255, 63, 218, 162);
int fillColor78 = Color.argb(255, 255, 148, 76);
int fillColor83 = Color.argb(255, 119, 244, 199);
int strokeColor3 = Color.argb(255, 253, 124, 127);
int strokeColor9 = Color.argb(255, 52, 181, 193);
+ int fillColor2 = Color.argb(255, 255, 97, 101);
int strokeColor7 = Color.argb(255, 238, 145, 9);
int strokeColor5 = Color.argb(255, 225, 22, 27);
+ int fillColor7 = Color.argb(255, 41, 149, 205);
int strokeColor11 = Color.argb(255, 106, 52, 204);
int fillColor76 = Color.argb(255, 255, 182, 184);
- int fillColor81 = Color.argb(255, 94, 221, 233);
- int fillColor79 = Color.argb(255, 255, 190, 93);
// Local Variables
- boolean isTier8 = contributorTier == 8f && !isNPC;
- boolean isTier4 = contributorTier == 4f && !isNPC;
- boolean isTier6 = contributorTier == 6f && !isNPC;
- boolean isTier3 = contributorTier == 3f && !isNPC;
- boolean isTier2 = contributorTier == 2f && !isNPC;
- boolean isTier7 = contributorTier == 7f && !isNPC;
- boolean isTier1 = contributorTier == 1f && !isNPC;
- boolean isTier5 = contributorTier == 5f && !isNPC;
- boolean isTier9 = contributorTier == 9f && !isNPC;
+ boolean isTier7 = _1 == 7f && !isNPC;
+ boolean isTier1 = _1 == 1f && !isNPC;
+ boolean isTier9 = _1 == 9f && !isNPC;
+ boolean isTier6 = _1 == 6f && !isNPC;
+ boolean isTier5 = _1 == 5f && !isNPC;
+ boolean isTier4 = _1 == 4f && !isNPC;
+ boolean isTier2 = _1 == 2f && !isNPC;
+ boolean isTier8 = _1 == 8f && !isNPC;
+ boolean isTier3 = _1 == 3f && !isNPC;
// Resize to Target Frame
canvas.save();
@@ -8064,17 +8074,17 @@ public class HabiticaIcons {
Paint paint = CacheForDamage.paint;
// Local Colors
- int fillColor25 = Color.argb(255, 225, 224, 227);
int fillColor90 = Color.argb(179, 78, 74, 87);
int fillColor89 = Color.argb(255, 227, 143, 61);
- int fillColor37 = Color.argb(255, 255, 255, 255);
int fillColor88 = Color.argb(255, 179, 98, 19);
- int fillColor87 = Color.argb(255, 189, 168, 255);
- int fillColor86 = Color.argb(255, 213, 200, 255);
- int fillColor40 = Color.argb(255, 165, 161, 172);
int fillColor35 = Color.argb(255, 195, 192, 199);
+ int fillColor25 = Color.argb(255, 225, 224, 227);
+ int fillColor40 = Color.argb(255, 165, 161, 172);
+ int fillColor86 = Color.argb(255, 213, 200, 255);
+ int fillColor87 = Color.argb(255, 189, 168, 255);
int fillColor75 = Color.argb(255, 135, 129, 144);
int fillColor85 = Color.argb(255, 237, 236, 238);
+ int fillColor37 = Color.argb(255, 255, 255, 255);
int fillColor84 = Color.argb(255, 240, 97, 102);
// Resize to Target Frame
@@ -8648,8 +8658,8 @@ public class HabiticaIcons {
Paint paint = CacheForRageStrikeInactive.paint;
// Local Colors
- int textForeground = Color.argb(255, 183, 90, 28);
int fillColor91 = Color.argb(255, 255, 200, 167);
+ int textForeground = Color.argb(255, 183, 90, 28);
int strokeColor14 = Color.argb(255, 255, 148, 76);
int strokeColor13 = Color.argb(255, 244, 120, 37);
@@ -8814,8 +8824,8 @@ public class HabiticaIcons {
Paint paint = CacheForRageStrikeActive.paint;
// Local Colors
- int strokeColor13 = Color.argb(255, 244, 120, 37);
int strokeColor14 = Color.argb(255, 255, 148, 76);
+ int strokeColor13 = Color.argb(255, 244, 120, 37);
// Local Images
PaintCodeBitmap pc_rageStrikeNPC = GlobalCache.rageStrikeNPC.get(rageStrikeNPC);
@@ -8953,12 +8963,12 @@ public class HabiticaIcons {
Paint paint = CacheForRage.paint;
// Local Colors
- int fillColor93 = Color.argb(255, 244, 120, 37);
- int fillColor14 = Color.argb(255, 36, 204, 143);
- int fillColor15 = Color.argb(89, 27, 153, 107);
- int fillColor92 = Color.argb(255, 90, 228, 178);
- int fillColor3 = Color.argb(128, 255, 255, 255);
int fillColor5 = Color.argb(64, 255, 255, 255);
+ int fillColor15 = Color.argb(89, 27, 153, 107);
+ int fillColor93 = Color.argb(255, 244, 120, 37);
+ int fillColor3 = Color.argb(128, 255, 255, 255);
+ int fillColor92 = Color.argb(255, 90, 228, 178);
+ int fillColor14 = Color.argb(255, 36, 204, 143);
int fillColor94 = Color.argb(89, 180, 89, 27);
// Resize to Target Frame
@@ -9268,9 +9278,9 @@ public class HabiticaIcons {
public static void drawTaskDifficultyStars(Canvas canvas, RectF targetFrame, ResizingBehavior resizing, int taskTintColor, float difficulty, boolean isActive) {
// Local Variables
boolean isTrivial = difficulty == 0.1f;
+ boolean isMedium = difficulty == 1.5f;
boolean isHard = difficulty == 2f;
boolean isEasy = difficulty == 1f;
- boolean isMedium = difficulty == 1.5f;
// Resize to Target Frame
canvas.save();
@@ -9424,8 +9434,8 @@ public class HabiticaIcons {
int strokeColor15 = Color.argb(255, 195, 192, 199);
// Local Variables
- int borderColor = isActive ? taskTintColor : strokeColor15;
int starFillColor = isActive ? taskTintColor : transparent;
+ int borderColor = isActive ? taskTintColor : strokeColor15;
// Resize to Target Frame
canvas.save();
@@ -9488,8 +9498,8 @@ public class HabiticaIcons {
Paint paint = CacheForHabitControlPlus.paint;
// Local Colors
- int strokeColor15 = Color.argb(255, 195, 192, 199);
int fillColor37 = Color.argb(255, 255, 255, 255);
+ int strokeColor15 = Color.argb(255, 195, 192, 199);
int transparent = Color.argb(0, 204, 82, 82);
// Local Variables
@@ -9576,8 +9586,8 @@ public class HabiticaIcons {
Paint paint = CacheForHabitControlMinus.paint;
// Local Colors
- int strokeColor15 = Color.argb(255, 195, 192, 199);
int fillColor37 = Color.argb(255, 255, 255, 255);
+ int strokeColor15 = Color.argb(255, 195, 192, 199);
int transparent = Color.argb(0, 204, 82, 82);
// Local Variables
@@ -9837,35 +9847,36 @@ public class HabiticaIcons {
Stack currentTransformation = new Stack();
currentTransformation.push(new Matrix());
Paint paint = CacheForGuildCrest.paint;
+ GlobalCache.initializeTypefaceBuilders(context.getAssets());
// Local Colors
- int crestSilver5 = Color.argb(255, 141, 141, 141);
- int crestBronze3 = Color.argb(255, 179, 98, 19);
- int crestGold5 = Color.argb(255, 223, 145, 30);
- int crestSilver6 = Color.argb(255, 141, 141, 141);
- int crestBronze2 = Color.argb(255, 244, 165, 89);
- int crestGold6 = Color.argb(255, 223, 145, 30);
- int crestSilver4 = Color.argb(255, 175, 175, 175);
- int crestBronze1 = Color.argb(255, 234, 140, 49);
- int crestGold4 = Color.argb(255, 255, 166, 35);
- int crestSilver3 = Color.argb(255, 141, 141, 141);
- int crestGold3 = Color.argb(255, 223, 145, 30);
- int crestSilver1 = Color.argb(255, 194, 194, 194);
- int crestGold2 = Color.argb(255, 255, 210, 145);
int crestBronze6 = Color.argb(255, 179, 98, 19);
int crestGold1 = Color.argb(255, 255, 188, 90);
- int crestBronze5 = Color.argb(255, 179, 98, 19);
- int crestSilver2 = Color.argb(255, 217, 217, 217);
+ int crestSilver6 = Color.argb(255, 141, 141, 141);
+ int crestSilver1 = Color.argb(255, 194, 194, 194);
+ int crestBronze3 = Color.argb(255, 179, 98, 19);
+ int crestSilver5 = Color.argb(255, 141, 141, 141);
int crestBronze4 = Color.argb(255, 215, 122, 32);
+ int crestGold4 = Color.argb(255, 255, 166, 35);
+ int crestGold2 = Color.argb(255, 255, 210, 145);
+ int crestBronze2 = Color.argb(255, 244, 165, 89);
+ int crestGold6 = Color.argb(255, 223, 145, 30);
+ int crestSilver3 = Color.argb(255, 141, 141, 141);
+ int crestSilver4 = Color.argb(255, 175, 175, 175);
+ int crestSilver2 = Color.argb(255, 217, 217, 217);
+ int crestBronze1 = Color.argb(255, 234, 140, 49);
+ int crestGold3 = Color.argb(255, 223, 145, 30);
+ int crestBronze5 = Color.argb(255, 179, 98, 19);
+ int crestGold5 = Color.argb(255, 223, 145, 30);
// Local Variables
boolean isPrivate = !isPublic;
- int crestColor1 = memberCount < 100f ? crestBronze1 : (memberCount < 1000f ? crestSilver1 : crestGold1);
+ int crestColor5 = memberCount < 100f ? crestBronze5 : (memberCount < 1000f ? crestSilver5 : crestGold5);
int crestColor4 = memberCount < 100f ? crestBronze4 : (memberCount < 1000f ? crestSilver4 : crestGold4);
+ int crestColor3 = memberCount < 100f ? crestBronze3 : (memberCount < 1000f ? crestSilver3 : crestGold3);
+ int crestColor1 = memberCount < 100f ? crestBronze1 : (memberCount < 1000f ? crestSilver1 : crestGold1);
int crestColor6 = memberCount < 100f ? crestBronze6 : (memberCount < 1000f ? crestSilver6 : crestGold6);
int crestColor2 = memberCount < 100f ? crestBronze2 : (memberCount < 1000f ? crestSilver2 : crestGold2);
- int crestColor3 = memberCount < 100f ? crestBronze3 : (memberCount < 1000f ? crestSilver3 : crestGold3);
- int crestColor5 = memberCount < 100f ? crestBronze5 : (memberCount < 1000f ? crestSilver5 : crestGold5);
// Resize to Target Frame
canvas.save();
@@ -10324,6 +10335,8 @@ public class HabiticaIcons {
labelTextPaint.reset();
labelTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
labelTextPaint.setColor(crestColor5);
+ GlobalCache.helveticaNeue.setTtcIndex(0);
+ labelTextPaint.setTypeface(GlobalCache.helveticaNeue.build());
labelTextPaint.setTextSize(12f);
StaticLayout labelStaticLayout = CacheForGuildCrest.labelStaticLayout.get((int) labelRect.width(), Layout.Alignment.ALIGN_CENTER, memberCountLabel, labelTextPaint);
canvas.save();
@@ -10366,28 +10379,28 @@ public class HabiticaIcons {
Paint paint = CacheForGuildCrestMedium.paint;
// Local Colors
- int crestBronze3 = Color.argb(255, 179, 98, 19);
- int crestSilver6 = Color.argb(255, 141, 141, 141);
- int crestBronze2 = Color.argb(255, 244, 165, 89);
- int crestGold6 = Color.argb(255, 223, 145, 30);
- int crestSilver4 = Color.argb(255, 175, 175, 175);
- int crestBronze1 = Color.argb(255, 234, 140, 49);
- int crestGold4 = Color.argb(255, 255, 166, 35);
- int crestSilver3 = Color.argb(255, 141, 141, 141);
- int crestGold3 = Color.argb(255, 223, 145, 30);
- int crestSilver1 = Color.argb(255, 194, 194, 194);
- int crestGold2 = Color.argb(255, 255, 210, 145);
int crestBronze6 = Color.argb(255, 179, 98, 19);
int crestGold1 = Color.argb(255, 255, 188, 90);
- int crestSilver2 = Color.argb(255, 217, 217, 217);
+ int crestSilver6 = Color.argb(255, 141, 141, 141);
+ int crestSilver1 = Color.argb(255, 194, 194, 194);
+ int crestBronze3 = Color.argb(255, 179, 98, 19);
int crestBronze4 = Color.argb(255, 215, 122, 32);
+ int crestGold4 = Color.argb(255, 255, 166, 35);
+ int crestGold2 = Color.argb(255, 255, 210, 145);
+ int crestBronze2 = Color.argb(255, 244, 165, 89);
+ int crestGold6 = Color.argb(255, 223, 145, 30);
+ int crestSilver3 = Color.argb(255, 141, 141, 141);
+ int crestSilver4 = Color.argb(255, 175, 175, 175);
+ int crestSilver2 = Color.argb(255, 217, 217, 217);
+ int crestBronze1 = Color.argb(255, 234, 140, 49);
+ int crestGold3 = Color.argb(255, 223, 145, 30);
// Local Variables
- int crestColor6 = memberCount < 100f ? crestBronze6 : (memberCount < 1000f ? crestSilver6 : crestGold6);
- int crestColor3 = memberCount < 100f ? crestBronze3 : (memberCount < 1000f ? crestSilver3 : crestGold3);
- int crestColor1 = memberCount < 100f ? crestBronze1 : (memberCount < 1000f ? crestSilver1 : crestGold1);
- int crestColor2 = memberCount < 100f ? crestBronze2 : (memberCount < 1000f ? crestSilver2 : crestGold2);
int crestColor4 = memberCount < 100f ? crestBronze4 : (memberCount < 1000f ? crestSilver4 : crestGold4);
+ int crestColor3 = memberCount < 100f ? crestBronze3 : (memberCount < 1000f ? crestSilver3 : crestGold3);
+ int crestColor2 = memberCount < 100f ? crestBronze2 : (memberCount < 1000f ? crestSilver2 : crestGold2);
+ int crestColor6 = memberCount < 100f ? crestBronze6 : (memberCount < 1000f ? crestSilver6 : crestGold6);
+ int crestColor1 = memberCount < 100f ? crestBronze1 : (memberCount < 1000f ? crestSilver1 : crestGold1);
// Resize to Target Frame
canvas.save();
@@ -10567,9 +10580,9 @@ public class HabiticaIcons {
Paint paint = CacheForGuildCrestSmall.paint;
// Local Colors
- int crestGold4 = Color.argb(255, 255, 166, 35);
int fillColor37 = Color.argb(255, 255, 255, 255);
int crestSilver4 = Color.argb(255, 175, 175, 175);
+ int crestGold4 = Color.argb(255, 255, 166, 35);
int crestBronze4 = Color.argb(255, 215, 122, 32);
// Local Variables
@@ -10682,13 +10695,13 @@ public class HabiticaIcons {
Paint paint = CacheForGuildHeaderCrest.paint;
// Local Colors
- int guildHeaderDark3 = PaintCodeColor.colorByChangingValue(guildHeader, 1f);
- int guildHeaderDark5 = PaintCodeColor.colorByApplyingHighlight(guildHeaderDark3, 0.7f);
- int guildHeaderDark4 = PaintCodeColor.colorByApplyingHighlight(guildHeaderDark3, 0.15f);
int guildHeaderDark2 = PaintCodeColor.colorByChangingValue(guildHeader, 0.7f);
- int guildHeaderDark6 = PaintCodeColor.colorByApplyingHighlight(guildHeaderDark2, 0.3f);
int guildHeaderDark8 = PaintCodeColor.colorByApplyingHighlight(guildHeaderDark2, 0.4f);
int guildHeaderDark7 = PaintCodeColor.colorByChangingValue(guildHeaderDark8, 1f);
+ int guildHeaderDark6 = PaintCodeColor.colorByApplyingHighlight(guildHeaderDark2, 0.3f);
+ int guildHeaderDark3 = PaintCodeColor.colorByChangingValue(guildHeader, 1f);
+ int guildHeaderDark4 = PaintCodeColor.colorByApplyingHighlight(guildHeaderDark3, 0.15f);
+ int guildHeaderDark5 = PaintCodeColor.colorByApplyingHighlight(guildHeaderDark3, 0.7f);
int textForeground2 = Color.argb(255, 255, 255, 255);
// Resize to Target Frame
@@ -11352,8 +11365,8 @@ public class HabiticaIcons {
Paint paint = CacheForBlankAvatarIcon.paint;
// Local Colors
- int fillColor40 = Color.argb(255, 165, 161, 172);
int fillColor101 = Color.argb(255, 247, 78, 82);
+ int fillColor40 = Color.argb(255, 165, 161, 172);
// Resize to Target Frame
canvas.save();
@@ -11627,9 +11640,9 @@ public class HabiticaIcons {
int strokeColor19 = Color.argb(255, 154, 98, 255);
// Local Variables
- float filterNode3Position = 3f + 5f * percentage;
float filterNode1Position = 3f + 10f * percentage;
float filterNode2Position = 13f - 10f * percentage;
+ float filterNode3Position = 3f + 5f * percentage;
// Resize to Target Frame
canvas.save();
@@ -11954,85 +11967,6 @@ public class HabiticaIcons {
canvas.restore();
}
-
- private static class CacheForBuffIconDark {
- private static Paint paint = new Paint();
- private static RectF originalFrame = new RectF(0f, 0f, 15f, 15f);
- private static RectF resizedFrame = new RectF();
- private static RectF rectangleRect = new RectF();
- private static Path rectanglePath = new Path();
- private static RectF bezier2Rect = new RectF();
- private static Path bezier2Path = new Path();
- }
-
- public static void drawBuffIconDark(Canvas canvas) {
- HabiticaIcons.drawBuffIconDark(canvas, new RectF(0f, 0f, 15f, 15f), ResizingBehavior.AspectFit);
- }
-
- public static void drawBuffIconDark(Canvas canvas, RectF targetFrame, ResizingBehavior resizing) {
- // General Declarations
- Paint paint = CacheForBuffIcon.paint;
-
- // Local Colors
- int fillColor18 = Color.argb(255, 185, 172, 252);
- int fillColor85 = Color.argb(255, 37, 29, 60);
-
- // Resize to Target Frame
- canvas.save();
- RectF resizedFrame = CacheForBuffIcon.resizedFrame;
- HabiticaIcons.resizingBehaviorApply(resizing, CacheForBuffIcon.originalFrame, targetFrame, resizedFrame);
- canvas.translate(resizedFrame.left, resizedFrame.top);
- canvas.scale(resizedFrame.width() / 15f, resizedFrame.height() / 15f);
-
- // Rectangle
- RectF rectangleRect = CacheForBuffIcon.rectangleRect;
- rectangleRect.set(0f, 0f, 15f, 15f);
- Path rectanglePath = CacheForBuffIcon.rectanglePath;
- rectanglePath.reset();
- rectanglePath.addRoundRect(rectangleRect, 2f, 2f, Path.Direction.CW);
-
- paint.reset();
- paint.setFlags(Paint.ANTI_ALIAS_FLAG);
- paint.setStyle(Paint.Style.FILL);
- paint.setColor(fillColor85);
- canvas.drawPath(rectanglePath, paint);
-
- // Bezier 2
- RectF bezier2Rect = CacheForBuffIcon.bezier2Rect;
- bezier2Rect.set(3.75f, 3f, 11.25f, 12f);
- Path bezier2Path = CacheForBuffIcon.bezier2Path;
- bezier2Path.reset();
- bezier2Path.moveTo(7.5f, 3f);
- bezier2Path.lineTo(11.25f, 7.5f);
- bezier2Path.lineTo(9f, 7.5f);
- bezier2Path.lineTo(9f, 9f);
- bezier2Path.lineTo(6f, 9f);
- bezier2Path.lineTo(6f, 7.5f);
- bezier2Path.lineTo(3.75f, 7.5f);
- bezier2Path.lineTo(7.5f, 3f);
- bezier2Path.close();
- bezier2Path.moveTo(6f, 9.75f);
- bezier2Path.lineTo(9f, 9.75f);
- bezier2Path.lineTo(9f, 10.5f);
- bezier2Path.lineTo(6f, 10.5f);
- bezier2Path.lineTo(6f, 9.75f);
- bezier2Path.close();
- bezier2Path.moveTo(6f, 11.25f);
- bezier2Path.lineTo(9f, 11.25f);
- bezier2Path.lineTo(9f, 12f);
- bezier2Path.lineTo(6f, 12f);
- bezier2Path.lineTo(6f, 11.25f);
- bezier2Path.close();
-
- paint.reset();
- paint.setFlags(Paint.ANTI_ALIAS_FLAG);
- bezier2Path.setFillType(Path.FillType.EVEN_ODD);
- paint.setStyle(Paint.Style.FILL);
- paint.setColor(fillColor18);
- canvas.drawPath(bezier2Path, paint);
-
- canvas.restore();
- }
private static class CacheForParticipantsIcon {
private static Paint paint = new Paint();
@@ -12055,9 +11989,9 @@ public class HabiticaIcons {
Paint paint = CacheForParticipantsIcon.paint;
// Local Colors
- int fillColor25 = Color.argb(255, 225, 224, 227);
- int fillColor39 = Color.argb(255, 189, 168, 255);
int fillColor104 = Color.argb(255, 249, 249, 249);
+ int fillColor39 = Color.argb(255, 189, 168, 255);
+ int fillColor25 = Color.argb(255, 225, 224, 227);
// Resize to Target Frame
canvas.save();
@@ -12567,6 +12501,449 @@ public class HabiticaIcons {
canvas.restore();
}
+ private static class CacheForItemIndicatorLockedDark {
+ private static RectF originalFrame = new RectF(0f, 0f, 28f, 28f);
+ private static RectF resizedFrame = new RectF();
+ private static RectF symbolRect = new RectF();
+ private static RectF symbolTargetRect = new RectF();
+ }
+
+ private static void drawItemIndicatorLockedDark(Canvas canvas, int lockColor) {
+ HabiticaIcons.drawItemIndicatorLockedDark(canvas, new RectF(0f, 0f, 28f, 28f), ResizingBehavior.AspectFit, lockColor);
+ }
+
+ private static void drawItemIndicatorLockedDark(Canvas canvas, RectF targetFrame, ResizingBehavior resizing, int lockColor) {
+ // Resize to Target Frame
+ canvas.save();
+ RectF resizedFrame = CacheForItemIndicatorLockedDark.resizedFrame;
+ HabiticaIcons.resizingBehaviorApply(resizing, CacheForItemIndicatorLockedDark.originalFrame, targetFrame, resizedFrame);
+ canvas.translate(resizedFrame.left, resizedFrame.top);
+ canvas.scale(resizedFrame.width() / 28f, resizedFrame.height() / 28f);
+
+ // Symbol
+ RectF symbolRect = CacheForItemIndicatorLockedDark.symbolRect;
+ symbolRect.set(0f, 0f, 28f, 28f);
+ canvas.save();
+ canvas.clipRect(symbolRect);
+ canvas.translate(symbolRect.left, symbolRect.top);
+ RectF symbolTargetRect = CacheForItemIndicatorLockedDark.symbolTargetRect;
+ symbolTargetRect.set(0f, 0f, symbolRect.width(), symbolRect.height());
+ HabiticaIcons.drawItemIndicator(canvas, symbolTargetRect, ResizingBehavior.Stretch, lockColor, true, false, true);
+ canvas.restore();
+
+ canvas.restore();
+ }
+
+ private static class CacheForBuffIconDark {
+ private static Paint paint = new Paint();
+ private static RectF originalFrame = new RectF(0f, 0f, 15f, 15f);
+ private static RectF resizedFrame = new RectF();
+ private static RectF rectangleRect = new RectF();
+ private static Path rectanglePath = new Path();
+ private static RectF bezier2Rect = new RectF();
+ private static Path bezier2Path = new Path();
+ }
+
+ public static void drawBuffIconDark(Canvas canvas) {
+ HabiticaIcons.drawBuffIconDark(canvas, new RectF(0f, 0f, 15f, 15f), ResizingBehavior.AspectFit);
+ }
+
+ public static void drawBuffIconDark(Canvas canvas, RectF targetFrame, ResizingBehavior resizing) {
+ // General Declarations
+ Paint paint = CacheForBuffIconDark.paint;
+
+ // Local Colors
+ int fillColor39 = Color.argb(255, 189, 168, 255);
+ int buffBackgroundColorDark = Color.argb(255, 70, 40, 125);
+
+ // Resize to Target Frame
+ canvas.save();
+ RectF resizedFrame = CacheForBuffIconDark.resizedFrame;
+ HabiticaIcons.resizingBehaviorApply(resizing, CacheForBuffIconDark.originalFrame, targetFrame, resizedFrame);
+ canvas.translate(resizedFrame.left, resizedFrame.top);
+ canvas.scale(resizedFrame.width() / 15f, resizedFrame.height() / 15f);
+
+ // Rectangle
+ RectF rectangleRect = CacheForBuffIconDark.rectangleRect;
+ rectangleRect.set(0f, 0f, 15f, 15f);
+ Path rectanglePath = CacheForBuffIconDark.rectanglePath;
+ rectanglePath.reset();
+ rectanglePath.addRoundRect(rectangleRect, 2f, 2f, Path.Direction.CW);
+
+ paint.reset();
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ paint.setStyle(Paint.Style.FILL);
+ paint.setColor(buffBackgroundColorDark);
+ canvas.drawPath(rectanglePath, paint);
+
+ // Bezier 2
+ RectF bezier2Rect = CacheForBuffIconDark.bezier2Rect;
+ bezier2Rect.set(3.75f, 3f, 11.25f, 12f);
+ Path bezier2Path = CacheForBuffIconDark.bezier2Path;
+ bezier2Path.reset();
+ bezier2Path.moveTo(7.5f, 3f);
+ bezier2Path.lineTo(11.25f, 7.5f);
+ bezier2Path.lineTo(9f, 7.5f);
+ bezier2Path.lineTo(9f, 9f);
+ bezier2Path.lineTo(6f, 9f);
+ bezier2Path.lineTo(6f, 7.5f);
+ bezier2Path.lineTo(3.75f, 7.5f);
+ bezier2Path.lineTo(7.5f, 3f);
+ bezier2Path.close();
+ bezier2Path.moveTo(6f, 9.75f);
+ bezier2Path.lineTo(9f, 9.75f);
+ bezier2Path.lineTo(9f, 10.5f);
+ bezier2Path.lineTo(6f, 10.5f);
+ bezier2Path.lineTo(6f, 9.75f);
+ bezier2Path.close();
+ bezier2Path.moveTo(6f, 11.25f);
+ bezier2Path.lineTo(9f, 11.25f);
+ bezier2Path.lineTo(9f, 12f);
+ bezier2Path.lineTo(6f, 12f);
+ bezier2Path.lineTo(6f, 11.25f);
+ bezier2Path.close();
+
+ paint.reset();
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ bezier2Path.setFillType(Path.FillType.EVEN_ODD);
+ paint.setStyle(Paint.Style.FILL);
+ paint.setColor(fillColor39);
+ canvas.drawPath(bezier2Path, paint);
+
+ canvas.restore();
+ }
+
+ private static class CacheForStats {
+ private static Paint paint = new Paint();
+ private static RectF originalFrame = new RectF(0f, 0f, 30f, 22f);
+ private static RectF resizedFrame = new RectF();
+ private static RectF bezierRect = new RectF();
+ private static Path bezierPath = new Path();
+ private static RectF group = new RectF();
+ private static Path clipPath = new Path();
+ private static RectF bezier2Rect = new RectF();
+ private static Path bezier2Path = new Path();
+ private static RectF rectangleRect = new RectF();
+ private static Path rectanglePath = new Path();
+ private static RectF rectangle2Rect = new RectF();
+ private static Path rectangle2Path = new Path();
+ private static RectF rectangle3Rect = new RectF();
+ private static Path rectangle3Path = new Path();
+ private static RectF rectangle4Rect = new RectF();
+ private static Path rectangle4Path = new Path();
+ private static RectF rectangle5Rect = new RectF();
+ private static Path rectangle5Path = new Path();
+ private static RectF rectangle6Rect = new RectF();
+ private static Path rectangle6Path = new Path();
+ private static RectF rectangle7Rect = new RectF();
+ private static Path rectangle7Path = new Path();
+ private static RectF rectangle8Rect = new RectF();
+ private static Path rectangle8Path = new Path();
+ private static RectF rectangle9Rect = new RectF();
+ private static Path rectangle9Path = new Path();
+ private static RectF rectangle10Rect = new RectF();
+ private static Path rectangle10Path = new Path();
+ private static RectF rectangle11Rect = new RectF();
+ private static Path rectangle11Path = new Path();
+ }
+
+ public static void drawStats(Canvas canvas) {
+ HabiticaIcons.drawStats(canvas, new RectF(0f, 0f, 30f, 22f), ResizingBehavior.AspectFit);
+ }
+
+ public static void drawStats(Canvas canvas, RectF targetFrame, ResizingBehavior resizing) {
+ // General Declarations
+ Paint paint = CacheForStats.paint;
+
+ // Local Colors
+ int fillColor89 = Color.argb(255, 227, 143, 61);
+ int strokeColor21 = Color.argb(255, 135, 129, 144);
+ int fillColor25 = Color.argb(255, 225, 224, 227);
+ int fillColor106 = Color.argb(255, 216, 216, 216);
+ int fillColor107 = Color.argb(255, 179, 98, 19);
+ int fillColor35 = Color.argb(255, 195, 192, 199);
+ int fillColor40 = Color.argb(255, 165, 161, 172);
+
+ // Resize to Target Frame
+ canvas.save();
+ RectF resizedFrame = CacheForStats.resizedFrame;
+ HabiticaIcons.resizingBehaviorApply(resizing, CacheForStats.originalFrame, targetFrame, resizedFrame);
+ canvas.translate(resizedFrame.left, resizedFrame.top);
+ canvas.scale(resizedFrame.width() / 30f, resizedFrame.height() / 22f);
+
+ // Bezier
+ RectF bezierRect = CacheForStats.bezierRect;
+ bezierRect.set(1.75f, 1.75f, 28.25f, 20.25f);
+ Path bezierPath = CacheForStats.bezierPath;
+ bezierPath.reset();
+ bezierPath.moveTo(11.25f, 19f);
+ bezierPath.lineTo(10f, 20.25f);
+ bezierPath.lineTo(6f, 20.25f);
+ bezierPath.lineTo(4.75f, 19f);
+ bezierPath.lineTo(4.75f, 17.25f);
+ bezierPath.lineTo(3f, 17.25f);
+ bezierPath.lineTo(1.75f, 16f);
+ bezierPath.lineTo(1.75f, 6f);
+ bezierPath.lineTo(3f, 4.75f);
+ bezierPath.lineTo(4.75f, 4.75f);
+ bezierPath.lineTo(4.75f, 3f);
+ bezierPath.lineTo(6f, 1.75f);
+ bezierPath.lineTo(10f, 1.75f);
+ bezierPath.lineTo(11.25f, 3f);
+ bezierPath.lineTo(11.25f, 7.75f);
+ bezierPath.lineTo(18.75f, 7.75f);
+ bezierPath.lineTo(18.75f, 3f);
+ bezierPath.lineTo(20f, 1.75f);
+ bezierPath.lineTo(24f, 1.75f);
+ bezierPath.lineTo(25.25f, 3f);
+ bezierPath.lineTo(25.25f, 4.75f);
+ bezierPath.lineTo(27f, 4.75f);
+ bezierPath.lineTo(28.25f, 6f);
+ bezierPath.lineTo(28.25f, 16f);
+ bezierPath.lineTo(27f, 17.25f);
+ bezierPath.lineTo(25.25f, 17.25f);
+ bezierPath.lineTo(25.25f, 19f);
+ bezierPath.lineTo(24f, 20.25f);
+ bezierPath.lineTo(20f, 20.25f);
+ bezierPath.lineTo(18.75f, 19f);
+ bezierPath.lineTo(18.75f, 14.25f);
+ bezierPath.lineTo(11.25f, 14.25f);
+ bezierPath.lineTo(11.25f, 19f);
+ bezierPath.close();
+
+ paint.reset();
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ bezierPath.setFillType(Path.FillType.EVEN_ODD);
+ paint.setStyle(Paint.Style.FILL);
+ paint.setColor(fillColor106);
+ canvas.drawPath(bezierPath, paint);
+
+ // Group
+ {
+ RectF group = CacheForStats.group;
+ group.set(-22f, -22f, 52f, 44f);
+ canvas.save();
+
+ // Clip
+ Path clipPath = CacheForStats.clipPath;
+ clipPath.reset();
+ clipPath.moveTo(-22f, -22f);
+ clipPath.lineTo(52f, -22f);
+ clipPath.lineTo(52f, 44f);
+ clipPath.lineTo(-22f, 44f);
+ clipPath.lineTo(-22f, -22f);
+ clipPath.close();
+ clipPath.moveTo(10f, 19f);
+ clipPath.lineTo(6f, 19f);
+ clipPath.lineTo(6f, 16f);
+ clipPath.lineTo(3f, 16f);
+ clipPath.lineTo(3f, 6f);
+ clipPath.lineTo(6f, 6f);
+ clipPath.lineTo(6f, 3f);
+ clipPath.lineTo(10f, 3f);
+ clipPath.lineTo(10f, 9f);
+ clipPath.lineTo(20f, 9f);
+ clipPath.lineTo(20f, 3f);
+ clipPath.lineTo(24f, 3f);
+ clipPath.lineTo(24f, 6f);
+ clipPath.lineTo(27f, 6f);
+ clipPath.lineTo(27f, 16f);
+ clipPath.lineTo(24f, 16f);
+ clipPath.lineTo(24f, 19f);
+ clipPath.lineTo(20f, 19f);
+ clipPath.lineTo(20f, 13f);
+ clipPath.lineTo(10f, 13f);
+ clipPath.lineTo(10f, 19f);
+ clipPath.close();
+ canvas.clipPath(clipPath);
+
+ // Bezier 2
+ RectF bezier2Rect = CacheForStats.bezier2Rect;
+ bezier2Rect.set(3f, 3f, 27f, 19f);
+ Path bezier2Path = CacheForStats.bezier2Path;
+ bezier2Path.reset();
+ bezier2Path.moveTo(10f, 19f);
+ bezier2Path.lineTo(6f, 19f);
+ bezier2Path.lineTo(6f, 16f);
+ bezier2Path.lineTo(3f, 16f);
+ bezier2Path.lineTo(3f, 6f);
+ bezier2Path.lineTo(6f, 6f);
+ bezier2Path.lineTo(6f, 3f);
+ bezier2Path.lineTo(10f, 3f);
+ bezier2Path.lineTo(10f, 9f);
+ bezier2Path.lineTo(20f, 9f);
+ bezier2Path.lineTo(20f, 3f);
+ bezier2Path.lineTo(24f, 3f);
+ bezier2Path.lineTo(24f, 6f);
+ bezier2Path.lineTo(27f, 6f);
+ bezier2Path.lineTo(27f, 16f);
+ bezier2Path.lineTo(24f, 16f);
+ bezier2Path.lineTo(24f, 19f);
+ bezier2Path.lineTo(20f, 19f);
+ bezier2Path.lineTo(20f, 13f);
+ bezier2Path.lineTo(10f, 13f);
+ bezier2Path.lineTo(10f, 19f);
+ bezier2Path.close();
+
+ paint.reset();
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ paint.setStrokeWidth(5f);
+ paint.setStrokeMiter(10f);
+ canvas.save();
+ paint.setStyle(Paint.Style.STROKE);
+ paint.setColor(strokeColor21);
+ canvas.drawPath(bezier2Path, paint);
+ canvas.restore();
+
+ canvas.restore();
+ }
+
+ // Rectangle
+ RectF rectangleRect = CacheForStats.rectangleRect;
+ rectangleRect.set(3f, 6f, 6f, 16f);
+ Path rectanglePath = CacheForStats.rectanglePath;
+ rectanglePath.reset();
+ rectanglePath.addRect(rectangleRect, Path.Direction.CW);
+
+ paint.reset();
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ paint.setStyle(Paint.Style.FILL);
+ paint.setColor(fillColor40);
+ canvas.drawPath(rectanglePath, paint);
+
+ // Rectangle 2
+ RectF rectangle2Rect = CacheForStats.rectangle2Rect;
+ rectangle2Rect.set(24f, 6f, 27f, 16f);
+ Path rectangle2Path = CacheForStats.rectangle2Path;
+ rectangle2Path.reset();
+ rectangle2Path.addRect(rectangle2Rect, Path.Direction.CW);
+
+ paint.reset();
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ paint.setStyle(Paint.Style.FILL);
+ paint.setColor(fillColor40);
+ canvas.drawPath(rectangle2Path, paint);
+
+ // Rectangle 3
+ RectF rectangle3Rect = CacheForStats.rectangle3Rect;
+ rectangle3Rect.set(10f, 9f, 20f, 13f);
+ Path rectangle3Path = CacheForStats.rectangle3Path;
+ rectangle3Path.reset();
+ rectangle3Path.addRect(rectangle3Rect, Path.Direction.CW);
+
+ paint.reset();
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ paint.setStyle(Paint.Style.FILL);
+ paint.setColor(fillColor107);
+ canvas.drawPath(rectangle3Path, paint);
+
+ // Rectangle 4
+ RectF rectangle4Rect = CacheForStats.rectangle4Rect;
+ rectangle4Rect.set(12f, 9f, 14f, 13f);
+ Path rectangle4Path = CacheForStats.rectangle4Path;
+ rectangle4Path.reset();
+ rectangle4Path.addRect(rectangle4Rect, Path.Direction.CW);
+
+ paint.reset();
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ paint.setStyle(Paint.Style.FILL);
+ paint.setColor(fillColor89);
+ canvas.drawPath(rectangle4Path, paint);
+
+ // Rectangle 5
+ RectF rectangle5Rect = CacheForStats.rectangle5Rect;
+ rectangle5Rect.set(16f, 9f, 18f, 13f);
+ Path rectangle5Path = CacheForStats.rectangle5Path;
+ rectangle5Path.reset();
+ rectangle5Path.addRect(rectangle5Rect, Path.Direction.CW);
+
+ paint.reset();
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ paint.setStyle(Paint.Style.FILL);
+ paint.setColor(fillColor89);
+ canvas.drawPath(rectangle5Path, paint);
+
+ // Rectangle 6
+ RectF rectangle6Rect = CacheForStats.rectangle6Rect;
+ rectangle6Rect.set(6f, 3f, 10f, 19f);
+ Path rectangle6Path = CacheForStats.rectangle6Path;
+ rectangle6Path.reset();
+ rectangle6Path.addRect(rectangle6Rect, Path.Direction.CW);
+
+ paint.reset();
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ paint.setStyle(Paint.Style.FILL);
+ paint.setColor(fillColor35);
+ canvas.drawPath(rectangle6Path, paint);
+
+ // Rectangle 7
+ RectF rectangle7Rect = CacheForStats.rectangle7Rect;
+ rectangle7Rect.set(20f, 3f, 24f, 19f);
+ Path rectangle7Path = CacheForStats.rectangle7Path;
+ rectangle7Path.reset();
+ rectangle7Path.addRect(rectangle7Rect, Path.Direction.CW);
+
+ paint.reset();
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ paint.setStyle(Paint.Style.FILL);
+ paint.setColor(fillColor35);
+ canvas.drawPath(rectangle7Path, paint);
+
+ // Rectangle 8
+ RectF rectangle8Rect = CacheForStats.rectangle8Rect;
+ rectangle8Rect.set(6f, 3f, 10f, 11f);
+ Path rectangle8Path = CacheForStats.rectangle8Path;
+ rectangle8Path.reset();
+ rectangle8Path.addRect(rectangle8Rect, Path.Direction.CW);
+
+ paint.reset();
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ paint.setStyle(Paint.Style.FILL);
+ paint.setColor(fillColor25);
+ canvas.drawPath(rectangle8Path, paint);
+
+ // Rectangle 9
+ RectF rectangle9Rect = CacheForStats.rectangle9Rect;
+ rectangle9Rect.set(3f, 6f, 6f, 11f);
+ Path rectangle9Path = CacheForStats.rectangle9Path;
+ rectangle9Path.reset();
+ rectangle9Path.addRect(rectangle9Rect, Path.Direction.CW);
+
+ paint.reset();
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ paint.setStyle(Paint.Style.FILL);
+ paint.setColor(fillColor35);
+ canvas.drawPath(rectangle9Path, paint);
+
+ // Rectangle 10
+ RectF rectangle10Rect = CacheForStats.rectangle10Rect;
+ rectangle10Rect.set(24f, 6f, 27f, 11f);
+ Path rectangle10Path = CacheForStats.rectangle10Path;
+ rectangle10Path.reset();
+ rectangle10Path.addRect(rectangle10Rect, Path.Direction.CW);
+
+ paint.reset();
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ paint.setStyle(Paint.Style.FILL);
+ paint.setColor(fillColor35);
+ canvas.drawPath(rectangle10Path, paint);
+
+ // Rectangle 11
+ RectF rectangle11Rect = CacheForStats.rectangle11Rect;
+ rectangle11Rect.set(20f, 3f, 24f, 11f);
+ Path rectangle11Path = CacheForStats.rectangle11Path;
+ rectangle11Path.reset();
+ rectangle11Path.addRect(rectangle11Rect, Path.Direction.CW);
+
+ paint.reset();
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ paint.setStyle(Paint.Style.FILL);
+ paint.setColor(fillColor25);
+ canvas.drawPath(rectangle11Path, paint);
+
+ canvas.restore();
+ }
+
// Canvas Images
// Tab
@@ -12967,10 +13344,10 @@ public class HabiticaIcons {
return imageOfInfoIcon;
}
- public static Bitmap imageOfContributorBadge(float contributorTier, boolean isNPC) {
+ public static Bitmap imageOfContributorBadge(float _1, boolean isNPC) {
Bitmap imageOfContributorBadge = Bitmap.createBitmap(16, 16, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(imageOfContributorBadge);
- HabiticaIcons.drawContributorBadge(canvas, contributorTier, isNPC);
+ HabiticaIcons.drawContributorBadge(canvas, _1, isNPC);
return imageOfContributorBadge;
}
@@ -13199,6 +13576,26 @@ public class HabiticaIcons {
return imageOfAlertIcon;
}
+ public static Bitmap imageOfItemIndicatorLockedDark(int lockColor) {
+ Bitmap imageOfItemIndicatorLockedDark = Bitmap.createBitmap(28, 28, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(imageOfItemIndicatorLockedDark);
+ HabiticaIcons.drawItemIndicatorLockedDark(canvas, lockColor);
+
+ return imageOfItemIndicatorLockedDark;
+ }
+
+ private static Bitmap imageOfBuffIconDark = null;
+ public static Bitmap imageOfBuffIconDark() {
+ if (imageOfBuffIconDark != null)
+ return imageOfBuffIconDark;
+
+ imageOfBuffIconDark = Bitmap.createBitmap(15, 15, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(imageOfBuffIconDark);
+ HabiticaIcons.drawBuffIconDark(canvas);
+
+ return imageOfBuffIconDark;
+ }
+
// Resizing Behavior
public static void resizingBehaviorApply(ResizingBehavior behavior, RectF rect, RectF target, RectF result) {
@@ -13244,25 +13641,25 @@ public class HabiticaIcons {
class PaintCodeColor extends Color {
private static float[] ColorToHSV(int originalColor) {
- float[] hsv = new float[3];
+ float hsv[] = new float[3];
RGBToHSV(red(originalColor), green(originalColor), blue(originalColor), hsv);
return hsv;
}
public static int colorByChangingHue(int originalColor, float newHue) {
- float[] hsv = ColorToHSV(originalColor);
+ float hsv[] = ColorToHSV(originalColor);
hsv[0] = newHue;
return HSVToColor(alpha(originalColor), hsv);
}
public static int colorByChangingSaturation(int originalColor, float newSaturation) {
- float[] hsv = ColorToHSV(originalColor);
+ float hsv[] = ColorToHSV(originalColor);
hsv[1] = newSaturation;
return HSVToColor(alpha(originalColor), hsv);
}
public static int colorByChangingValue(int originalColor, float newValue) {
- float[] hsv = ColorToHSV(originalColor);
+ float hsv[] = ColorToHSV(originalColor);
hsv[2] = newValue;
return HSVToColor(alpha(originalColor), hsv);
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaIconsHelper.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaIconsHelper.java
index 79889cb5d..16ac411a9 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaIconsHelper.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaIconsHelper.java
@@ -51,6 +51,19 @@ public class HabiticaIconsHelper {
return imageOfMagic;
}
+ private static Bitmap imageOfMagicLarge = null;
+ public static Bitmap imageOfMagicLarge() {
+ if (imageOfMagicLarge != null)
+ return imageOfMagicLarge;
+
+ int size = scaleSize(36);
+ imageOfMagicLarge = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(imageOfMagicLarge);
+ HabiticaIcons.drawMagic(canvas, new RectF(0f, 0f, size, size), HabiticaIcons.ResizingBehavior.AspectFit);
+
+ return imageOfMagicLarge;
+ }
+
private static Bitmap imageOfGold = null;
public static Bitmap imageOfGold() {
if (imageOfGold != null)
@@ -93,6 +106,19 @@ public class HabiticaIconsHelper {
return imageOfHourglass;
}
+ private static Bitmap imageOfHourglassLarge = null;
+ public static Bitmap imageOfHourglassLarge() {
+ if (imageOfHourglassLarge != null)
+ return imageOfHourglassLarge;
+
+ int size = scaleSize(40);
+ imageOfHourglassLarge = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(imageOfHourglassLarge);
+ HabiticaIcons.drawHourglass(canvas, new RectF(0f, 0f, size, size), HabiticaIcons.ResizingBehavior.AspectFit);
+
+ return imageOfHourglassLarge;
+ }
+
private static Bitmap imageOfExperienceReward = null;
public static Bitmap imageOfExperienceReward() {
if (imageOfExperienceReward != null)
@@ -155,8 +181,7 @@ public class HabiticaIconsHelper {
int size = scaleSize(36);
imageOfHeartLarge = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(imageOfHeartLarge);
- canvas.scale(displayDensity, displayDensity);
- HabiticaIcons.drawHeart(canvas, false);
+ HabiticaIcons.drawHeart(canvas, new RectF(0f, 0f, size, size), HabiticaIcons.ResizingBehavior.AspectFit, false);
return imageOfHeartLarge;
}
@@ -263,7 +288,7 @@ public class HabiticaIconsHelper {
imageOfItemIndicatorNumber = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(imageOfItemIndicatorNumber);
canvas.scale(displayDensity, displayDensity);
- HabiticaIcons.drawItemIndicator(canvas, Color.parseColor("#C3C0C7"), false, false);
+ HabiticaIcons.drawItemIndicator(canvas, Color.parseColor("#C3C0C7"), false, false, false);
return imageOfItemIndicatorNumber;
}
@@ -277,7 +302,7 @@ public class HabiticaIconsHelper {
imageOfItemIndicatorLocked = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(imageOfItemIndicatorLocked);
canvas.scale(displayDensity, displayDensity);
- HabiticaIcons.drawItemIndicator(canvas, Color.parseColor("#C3C0C7"), true, false);
+ HabiticaIcons.drawItemIndicator(canvas, Color.parseColor("#C3C0C7"), true, false, false);
return imageOfItemIndicatorLocked;
}
@@ -290,7 +315,7 @@ public class HabiticaIconsHelper {
imageOfItemIndicatorLimited = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(imageOfItemIndicatorLimited);
canvas.scale(displayDensity, displayDensity);
- HabiticaIcons.drawItemIndicator(canvas, Color.parseColor("#C3C0C7"), false, true);
+ HabiticaIcons.drawItemIndicator(canvas, Color.parseColor("#C3C0C7"), false, true, false);
return imageOfItemIndicatorLimited;
}
@@ -750,4 +775,15 @@ public class HabiticaIconsHelper {
return imageOfHabitControlMinus;
}
+
+ public static Bitmap imageOfStats() {
+ int width = scaleSize(30);
+ int height = scaleSize(30);
+ Bitmap imageOfHabitControlMinus = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(imageOfHabitControlMinus);
+ canvas.scale(displayDensity, displayDensity);
+ HabiticaIcons.drawStats(canvas);
+
+ return imageOfHabitControlMinus;
+ }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/SupportCollapsibleSection.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/SupportCollapsibleSection.kt
new file mode 100644
index 000000000..b960feebd
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/SupportCollapsibleSection.kt
@@ -0,0 +1,52 @@
+package com.habitrpg.android.habitica.ui.views
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import android.widget.LinearLayout
+import androidx.core.content.ContextCompat
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.databinding.SupportCollapsibleSectionBinding
+import com.habitrpg.android.habitica.extensions.layoutInflater
+import com.habitrpg.android.habitica.ui.helpers.MarkdownParser
+
+class SupportCollapsibleSection : LinearLayout {
+ constructor(context: Context) : super(context) {
+ init(null, 0)
+ }
+
+ constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
+ init(attrs, 0)
+ }
+
+ constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
+ init(attrs, defStyle)
+ }
+
+ private fun init(attrs: AttributeSet?, defStyle: Int) {
+ val binding = SupportCollapsibleSectionBinding.inflate(context.layoutInflater, this)
+ val a = context.obtainStyledAttributes(
+ attrs, R.styleable.SupportCollapsibleSection, defStyle, 0)
+
+ orientation = VERTICAL
+
+ binding.titleView.text = a.getString(R.styleable.SupportCollapsibleSection_title)
+ binding.subtitleView.text = a.getString(R.styleable.SupportCollapsibleSection_subtitle)
+ binding.descriptionView.text = MarkdownParser.parseMarkdown(a.getString(R.styleable.SupportCollapsibleSection_description))
+ binding.titleView.setTextColor(a.getColor(R.styleable.SupportCollapsibleSection_titleColor, ContextCompat.getColor(context, R.color.gray_50)))
+
+ background = context.getDrawable(R.drawable.layout_rounded_bg_gray_700)
+
+ a.recycle()
+
+ setOnClickListener {
+ binding.descriptionView.visibility = if (binding.descriptionView.visibility == View.VISIBLE){
+ binding.caretView.setImageResource(R.drawable.ic_keyboard_arrow_down_black_24dp)
+ View.GONE
+ } else{
+ binding.caretView.setImageResource(R.drawable.ic_keyboard_arrow_up_black_24dp)
+ View.VISIBLE
+ }
+ }
+ }
+}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/HabiticaAlertDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/HabiticaAlertDialog.kt
index 8999e319a..3d099bff8 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/HabiticaAlertDialog.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/HabiticaAlertDialog.kt
@@ -2,6 +2,7 @@ package com.habitrpg.android.habitica.ui.views.dialogs
import android.app.Activity
import android.content.Context
+import android.text.method.ScrollingMovementMethod
import android.view.*
import android.view.animation.AccelerateInterpolator
import android.widget.*
@@ -61,7 +62,7 @@ open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style.
}
override fun setTitle(title: CharSequence?) {
- if (title != null) {
+ if ((title?.length ?: 0) > 0) {
titleTextView.visibility = View.VISIBLE
} else {
titleTextView.visibility = View.GONE
@@ -74,12 +75,13 @@ open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style.
}
override fun setMessage(message: CharSequence?) {
- if (message != null) {
+ if ((message?.length ?: 0) > 0) {
messageTextView.visibility = View.VISIBLE
} else {
messageTextView.visibility = View.GONE
}
messageTextView.text = message
+ messageTextView.movementMethod = ScrollingMovementMethod()
}
fun setMessage(messageId: Int) {
@@ -87,7 +89,7 @@ open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style.
}
fun setNotice(notice: CharSequence?) {
- if (notice != null) {
+ if ((notice?.length ?: 0) > 0) {
noticeTextView.visibility = View.VISIBLE
} else {
noticeTextView.visibility = View.GONE
@@ -147,10 +149,10 @@ open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style.
fun getContentView(): View? = additionalContentView
fun addButton(stringRes: Int, isPrimary: Boolean, isDestructive: Boolean = false, function: ((HabiticaAlertDialog, Int) -> Unit)? = null): Button {
- return addButton(context.getString(stringRes), isPrimary, isDestructive, function)
+ return addButton(context.getString(stringRes), isPrimary, isDestructive, true, function)
}
- fun addButton(string: String, isPrimary: Boolean, isDestructive: Boolean = false, function: ((HabiticaAlertDialog, Int) -> Unit)? = null): Button {
+ fun addButton(string: String, isPrimary: Boolean, isDestructive: Boolean = false, autoDismiss: Boolean = true, function: ((HabiticaAlertDialog, Int) -> Unit)? = null): Button {
val button: Button = if (isPrimary) {
if (isDestructive) {
buttonsWrapper.inflate(R.layout.dialog_habitica_primary_destructive_button) as? Button
@@ -167,7 +169,7 @@ open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style.
button.text = string
button.minWidth = 147.dpToPx(context)
button.setScaledPadding(context, 20, 0, 20, 0)
- return addButton(button, true, function) as Button
+ return addButton(button, autoDismiss, function) as Button
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/QuestCompletedDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/QuestCompletedDialog.kt
new file mode 100644
index 000000000..0352531ff
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/QuestCompletedDialog.kt
@@ -0,0 +1,37 @@
+package com.habitrpg.android.habitica.ui.views.dialogs
+
+import android.content.Context
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.models.inventory.QuestContent
+
+class QuestCompletedDialog(context: Context) : HabiticaAlertDialog(context) {
+
+ var quest: QuestContent? = null
+ set(value) {
+ field = value
+ if (value == null) return
+
+ val contentView = QuestCompletedDialogContent(context)
+ contentView.setQuestContent(value)
+ setAdditionalContentView(contentView)
+ }
+
+ override fun dismiss() {
+ dialog = null
+ super.dismiss()
+ }
+
+ companion object {
+ private var dialog: QuestCompletedDialog? = null
+
+ fun showWithQuest(context: Context, quest: QuestContent) {
+ if (dialog != null) return
+
+ dialog = QuestCompletedDialog(context)
+ dialog?.quest = quest
+ dialog?.setTitle(R.string.quest_completed)
+ dialog?.addButton(R.string.onwards, isPrimary = true, isDestructive = false)
+ dialog?.enqueue()
+ }
+ }
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/QuestCompletedDialogContent.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/QuestCompletedDialogContent.kt
new file mode 100644
index 000000000..137586a8c
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/QuestCompletedDialogContent.kt
@@ -0,0 +1,99 @@
+package com.habitrpg.android.habitica.ui.views.dialogs
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.facebook.drawee.view.SimpleDraweeView
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.databinding.DialogCompletedQuestContentBinding
+import com.habitrpg.android.habitica.extensions.layoutInflater
+import com.habitrpg.android.habitica.models.inventory.QuestContent
+import com.habitrpg.android.habitica.models.inventory.QuestDropItem
+import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
+import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
+
+class QuestCompletedDialogContent : LinearLayout {
+
+
+ private lateinit var binding: DialogCompletedQuestContentBinding
+
+ constructor(context: Context) : super(context) {
+ setupView()
+ }
+
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
+ setupView()
+ }
+
+ private fun setupView() {
+ orientation = VERTICAL
+ gravity = Gravity.CENTER
+
+ binding = DialogCompletedQuestContentBinding.inflate(context.layoutInflater, this)
+ }
+
+ fun setQuestContent(questContent: QuestContent) {
+ binding.titleTextView.text = questContent.text
+ binding.notesTextView.text = questContent.completion
+ DataBindingUtils.loadImage(binding.imageView, "quest_" + questContent.key)
+
+ val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as? LayoutInflater
+
+ if (questContent.drop != null && questContent.drop?.items != null) {
+ questContent.drop?.items
+ ?.filterNot { it.isOnlyOwner }
+ ?.forEach { addRewardsRow(inflater, it, binding.rewardsList) }
+
+ var hasOwnerRewards = false
+ for (item in questContent.drop?.items ?: emptyList()) {
+ if (item.isOnlyOwner) {
+ addRewardsRow(inflater, item, binding.ownerRewardsList)
+ hasOwnerRewards = true
+ }
+ }
+ if (!hasOwnerRewards) {
+ binding.ownerRewardsTitle.visibility = View.GONE
+ binding.ownerRewardsList.visibility = View.GONE
+ }
+
+ if (questContent.drop?.exp ?: 0 > 0) {
+ val view = inflater?.inflate(R.layout.row_quest_reward_imageview, binding.rewardsList, false) as? ViewGroup
+ val imageView = view?.findViewById(R.id.imageView)
+ imageView?.scaleType = ImageView.ScaleType.CENTER
+ imageView?.setImageBitmap(HabiticaIconsHelper.imageOfExperienceReward())
+ val titleTextView = view?.findViewById(R.id.titleTextView)
+ titleTextView?.text = context.getString(R.string.experience_reward, questContent.drop?.exp)
+ binding.rewardsList.addView(view)
+ }
+
+ if (questContent.drop?.gp ?: 0 > 0) {
+ val view = inflater?.inflate(R.layout.row_quest_reward_imageview, binding.rewardsList, false) as? ViewGroup
+ val imageView = view?.findViewById(R.id.imageView)
+ imageView?.scaleType = ImageView.ScaleType.CENTER
+ imageView?.setImageBitmap(HabiticaIconsHelper.imageOfGoldReward())
+ val titleTextView = view?.findViewById(R.id.titleTextView)
+ titleTextView?.text = context.getString(R.string.gold_reward, questContent.drop?.gp)
+ binding.rewardsList.addView(view)
+ }
+ }
+ }
+
+ private fun addRewardsRow(inflater: LayoutInflater?, item: QuestDropItem, containerView: ViewGroup?) {
+ val view = inflater?.inflate(R.layout.row_quest_reward, containerView, false) as? ViewGroup
+ val imageView = view?.findViewById(R.id.imageView) as? SimpleDraweeView
+ val titleTextView = view?.findViewById(R.id.titleTextView) as? TextView
+ DataBindingUtils.loadImage(imageView, item.imageName)
+ if (item.count > 1) {
+ titleTextView?.text = context.getString(R.string.quest_reward_count, item.text, item.count)
+ } else {
+ titleTextView?.text = item.text
+ }
+ containerView?.addView(view)
+ }
+}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/settings/FixValuesEditText.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/settings/FixValuesEditText.kt
index dca2892c6..fce3e9276 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/settings/FixValuesEditText.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/settings/FixValuesEditText.kt
@@ -3,21 +3,22 @@ package com.habitrpg.android.habitica.ui.views.settings
import android.content.Context
import android.graphics.Bitmap
import android.graphics.PorterDuff
+import android.util.AttributeSet
+import android.widget.FrameLayout
import androidx.annotation.ColorRes
import androidx.core.content.ContextCompat
-import android.util.AttributeSet
-import android.view.View
-import android.widget.FrameLayout
import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.databinding.FixvaluesEdittextBinding
+import com.habitrpg.android.habitica.extensions.layoutInflater
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
-import kotlinx.android.synthetic.main.fixvalues_edittext.view.*
class FixValuesEditText(context: Context, attrs: AttributeSet) : FrameLayout(context, attrs) {
+ private val binding: FixvaluesEdittextBinding = FixvaluesEdittextBinding.inflate(context.layoutInflater, this)
var text: String
- get() = editText?.text.toString()
+ get() = binding.editText.text.toString()
set(value) {
- editText?.setText(value)
+ binding.editText.setText(value)
}
@ColorRes
@@ -27,34 +28,32 @@ class FixValuesEditText(context: Context, attrs: AttributeSet) : FrameLayout(con
val backgroundDrawable = ContextCompat.getDrawable(context, R.drawable.layout_rounded_bg)
backgroundDrawable?.setColorFilter(field, PorterDuff.Mode.MULTIPLY)
backgroundDrawable?.alpha = 50
- iconBackgroundView.background = backgroundDrawable
+ binding.iconBackgroundView.background = backgroundDrawable
}
init {
- View.inflate(context, R.layout.fixvalues_edittext, this)
-
val attributes = context.theme.obtainStyledAttributes(
attrs,
R.styleable.FixValuesEditText,
0, 0)
- editText.hint = attributes.getString(R.styleable.FixValuesEditText_title)
- editTextWrapper.hint = editText.hint
- editTextWrapper.setHintTextAppearance(attributes.getResourceId(R.styleable.FixValuesEditText_hintStyle, R.style.PurpleTextLabel))
+ binding.editText.hint = attributes.getString(R.styleable.FixValuesEditText_title)
+ binding.editTextWrapper.hint = binding.editText.hint
+ binding.editTextWrapper.setHintTextAppearance(attributes.getResourceId(R.styleable.FixValuesEditText_hintStyle, R.style.PurpleTextLabel))
iconBackgroundColor = attributes.getColor(R.styleable.FixValuesEditText_iconBgColor, 0)
val iconName = attributes.getString(R.styleable.FixValuesEditText_fixIconName)
when (iconName) {
- "health" -> iconView.setImageBitmap(HabiticaIconsHelper.imageOfHeartLightBg())
- "experience" -> iconView.setImageBitmap(HabiticaIconsHelper.imageOfExperience())
- "mana" -> iconView.setImageBitmap(HabiticaIconsHelper.imageOfMagic())
- "gold" -> iconView.setImageBitmap(HabiticaIconsHelper.imageOfGold())
- "level" -> iconView.setImageBitmap(HabiticaIconsHelper.imageOfRogueLightBg())
- "streak" -> iconView.setImageResource(R.drawable.achievement_thermometer)
+ "health" -> binding.iconView.setImageBitmap(HabiticaIconsHelper.imageOfHeartLightBg())
+ "experience" -> binding.iconView.setImageBitmap(HabiticaIconsHelper.imageOfExperience())
+ "mana" -> binding.iconView.setImageBitmap(HabiticaIconsHelper.imageOfMagic())
+ "gold" -> binding.iconView.setImageBitmap(HabiticaIconsHelper.imageOfGold())
+ "level" -> binding.iconView.setImageBitmap(HabiticaIconsHelper.imageOfRogueLightBg())
+ "streak" -> binding.iconView.setImageResource(R.drawable.achievement_thermometer)
}
}
fun setIconBitmap(icon: Bitmap) {
- iconView.setImageBitmap(icon)
+ binding.iconView.setImageBitmap(icon)
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialog.kt
index f0114ca20..4cf8704e4 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialog.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialog.kt
@@ -7,6 +7,7 @@ import android.view.View
import android.widget.Button
import android.widget.TextView
import androidx.core.content.ContextCompat
+import androidx.core.graphics.drawable.toDrawable
import androidx.core.os.bundleOf
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
@@ -57,21 +58,17 @@ class PurchaseDialog(context: Context, component: UserComponent?, val item: Shop
private val buyButton: View
private val priceLabel: CurrencyView
private val buyLabel: TextView
+ private var amountErrorLabel: TextView? = null
private val pinButton: Button by bindView(customHeader, R.id.pin_button)
+ private var purchaseQuantity = 1
+
var purchaseCardAction: ((ShopItem) -> Unit)? = null
private var shopItem: ShopItem = item
set(value) {
field = value
- if (shopItem.unlockCondition == null) {
- priceLabel.value = shopItem.value.toDouble()
- priceLabel.currency = shopItem.currency
- } else {
- setBuyButtonEnabled(false)
- }
-
if (shopItem.isLimited) {
//TODO: replace with correct date once API is final
limitedTextView.text = context.getString(R.string.available_until, Date().toString())
@@ -79,7 +76,20 @@ class PurchaseDialog(context: Context, component: UserComponent?, val item: Shop
limitedTextView.visibility = View.GONE
}
- priceLabel.isLocked = shopItem.locked
+ if (shopItem.lockedReason(context) == null) {
+ updatePurchaseTotal()
+ priceLabel.currency = shopItem.currency
+ } else {
+ limitedTextView.text = shopItem.lockedReason(context)
+ }
+ if (shopItem.locked) {
+ buyLabel.text = context.getString(R.string.locked)
+ limitedTextView.visibility = View.VISIBLE
+ limitedTextView.background = ContextCompat.getColor(context, R.color.gray_600).toDrawable()
+ limitedTextView.setTextColor(ContextCompat.getColor(context, R.color.gray_100))
+ }
+
+ priceLabel.isLocked = shopItem.locked || shopItem.lockedReason(context) != null
val contentView: PurchaseDialogContent
when {
@@ -93,13 +103,43 @@ class PurchaseDialog(context: Context, component: UserComponent?, val item: Shop
inventoryRepository.getEquipment(shopItem.key).firstElement().subscribe(Consumer { contentView.setEquipment(it) }, RxErrorHandler.handleEmptyError())
checkGearClass()
}
- "gems" == shopItem.purchaseType -> contentView = PurchaseDialogGemsContent(context)
+ "gems" == shopItem.purchaseType -> {
+ val gemContent = PurchaseDialogGemsContent(context)
+ gemContent.stepperView.onValueChanged = {
+ purchaseQuantity = it.toInt()
+ updatePurchaseTotal()
+ }
+ contentView = gemContent
+ }
else -> contentView = PurchaseDialogBaseContent(context)
}
+
+ amountErrorLabel = contentView.findViewById(R.id.amount_error_label)
+
contentView.setItem(shopItem)
setAdditionalContentView(contentView)
}
+ private fun updatePurchaseTotal() {
+ priceLabel.value = shopItem.value.toDouble() * purchaseQuantity
+
+ if (shopItem.canAfford(user, purchaseQuantity) && !shopItem.locked && purchaseQuantity >= 1) {
+ buyButton.background = context.getDrawable(R.drawable.button_background_primary)
+ priceLabel.setTextColor(ContextCompat.getColor(context, R.color.white))
+ buyLabel.setTextColor(ContextCompat.getColor(context, R.color.white))
+ } else {
+ buyButton.background = context.getDrawable(R.drawable.button_background_gray_600)
+ priceLabel.setTextColor(ContextCompat.getColor(context, R.color.gray_100))
+ buyLabel.setTextColor(ContextCompat.getColor(context, R.color.gray_100))
+ }
+
+ if (purchaseQuantity < 1 || (shopItem.limitedNumberLeft != null && (shopItem.limitedNumberLeft ?: 0) < purchaseQuantity)) {
+ amountErrorLabel?.visibility = View.VISIBLE
+ } else {
+ amountErrorLabel?.visibility = View.GONE
+ }
+ }
+
private fun checkGearClass() {
val user = user ?: return
@@ -176,15 +216,7 @@ class PurchaseDialog(context: Context, component: UserComponent?, val item: Shop
}
buyButton.elevation = 0f
- if (shopItem.canAfford(user, configManager.insufficientGemPurchase())) {
- buyButton.background = context.getDrawable(R.drawable.button_background_primary)
- priceLabel.setTextColor(ContextCompat.getColor(context, R.color.white))
- buyLabel.setTextColor(ContextCompat.getColor(context, R.color.white))
- } else {
- buyButton.background = context.getDrawable(R.drawable.button_background_gray_700)
- priceLabel.setTextColor(ContextCompat.getColor(context, R.color.gray_200))
- buyLabel.setTextColor(ContextCompat.getColor(context, R.color.gray_200))
- }
+ updatePurchaseTotal()
if (shopItem.isTypeGear) {
checkGearClass()
@@ -204,9 +236,9 @@ class PurchaseDialog(context: Context, component: UserComponent?, val item: Shop
val snackbarText = arrayOf("")
if (shopItem.isValid && !shopItem.locked) {
val gemsLeft = if (shopItem.limitedNumberLeft != null) shopItem.limitedNumberLeft else 0
- if ((gemsLeft == 0 && shopItem.purchaseType == "gems") || shopItem.canAfford(user, false)) {
+ if ((gemsLeft == 0 && shopItem.purchaseType == "gems") || shopItem.canAfford(user, purchaseQuantity)) {
val observable: Flowable
- if (shopIdentifier != null && shopIdentifier == Shop.TIME_TRAVELERS_SHOP || "mystery_set" == shopItem.purchaseType) {
+ if (shopIdentifier != null && shopIdentifier == Shop.TIME_TRAVELERS_SHOP || "mystery_set" == shopItem.purchaseType || shopItem.currency == "hourglasses") {
observable = if (shopItem.purchaseType == "gear") {
inventoryRepository.purchaseMysterySet(shopItem.key)
} else {
@@ -219,7 +251,7 @@ class PurchaseDialog(context: Context, component: UserComponent?, val item: Shop
dismiss()
return
} else if ("gold" == shopItem.currency && "gem" != shopItem.key) {
- observable = inventoryRepository.buyItem(user, shopItem.key, shopItem.value.toDouble()).map { buyResponse ->
+ observable = inventoryRepository.buyItem(user, shopItem.key, shopItem.value.toDouble(), purchaseQuantity).map { buyResponse ->
if (shopItem.key == "armoire") {
snackbarText[0] = when {
buyResponse.armoire["type"] == "gear" -> context.getString(R.string.armoireEquipment, buyResponse.armoire["dropText"])
@@ -230,7 +262,7 @@ class PurchaseDialog(context: Context, component: UserComponent?, val item: Shop
buyResponse
}
} else {
- observable = inventoryRepository.purchaseItem(shopItem.purchaseType, shopItem.key)
+ observable = inventoryRepository.purchaseItem(shopItem.purchaseType, shopItem.key, purchaseQuantity)
}
observable
.doOnNext {
@@ -253,7 +285,7 @@ class PurchaseDialog(context: Context, component: UserComponent?, val item: Shop
.flatMap { userRepository.retrieveUser(withTasks = false, forced = true) }
.flatMap { inventoryRepository.retrieveInAppRewards() }
.subscribe({
- if (item.isTypeGear) {
+ if (item.isTypeGear || item.currency == "hourglasses") {
EventBus.getDefault().post(GearPurchasedEvent(item))
}
}) { throwable ->
@@ -278,12 +310,5 @@ class PurchaseDialog(context: Context, component: UserComponent?, val item: Shop
dismiss()
}
- private fun setBuyButtonEnabled(enabled: Boolean) {
- if (enabled) {
- buyButton.alpha = 0.5f
- } else {
- buyButton.alpha = 1.0f
- }
- }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialogContent.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialogContent.kt
index b4189d4bd..0cf7b2576 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialogContent.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialogContent.kt
@@ -8,11 +8,13 @@ import android.widget.TextView
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.drawee.view.SimpleDraweeView
import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.extensions.dpToPx
import com.habitrpg.android.habitica.models.inventory.QuestContent
import com.habitrpg.android.habitica.models.shops.ShopItem
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
import com.habitrpg.android.habitica.ui.helpers.bindView
+
abstract class PurchaseDialogContent : LinearLayout {
private val imageView: SimpleDraweeView by bindView(R.id.imageView)
@@ -43,6 +45,10 @@ abstract class PurchaseDialogContent : LinearLayout {
.setAutoPlayAnimations(true)
.build()
imageView.controller = controller
+ val params = imageView.layoutParams
+ params.height = 147.dpToPx(context)
+ params.width = 140.dpToPx(context)
+ imageView.layoutParams = params
} else {
DataBindingUtils.loadImage(imageView, item.imageName)
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialogGemsContent.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialogGemsContent.kt
index 9b64ff5fb..f84a51a23 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialogGemsContent.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialogGemsContent.kt
@@ -3,20 +3,29 @@ package com.habitrpg.android.habitica.ui.views.shops
import android.content.Context
import android.widget.TextView
import com.habitrpg.android.habitica.R
-import com.habitrpg.android.habitica.ui.helpers.bindView
+import com.habitrpg.android.habitica.extensions.asDrawable
import com.habitrpg.android.habitica.models.shops.ShopItem
+import com.habitrpg.android.habitica.ui.helpers.bindView
+import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
+import com.habitrpg.android.habitica.ui.views.tasks.form.StepperValueFormView
internal class PurchaseDialogGemsContent(context: Context) : PurchaseDialogContent(context) {
val notesTextView: TextView by bindView(R.id.notesTextView)
+ val stepperView: StepperValueFormView by bindView(R.id.stepper_view)
override val viewId: Int
- get() = R.layout.dialog_purchase_content_item
+ get() = R.layout.dialog_purchase_gems
+ init {
+ stepperView.iconDrawable = HabiticaIconsHelper.imageOfGem().asDrawable(context.resources)
+ }
override fun setItem(item: ShopItem) {
super.setItem(item)
notesTextView.text = item.notes
+
+ stepperView.maxValue = item.limitedNumberLeft?.toDouble()
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/ChatBarView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/ChatBarView.kt
index 97be64169..b25d82eb8 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/ChatBarView.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/ChatBarView.kt
@@ -55,6 +55,10 @@ class ChatBarView : LinearLayout {
autocompleteAdapter?.groupID = value
}
+ var message: String?
+ get() = chatEditText.text.toString()
+ set(value) = chatEditText.setText(value)
+
constructor(context: Context) : super(context) {
setupView(context)
}
@@ -115,7 +119,7 @@ class ChatBarView : LinearLayout {
}
private fun sendButtonPressed() {
- val chatText = chatEditText.text.toString()
+ val chatText = message ?: ""
if (chatText.isNotEmpty()) {
chatEditText.text = null
sendAction?.invoke(chatText)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/subscriptions/SubscriptionDetailsView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/subscriptions/SubscriptionDetailsView.kt
index 284d872d0..1585ed10c 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/subscriptions/SubscriptionDetailsView.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/subscriptions/SubscriptionDetailsView.kt
@@ -47,7 +47,7 @@ class SubscriptionDetailsView : LinearLayout {
var duration: String? = null
- if (plan.planId != null) {
+ if (plan.planId != null && plan.dateTerminated == null) {
if (plan.planId == SubscriptionPlan.PLANID_BASIC || plan.planId == SubscriptionPlan.PLANID_BASICEARNED) {
duration = resources.getString(R.string.month)
} else if (plan.planId == SubscriptionPlan.PLANID_BASIC3MONTH) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/TaskFilterDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/TaskFilterDialog.kt
index 8fe8aa7b7..0ede16d8d 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/TaskFilterDialog.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/TaskFilterDialog.kt
@@ -112,6 +112,7 @@ class TaskFilterDialog(context: Context, component: UserComponent?) : AlertDialo
}
private fun createTagViews() {
+ tagsList.removeAllViews()
val colorStateList = ColorStateList(
arrayOf(intArrayOf(-android.R.attr.state_checked), //disabled
intArrayOf(android.R.attr.state_checked) //enabled
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/RewardValueFormView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/StepperValueFormView.kt
similarity index 57%
rename from Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/RewardValueFormView.kt
rename to Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/StepperValueFormView.kt
index 1c58d7265..b973f7bc6 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/RewardValueFormView.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/StepperValueFormView.kt
@@ -1,6 +1,7 @@
package com.habitrpg.android.habitica.ui.views.tasks.form
import android.content.Context
+import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.widget.EditText
import android.widget.ImageButton
@@ -13,7 +14,7 @@ import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
import java.text.DecimalFormat
-class RewardValueFormView @JvmOverloads constructor(
+class StepperValueFormView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : RelativeLayout(context, attrs, defStyleAttr) {
@@ -21,20 +22,36 @@ class RewardValueFormView @JvmOverloads constructor(
private val upButton: ImageButton by bindView(R.id.up_button)
private val downButton: ImageButton by bindView(R.id.down_button)
+ var onValueChanged: ((Double) -> Unit)? = null
+
private val decimalFormat = DecimalFormat("0.###")
private var editTextIsFocused = false
var value = 0.0
- set(value) {
- val newValue = if (value >= 0) value else 0.0
+ set(new) {
+ var newValue = if (new >= minValue) new else minValue
+ maxValue?.let {
+ if (newValue > it && it > 0) {
+ newValue = it
+ }
+ }
val oldValue = field
field = newValue
- if (oldValue != newValue) {
+ if (oldValue != new) {
valueString = decimalFormat.format(newValue)
}
- downButton.isEnabled = field > 0
+ downButton.isEnabled = field > minValue
+ maxValue?.let {
+ if (it == 0.0) return@let
+ upButton.isEnabled = value < it
+ }
+
+ onValueChanged?.invoke(value)
}
+ var maxValue: Double? = null
+ var minValue: Double = 0.0
+
private var valueString = ""
set(value) {
field = value
@@ -51,11 +68,28 @@ class RewardValueFormView @JvmOverloads constructor(
}
}
+ var iconDrawable: Drawable?
+ get() {
+ return editText.compoundDrawables.firstOrNull()
+ }
+ set(value) {
+ editText.setCompoundDrawablesWithIntrinsicBounds(value, null, null, null)
+ }
+
init {
- inflate(R.layout.task_form_reward_value, true)
+ inflate(R.layout.form_stepper_value, true)
+
+ val attributes = context.theme?.obtainStyledAttributes(
+ attrs,
+ R.styleable.StepperValueFormView,
+ 0, 0)
+
//set value here, so that the setter is called and everything is set up correctly
- value = 10.0
- editText.setCompoundDrawablesWithIntrinsicBounds(HabiticaIconsHelper.imageOfGold().asDrawable(context.resources), null, null, null)
+ maxValue = attributes?.getFloat(R.styleable.StepperValueFormView_maxValue, 0f)?.toDouble()
+ minValue = attributes?.getFloat(R.styleable.StepperValueFormView_minValue, 0f)?.toDouble() ?: 0.0
+ value = attributes?.getFloat(R.styleable.StepperValueFormView_defaultValue, 10.0f)?.toDouble() ?: 10.0
+ iconDrawable = attributes?.getDrawable(R.styleable.StepperValueFormView_iconDrawable) ?: HabiticaIconsHelper.imageOfGold().asDrawable(context.resources)
+
upButton.setOnClickListener {
value += 1
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/yesterdailies/YesterdailyDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/yesterdailies/YesterdailyDialog.kt
index 46bf1431f..c957b8ece 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/yesterdailies/YesterdailyDialog.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/yesterdailies/YesterdailyDialog.kt
@@ -95,12 +95,7 @@ class YesterdailyDialog private constructor(context: Context, private val userRe
val checklistContainer = taskView.findViewById(R.id.checklistView)
for (item in task.checklist ?: emptyList()) {
val checklistView = inflater.inflate(R.layout.checklist_item_row, yesterdailiesList, false)
- configureChecklistView(checklistView, item)
- checklistView.setOnClickListener {
- item.completed = !item.completed
- taskRepository.scoreChecklistItem(task.id ?: "", item.id ?: "").subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
- configureChecklistView(checklistView, item)
- }
+ configureChecklistView(checklistView, task, item)
checklistContainer.addView(checklistView)
}
}
@@ -111,9 +106,19 @@ class YesterdailyDialog private constructor(context: Context, private val userRe
}
}
- private fun configureChecklistView(checklistView: View, item: ChecklistItem) {
+ private fun configureChecklistView(checklistView: View, task: Task, item: ChecklistItem) {
val checkbox = checklistView.findViewById(R.id.checkBox) as? CheckBox
checkbox?.isChecked = item.completed
+ checkbox?.setOnCheckedChangeListener { _, isChecked ->
+ item.completed = isChecked
+ taskRepository.scoreChecklistItem(task.id ?: "", item.id ?: "").subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
+ configureChecklistView(checklistView, task, item)
+ }
+ checklistView.setOnClickListener {
+ item.completed = !item.completed
+ taskRepository.scoreChecklistItem(task.id ?: "", item.id ?: "").subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
+ configureChecklistView(checklistView, task, item)
+ }
val checkboxHolder = checklistView.findViewById(R.id.checkBoxHolder)
checkboxHolder.setBackgroundResource(R.color.gray_700)
val textView = checklistView.findViewById(R.id.checkedTextView) as? TextView
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/userpicture/BitmapUtils.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/userpicture/BitmapUtils.kt
index 22cc2964a..ad52b4523 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/userpicture/BitmapUtils.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/userpicture/BitmapUtils.kt
@@ -5,10 +5,19 @@ import java.io.File
import java.io.FileOutputStream
object BitmapUtils {
+ fun clearDirectoryContent(directory: String) {
+ val dirFile = File(directory)
+ dirFile.listFiles()?.forEach { it.delete() }
+ }
+
fun saveToShareableFile(directory: String, filename: String, bmp: Bitmap): File? {
var name = filename
try {
name = "$directory/$name"
+ val dirFile = File(directory)
+ if (!dirFile.exists()) {
+ dirFile.mkdir()
+ }
val out = FileOutputStream(name)
bmp.compress(Bitmap.CompressFormat.PNG, 100, out)
@@ -16,6 +25,7 @@ object BitmapUtils {
out.close()
return File(name)
} catch (ignored: Exception) {
+ ignored.printStackTrace()
}
return null
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/utils/CustomizationDeserializer.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/utils/CustomizationDeserializer.kt
index d1416bc31..035e2622c 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/utils/CustomizationDeserializer.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/utils/CustomizationDeserializer.kt
@@ -16,9 +16,8 @@ class CustomizationDeserializer : JsonDeserializer> {
val customizations = RealmList()
val realm = Realm.getDefaultInstance()
+ val existingCustomizations = realm.copyFromRealm(realm.where(Customization::class.java).findAll())
if (jsonObject.has("shirt")) {
- val existingCustomizations = realm.copyFromRealm(realm.where(Customization::class.java).findAll())
-
for (customization in existingCustomizations) {
if (jsonObject.has(customization.type)) {
var nestedObject = jsonObject.get(customization.type).asJsonObject
@@ -48,13 +47,11 @@ class CustomizationDeserializer : JsonDeserializer> {
}
}
} else {
- val existingCustomizations = realm.copyFromRealm(realm.where(Customization::class.java).findAll())
-
for (customization in existingCustomizations) {
if (jsonObject.has(customization.customizationSet)) {
val nestedObject = jsonObject.get(customization.customizationSet).asJsonObject
if (nestedObject.has(customization.identifier)) {
- customizations.add(this.parseBackground(customization, customization.customizationSet, customization.identifier, nestedObject.get(customization.identifier).asJsonObject))
+ customizations.add(this.parseBackground(customization, customization.customizationSet ?: "", customization.identifier, nestedObject.get(customization.identifier).asJsonObject))
nestedObject.remove(customization.identifier)
}
}
@@ -123,19 +120,23 @@ class CustomizationDeserializer : JsonDeserializer> {
customization.type = "background"
customization.identifier = key
}
- if ("incentiveBackgrounds" == setName) {
- customization.customizationSetName = "Login Incentive"
- customization.price = 0
- customization.setPrice = 0
- customization.isBuyable = false
- } else if ("timeTravelBackgrounds" == setName) {
- customization.customizationSetName = "Time Travel Backgrounds"
- customization.price = 1
- customization.setPrice = 0
- customization.isBuyable = false
- } else {
- customization.price = 7
- customization.setPrice = 15
+ when (setName) {
+ "incentiveBackgrounds" -> {
+ customization.customizationSetName = "Login Incentive"
+ customization.price = 0
+ customization.setPrice = 0
+ customization.isBuyable = false
+ }
+ "timeTravelBackgrounds" -> {
+ customization.customizationSetName = "Time Travel Backgrounds"
+ customization.price = 1
+ customization.setPrice = 0
+ customization.isBuyable = false
+ }
+ else -> {
+ customization.price = 7
+ customization.setPrice = 15
+ }
}
customization.text = entry.get("text").asString
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/utils/EquipmentListDeserializer.java b/Habitica/src/main/java/com/habitrpg/android/habitica/utils/EquipmentListDeserializer.java
index 1f35d1c32..366a3009a 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/utils/EquipmentListDeserializer.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/utils/EquipmentListDeserializer.java
@@ -43,6 +43,8 @@ public class EquipmentListDeserializer implements JsonDeserializer {
@@ -23,47 +20,21 @@ public class PurchasedDeserializer implements JsonDeserializer {
@Override
public Purchases deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject object = json.getAsJsonObject();
- RealmList customizations = new RealmList<>();
+ RealmList customizations = new RealmList<>();
Purchases purchases = new Purchases();
- List existingCustomizations;
- try {
- Realm realm = Realm.getDefaultInstance();
- existingCustomizations = realm.copyFromRealm(realm.where(Customization.class).findAll());
- realm.close();
- } catch (RuntimeException e) {
- //Tests don't have a database
- existingCustomizations = new ArrayList<>();
- }
- for (Customization customization : existingCustomizations) {
- if (object.has(customization.getType())) {
- JsonObject nestedObject = object.get(customization.getType()).getAsJsonObject();
- if (customization.getCategory() != null) {
- if (nestedObject.has(customization.getCategory())) {
- nestedObject = nestedObject.get(customization.getCategory()).getAsJsonObject();
- } else {
- continue;
- }
- }
- if (nestedObject.has(customization.getIdentifier())) {
- customizations.add(this.parseCustomization(customization, customization.getType(), customization.getCategory(), customization.getIdentifier(), nestedObject.get(customization.getIdentifier()).getAsBoolean()));
- nestedObject.remove(customization.getIdentifier());
- }
- }
- }
-
for (String type : Arrays.asList("background", "shirt", "skin")) {
if (!object.has(type)) {
continue;
}
for (Map.Entry entry : object.get(type).getAsJsonObject().entrySet()) {
- customizations.add(this.parseCustomization(null, type, null, entry.getKey(), entry.getValue().getAsBoolean()));
+ customizations.add(this.parseCustomization(type, null, entry.getKey(), entry.getValue().getAsBoolean()));
}
}
if (object.has("hair")) {
for (Map.Entry categoryEntry : object.get("hair").getAsJsonObject().entrySet()) {
for (Map.Entry entry : categoryEntry.getValue().getAsJsonObject().entrySet()) {
- customizations.add(this.parseCustomization(null, "hair", categoryEntry.getKey(), entry.getKey(), entry.getValue().getAsBoolean()));
+ customizations.add(this.parseCustomization("hair", categoryEntry.getKey(), entry.getKey(), entry.getValue().getAsBoolean()));
}
}
}
@@ -74,15 +45,12 @@ public class PurchasedDeserializer implements JsonDeserializer {
return purchases;
}
- private Customization parseCustomization(Customization existingCustomizaion, String type, String category, String key, boolean wasPurchased) {
- Customization customization = existingCustomizaion;
- if (customization == null) {
- customization = new Customization();
- customization.setIdentifier(key);
- customization.setType(type);
- if (category != null) {
- customization.setCategory(category);
- }
+ private OwnedCustomization parseCustomization(String type, String category, String key, boolean wasPurchased) {
+ OwnedCustomization customization = new OwnedCustomization();
+ customization.setKey(key);
+ customization.setType(type);
+ if (category != null) {
+ customization.setCategory(category);
}
customization.setPurchased(wasPurchased);
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/utils/TaskListDeserializer.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/utils/TaskListDeserializer.kt
index 7ce98ab5f..08276f405 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/utils/TaskListDeserializer.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/utils/TaskListDeserializer.kt
@@ -71,6 +71,9 @@ class TaskListDeserializer : JsonDeserializer {
task.up = obj.get("up")?.asBoolean ?: false
task.down = obj.get("down")?.asBoolean ?: false
task.streak = obj.get("streak")?.asInt
+ if (obj.getAsJsonObject("challenge").has("id")) {
+ task.challengeID = obj.getAsJsonObject("challenge").get("id").asString
+ }
try {
task.counterUp = obj.get("counterUp")?.asInt
task.counterDown = obj.get("counterDown")?.asInt
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/utils/UserDeserializer.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/utils/UserDeserializer.kt
index 5ff7f7ddd..f91f0e761 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/utils/UserDeserializer.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/utils/UserDeserializer.kt
@@ -52,16 +52,22 @@ class UserDeserializer : JsonDeserializer {
user.profile = context.deserialize(obj.get("profile"), Profile::class.java)
}
if (obj.has("party")) {
- user.party = context.deserialize(obj.get("party"), UserParty::class.java)
+ val partyObj = obj.getAsJsonObject("party")
+ user.party = context.deserialize(partyObj, UserParty::class.java)
if (user.party != null && user.party?.quest != null) {
user.party?.quest?.id = user.id
- if (!obj.getAsJsonObject("party").getAsJsonObject("quest").has("RSVPNeeded")) {
+ if (!partyObj.getAsJsonObject("quest").has("RSVPNeeded")) {
val realm = Realm.getDefaultInstance()
val quest = realm.where(Quest::class.java).equalTo("id", user.id).findFirst()
if (quest != null && quest.isValid) {
user.party?.quest?.RSVPNeeded = quest.RSVPNeeded
}
}
+ if (partyObj.getAsJsonObject("quest").has("completed")) {
+ if (!partyObj.getAsJsonObject("quest").get("completed").isJsonNull) {
+ user.party?.quest?.completed = obj.getAsJsonObject("party").getAsJsonObject("quest").get("completed").asString
+ }
+ }
}
}
if (obj.has("purchased")) {
diff --git a/Habitica/src/test/java/com/habitrpg/android/habitica/models/inventory/MountTest.kt b/Habitica/src/test/java/com/habitrpg/android/habitica/models/inventory/MountTest.kt
new file mode 100644
index 000000000..8423b781c
--- /dev/null
+++ b/Habitica/src/test/java/com/habitrpg/android/habitica/models/inventory/MountTest.kt
@@ -0,0 +1,38 @@
+package com.habitrpg.android.habitica.models.inventory;
+
+import android.content.Context
+import com.google.common.truth.Truth.assertThat
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.extensions.getTranslatedType
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.mock
+
+private const val FAKE_STANDARD = "Standard"
+private const val FAKE_PREMIUM = "premium"
+
+class MountTest {
+ @Mock
+ private var mockContext: Context = mock(Context::class.java)
+ private var mount: Mount = Mount()
+
+ @Test
+ fun testGetTranslatedStringReturnsStandard() {
+ mount.type = "drop"
+ `when`(mockContext.getString(R.string.standard)).thenReturn(FAKE_STANDARD)
+
+ val result: String = mount.getTranslatedType(mockContext)
+
+ assertThat(result).isEqualTo(FAKE_STANDARD)
+ }
+
+ @Test
+ fun testGetTranslatedStringReturnsPremiumWhenContextIsNull() {
+ mount.type = "premium"
+
+ val result: String = mount.getTranslatedType(null)
+
+ assertThat(result).isEqualTo(FAKE_PREMIUM)
+ }
+}
\ No newline at end of file
diff --git a/Habitica/src/test/java/com/habitrpg/android/habitica/models/inventory/PetTest.kt b/Habitica/src/test/java/com/habitrpg/android/habitica/models/inventory/PetTest.kt
new file mode 100644
index 000000000..f0f19d8d8
--- /dev/null
+++ b/Habitica/src/test/java/com/habitrpg/android/habitica/models/inventory/PetTest.kt
@@ -0,0 +1,38 @@
+package com.habitrpg.android.habitica.models.inventory;
+
+import android.content.Context
+import com.google.common.truth.Truth.assertThat
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.extensions.getTranslatedType
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.mock
+
+private const val FAKE_STANDARD = "Standard"
+private const val FAKE_PREMIUM = "premium"
+
+class PetTest {
+ @Mock
+ private var mockContext: Context = mock(Context::class.java)
+ private var pet: Pet = Pet()
+
+ @Test
+ fun testGetTranslatedStringReturnsStandard() {
+ pet.type = "drop"
+ `when`(mockContext.getString(R.string.standard)).thenReturn(FAKE_STANDARD)
+
+ val result: String = pet.getTranslatedType(mockContext)
+
+ assertThat(result).isEqualTo(FAKE_STANDARD)
+ }
+
+ @Test
+ fun testGetTranslatedStringReturnsPremiumWhenContextIsNull() {
+ pet.type = "premium"
+
+ val result: String = pet.getTranslatedType(null)
+
+ assertThat(result).isEqualTo(FAKE_PREMIUM)
+ }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 7e30c061f..520febb9d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -11,7 +11,7 @@ buildscript {
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.6.0-rc01'
+ classpath 'com.android.tools.build:gradle:3.6.1'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
classpath 'com.google.gms:google-services:4.3.3'
classpath "io.realm:realm-gradle-plugin:6.0.2"
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 761aae694..1ea99e237 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Mon Oct 15 18:23:38 CEST 2018
+#Sun Feb 02 18:26:34 CET 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-rc-1-all.zip