diff --git a/Habitica/build.gradle b/Habitica/build.gradle index 89040842d..2c05a47a1 100644 --- a/Habitica/build.gradle +++ b/Habitica/build.gradle @@ -104,7 +104,7 @@ dependencies { } //Tests testImplementation 'junit:junit:4.12' - testImplementation 'androidx.test:core:1.0.0' + testImplementation 'androidx.test:core:1.2.0' testImplementation "com.google.truth:truth:1.0.1" testImplementation 'org.assertj:assertj-core:2.6.0' testImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.2' @@ -119,11 +119,11 @@ dependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.2' releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.2' //Push Notifications - implementation 'com.google.firebase:firebase-core:17.2.2' - implementation 'com.google.firebase:firebase-messaging:20.1.0' - implementation 'com.google.firebase:firebase-config:19.1.1' - implementation 'com.google.firebase:firebase-perf:19.0.5' - implementation 'com.google.android.gms:play-services-auth:17.0.0' + implementation 'com.google.firebase:firebase-core:17.4.4' + implementation 'com.google.firebase:firebase-messaging:20.2.3' + implementation 'com.google.firebase:firebase-config:19.2.0' + implementation 'com.google.firebase:firebase-perf:19.0.7' + implementation 'com.google.android.gms:play-services-auth:18.0.0' implementation 'io.realm:android-adapters:3.1.0' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'androidx.multidex:multidex:2.0.1' @@ -132,8 +132,8 @@ dependencies { implementation 'androidx.core:core-ktx:1.3.0' implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0" - implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2' - implementation 'androidx.navigation:navigation-ui-ktx:2.2.2' + implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0' + implementation 'androidx.navigation:navigation-ui-ktx:2.3.0' implementation "androidx.paging:paging-runtime-ktx:2.1.2" implementation 'com.plattysoft.leonids:LeonidsLib:1.3.2' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3' @@ -162,8 +162,8 @@ android { multiDexEnabled true resConfigs "en", "bg", "de", "en-rGB", "es", "fr", "hr-rHR", "in", "it", "iw", "ja", "ko", "lt", "nl", "pl", "pt-rBR", "pt-rPT", "ru", "tr", "zh", "zh-rTW" - versionCode 2462 - versionName "2.7" + versionCode 2500 + versionName "2.8" } viewBinding { diff --git a/Habitica/res/color/task_form_box_stroke.xml b/Habitica/res/color/task_form_box_stroke.xml new file mode 100644 index 000000000..f8eaeebf9 --- /dev/null +++ b/Habitica/res/color/task_form_box_stroke.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Habitica/res/drawable-hdpi/pet_checkmark.png b/Habitica/res/drawable-hdpi/pet_checkmark.png new file mode 100644 index 000000000..3ddfe89d1 Binary files /dev/null and b/Habitica/res/drawable-hdpi/pet_checkmark.png differ diff --git a/Habitica/res/drawable-hdpi/pet_status_egg.png b/Habitica/res/drawable-hdpi/pet_status_egg.png new file mode 100644 index 000000000..5dd39f87d Binary files /dev/null and b/Habitica/res/drawable-hdpi/pet_status_egg.png differ diff --git a/Habitica/res/drawable-hdpi/pet_status_potion.png b/Habitica/res/drawable-hdpi/pet_status_potion.png new file mode 100644 index 000000000..dc63d40c8 Binary files /dev/null and b/Habitica/res/drawable-hdpi/pet_status_potion.png differ diff --git a/Habitica/res/drawable-mdpi/pet_checkmark.png b/Habitica/res/drawable-mdpi/pet_checkmark.png new file mode 100644 index 000000000..e31aff0ea Binary files /dev/null and b/Habitica/res/drawable-mdpi/pet_checkmark.png differ diff --git a/Habitica/res/drawable-mdpi/pet_status_egg.png b/Habitica/res/drawable-mdpi/pet_status_egg.png new file mode 100644 index 000000000..cc358b246 Binary files /dev/null and b/Habitica/res/drawable-mdpi/pet_status_egg.png differ diff --git a/Habitica/res/drawable-mdpi/pet_status_potion.png b/Habitica/res/drawable-mdpi/pet_status_potion.png new file mode 100644 index 000000000..34a1c5c92 Binary files /dev/null and b/Habitica/res/drawable-mdpi/pet_status_potion.png differ diff --git a/Habitica/res/drawable-xhdpi/pet_checkmark.png b/Habitica/res/drawable-xhdpi/pet_checkmark.png new file mode 100644 index 000000000..d1b5d1ec3 Binary files /dev/null and b/Habitica/res/drawable-xhdpi/pet_checkmark.png differ diff --git a/Habitica/res/drawable-xhdpi/pet_status_egg.png b/Habitica/res/drawable-xhdpi/pet_status_egg.png new file mode 100644 index 000000000..de8b3fdc2 Binary files /dev/null and b/Habitica/res/drawable-xhdpi/pet_status_egg.png differ diff --git a/Habitica/res/drawable-xhdpi/pet_status_potion.png b/Habitica/res/drawable-xhdpi/pet_status_potion.png new file mode 100644 index 000000000..3a2ebe35e Binary files /dev/null and b/Habitica/res/drawable-xhdpi/pet_status_potion.png differ diff --git a/Habitica/res/drawable-xxhdpi/pet_checkmark.png b/Habitica/res/drawable-xxhdpi/pet_checkmark.png new file mode 100644 index 000000000..8faedaa58 Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/pet_checkmark.png differ diff --git a/Habitica/res/drawable-xxhdpi/pet_status_egg.png b/Habitica/res/drawable-xxhdpi/pet_status_egg.png new file mode 100644 index 000000000..b4f145ea0 Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/pet_status_egg.png differ diff --git a/Habitica/res/drawable-xxhdpi/pet_status_potion.png b/Habitica/res/drawable-xxhdpi/pet_status_potion.png new file mode 100644 index 000000000..5edb9dab0 Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/pet_status_potion.png differ diff --git a/Habitica/res/drawable/layout_rounded_bg_gray_700_brand_border.xml b/Habitica/res/drawable/layout_rounded_bg_gray_700_brand_border.xml index 727262a5e..995c49409 100644 --- a/Habitica/res/drawable/layout_rounded_bg_gray_700_brand_border.xml +++ b/Habitica/res/drawable/layout_rounded_bg_gray_700_brand_border.xml @@ -3,5 +3,5 @@ - + \ No newline at end of file diff --git a/Habitica/res/drawable/layout_rounded_bg_header_bar.xml b/Habitica/res/drawable/layout_rounded_bg_header_bar.xml new file mode 100644 index 000000000..3ab4bfac4 --- /dev/null +++ b/Habitica/res/drawable/layout_rounded_bg_header_bar.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Habitica/res/drawable/pill_bg_gray.xml b/Habitica/res/drawable/pill_bg_gray.xml index c5f636827..fc8626c84 100644 --- a/Habitica/res/drawable/pill_bg_gray.xml +++ b/Habitica/res/drawable/pill_bg_gray.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/Habitica/res/layout/activity_adventure_guide.xml b/Habitica/res/layout/activity_adventure_guide.xml index 7674b7f6c..5feab0ad1 100644 --- a/Habitica/res/layout/activity_adventure_guide.xml +++ b/Habitica/res/layout/activity_adventure_guide.xml @@ -89,7 +89,6 @@ android:layout_marginBottom="@dimen/spacing_medium" style="@style/Widget.AppCompat.ProgressBar.Horizontal" android:progressBackgroundTint="@color/gray_600" - android:progressBackgroundTintMode="src_over" android:progressTint="@color/yellow_50"/> @@ -51,6 +51,7 @@ android:layout_height="wrap_content" android:minHeight="?attr/actionBarSize" android:theme="@style/Toolbar" + android:background="?attr/colorPrimaryDark" app:layout_collapseMode="pin" app:popupTheme="@style/Theme.AppCompat.Light"> @@ -43,20 +42,29 @@ android:paddingEnd="@dimen/spacing_large" android:paddingBottom="@dimen/spacing_large"> + style="@style/TaskFormTextInputLayoutAppearance" + app:boxStrokeColor="?attr/colorPrimary" + android:backgroundTint="?attr/colorPrimary" + android:alpha="0.75"> + android:layout_height="wrap_content" + app:boxStrokeColor="?attr/colorPrimary" + android:backgroundTint="?attr/colorPrimary" + android:alpha="0.75"> + + android:layout_height="55dp" + android:paddingStart="16dp" + android:paddingEnd="16dp" + android:gravity="bottom"> - + tools:text="Test header" + style="@style/Overline" + android:textColor="@color/gray_200"/> + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Habitica/res/layout/drawer_main.xml b/Habitica/res/layout/drawer_main.xml index 60a527613..ce2b4dde3 100644 --- a/Habitica/res/layout/drawer_main.xml +++ b/Habitica/res/layout/drawer_main.xml @@ -68,7 +68,8 @@ android:background="@color/transparent" android:src="@drawable/menu_notifications" android:layout_centerVertical="true" - android:clickable="false" /> + android:clickable="false" + android:contentDescription="@string/notifications" /> + android:clickable="false" + android:contentDescription="@string/inbox" /> - + android:layout_centerVertical="true" + android:contentDescription="@string/PS_settings_title" /> + + + + + + + diff --git a/Habitica/res/layout/main_navigation_view.xml b/Habitica/res/layout/main_navigation_view.xml index 43a1d455f..ce12da747 100644 --- a/Habitica/res/layout/main_navigation_view.xml +++ b/Habitica/res/layout/main_navigation_view.xml @@ -5,7 +5,8 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" - tools:parentTag="android.widget.RelativeLayout"> + tools:parentTag="android.widget.RelativeLayout" + tools:background="@color/gray_600"> + android:layout_height="62dp" + android:src="@drawable/bottom_navigation_inset" + android:layout_gravity="top"/> diff --git a/Habitica/res/layout/menu_bottom_sheet.xml b/Habitica/res/layout/menu_bottom_sheet.xml index 943ae2746..fcc654b20 100644 --- a/Habitica/res/layout/menu_bottom_sheet.xml +++ b/Habitica/res/layout/menu_bottom_sheet.xml @@ -1,7 +1,23 @@ + + \ No newline at end of file diff --git a/Habitica/res/layout/mount_overview_item.xml b/Habitica/res/layout/mount_overview_item.xml index 136617b28..2c96f6fb1 100644 --- a/Habitica/res/layout/mount_overview_item.xml +++ b/Habitica/res/layout/mount_overview_item.xml @@ -2,41 +2,42 @@ - style="@style/CardContent"> + app:actualImageScaleType="fitCenter" /> + - + + + + + + + \ No newline at end of file diff --git a/Habitica/res/layout/pet_overview_item.xml b/Habitica/res/layout/pet_overview_item.xml index 7aa7a1f06..cea0dfe82 100644 --- a/Habitica/res/layout/pet_overview_item.xml +++ b/Habitica/res/layout/pet_overview_item.xml @@ -2,37 +2,42 @@ - - style="@style/CardContent"> + + android:layout_width="68dp" + android:layout_height="68dp" + android:layout_gravity="center" + app:actualImageScaleType="fitCenter" + /> + + android:layout_height="@dimen/shop_scene_height" + android:paddingStart="-8dp" + android:paddingEnd="-8dp"/> + android:layout_marginLeft="16dp" + android:layout_marginRight="16dp" /> + tools:text="To Do Title" /> @@ -24,7 +21,6 @@ android:layout_width="match_parent" android:layout_height="@dimen/bar_size" android:layout_toEndOf="@id/ic_header" - android:layout_toRightOf="@id/ic_header" android:orientation="horizontal"/> + android:paddingTop="2dp"> + android:name="com.habitrpg.android.habitica.ui.fragments.inventory.stable.PetDetailRecyclerFragment"> @@ -319,8 +318,7 @@ + android:name="com.habitrpg.android.habitica.ui.fragments.inventory.stable.MountDetailRecyclerFragment"> diff --git a/Habitica/res/values-da/strings.xml b/Habitica/res/values-da/strings.xml index f1ea13f04..5bcbabe49 100755 --- a/Habitica/res/values-da/strings.xml +++ b/Habitica/res/values-da/strings.xml @@ -36,7 +36,7 @@ Inviteret til Quest Værdi - Ny To-Do + Ny To Do Ny Belønning Ny Daglig Opgave Ny Vane @@ -136,7 +136,7 @@ Medlemmer Vaner Daglige - To-Dos + To Do\'s Belønninger Er du sikker? Vil du virkelig slette? @@ -302,10 +302,10 @@ Tilføj Opgave Tilføj Vane Tilføj Daglig Opgave - Tilføj To-Do + Tilføj To Do Tilføj Belønning Habitica Daglige Opgaver - Habitica To-Do Liste + Habitica To Do Liste Habitica Tilføj Opgave Køb @@ -334,7 +334,7 @@ Daglig Vane Belønning - To-Do + To Do Officiel Deltager Udfordring diff --git a/Habitica/res/values-de/strings.tutorial.xml b/Habitica/res/values-de/strings.tutorial.xml index 829e8e7e1..2025c4a05 100644 --- a/Habitica/res/values-de/strings.tutorial.xml +++ b/Habitica/res/values-de/strings.tutorial.xml @@ -7,8 +7,8 @@ Probier es aus! Du kannst die anderen Aufgaben-Typen kennenernen, indem du die untere Navigation benutzt. Erstelle tägliche Aufgaben für zeitkritische Aufgaben, die regelmäßig erledigt werden müssen. Pass auf - wenn Du eine dieser Aufgaben nicht erledigst, wird Deinem Avatar über Nacht Schaden zugefügt. Erledige Deine Aufgaben täglich und Du wirst tolle Belohnungen erhalten! - Nutze die To-Dos, um Aufgaben zu organisieren, die du nur einmalig erledigen musst. - Falls Dein To-Do an einem bestimmten Tag erledigt sein muss, kannst du dafür ein Fälligkeitsdatum erstellen. Es sieht so aus, als ob du jetzt auch eine Aufgabe abhaken kannst - probier es aus! + Nutze die To Do\'s, um Aufgaben zu organisieren, die du nur einmalig erledigen musst. + Falls Dein To Do an einem bestimmten Tag erledigt sein muss, kannst du dafür ein Fälligkeitsdatum erstellen. Es sieht so aus, als ob du jetzt auch eine Aufgabe abhaken kannst - probier es aus! Kaufe dir mit dem verdienten Gold Ausrüstung für deinen Avatar! Du kannst auch benutzerdefinierte Belohnungen erstellen, die sich auf etwas in Deinem wirklichen Leben beziehen, je nachdem was Dich motiviert. Das war\'s für das Erste. Falls du eine Erinnerungshilfe brauchst, dann schau in die FAQs. diff --git a/Habitica/res/values-de/strings.xml b/Habitica/res/values-de/strings.xml index 897543a21..916ddc6bf 100644 --- a/Habitica/res/values-de/strings.xml +++ b/Habitica/res/values-de/strings.xml @@ -106,7 +106,7 @@ Mitglieder Gewohnheiten Tagesaufgaben - To-Dos + To Do\'s Belohnungen Ja Nein @@ -305,12 +305,12 @@ Neue Aufgabe hinzufügen Neue Gewohnheit hinzufügen Neue Tagesaufgabe hinzufügen - Neues To-Do hinzufügen + Neues To Do hinzufügen Neue Belohnung hinzufügen Du hast alle Deine Tagesaufgaben erledigt. Gut gemacht! Habitica: Gewohnheit Habitica: Tagesaufgaben - Habitica To-Do-Liste + Habitica To Do-Liste Google Play Dienste konnten nicht gefunden werden. Kaufen Das Erwerben von Edelsteinen unterstützt die Entwickler und hilft Habitica am Laufen zu halten @@ -344,7 +344,7 @@ Abonnieren Alexander der Händler wird Dir nun Edelsteine für jeweils 20 Goldmünzen pro Stück verkaufen.\n\nSeine monatlichen Lieferungen sind zunächst auf 25 Edelsteine pro Monat beschränkt, welche jedoch durch die Dauer Deines Abonnements erhöht werden.\n\nDas Limit steigt um 5 Edelsteine für jeweils drei durchgehend abonnierte Monate, bis zu einem Maximum von 50 Edelsteinen pro Monat! Jeden Monat wirst Du einen einzigartigen optischen Gegenstand für Deinen Avatar erhalten.\n\nAußerdem gewährt Dir der Mysteriöse Zeitreisende für jeweils drei durchgehend abonnierte Monate Zugang zu historischen (und futuristischen!) optischen Gegenständen. - Verlängert die Historie erledigter To-Dos und Aufgaben. + Verlängert die Historie erledigter To Do\'s und Aufgaben. Verdoppelte Beute-Limits ermöglichen es Dir, täglich von erledigten Aufgaben mehr Gegenstände zu finden und deinen Stall schneller zu vervollständigen! +%d Mystische Sanduhr Zahlungsmethode @@ -378,7 +378,7 @@ Tagesaufgabe Gewohnheit Belohnung - To-Do + To Do Offiziell Herausforderung Du nimmst im Moment an keiner Herausforderung teil! @@ -426,8 +426,8 @@ Gewohnheiten sind Aufgaben ohne festen Zeitplan. Du kannst sie mehrmals am Tag oder gar nicht abhaken. Du hast keine Tagesaufgaben Tagesaufgaben sind sich regelmäßig wiederholende Aufgaben. Such Dir den Zeitplan aus, der Dir am besten passt! - Du hast keine To-Dos - To-Dos sind Aufgaben, die nur einmal abgehakt werden. Füge Unteraufgaben zu den To-Dos hinzu, um deren Wert zu steigern. + Du hast keine To Do\'s + To Do\'s sind Aufgaben, die nur einmal abgehakt werden. Füge Unteraufgaben zu den To Do\'s hinzu, um deren Wert zu steigern. Du hast keine Belohnungen Setze Justins Einführung zurück Bitte lies Dir unsere Community-Richtlinien durch, bevor du etwas postest @@ -620,7 +620,7 @@ Die Quest-Schriftrolle wird an den Quest-Besitzer zurückgegeben. Hast Du Deine Aufgaben heute abgehakt\? Es gibt eine Menge freizuschalten und zu entdecken während Du in den Leveln aufsteigst, also bleib an Deinen Aufgaben dran und hab Spaß! Willst Du etwas Neues ausprobieren\? Tritt einer Herausforderung bei um Deine Aufgabenliste zu erweitern und ein paar Edelsteine zu gewinnen! - Erweitere Deine To-Dos mit Kontrolllisten um Deine Belohnungen zu vervielfachen! + Erweitere Deine To Do\'s mit Kontrolllisten um Deine Belohnungen zu vervielfachen! Du kannst verändern wie oft sich eine Tagesaufgabe wiederholt. Sogar gelegentliche Aufgaben können festgelegt werden. Du kannst auch Erinnerungen für Deine Tagesaufgaben einplanen. Deine Aufgaben hin und wieder zu überdenken hilft Dir auf dem richtigen Weg zu bleiben. @@ -643,7 +643,7 @@ Die Quest-Schriftrolle wird an den Quest-Besitzer zurückgegeben. Wie sollen wir Dich nennen? Anzeigenamen müssen zwischen 1 und 30 Zeichen lang sein Tritt Habitica bei (Hak mich ab!) - Du kannst dieses To-Do erledigen, bearbeiten oder entfernen. + Du kannst dieses To Do erledigen, bearbeiten oder entfernen. Belohne Dich Schau Fernsehen, spiel ein Spiel, iss etwas Süßes, es liegt ganz an Dir! Internetseite besuchen diff --git a/Habitica/res/values-en-rGB/strings.tutorial.xml b/Habitica/res/values-en-rGB/strings.tutorial.xml index 6a115f2c0..9745f2f18 100644 --- a/Habitica/res/values-en-rGB/strings.tutorial.xml +++ b/Habitica/res/values-en-rGB/strings.tutorial.xml @@ -7,8 +7,8 @@ Give it a shot! You can explore the other task types through the bottom navigation. Make Dailies for time sensitive tasks that need to be done on a regular schedule. Be careful — if you miss one, your avatar will take damage overnight. Checking them off consistently brings great rewards! - Use To-dos to keep track of tasks you need to do just once. - If your To-do has to be done by a certain time, set a due date. Looks like you can check one off — go ahead! + Use To Do\'s to keep track of tasks you need to do just once. + If your To Do has to be done by a certain time, set a due date. Looks like you can check one off — go ahead! Buy gear for your avatar with the gold you earn! You can also make real-world Custom Rewards based on what motivates you. That\'s all for now. If you need a reminder, check the FAQ section. diff --git a/Habitica/res/values-en-rGB/strings.xml b/Habitica/res/values-en-rGB/strings.xml index ba0000027..8f18b12ef 100644 --- a/Habitica/res/values-en-rGB/strings.xml +++ b/Habitica/res/values-en-rGB/strings.xml @@ -102,7 +102,7 @@ Members Habits Dailies - To-Dos + To Do\'s Rewards Yes No @@ -280,12 +280,12 @@ Add Task Add Habit Add Daily - Add To-Do + Add To Do Add Reward You completed all your dailies. Well done! Habitica Do Habit Habitica Dailies - Habitica To-Do List + Habitica To Do List Google Play services could not be found. Purchase Buying gems supports the developers and helps keep Habitica running @@ -318,7 +318,7 @@ Subscribe Alexander the Merchant will now sell you gems for 20 gold each!\n\nHis monthly shipments are initially capped at 25 Gems per month, but can increase based on your subscription length.\n\nThe cap increases by 5 Gems for every three months of consecutive subscription, up to a maximum of 50 Gems per month! Each month you will receive a unique cosmetic item for your avatar!\n\nPlus, for every three months of consecutive subscription, the Mysterious Time Travellers will grant you access to historic (and futuristic!) cosmetic items. - Makes completed To-Dos and task history available for longer. + Makes completed To Do\'s and task history available for longer. Double drop caps will let you receive more items from your completed tasks every day, helping you complete your stable faster! +%d Mystic Hourglass Payment method @@ -351,7 +351,7 @@ Daily Habit Reward - To-Do + To Do Official Challenge You’re not part of any Challenges right now! @@ -394,8 +394,8 @@ Habits are tasks that don&rsquo;t have a rigid schedule. You can check them off many times a day, or not at all. You don&rsquo;t have any Dailies Dailies are tasks that repeat on a regular basis. Choose the schedule that works for you! - You don&rsquo;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&rsquo;t have any To Do\'s + To Do\'s are tasks that only need to be completed once. Add checklists to your To Do\'s to increase their value. You don&rsquo;t have any Rewards Reset Justin&rsquo;s Walkthrough Please read our Community Guidelines before posting @@ -622,7 +622,7 @@ Did you check off your tasks today\? There\'s lots to unlock and discover as you level up, so keep up with your tasks and have fun! Want to try something new\? Join a Challenge to expand your task list and win some Gems! - Add checklists to your To-Dos to multiply your rewards! + Add checklists to your To Do\'s to multiply your rewards! You can change how often each Daily repeats. Even infrequent tasks can be scheduled. You can schedule specific reminders for Dailies too. Occasionally re-evaluating your tasks can help keep you on the right path. @@ -657,7 +657,7 @@ What should we call you\? Display names must be between 1 and 30 characters Join Habitica (Check me off!) - You can either complete this To-Do, edit it, or remove it. + You can either complete this To Do, edit it, or remove it. Reward yourself Watch TV, play a game, eat a treat, it’s up to you! STR: diff --git a/Habitica/res/values-in/strings.tutorial.xml b/Habitica/res/values-in/strings.tutorial.xml index 9624db2fa..da5d2aa6e 100644 --- a/Habitica/res/values-in/strings.tutorial.xml +++ b/Habitica/res/values-in/strings.tutorial.xml @@ -7,8 +7,8 @@ Coba deh! Kamu bisa mengeksplor macam-macam tugas lain melalui tombol navigasi di bawah. Buat Keseharian untuk tugas-tugas yang terikat waktu dan butuh untuk diselesaikan secara berkala. Hati-hati — kalau ada yang terlewat, avatar kamu akan terkena damage besoknya. Menyelesaikan Keseharian secara konsisten memberi banyak keuntungan! - Gunakan To-do untuk mencatat tugas-tugas yang hanya perlu kamu lakukan sekali. - Kalau To-do-mu harus diselesaikan pada waktu tertentu, tetapkan tenggat waktunya. Sepertinya ada satu yang bisa kamu centang sekarang — sana! + Gunakan To Do untuk mencatat tugas-tugas yang hanya perlu kamu lakukan sekali. + Kalau To Do-mu harus diselesaikan pada waktu tertentu, tetapkan tenggat waktunya. Sepertinya ada satu yang bisa kamu centang sekarang — sana! Beli perlengkapan untuk avatarmu menggunakan koin emas yang kamu dapatkan! Kamu juga bisa membuat Hadiah yang benar-benar ada di dunia nyata tergantung apa yang memotivasimu. Itu dulu untuk sekarang. Kalau kamu butuh pengingat, lihat bagian FAQ. diff --git a/Habitica/res/values-in/strings.xml b/Habitica/res/values-in/strings.xml index 827b3a8e7..5c7277a46 100644 --- a/Habitica/res/values-in/strings.xml +++ b/Habitica/res/values-in/strings.xml @@ -102,7 +102,7 @@ Anggota Kebiasaan Keseharian - To-Do + To Do Hadiah Ya Tidak @@ -296,7 +296,7 @@ Anda telah menyelesaikan semua keseharianmu. Selamat! Habitica Melakukan Kebiasaan Keseharian Habitica - Daftar To-Do Habitica + Daftar To Do Habitica Google play services tidak dapat ditemukan. Bayar Membeli gem dapat mendukung pengembang dan menjaga Habitica tetap berjalan @@ -329,7 +329,7 @@ Berlangganan Alexander sang Saudagar akan menjual permata kepadamu masing-masing seharga 20 koin emas!\n\nPengiriman bulanannya dibatasi di 25 Permata per bulan, tapi bisa meningkat berdasarkan lama berlangganan-mu. \n\nBatasnya meningkat sebanyak 5 Permata setiap tiga bulan berturut-turut kamu berlangganan, hingga batas maksimal 50 Permata per bulan! Setiap bulan kamu akan menerima sebuah item kosmetik unik untuk avatarmu!\n\nPlus, setiap tiga bulan berturut-turut kamu berlangganan, sang Penjelajah Waktu Misterius akan memberimu akses kepada item kosmetik bersejarah (atau futuristik!). - Buat To-Do yang telah diselesaikan dan riwayat tugas tersedia lebih lama. + Buat To Do yang telah diselesaikan dan riwayat tugas tersedia lebih lama. Dua kali lipat batas drop akan mengizinkanmu mendapat lebih banyak item untuk tugas yang terselesaikan setiap hari, membantumu melengkapi istalmu lebih cepat! +%d Jam Pasir Mistis Metode Pembayaran @@ -362,7 +362,7 @@ Keseharian Kebiasaan Hadiah - To-Do + To Do Resmi Tantangan Kamu tidak mengambil bagian dalam Tantangan apapun saat ini! @@ -407,8 +407,8 @@ Kebiasaan adalah tugas yang tidak memerlukan jadwal ketat. Kamu bisa menyelesaikannya beberapa kali sehari, atau tidak sama sekali. Kamu tidak punya Keseharian apapun Keseharian adalah tugas yang berulang setiap waktu tertentu secara teratur. Pilih jadwal yang bekerja untukmu! - Kamu tidak punya To-Do apapun - To-Do adalah tugas yang hanya perlu diselesaikan sekali. Tambahkan ceklis kepada To-Do-mu untuk meningkatkan harganya. + Kamu tidak punya To Do apapun + To Do adalah tugas yang hanya perlu diselesaikan sekali. Tambahkan ceklis kepada To Do-mu untuk meningkatkan harganya. Kamu tidak punya Hadiah apapun Ulang Panduan Justin Tolong baca Pedoman Komunitas kami sebelum posting diff --git a/Habitica/res/values-it/strings.tutorial.xml b/Habitica/res/values-it/strings.tutorial.xml index 787597fa7..0c364ede2 100644 --- a/Habitica/res/values-it/strings.tutorial.xml +++ b/Habitica/res/values-it/strings.tutorial.xml @@ -7,8 +7,8 @@ Ora provaci tu! Puoi trovare altri tipi di attività usando la barra di navigazione in basso. Crea Daily per attività che devono essere svolte periodicamente. Sta attento - se ne salti una il tuo avatar subirà dei danni durante la notte. Completale con regolarità e otterrai grandi premi! - Usa le To-do per tenere traccia delle cose che devi fare soltanto una volta. - Se le tue To-do devono essere completate entro un certo giorno, imposta una data di scadenza. Pare che tu possa completarne una, prova! + Usa le To Do per tenere traccia delle cose che devi fare soltanto una volta. + Se le tue To Do devono essere completate entro un certo giorno, imposta una data di scadenza. Pare che tu possa completarne una, prova! Compra oggetti per il tuo avatar con l\'oro che ottieni! Puoi anche creare delle ricompense reali, basate su ciò che ti motiva. Per ora è tutto. Se hai bisogno di un ripasso, consulta la sezione FAQ. diff --git a/Habitica/res/values-it/strings.xml b/Habitica/res/values-it/strings.xml index d4b0684ec..b54de3ce2 100644 --- a/Habitica/res/values-it/strings.xml +++ b/Habitica/res/values-it/strings.xml @@ -106,7 +106,7 @@ Membri Abitudini Daily - To-do + To Do Ricompense No @@ -305,12 +305,12 @@ Le Dailies mancate e le cattive Abitudini non li danneggiano molto, e hanno semp Aggiungi Attività Aggiungi Abitudine Aggiungi Daily - Aggiungi To-Do + Aggiungi To Do Aggiungi Ricompensa Hai completato tutte le tue Daily. Ottimo lavoro! Habitica esegui Abitudine Habitica Daily - Habitica lista To-Do + Habitica lista To Do Impossibile trovare Google Play Services. Compra Comprando gemme supporti gli sviluppatori e tieni in vita Habitica @@ -344,7 +344,7 @@ Le Dailies mancate e le cattive Abitudini non li danneggiano molto, e hanno semp Abbonati Ora Alexander il Mercante ti venderà Gemme al prezzo di 20 Oro l\'una!\n\nLe sue consegne mensili sono inizialmente limitate a 25 Gemme al mese, ma possono aumentare a seconda della durata del tuo abbonamento.\n\nIl limite aumenta di 5 Gemme ogni tre mesi di abbonamento consecutivi, fino ad un massimo di 50 Gemme al mese! Ogni mese riceverai un oggetto davvero unico per il tuo avatar!\n\nIn più, ogni tre mesi di abbonamento consecutivi, i misteriosi Viaggiatori del Tempo ti permetteranno di ottenere oggetti antichi (e futuri!). - Rende le To-Do completate e la cronologia dei progressi delle attività disponibili più a lungo. + Rende le To Do completate e la cronologia dei progressi delle attività disponibili più a lungo. Raddoppiare il numero massimo di drop giornalieri ti farà ricevere più oggetti dalle attività completate ogni giorno, aiutandoti a completare la tua scuderia più velocemente! Limite di 25 Gemme Limite di 30 Gemme @@ -382,7 +382,7 @@ Le Dailies mancate e le cattive Abitudini non li danneggiano molto, e hanno semp Daily Abitudine Ricompensa - To-Do + To Do Ufficiale Sfida Al momento non stai partecipando a nessuna sfida! @@ -428,8 +428,8 @@ Le Dailies mancate e le cattive Abitudini non li danneggiano molto, e hanno semp Le Abitudini sono attività che possono essere svolte in modo molto flessibile. Puoi usarle diverse volte al giorno, o non usarle affatto. Non hai nessuna Daily Le Daily sono delle attività che vanno svolte regolarmente. Scegli la frequenza con cui ti trovi meglio! - Non hai alcuna To-do. - Le To-do sono attività da completare solo una volta. Aggiungi una checklist alle tue To-do per aumentarne il valore. + Non hai alcuna To Do. + Le To Do sono attività da completare solo una volta. Aggiungi una checklist alle tue To Do per aumentarne il valore. Non hai nessuna Ricompensa Ripristina la spiegazione introduttiva di Justin Per favore, leggi le Linee guida della community prima di scrivere un post. @@ -617,7 +617,7 @@ Le Dailies mancate e le cattive Abitudini non li danneggiano molto, e hanno semp Hai completato le tue attività oggi? Sbloccherai e scoprirai cose nuove man mano che salirai di livello, quindi continua a completare le tue attività e divertiti! Vuoi provare qualcosa di nuovo? Unisciti ad una Sfida per espandere la tua lista delle attività e vincere qualche Gemma! - Aggiungi delle checklist alle tue To-Do perché le tue ricompense si moltiplichino! + Aggiungi delle checklist alle tue To Do perché le tue ricompense si moltiplichino! Puoi regolare la frequenza di ripetizione di ogni Daily. Perfino la data delle attività infrequenti può essere programmata. Puoi impostare dei promemoria specifici anche per le Daily. Rivalutare occasionalmente le tue attività può aiutarti a mantenere la giusta strada. @@ -653,7 +653,7 @@ Le Dailies mancate e le cattive Abitudini non li danneggiano molto, e hanno semp Come dovremmo chiamarti? Il nome pubblico dev\'essere tra 1 e 30 caratteri. Raggiungi Habitica (spunta questa casella!) - Puoi completare questa To-Do, modificarla, o rimuoverla. + Puoi completare questa To Do, modificarla, o rimuoverla. Premiati Guarda la TV, divertiti con un videogioco, mangia un dolcetto, a te la scelta! FOR: @@ -708,7 +708,7 @@ Le Dailies mancate e le cattive Abitudini non li danneggiano molto, e hanno semp Riepilogo della Gilda O cancellala dalla schermata di modifica Clicca qui per modificarla in una cattiva abitudine che vorresti perdere - Può essere un\'Abitudine, una Daily o una To-Do + Può essere un\'Abitudine, una Daily o una To Do Aggiungi un\'attività su Habitica Costo Ti sei unito alla Gilda diff --git a/Habitica/res/values-ja/strings.tutorial.xml b/Habitica/res/values-ja/strings.tutorial.xml index 4a65497a9..2eaf48580 100644 --- a/Habitica/res/values-ja/strings.tutorial.xml +++ b/Habitica/res/values-ja/strings.tutorial.xml @@ -7,8 +7,8 @@ 挑戦してみよう!下のほうのナビゲーションから、異なるタスクタイプも選ぶことができます。 通常のスケジュールにしたがって行うべき期限付きのタスクは、日課に設定しましょう。 気をつけて…もし一つでもやり逃すと、あなたのアバターは夜のうちにダメージを受けてしまいます。継続的にチェックを入れることで、素晴らしい報酬を手に入れましょう! - 単発のタスクをこなすために、To-Doを使いましょう。 - もしTo-Doに期限があるのなら、設定しましょう。あなたにはクリアできるはず…がんばって! + 単発のタスクをこなすために、To Doを使いましょう。 + もしTo Doに期限があるのなら、設定しましょう。あなたにはクリアできるはず…がんばって! 得られたゴールドであなたのアバター用に装備を購入しましょう! あなた自身のモチベーションを上げるために、現実世界での報酬を登録することもできます。 これで説明は終わり。もし忘れちゃったときは,FAQをチェックしてみてね。 diff --git a/Habitica/res/values-ja/strings.xml b/Habitica/res/values-ja/strings.xml index 85e5d4b0f..5134148c8 100644 --- a/Habitica/res/values-ja/strings.xml +++ b/Habitica/res/values-ja/strings.xml @@ -106,7 +106,7 @@ メンバー 習慣 日課 - To-Do + To Do ごほうび はい いいえ @@ -304,12 +304,12 @@ タスクを追加 習慣を追加 日課を追加 - To-Do を追加 + To Do を追加 ごほうびを追加 日課をすべて完了した。お見事! Habitica の習慣 Habitica の日課 - Habitica のTo-Do リスト + Habitica のTo Do リスト Google Play サービスが見つかりません。 購入 ジェムを購入することで、開発者を支援し、Habitica の運営を維持する手助けができます @@ -348,7 +348,7 @@ 毎月、アバターを個性的に装飾するアイテムが手に入ります! \n \nさらに寄付の継続3カ月ごとに、謎のタイムトラベラーによって、歴史的(そして未来的!)な装飾アイテムが入手可能になります。 - 完了した To-Do やタスクの履歴を、より長期間閲覧可能になります。 + 完了した To Do やタスクの履歴を、より長期間閲覧可能になります。 落とし物の上限が2倍になり、毎日の完了したタスクから、より多くのアイテムを獲得することができます。動物小屋のコンプリートも早くなります! 上限 25 ジェム 上限 30 ジェム @@ -385,7 +385,7 @@ 日課 習慣 ごほうび - To-Do + To Do Habitica公式 チャレンジ 今参加しているチャレンジはありません! @@ -433,8 +433,8 @@ 習慣は、タスクの中でもこなすスケジュールがきっちりと決まっていないもの。1日に何回チェックしてもいいし、チェックしない日があっても大丈夫。 日課を持っていません 日課は一定のペースでこなすタスクだよ。自分に合ったスケジュールを組もう! - To-Doがありません - To-Doは一度だけ実行すればいいタスク。チェックリストを加えて忘れないようにしよう。 + To Doがありません + To Doは一度だけ実行すればいいタスク。チェックリストを加えて忘れないようにしよう。 ごほうびがありません チュートリアルをリセット 投稿する前に、私たちのコミュニティガイドラインを見直してください @@ -625,7 +625,7 @@ 今日のタスクはチェックしましたか? あなたがレベルアップするとアンロックされたり発見したりできるものがたくさんあります。だからタスクへ取り組み続けて、楽しんでいきましょう! 何か新しいことに挑戦したいですか? あなたのタスクリストを発展させてくれるチャレンジに参加して、いくつかのジェムを獲得しましょう! - 報酬を増やすためにTo-Doにチェックリストを加えてみましょう! + 報酬を増やすためにTo Doにチェックリストを加えてみましょう! それぞれの日課をどのくらいの頻度で繰り返すかを変更することができます。たまにしなかないタスクでさえスケジュールすることができます。 日課に対して特定のリマインダーを設定することもできます。 ときどきタスクを見直して再評価することは、正しい道筋を進み続けるために役立ちます。 @@ -660,7 +660,7 @@ あなたを何と呼べばいいですか? 表示名は1~30文字以内にしてください Habiticaに参加する(チェックして!) - To-Doは完了したり、編集したり、削除することができます。 + To Doは完了したり、編集したり、削除することができます。 自分自身へごほうびを与える TVを見ましょう、ゲームで遊びましょう、おやつを食べましょう、あなた次第です! 力: diff --git a/Habitica/res/values-nl/strings.tutorial.xml b/Habitica/res/values-nl/strings.tutorial.xml index 00166cd28..a85bd493b 100644 --- a/Habitica/res/values-nl/strings.tutorial.xml +++ b/Habitica/res/values-nl/strings.tutorial.xml @@ -7,8 +7,8 @@ Probeer het maar! Je kan de andere soorten taken doornemen met de onderstaande navigatie. Maak dagelijkse taken voor tijdgevoelige taken die volgens een vast schema gedaan moeten worden. Wees voorzichtig - als je er een mist, verliest je avatar gezondheid de volgende dag. Vink ze regelmatig af voor grootse beloningen! - Gebruik to-do\'s om taken bij te houden die je slechts één keer moet doen. - Als je to-do tegen een bepaalde dag afgewerkt moet zijn, voer dan een einddatum in. Je kan er een afvinken - doe maar! + Gebruik To Do\'s om taken bij te houden die je slechts één keer moet doen. + Als je To Do tegen een bepaalde dag afgewerkt moet zijn, voer dan een einddatum in. Je kan er een afvinken - doe maar! Koop een uitrusting voor je avatar met het goud dat je verdient! Je kan ook zelf beloningen maken voor in de echte wereld, gebaseerd op wat je motiveert. Dat was het voorlopig. Als je iets vergeet, kijk dan in de FAQ sectie. diff --git a/Habitica/res/values-nl/strings.xml b/Habitica/res/values-nl/strings.xml index b8e0e58ca..3572a6f8d 100644 --- a/Habitica/res/values-nl/strings.xml +++ b/Habitica/res/values-nl/strings.xml @@ -106,7 +106,7 @@ Leden Gewoontes Dagelijkse taken - To-do\'s + To Do\'s Beloningen Ja Nee @@ -304,12 +304,12 @@ Voeg taak toe Voeg Gewoonte toe Voeg Dagelijkse taak toe - Voeg To-do toe + Voeg To Do toe Voeg Beloning toe Je hebt al je Dagelijkse taken voltooid. Goed gedaan! Habitica Doe Gewoonte Habitica Dagelijkse taken - Habitica to-do lijst + Habitica To Do lijst Google play diensten niet gevonden. Kopen Door het kopen van edelstenen steun je de ontwikkelaars en help je Habitica online te houden @@ -343,7 +343,7 @@ Abonneer Alexander de Koopman zal nu edelstenen aan je verkopen voor 20 goud per edelsteen!\n\nZijn maandelijkse levering is initieel beperkt tot 25 edelstenen per maand, maar stijgt afhankelijk van de duur van je abonnement.\n\nDit limiet verhoogt met 5 edelstenen voor elke 3 maanden van opeenvolgende abonnement periodes, tot een maximum van 50 edelstenen per maand! Iedere maand ontvang je een uniek cosmetisch voorwerp voor je avatar!\n\nEn voor iedere 3 maanden van opeenvolgende abonnementsperiodes, geven de mysterieuze tijdreizigers je toegang tot historische (en futuristische!) cosmetische voorwerpen. - Maakt voltooide To-do\'s en taakgeschiedenis langer beschikbaar. + Maakt voltooide To Do\'s en taakgeschiedenis langer beschikbaar. Dubbele vondstlimieten laten je dagelijks meer voorwerpen verdienen van je voltooide taken, waardoor je je stal sneller kan voltooien! 25 Edelsteen drempel 30 Edelsteen drempel @@ -381,7 +381,7 @@ Dagelijkse taak Gewoonte Beloning - To-do + To Do Officieel Uitdaging Je maakt geen deel uit van uitdagingen op dit moment! @@ -426,8 +426,8 @@ Gewoontes zijn taken die geen vast schema hebben. Je kan ze meerdere keren per dag afvinken of helemaal niet. Je hebt geen dagelijkse taken Dagelijkse taken zijn taken die op een vast schema herhalen. Kies de routine die voor jou werkt! - Je hebt geen to-do\'s - To-do\'s zijn taken die slechts een keer voltooid moeten worden. Voeg checklijsten aan je to-do\'s toe om hun waarde te verhogen. + Je hebt geen To Do\'s + To Do\'s zijn taken die slechts een keer voltooid moeten worden. Voeg checklijsten aan je To Do\'s toe om hun waarde te verhogen. Je hebt geen beloningen Herstart Justins inleiding Lees alsjeblieft onze gemeenschapsrichtlijnen voordat je berichten plaatst @@ -615,7 +615,7 @@ Heb je jou taken afgevinkt vandaag? Er is veel om te ontgrendelen en te ontdekken als je niveau omhoog gaat, dus houd je taken bij en veel plezier! Wil je iets nieuws proberen? Sluit je aan bij een uitdaging om je takenlijst te vergroten en win wat Edelstenen! - Voeg checklijsten toe aan jou To-Dos om de beloningen te vermenigvuldigen! + Voeg checklijsten toe aan jou To Do\'s om de beloningen te vermenigvuldigen! Je kunt veranderen hoe vaak een Dagelijkse taak zich herhaalt. Zelfs onregelmatige taken kunnen ingepland worden. Je kunt ook specifieke herinneringen voor Dagelijkse taken inplannen. Het van tijd tot tijd herevalueren van je taken kan je helpen om op het rechte pad te blijven. @@ -643,7 +643,7 @@ Gebruikersnaam gekopieerd naar het prikbord Gebruikersnamen moeten tussen 1 en 30 karakters lang zijn Sluit je aan bij Habitica (Aanvinken) - Je kan deze To-do voltooien, bewerken of verwijderen + Je kan deze To Do voltooien, bewerken of verwijderen Beloon jezelf Kijk TV, speel een spel, eet een belonging, aan jou de keuze! KRA: diff --git a/Habitica/res/values-ro/strings.xml b/Habitica/res/values-ro/strings.xml index 745465d84..211d3921f 100755 --- a/Habitica/res/values-ro/strings.xml +++ b/Habitica/res/values-ro/strings.xml @@ -38,7 +38,7 @@ Invitat în Expediție Valoare - Nou To-Do + Nou To Do Răsplată noua Sarcină Zilnică Noua Obicei Nou diff --git a/Habitica/res/values-sv/strings.xml b/Habitica/res/values-sv/strings.xml index 78347ae35..1188c11b9 100755 --- a/Habitica/res/values-sv/strings.xml +++ b/Habitica/res/values-sv/strings.xml @@ -463,7 +463,7 @@ Daglig Vana Belöning - To-Do + To Do Officiell Deltar Utmaning @@ -517,8 +517,8 @@ Vanor är uppgifter som inte har något fast schema. Du kan klara dom flera gånger per dag, eller inga alls. Du har inga \"Dailies\" \"Dailies\" är uppgifter som upprepas på återkommande vis. Välj ett schema som passar dig! - Du har inga To-Do\'s - To-Do\'s är uppgifter som bara behöver göras en gång. Lägg till checklistor i dina To-Do\'s för att öka på värdet. + Du har inga To Do\'s + To Do\'s är uppgifter som bara behöver göras en gång. Lägg till checklistor i dina To Do\'s för att öka på värdet. Du har inga Belöningar Återställ Justins genomgång Snälla läs Gemenskapens Riktlinjer innan du du gör ett inlägg diff --git a/Habitica/res/values-zh/strings.xml b/Habitica/res/values-zh/strings.xml index 4470e0edd..3b8577b30 100644 --- a/Habitica/res/values-zh/strings.xml +++ b/Habitica/res/values-zh/strings.xml @@ -629,7 +629,7 @@ 分配点数的功能在10级解锁 你干的越多,奖励就会越多。溪流终将汇聚成大海。 想试试新东西?加入一个挑战来扩充你的人物列表并赢得一些宝石吧! - 把待办事项都加到你的To-Do列表来赢取奖励! + 把待办事项都加到你的To Do列表来赢取奖励! 你可以修改你的每日任务的重复频率。多罕见的任务都能被安排得明明白白。 你也可以安排特定的日程提醒。 深呼吸!保持专注!你能办到! diff --git a/Habitica/res/values/attrs.xml b/Habitica/res/values/attrs.xml index c892eeec8..9626196e1 100644 --- a/Habitica/res/values/attrs.xml +++ b/Habitica/res/values/attrs.xml @@ -9,6 +9,8 @@ + + diff --git a/Habitica/res/values/colors.xml b/Habitica/res/values/colors.xml index 153ffc3a5..f7db6cabc 100644 --- a/Habitica/res/values/colors.xml +++ b/Habitica/res/values/colors.xml @@ -23,7 +23,7 @@ #F74E52 #F23035 #BF262B - #6C0406 + #6c0406 #ffc8a7 #FF944C @@ -151,4 +151,9 @@ #6ECDB2 #794b00 + #033f5e + #005158 + #005737 + #794b00 + #7f3300 diff --git a/Habitica/res/values/dimens.xml b/Habitica/res/values/dimens.xml index 4287e6204..c4ac4e1b8 100644 --- a/Habitica/res/values/dimens.xml +++ b/Habitica/res/values/dimens.xml @@ -19,14 +19,14 @@ 4dp 140dp - 15dp + 8dp 5dp 2dp 5dp 140dp 147dp - 108dp - 113dp + 94dp + 98dp 219dp @@ -58,15 +58,15 @@ 2dp 84dp 120dp - 28dp + 20dp 68dp 65dp 81dp 99dp 124dp - 10dp + 4dp 16dp - 7dp + 9dp 13dp 4dp diff --git a/Habitica/res/values/strings.tutorial.xml b/Habitica/res/values/strings.tutorial.xml index 42566a9d1..f1741fee0 100644 --- a/Habitica/res/values/strings.tutorial.xml +++ b/Habitica/res/values/strings.tutorial.xml @@ -7,8 +7,8 @@ Give it a shot! You can explore the other task types through the bottom navigation. Make Dailies for time sensitive tasks that need to be done on a regular schedule. Be careful — if you miss one, your avatar will take damage overnight. Checking them off consistently brings great rewards! - Use To-dos to keep track of tasks you need to do just once. - If your To-do has to be done by a certain time, set a due date. Looks like you can check one off — go ahead! + Use To Do\'s to keep track of tasks you need to do just once. + If your To Do has to be done by a certain time, set a due date. Looks like you can check one off — go ahead! Buy gear for your avatar with the gold you earn! You can also make real-world Custom Rewards based on what motivates you. That\'s all for now. If you need a reminder, check the FAQ section. diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml index f9d45dcce..eca3d05e5 100644 --- a/Habitica/res/values/strings.xml +++ b/Habitica/res/values/strings.xml @@ -138,7 +138,7 @@ Members Habits Dailies - To-Do\'s + To Do\'s Rewards Yes No @@ -363,12 +363,12 @@ Add Task Add Habit Add Daily - Add To-Do + Add To Do Add Reward You completed all your dailies. Well done! Habitica Do Habit Habitica Dailies - Habitica To-Do List + Habitica To Do List Google play services could not be found. Purchase Buying gems supports the developers and helps keep Habitica running @@ -437,16 +437,16 @@ Subscription Status Leave Challenge - Are you sure you want to leave the Challenge “%s”? - Remove tasks - Do you want to remove the tasks? + You can choose to keep this Challenge\'s tasks on your personal task board or delete them when you leave + Leave & Keep Tasks + Leave & Delete Tasks Remove Keep My Challenges Daily Habit Reward - To-Do + To Do Official Challenge You’re not part of any Challenges right now! @@ -497,15 +497,15 @@ Habits are tasks that don\'t have a rigid schedule. You can check them off many times a day, or not at all. You don\'t have any Dailies Dailies are tasks that repeat on a regular basis. Choose the schedule that works for you! - You don\'t have any To-Do\'s - To-Do\'s are tasks that only need to be completed once. Add checklists to your To-Do\'s to increase their value. + You don\'t have any To Do\'s + To Do\'s are tasks that only need to be completed once. Add checklists to your To Do\'s to increase their value. You don\'t have any Rewards No Habits There aren\'t any Habits visible with your current filters. No Dailies There aren\'t any Dailies visible with your current filters. - No To-Do\'s - There aren\'t any To-Do\'s visible with your current filters. + No To Do\'s + There aren\'t any To Do\'s visible with your current filters. No Rewards Reset Tutorials Review our Community Guidelines before posting @@ -699,7 +699,7 @@ Did you check off your tasks today? There\'s lots to unlock and discover as you level up, so keep up with your tasks and have fun! Want to try something new? Join a Challenge to expand your task list and win some Gems! - Add checklists to your To-Do\'s to multiply your rewards! + Add checklists to your To Do\'s to multiply your rewards! You can change how often each Daily repeats. Even infrequent tasks can be scheduled. You can schedule specific reminders for Dailies too. Occasionally re-evaluating your tasks can help keep you on the right path. @@ -734,7 +734,7 @@ What should we call you? Display names must be between 1 and 30 characters Join Habitica (Check me off!) - You can either complete this To-Do, edit it, or remove it. + You can either complete this To Do, edit it, or remove it. Reward yourself Watch TV, play a game, eat a treat, it’s up to you! STR: @@ -817,7 +817,7 @@ You joined the guild Cost Add a task to Habitica - Either a Habit, a Daily or a To-Do + Either a Habit, a Daily or a To Do Tap here to edit this into a bad habit you\'d like to quit Or delete it from the edit screen Guild Summary @@ -1015,7 +1015,7 @@ Read More You are unable to buy that amount. Still have a question? - %1$d/%2$d + %1$d / %2$d Task list display Onboarding Tasks 100 Gold!]]> @@ -1027,8 +1027,8 @@ %d%% Complete Create a Task Complete a Task - Hatch a Pet - Feed a Pet + Hatch a new pet + Feed a pet Purchase Equipment Add a task for something you would like to accomplish this week Check off any of your tasks to earn rewards @@ -1058,4 +1058,30 @@ You already have everything you need for all %s pets. Are you sure you want to purchase %d %ss? Equip View Onboarding Tasks + You still need a %s Egg to hatch this pet + You still need a %s Potion to hatch this pet + You need a %s and %s Potion to hatch this pet + You still need a %s Egg to hatch this pet again + You still need a %s Potion to hatch this pet again + You need a %s and %s Potion to hatch this pet again + Combine your %s Egg and %s Potion to hatch this pet! + Hatch Pet + Unhatched Pet + Hatch Pet again + Magic Potions + Magic Potion + Use Saddle + Hatch your Pet + Hatch + Delete Challenge Task? + This is one of %d tasks that are part of the “%s” Challenge. You must leave the Challenge to delete this task. + Leave & Delete Task + Leave & Delete %d Tasks + Broken Challenge + This is one of %d tasks that are part of a Challenge that no longer exists. What would you like to do with these left over tasks? + Keep %d Tasks + Delete %d Tasks + %s Challenge Task + %s Pets + %s Mounts diff --git a/Habitica/res/values/styles.xml b/Habitica/res/values/styles.xml index 7b7f43b96..f6e75cc06 100644 --- a/Habitica/res/values/styles.xml +++ b/Habitica/res/values/styles.xml @@ -48,12 +48,14 @@ @color/brand_50 @style/SearchViewStyle + @color/white + @color/brand_50 @@ -365,6 +387,7 @@ @dimen/pill_horizontal_padding @dimen/pill_vertical_padding @dimen/pill_vertical_padding + @color/gray_200 + + diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt index fc98d19c1..463ccd96e 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt @@ -76,6 +76,9 @@ interface ApiService { @POST("user/buy-quest/{key}") fun purchaseQuest(@Path("key") key: String): Flowable> + @POST("user/buy-special-spell/{key}") + fun purchaseSpecialSpell(@Path("key") key: String): Flowable> + @POST("user/sell/{type}/{key}") fun sellItem(@Path("type") itemType: String, @Path("key") itemKey: String): Flowable> @@ -380,4 +383,7 @@ interface ApiService { @POST("members/transfer-gems") fun transferGems(@Body data: Map): Flowable> + + @POST("tasks/unlink-all/{challengeID}") + fun unlinkAllTasks(@Path("challengeID") challengeID: String?, @Query("keep") keepOption: String): Flowable> } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt index f0437e8b6..795a9e4ce 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt @@ -61,6 +61,7 @@ interface ApiClient { fun purchaseMysterySet(itemKey: String): Flowable fun purchaseQuest(key: String): Flowable + fun purchaseSpecialSpell(key: String): Flowable fun validateSubscription(request: SubscriptionValidationRequest): Flowable fun validateNoRenewSubscription(request: PurchaseValidationRequest): Flowable fun cancelSubscription(): Flowable @@ -257,4 +258,5 @@ interface ApiClient { fun findUsernames(username: String, context: String?, id: String?): Flowable> fun transferGems(giftedID: String, amount: Int): Flowable + fun unlinkAllTasks(challengeID: String?, keepOption: String): Flowable } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/InventoryRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/InventoryRepository.kt index e420d130a..b75880b2a 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/InventoryRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/InventoryRepository.kt @@ -33,16 +33,16 @@ interface InventoryRepository : BaseRepository { fun getOwnedEquipment(type: String): Flowable> fun getEquipmentType(type: String, set: String): Flowable> - fun getOwnedItems(itemType: String): Flowable> - fun getOwnedItems(): Flowable> + fun getOwnedItems(itemType: String, includeZero: Boolean = false): Flowable> + fun getOwnedItems(includeZero: Boolean = false): Flowable> fun getEquipment(key: String): Flowable fun openMysteryItem(user: User?): Flowable fun saveEquipment(equipment: Equipment) - fun getMounts(type: String, group: String, color: String?): Flowable> - fun getPets(type: String, group: String, color: String?): Flowable> + fun getMounts(type: String?, group: String?, color: String?): Flowable> + fun getPets(type: String?, group: String?, color: String?): Flowable> fun updateOwnedEquipment(user: User) @@ -70,10 +70,13 @@ interface InventoryRepository : BaseRepository { fun purchaseHourglassItem(purchaseType: String, key: String): Flowable fun purchaseQuest(key: String): Flowable + fun purchaseSpecialSpell(key: String): Flowable fun purchaseItem(purchaseType: String, key: String, purchaseQuantity: Int): Flowable fun togglePinnedItem(item: ShopItem): Flowable> - fun getItems(itemClass: Class, keys: Array, user: User?): Flowable> + fun getItems(itemClass: Class, keys: Array): Flowable> + fun getItems(itemClass: Class): Flowable> fun getLatestMysteryItem(): Flowable + fun getItem(type: String, key: String): Flowable } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/TaskRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/TaskRepository.kt index 01026d5ac..05447f8ef 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/TaskRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/TaskRepository.kt @@ -59,4 +59,6 @@ interface TaskRepository : BaseRepository { fun retrieveDailiesFromDate(date: Date): Flowable fun retrieveCompletedTodos(userId: String): Flowable fun syncErroredTasks(): Single> + fun unlinkAllTasks(challengeID: String?, keepOption: String): Flowable + fun getTasksForChallenge(challengeID: String?): Flowable> } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt index cb2e9a9c0..2e0f91d05 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt @@ -2,6 +2,7 @@ package com.habitrpg.android.habitica.data.implementation import android.content.Context import com.amplitude.api.Amplitude +import com.facebook.FacebookSdk.getCacheDir import com.google.gson.JsonSyntaxException import com.habitrpg.android.habitica.BuildConfig import com.habitrpg.android.habitica.HabiticaBaseApplication @@ -37,6 +38,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.functions.BiFunction import io.reactivex.functions.Consumer import io.reactivex.schedulers.Schedulers +import okhttp3.Cache import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.logging.HttpLoggingInterceptor @@ -102,7 +104,12 @@ class ApiClientImpl//private OnHabitsAPIResult mResultListener; val timeZone = calendar.timeZone val timezoneOffset = -TimeUnit.MINUTES.convert(timeZone.getOffset(calendar.timeInMillis).toLong(), TimeUnit.MILLISECONDS) + val cacheSize: Long = 10 * 1024 * 1024 // 10 MB + + val cache = Cache(getCacheDir(), cacheSize) + val client = OkHttpClient.Builder() + .cache(cache) .addInterceptor(logging) .addNetworkInterceptor { chain -> val original = chain.request() @@ -325,6 +332,10 @@ class ApiClientImpl//private OnHabitsAPIResult mResultListener; return apiService.buyItem(itemKey, mapOf(Pair("quantity", purchaseQuantity))).compose(configureApiCallObserver()) } + override fun unlinkAllTasks(challengeID: String?, keepOption: String): Flowable { + return apiService.unlinkAllTasks(challengeID, keepOption).compose(configureApiCallObserver()) + } + override fun purchaseItem(type: String, itemKey: String, purchaseQuantity: Int): Flowable { return apiService.purchaseItem(type, itemKey, mapOf(Pair("quantity", purchaseQuantity))).compose(configureApiCallObserver()) } @@ -363,6 +374,10 @@ class ApiClientImpl//private OnHabitsAPIResult mResultListener; return apiService.purchaseQuest(key).compose(configureApiCallObserver()) } + override fun purchaseSpecialSpell(key: String): Flowable { + return apiService.purchaseSpecialSpell(key).compose(configureApiCallObserver()) + } + override fun sellItem(itemType: String, itemKey: String): Flowable { return apiService.sellItem(itemType, itemKey).compose(configureApiCallObserver()) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ContentRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ContentRepositoryImpl.kt index 9060fbf18..88949f64a 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ContentRepositoryImpl.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ContentRepositoryImpl.kt @@ -22,7 +22,7 @@ abstract class ContentRepositoryImpl(localRepository override fun retrieveContent(context: Context?, forced: Boolean): Flowable { val now = Date().time - return if (forced || now - this.lastContentSync > 3) { + return if (forced || now - this.lastContentSync > 300000) { lastContentSync = now apiClient.content.doOnNext { context?.let {context -> diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt index 61774d2f2..74569cb9b 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt @@ -50,16 +50,20 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie return localRepository.getEquipmentType(type, set) } - override fun getOwnedItems(itemType: String): Flowable> { - return localRepository.getOwnedItems(itemType, userID) + override fun getOwnedItems(itemType: String, includeZero: Boolean): Flowable> { + return localRepository.getOwnedItems(itemType, userID, includeZero) } - override fun getOwnedItems(): Flowable> { - return localRepository.getOwnedItems(userID) + override fun getOwnedItems(includeZero: Boolean): Flowable> { + return localRepository.getOwnedItems(userID, includeZero) } - override fun getItems(itemClass: Class, keys: Array, user: User?): Flowable> { - return localRepository.getItems(itemClass, keys, user) + override fun getItems(itemClass: Class, keys: Array): Flowable> { + return localRepository.getItems(itemClass, keys) + } + + override fun getItems(itemClass: Class): Flowable> { + return localRepository.getItems(itemClass) } override fun getEquipment(key: String): Flowable { @@ -82,7 +86,7 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie return localRepository.getMounts() } - override fun getMounts(type: String, group: String, color: String?): Flowable> { + override fun getMounts(type: String?, group: String?, color: String?): Flowable> { return localRepository.getMounts(type, group, color) } @@ -94,7 +98,7 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie return localRepository.getPets() } - override fun getPets(type: String, group: String, color: String?): Flowable> { + override fun getPets(type: String?, group: String?, color: String?): Flowable> { return localRepository.getPets(type, group, color) } @@ -111,7 +115,7 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie } override fun sellItem(user: User?, type: String, key: String): Flowable { - return localRepository.getOwnedItem(userID, type, key) + return localRepository.getOwnedItem(userID, type, key, true) .flatMap { item -> sellItem(user, item) } } @@ -124,6 +128,10 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie return localRepository.getLatestMysteryItem() } + override fun getItem(type: String, key: String): Flowable { + return localRepository.getItem(type, key) + } + private fun sellItem(user: User?, item: Item, ownedItem: OwnedItem): Flowable { if (user != null && appConfigManager.enableLocalChanges()) { localRepository.executeTransaction { @@ -280,6 +288,10 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie return apiClient.purchaseQuest(key) } + override fun purchaseSpecialSpell(key: String): Flowable { + return apiClient.purchaseSpecialSpell(key) + } + override fun purchaseItem(purchaseType: String, key: String, purchaseQuantity: Int): Flowable { return apiClient.purchaseItem(purchaseType, key, purchaseQuantity) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt index c8cc35aa8..b5663043c 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt @@ -307,4 +307,12 @@ class TaskRepositoryImpl(localRepository: TaskLocalRepository, apiClient: ApiCli } }.toList() } + + override fun unlinkAllTasks(challengeID: String?, keepOption: String): Flowable { + return apiClient.unlinkAllTasks(challengeID, keepOption) + } + + override fun getTasksForChallenge(challengeID: String?): Flowable> { + return localRepository.getTasksForChallenge(challengeID, userID) + } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/InventoryLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/InventoryLocalRepository.kt index 08331e26e..861e20023 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/InventoryLocalRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/InventoryLocalRepository.kt @@ -30,14 +30,15 @@ interface InventoryLocalRepository : ContentLocalRepository { fun getOwnedEquipment(type: String): Flowable> - fun getItems(itemClass: Class, keys: Array, user: User?): Flowable> - fun getOwnedItems(itemType: String, userID: String): Flowable> - fun getOwnedItems(userID: String): Flowable> + fun getItems(itemClass: Class, keys: Array): Flowable> + fun getItems(itemClass: Class): Flowable> + fun getOwnedItems(itemType: String, userID: String, includeZero: Boolean): Flowable> + fun getOwnedItems(userID: String, includeZero: Boolean): Flowable> fun getEquipmentType(type: String, set: String): Flowable> fun getEquipment(key: String): Flowable - fun getMounts(type: String, group: String, color: String?): Flowable> - fun getPets(type: String, group: String, color: String?): Flowable> + fun getMounts(type: String?, group: String?, color: String?): Flowable> + fun getPets(type: String?, group: String?, color: String?): Flowable> fun updateOwnedEquipment(user: User) @@ -45,7 +46,7 @@ interface InventoryLocalRepository : ContentLocalRepository { fun changeOwnedCount(item: OwnedItem, amountToAdd: Int?) fun getItem(type: String, key: String): Flowable - fun getOwnedItem(userID: String, type: String, key: String): Flowable + fun getOwnedItem(userID: String, type: String, key: String, includeZero: Boolean): Flowable fun decrementMysteryItemCount(user: User?) fun saveInAppRewards(onlineItems: List) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/TaskLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/TaskLocalRepository.kt index eab649202..02a96642c 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/TaskLocalRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/TaskLocalRepository.kt @@ -35,4 +35,5 @@ interface TaskLocalRepository : BaseLocalRepository { fun saveCompletedTodos(userId: String, tasks: MutableCollection) fun getErroredTasks(userID: String): Flowable> fun getUser(userID: String): Flowable + fun getTasksForChallenge(challengeID: String?, userID: String?): Flowable> } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt index 2d5aeb0d6..47efab428 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt @@ -79,10 +79,12 @@ class RealmInventoryLocalRepository(realm: Realm, private val context: Context) .filter { it.isLoaded } } - override fun getOwnedItems(itemType: String, userID: String): Flowable> { - return realm.where(OwnedItem::class.java) - .greaterThan("numberOwned", 0) - .equalTo("itemType", itemType) + override fun getOwnedItems(itemType: String, userID: String, includeZero: Boolean): Flowable> { + var query = realm.where(OwnedItem::class.java) + if (!includeZero) { + query = query.greaterThan("numberOwned", 0) + } + return query.equalTo("itemType", itemType) .equalTo("userID", userID) .sort("key") .findAll() @@ -90,15 +92,22 @@ class RealmInventoryLocalRepository(realm: Realm, private val context: Context) .filter { it.isLoaded } } - override fun getItems(itemClass: Class, keys: Array, user: User?): Flowable> { + override fun getItems(itemClass: Class, keys: Array): Flowable> { return realm.where(itemClass).`in`("key", keys).findAll().asFlowable() .filter { it.isLoaded } } - override fun getOwnedItems(userID: String): Flowable> { - return realm.where(OwnedItem::class.java) - .greaterThan("numberOwned", 0) - .equalTo("userID", userID) + override fun getItems(itemClass: Class): Flowable> { + return realm.where(itemClass).findAll().asFlowable() + .filter { it.isLoaded } + } + + override fun getOwnedItems(userID: String, includeZero: Boolean): Flowable> { + var query = realm.where(OwnedItem::class.java) + if (!includeZero) { + query = query.greaterThan("numberOwned", 0) + } + return query.equalTo("userID", userID) .findAll() .asFlowable() .map { @@ -128,11 +137,15 @@ class RealmInventoryLocalRepository(realm: Realm, private val context: Context) .filter { it.isLoaded } } - override fun getMounts(type: String, group: String, color: String?): Flowable> { + override fun getMounts(type: String?, group: String?, color: String?): Flowable> { var query = realm.where(Mount::class.java) - .sort("color", Sort.ASCENDING) - .equalTo("type", group) - .equalTo("animal", type) + .sort("type", Sort.ASCENDING, if (color == null) "color" else "animal", Sort.ASCENDING) + if (type != null) { + query = query.equalTo("animal", type) + } + if (group != null) { + query = query.equalTo("type", group) + } if (color != null) { query = query.equalTo("color", color) } @@ -158,11 +171,15 @@ class RealmInventoryLocalRepository(realm: Realm, private val context: Context) .filter { it.isLoaded } } - override fun getPets(type: String, group: String, color: String?): Flowable> { + override fun getPets(type: String?, group: String?, color: String?): Flowable> { var query = realm.where(Pet::class.java) - .sort("color", Sort.ASCENDING) - .equalTo("type", group) - .equalTo("animal", type) + .sort("type", Sort.ASCENDING, if (color == null) "color" else "animal", Sort.ASCENDING) + if (type != null) { + query = query.equalTo("animal", type) + } + if (group != null) { + query = query.equalTo("type", group) + } if (color != null) { query = query.equalTo("color", color) } @@ -185,7 +202,7 @@ class RealmInventoryLocalRepository(realm: Realm, private val context: Context) } override fun changeOwnedCount(type: String, key: String, userID: String, amountToAdd: Int) { - getOwnedItem(userID, type, key).firstElement().subscribe( Consumer { changeOwnedCount(it, amountToAdd)}, RxErrorHandler.handleEmptyError()) + getOwnedItem(userID, type, key, true).firstElement().subscribe( Consumer { changeOwnedCount(it, amountToAdd)}, RxErrorHandler.handleEmptyError()) } override fun changeOwnedCount(item: OwnedItem, amountToAdd: Int?) { @@ -194,13 +211,15 @@ class RealmInventoryLocalRepository(realm: Realm, private val context: Context) } } - override fun getOwnedItem(userID: String, type: String, key: String): Flowable { - return realm.where(OwnedItem::class.java) + override fun getOwnedItem(userID: String, type: String, key: String, includeZero: Boolean): Flowable { + var query = realm.where(OwnedItem::class.java) .equalTo("itemType", type) .equalTo("key", key) .equalTo("userID", userID) - .greaterThan("numberOwned", 0) - .findFirstAsync() + if (!includeZero) { + query = query.greaterThan("numberOwned", 0) + } + return query.findFirstAsync() .asFlowable() .filter { realmObject -> realmObject.isLoaded } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt index 4f6b33ed7..007c0d2bf 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt @@ -225,7 +225,8 @@ class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm), .findAll() .asFlowable() .filter { it.isLoaded } - .retry(1) } + .retry(1) + } override fun getUser(userID: String): Flowable { return realm.where(User::class.java) @@ -235,4 +236,14 @@ class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm), .filter { realmObject -> realmObject.isLoaded && realmObject.isValid && !realmObject.isEmpty() } .map { users -> users.first() } } + + override fun getTasksForChallenge(challengeID: String?, userID: String?): Flowable> { + return realm.where(Task::class.java) + .equalTo("challengeID", challengeID) + .equalTo("userId", userID) + .findAll() + .asFlowable() + .filter { it.isLoaded } + .retry(1) + } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/Animal-Extensions.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/Animal-Extensions.kt index f8cb2c6bc..1f3b97fe7 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/Animal-Extensions.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/Animal-Extensions.kt @@ -9,15 +9,14 @@ fun Animal.getTranslatedType(c: Context?): String { return type } - var currType: String = when (type) { - "drop" -> c?.getString(R.string.standard).toString() - "quest" -> c?.getString(R.string.quest).toString() - "wacky" -> c?.getString(R.string.wacky).toString() - "special" -> c?.getString(R.string.special).toString() + return when (type) { + "drop" -> c.getString(R.string.standard) + "quest" -> c.getString(R.string.quest) + "wacky" -> c.getString(R.string.wacky) + "special" -> c.getString(R.string.special) + "premium" -> c.getString(R.string.magic_potion) else -> { type } } - - return currType } \ No newline at end of file diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/Keyhelper.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/Keyhelper.kt index 52fec9c52..3de8006b0 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/Keyhelper.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/Keyhelper.kt @@ -12,6 +12,7 @@ import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.IOException import java.io.UnsupportedEncodingException +import java.lang.IllegalStateException import java.math.BigInteger import java.security.* import java.util.* @@ -188,6 +189,8 @@ constructor(ctx: Context, var sharedPreferences: SharedPreferences, var keyStore null } catch (e: GeneralSecurityException) { null + } catch (e: IllegalStateException) { + null } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseHandler.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseHandler.kt index ada17a27b..35713840b 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseHandler.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseHandler.kt @@ -5,7 +5,9 @@ import android.content.Intent import com.habitrpg.android.habitica.HabiticaBaseApplication import com.habitrpg.android.habitica.proxy.CrashlyticsProxy import org.solovyev.android.checkout.* +import java.lang.NullPointerException import java.util.* +import javax.annotation.Nonnull class PurchaseHandler(activity: Activity, val crashlyticsProxy: CrashlyticsProxy) { private val billing = HabiticaBaseApplication.getInstance(activity.applicationContext)?.billing @@ -73,20 +75,29 @@ class PurchaseHandler(activity: Activity, val crashlyticsProxy: CrashlyticsProxy } private fun getProduct(type: String, identifiers: List, onSuccess: ((Inventory.Product) -> Unit)) { - inventory?.load(Inventory.Request.create() - .loadAllPurchases().loadSkus(type, identifiers)) { products -> + loadInventory(type, identifiers, Inventory.Callback { products -> val purchases = products.get(type) - if (!purchases.supported) return@load + if (!purchases.supported) return@Callback onSuccess(purchases) - } + }) } private fun getSKU(type: String, identifier: String, onSuccess: ((Sku) -> Unit)) { - inventory?.load(Inventory.Request.create() - .loadAllPurchases().loadSkus(type, listOf(identifier))) { products -> + loadInventory(type, listOf(identifier), Inventory.Callback { products -> val purchases = products.get(type) - if (!purchases.supported) return@load + if (!purchases.supported) return@Callback purchases.skus.firstOrNull()?.let { onSuccess(it) } + }) + } + + private fun loadInventory(type: String, skus: List, callback: Inventory.Callback) { + val request = Inventory.Request.create().loadAllPurchases().loadSkus(type, skus) + if (request != null) { + try { + inventory?.load(request, callback) + } catch (e: NullPointerException) { + return + } } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.java deleted file mode 100644 index 008888d84..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.habitrpg.android.habitica.helpers.notifications; - -import com.google.firebase.iid.FirebaseInstanceId; -import com.google.firebase.messaging.FirebaseMessagingService; -import com.google.firebase.messaging.RemoteMessage; -import com.habitrpg.android.habitica.HabiticaApplication; - -import java.util.Objects; - -import javax.inject.Inject; - -/** - * Created by keithholliday on 6/24/16. - */ -public class HabiticaFirebaseMessagingService extends FirebaseMessagingService { - - @Inject - public PushNotificationManager pushNotificationManager; - - @Override - public void onMessageReceived(RemoteMessage remoteMessage) { - Objects.requireNonNull(HabiticaApplication.Companion.getUserComponent()).inject(this); - pushNotificationManager.displayNotification(remoteMessage); - } - - @Override - public void onNewToken(String s) { - super.onNewToken(s); - Objects.requireNonNull(HabiticaApplication.Companion.getUserComponent()).inject(this); - String refreshedToken = FirebaseInstanceId.getInstance().getToken(); - if (refreshedToken != null) { - pushNotificationManager.setRefreshedToken(refreshedToken); - } - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.kt new file mode 100644 index 000000000..a895ba525 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.kt @@ -0,0 +1,35 @@ +package com.habitrpg.android.habitica.helpers.notifications + +import com.google.firebase.iid.FirebaseInstanceId +import com.google.firebase.messaging.FirebaseMessagingService +import com.google.firebase.messaging.RemoteMessage +import com.habitrpg.android.habitica.HabiticaApplication +import com.habitrpg.android.habitica.HabiticaBaseApplication +import com.habitrpg.android.habitica.components.UserComponent +import java.util.* +import javax.inject.Inject + +class HabiticaFirebaseMessagingService : FirebaseMessagingService() { + + private val userComponent: UserComponent? + get() = HabiticaBaseApplication.userComponent + + @Inject + internal lateinit var pushNotificationManager: PushNotificationManager + + override fun onMessageReceived(remoteMessage: RemoteMessage) { + userComponent?.inject(this) + if (this::pushNotificationManager.isInitialized) { + pushNotificationManager.displayNotification(remoteMessage) + } + } + + override fun onNewToken(s: String) { + super.onNewToken(s) + userComponent?.inject(this) + val refreshedToken = FirebaseInstanceId.getInstance().token + if (refreshedToken != null && this::pushNotificationManager.isInitialized) { + pushNotificationManager.refreshedToken = refreshedToken + } + } +} \ No newline at end of file diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/StableSection.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/StableSection.kt new file mode 100644 index 000000000..a8b390a9d --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/StableSection.kt @@ -0,0 +1,7 @@ +package com.habitrpg.android.habitica.models.inventory + +class StableSection(val key: Any?, val text: String) { + + var ownedCount = 0 + var totalCount = 0 +} \ No newline at end of file diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/shops/ShopItem.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/shops/ShopItem.kt index 4d96a3b6b..5e7fa29f4 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/shops/ShopItem.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/shops/ShopItem.kt @@ -57,7 +57,7 @@ open class ShopItem : RealmObject() { var level: Int? = null val isTypeItem: Boolean - get() = "eggs" == purchaseType || "hatchingPotions" == purchaseType || "food" == purchaseType || "armoire" == purchaseType || "potion" == purchaseType + get() = "eggs" == purchaseType || "hatchingPotions" == purchaseType || "food" == purchaseType || "armoire" == purchaseType || "potion" == purchaseType || "debuffPotion" == purchaseType val isTypeQuest: Boolean get() = "quests" == purchaseType diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AdventureGuideActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AdventureGuideActivity.kt index df1bef019..7553900bc 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AdventureGuideActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AdventureGuideActivity.kt @@ -1,6 +1,7 @@ package com.habitrpg.android.habitica.ui.activities import android.graphics.Paint +import android.graphics.PorterDuff import android.os.Build import android.os.Bundle import android.text.Html @@ -94,6 +95,9 @@ class AdventureGuideActivity : BaseActivity() { val completed = achievements.count { it.earned } binding.progressBar.max = achievements.size binding.progressBar.progress = completed + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { + binding.progressBar.progressBackgroundTintMode = PorterDuff.Mode.SRC_OVER + } if (completed > 0) { binding.progressTextview.text = getString(R.string.percent_completed, ((completed / achievements.size.toFloat()) * 100).toInt()) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ChallengeFormActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ChallengeFormActivity.kt index 0e6392b68..a3a82706e 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ChallengeFormActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ChallengeFormActivity.kt @@ -460,6 +460,11 @@ class ChallengeFormActivity : BaseActivity() { Task.TYPE_TODO -> addTodo else -> addReward } + if(!isExistingTask){ + // If the task is new we create a unique id for it + // Doing it we solve the issue #1278 + task.id = UUID.randomUUID().toString() + } challengeTasks.addTaskUnder(task, taskAbove) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.kt index c407e8d7e..51e65a860 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.kt @@ -31,6 +31,7 @@ import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.GoogleApiAvailability import com.google.android.gms.common.GooglePlayServicesUtil import com.google.android.gms.common.Scopes +import com.google.firebase.analytics.FirebaseAnalytics import com.habitrpg.android.habitica.BuildConfig import com.habitrpg.android.habitica.HabiticaBaseApplication import com.habitrpg.android.habitica.R @@ -369,6 +370,10 @@ class LoginActivity : BaseActivity(), Consumer { HabiticaBaseApplication.reloadUserComponent() + if (isRegistering) { + FirebaseAnalytics.getInstance(this).logEvent("user_registered", null) + } + compositeSubscription.add(userRepository.retrieveUser(true) .subscribe(Consumer { if (userAuthResponse.newUser) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt index 09092169f..6afef229a 100755 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt @@ -22,6 +22,8 @@ import androidx.appcompat.app.ActionBarDrawerToggle import androidx.core.content.FileProvider import androidx.core.content.edit import androidx.lifecycle.ViewModelProviders +import androidx.navigation.NavController +import androidx.navigation.NavDestination import androidx.navigation.findNavController import com.facebook.drawee.view.SimpleDraweeView import com.google.firebase.analytics.FirebaseAnalytics @@ -133,7 +135,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction { private var sideAvatarView: AvatarView? = null private var activeTutorialView: TutorialView? = null private var drawerFragment: NavigationDrawerFragment? = null - private var drawerToggle: ActionBarDrawerToggle? = null + var drawerToggle: ActionBarDrawerToggle? = null private var resumeFromActivity = false private var userIsOnQuest = false @@ -159,7 +161,11 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction { @SuppressLint("ObsoleteSdkInt") public override fun onCreate(savedInstanceState: Bundle?) { - launchTrace = FirebasePerformance.getInstance().newTrace("MainActivityLaunch") + try { + launchTrace = FirebasePerformance.getInstance().newTrace("MainActivityLaunch") + } catch (_: IllegalStateException) { + + } launchTrace?.start() super.onCreate(savedInstanceState) @@ -207,13 +213,8 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction { supportActionBar?.setHomeButtonEnabled(true) val navigationController = findNavController(R.id.nav_host_fragment) - navigationController.addOnDestinationChangedListener { _, destination, _ -> - if (destination.label.isNullOrEmpty() && user?.isValid == true) { - binding.toolbarTitle.text = user?.profile?.name - } else if (user?.isValid == true && user?.profile != null) { - binding.toolbarTitle.text = destination.label - } - drawerFragment?.setSelection(destination.id, null, false) + navigationController.addOnDestinationChangedListener { _, destination, arguments -> + updateToolbarTitle(destination, arguments) } MainNavigationController.setup(navigationController) @@ -227,6 +228,33 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction { } } + private fun updateToolbarTitle(destination: NavDestination, arguments: Bundle?) { + binding.toolbarTitle.text = if (destination.id == R.id.petDetailRecyclerFragment || destination.id == R.id.mountDetailRecyclerFragment) { + arguments?.getString("type") + } else if (destination.label.isNullOrEmpty() && user?.isValid == true) { + user?.profile?.name + } else if (user?.isValid == true && user?.profile != null) { + destination.label + } else { + "" + } + if (destination.id == R.id.petDetailRecyclerFragment || destination.id == R.id.mountDetailRecyclerFragment) { + compositeSubscription.add(inventoryRepository.getItem("egg", arguments?.getString("type") ?: "").firstElement().subscribe(Consumer { + binding.toolbarTitle.text = if (destination.id == R.id.petDetailRecyclerFragment) { + (it as? Egg)?.text + } else { + (it as? Egg)?.mountText + } + }, RxErrorHandler.handleEmptyError())) + } + drawerFragment?.setSelection(destination.id, null, false) + } + + override fun onSupportNavigateUp(): Boolean { + onBackPressed() + return true + } + private fun setupNotifications() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channelId = "default" @@ -358,11 +386,15 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction { val quest = user?.party?.quest if (quest?.completed?.isNotBlank() == true) { - compositeSubscription.add(inventoryRepository.getQuestContent(user?.party?.quest?.completed ?: "").firstElement().subscribe { + compositeSubscription.add(inventoryRepository.getQuestContent(user?.party?.quest?.completed ?: "").firstElement().subscribe(Consumer { QuestCompletedDialog.showWithQuest(this, it) userRepository.updateUser(user, "party.quest.completed", "").subscribe(Consumer {}, RxErrorHandler.handleEmptyError()) - }) + }, RxErrorHandler.handleEmptyError())) + } + + if (user?.flags?.welcomed == false) { + compositeSubscription.add(userRepository.updateUser(user, "flags.welcomed", true).subscribe(Consumer {}, RxErrorHandler.handleEmptyError())) } if (appConfigManager.enableAdventureGuide()) { 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 b6b579fa5..f2fe79897 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.kt @@ -4,6 +4,7 @@ import android.app.Activity import android.content.Context import android.content.Intent import android.content.res.ColorStateList +import android.graphics.Typeface import android.graphics.drawable.ColorDrawable import android.os.Bundle import android.os.Handler @@ -19,11 +20,10 @@ import androidx.core.content.ContextCompat import androidx.core.view.children import androidx.core.view.forEachIndexed import androidx.core.widget.NestedScrollView +import com.google.android.material.textfield.TextInputLayout import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.components.UserComponent -import com.habitrpg.android.habitica.data.TagRepository -import com.habitrpg.android.habitica.data.TaskRepository -import com.habitrpg.android.habitica.data.UserRepository +import com.habitrpg.android.habitica.data.* import com.habitrpg.android.habitica.extensions.OnChangeTextWatcher import com.habitrpg.android.habitica.extensions.addCancelButton import com.habitrpg.android.habitica.extensions.dpToPx @@ -31,6 +31,7 @@ import com.habitrpg.android.habitica.extensions.getThemeColor import com.habitrpg.android.habitica.helpers.RxErrorHandler import com.habitrpg.android.habitica.helpers.TaskAlarmManager import com.habitrpg.android.habitica.models.Tag +import com.habitrpg.android.habitica.models.social.Challenge import com.habitrpg.android.habitica.models.tasks.HabitResetOption import com.habitrpg.android.habitica.models.tasks.Task import com.habitrpg.android.habitica.models.user.Stats @@ -55,11 +56,15 @@ class TaskFormActivity : BaseActivity() { lateinit var tagRepository: TagRepository @Inject lateinit var taskAlarmManager: TaskAlarmManager + @Inject + lateinit var challengeRepository: ChallengeRepository private val toolbar: Toolbar by bindView(R.id.toolbar) private val scrollView: NestedScrollView by bindView(R.id.scroll_view) private val upperTextWrapper: LinearLayout by bindView(R.id.upper_text_wrapper) + private val textInputLayout: TextInputLayout by bindView(R.id.text_input_layout) private val textEditText: EditText by bindView(R.id.text_edit_text) + private val notesInputLayout: TextInputLayout by bindView(R.id.notes_input_layout) private val notesEditText: EditText by bindView(R.id.notes_edit_text) private val habitScoringButtons: HabitScoringButtonsView by bindView(R.id.habit_scoring_buttons) private val checklistTitleView: TextView by bindView(R.id.checklist_title) @@ -90,6 +95,10 @@ class TaskFormActivity : BaseActivity() { private val tagsTitleView: TextView by bindView(R.id.tags_title) private val tagsWrapper: LinearLayout by bindView(R.id.tags_wrapper) + private val challengeNameView: TextView by bindView(R.id.challenge_name_view) + + private var challenge: Challenge? = null + private var isCreating = true private var isChallengeTask = false private var usesTaskAttributeStats = false @@ -109,12 +118,10 @@ class TaskFormActivity : BaseActivity() { private var tintColor: Int = 0 set(value) { field = value - upperTextWrapper.setBackgroundColor(value) taskDifficultyButtons.tintColor = value habitScoringButtons.tintColor = value habitResetStreakButtons.tintColor = value taskSchedulingControls.tintColor = value - supportActionBar?.setBackgroundDrawable(ColorDrawable(value)) updateTagViewsColors() } @@ -144,12 +151,22 @@ class TaskFormActivity : BaseActivity() { } else { "purple" } - super.onCreate(savedInstanceState) + + if (forcedTheme == "yellow") { + taskDifficultyButtons.textTintColor = ContextCompat.getColor(this, R.color.yellow_5) + habitScoringButtons.textTintColor = ContextCompat.getColor(this, R.color.yellow_5) + } + + setSupportActionBar(toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true) tintColor = getThemeColor(R.attr.taskFormTint) + val upperTintColor = getThemeColor(R.attr.colorAccent) + supportActionBar?.setBackgroundDrawable(ColorDrawable(upperTintColor)) + upperTextWrapper.setBackgroundColor(upperTintColor) + isChallengeTask = bundle.getBoolean(IS_CHALLENGE_TASK, false) @@ -163,7 +180,7 @@ class TaskFormActivity : BaseActivity() { setTagViews() }, RxErrorHandler.handleEmptyError())) compositeSubscription.add(userRepository.getUser().subscribe(Consumer { - usesTaskAttributeStats = it.preferences?.allocationMode == "taskbased" + usesTaskAttributeStats = it.preferences?.allocationMode == "taskbased" && it.preferences?.automaticAllocation == true configureForm() }, RxErrorHandler.handleEmptyError())) @@ -171,6 +188,12 @@ class TaskFormActivity : BaseActivity() { textEditText.addTextChangedListener(OnChangeTextWatcher { _, _, _, _ -> checkCanSave() }) + textEditText.onFocusChangeListener = View.OnFocusChangeListener { view, isFocused -> + textInputLayout.alpha = if (isFocused) 1.0f else 0.75f + } + notesEditText.onFocusChangeListener = View.OnFocusChangeListener { view, isFocused -> + notesInputLayout.alpha = if (isFocused) 1.0f else 0.75f + } statStrengthButton.setOnClickListener { selectedStat = Stats.STRENGTH } statIntelligenceButton.setOnClickListener { selectedStat = Stats.INTELLIGENCE } statConstitutionButton.setOnClickListener { selectedStat = Stats.CONSTITUTION } @@ -193,6 +216,13 @@ class TaskFormActivity : BaseActivity() { task = it //tintColor = ContextCompat.getColor(this, it.mediumTaskColor) fillForm(it) + task?.challengeID?.let { + compositeSubscription.add(challengeRepository.retrieveChallenge(it).subscribe(Consumer { + challenge = it + challengeNameView.text = getString(R.string.challenge_task_name, it.name) + challengeNameView.visibility = View.VISIBLE + }, RxErrorHandler.handleEmptyError())) + } }, RxErrorHandler.handleEmptyError())) } bundle.containsKey(PARCELABLE_TASK) -> { @@ -210,6 +240,11 @@ class TaskFormActivity : BaseActivity() { configureForm() } + override fun onStart() { + super.onStart() + textEditText.requestFocus() + } + override fun onCreateOptionsMenu(menu: Menu): Boolean { if (isCreating) { menuInflater.inflate(R.menu.menu_task_create, menu) @@ -373,6 +408,11 @@ class TaskFormActivity : BaseActivity() { button.background.setTint(if (isSelected) tintColor else ContextCompat.getColor(this, R.color.taskform_gray)) val textColorID = if (isSelected) R.color.white else R.color.gray_100 button.setTextColor(ContextCompat.getColor(this, textColorID)) + if (isSelected) { + button.typeface = Typeface.create("sans-serif-medium", Typeface.NORMAL) + } else { + button.typeface = Typeface.create("sans-serif", Typeface.NORMAL) + } } private fun updateTagViewsColors() { @@ -464,6 +504,10 @@ class TaskFormActivity : BaseActivity() { } private fun deleteTask() { + if (task?.challengeID?.isNotBlank() == true && task?.challengeBroken?.isNotBlank() != true) { + showChallengeDeleteTask() + return + } val alert = HabiticaAlertDialog(this) alert.setTitle(R.string.are_you_sure) alert.addButton(R.string.delete_task, true) { _, _ -> @@ -476,6 +520,36 @@ class TaskFormActivity : BaseActivity() { alert.show() } + private fun showChallengeDeleteTask() { + compositeSubscription.add(taskRepository.getTasksForChallenge(task?.challengeID).firstElement().subscribe(Consumer { tasks -> + val taskCount = tasks.size + val alert = HabiticaAlertDialog(this) + alert.setTitle(getString(R.string.delete_challenge_task_title)) + alert.setMessage(getString(R.string.delete_challenge_task_description, taskCount, challenge?.name ?: "")) + alert.addButton(R.string.leave_delete_task, true, true) { _, _ -> + challenge?.let { + compositeSubscription.add(challengeRepository.leaveChallenge(it, "keep-all") + .flatMap { taskRepository.deleteTask(task?.id ?: "") } + .flatMap { userRepository.retrieveUser(true) } + .subscribe(Consumer { + finish() + }, RxErrorHandler.handleEmptyError())) + } + } + alert.addButton(getString(R.string.leave_delete_x_tasks, taskCount), false, true) { _, _ -> + challenge?.let { + compositeSubscription.add(challengeRepository.leaveChallenge(it, "remove-all") + .flatMap { userRepository.retrieveUser(true) } + .subscribe(Consumer { + finish() + }, RxErrorHandler.handleEmptyError())) + } + } + alert.setExtraCloseButtonVisibility(View.VISIBLE) + alert.show() + }, RxErrorHandler.handleEmptyError())) + } + private fun dismissKeyboard() { val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager val currentFocus = currentFocus diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationEquipmentRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationEquipmentRecyclerViewAdapter.kt index 68f6edf29..442c8fd26 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationEquipmentRecyclerViewAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationEquipmentRecyclerViewAdapter.kt @@ -65,6 +65,8 @@ class CustomizationEquipmentRecyclerViewAdapter : androidx.recyclerview.widget.R fun setEquipment(newEquipmentList: List) { this.equipmentList = newEquipmentList.toMutableList() + val emptyEquipment = Equipment() + equipmentList.add(0, emptyEquipment) this.notifyDataSetChanged() } @@ -104,7 +106,7 @@ class CustomizationEquipmentRecyclerViewAdapter : androidx.recyclerview.widget.R } } - if (activeEquipment == equipment.key) { + if (activeEquipment == equipment.key || (activeEquipment?.contains("base_0") == true && equipment.key?.isNotBlank() != true)) { binding.wrapper.background = itemView.context.getDrawable(R.drawable.layout_rounded_bg_gray_700_brand_border) } else { binding.wrapper.background = itemView.context.getDrawable(R.drawable.layout_rounded_bg_gray_700) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/MountDetailRecyclerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/MountDetailRecyclerAdapter.kt index 8608edbad..259eec1a0 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/MountDetailRecyclerAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/MountDetailRecyclerAdapter.kt @@ -1,107 +1,52 @@ package com.habitrpg.android.habitica.ui.adapter.inventory -import android.content.Context -import android.content.res.Resources -import android.graphics.drawable.BitmapDrawable -import android.view.View import android.view.ViewGroup -import android.widget.TextView -import com.facebook.drawee.view.SimpleDraweeView import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.extensions.inflate -import com.habitrpg.android.habitica.helpers.RxErrorHandler import com.habitrpg.android.habitica.models.inventory.Mount +import com.habitrpg.android.habitica.models.inventory.StableSection import com.habitrpg.android.habitica.models.user.OwnedMount -import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils -import com.habitrpg.android.habitica.ui.helpers.bindView -import com.habitrpg.android.habitica.ui.menu.BottomSheetMenu -import com.habitrpg.android.habitica.ui.menu.BottomSheetMenuItem +import com.habitrpg.android.habitica.ui.viewHolders.MountViewHolder +import com.habitrpg.android.habitica.ui.viewHolders.SectionViewHolder import io.reactivex.BackpressureStrategy import io.reactivex.Flowable -import io.reactivex.Observable -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.functions.Consumer import io.reactivex.subjects.PublishSubject -import io.realm.OrderedRealmCollection -import io.realm.RealmRecyclerViewAdapter -class MountDetailRecyclerAdapter(data: OrderedRealmCollection?, autoUpdate: Boolean) : RealmRecyclerViewAdapter(data, autoUpdate) { - - var itemType: String? = null - var context: Context? = null +class MountDetailRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter() { private var ownedMounts: Map? = null private val equipEvents = PublishSubject.create() + private var itemList: List = ArrayList() + + fun setItemList(itemList: List) { + this.itemList = itemList + this.notifyDataSetChanged() + } fun getEquipFlowable(): Flowable { return equipEvents.toFlowable(BackpressureStrategy.DROP) } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MountViewHolder { - return MountViewHolder(parent.inflate(R.layout.mount_overview_item)) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder = + when (viewType) { + 1 -> SectionViewHolder(parent) + else -> MountViewHolder(parent, equipEvents) + } + + override fun onBindViewHolder(holder: androidx.recyclerview.widget.RecyclerView.ViewHolder, position: Int) { + when (val obj = this.itemList[position]) { + is StableSection -> (holder as? SectionViewHolder)?.bind(obj) + is Mount -> (holder as? MountViewHolder)?.bind(obj, ownedMounts?.get(obj.key ?: "")?.owned == true) + } } - override fun onBindViewHolder(holder: MountViewHolder, position: Int) { - data?.let { holder.bind(it[position], ownedMounts?.get(it[position].key)) } - } + override fun getItemViewType(position: Int): Int = if (itemList[position] is StableSection) 1 else 2 + + override fun getItemCount(): Int = itemList.size fun setOwnedMounts(ownedMounts: Map) { this.ownedMounts = ownedMounts notifyDataSetChanged() } - - inner class MountViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener { - var animal: Mount? = null - private var ownedMount: OwnedMount? = null - - private val imageView: SimpleDraweeView by bindView(R.id.imageView) - private val titleView: TextView by bindView(R.id.titleTextView) - private val ownedTextView: TextView by bindView(R.id.ownedTextView) - - var resources: Resources = itemView.resources - - init { - itemView.setOnClickListener(this) - } - - fun bind(item: Mount, ownedMount: OwnedMount?) { - animal = item - this.ownedMount = ownedMount - titleView.text = when { - item.color == "Veggie" -> context?.getString(R.string.garden) - item.type == "special" ->item.text - else -> item.color - } - ownedTextView.visibility = View.GONE - val imageName = "Mount_Icon_" + itemType + "-" + item.color - this.imageView.alpha = 1.0f - if (ownedMount?.owned != true) { - this.imageView.alpha = 0.1f - } - imageView.background = null - val owned = ownedMount?.owned ?: false - DataBindingUtils.loadImage(imageName) { - val drawable = BitmapDrawable(context?.resources, if (owned) it else it.extractAlpha()) - - Observable.just(drawable) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(Consumer { - imageView.background = drawable - }, RxErrorHandler.handleEmptyError()) - } - } - - override fun onClick(v: View) { - if (ownedMount?.owned != true) { - return - } - val menu = BottomSheetMenu(itemView.context) - menu.addMenuItem(BottomSheetMenuItem(resources.getString(R.string.equip))) - menu.setSelectionRunnable { - animal?.let { equipEvents.onNext(it.key) } - } - menu.show() - } - } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/PetDetailRecyclerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/PetDetailRecyclerAdapter.kt index 88487a78b..8ca33104d 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/PetDetailRecyclerAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/PetDetailRecyclerAdapter.kt @@ -1,58 +1,86 @@ package com.habitrpg.android.habitica.ui.adapter.inventory -import android.content.Context -import android.graphics.drawable.BitmapDrawable -import android.view.View import android.view.ViewGroup -import android.widget.ProgressBar -import android.widget.TextView -import com.facebook.drawee.view.SimpleDraweeView import com.habitrpg.android.habitica.R -import com.habitrpg.android.habitica.events.commands.FeedCommand import com.habitrpg.android.habitica.extensions.inflate -import com.habitrpg.android.habitica.helpers.RxErrorHandler -import com.habitrpg.android.habitica.models.inventory.Mount -import com.habitrpg.android.habitica.models.inventory.Pet +import com.habitrpg.android.habitica.models.inventory.* +import com.habitrpg.android.habitica.models.user.OwnedItem import com.habitrpg.android.habitica.models.user.OwnedMount import com.habitrpg.android.habitica.models.user.OwnedPet -import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils -import com.habitrpg.android.habitica.ui.helpers.bindView -import com.habitrpg.android.habitica.ui.menu.BottomSheetMenu -import com.habitrpg.android.habitica.ui.menu.BottomSheetMenuItem +import com.habitrpg.android.habitica.ui.viewHolders.PetViewHolder +import com.habitrpg.android.habitica.ui.viewHolders.SectionViewHolder import io.reactivex.BackpressureStrategy import io.reactivex.Flowable -import io.reactivex.Observable -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.functions.Consumer import io.reactivex.subjects.PublishSubject -import io.realm.OrderedRealmCollection -import io.realm.RealmRecyclerViewAdapter import io.realm.RealmResults -import org.greenrobot.eventbus.EventBus -class PetDetailRecyclerAdapter(data: OrderedRealmCollection?, autoUpdate: Boolean) : RealmRecyclerViewAdapter(data, autoUpdate) { - - var itemType: String? = null - var context: Context? = null +class PetDetailRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter() { private var existingMounts: RealmResults? = null private var ownedPets: Map? = null private var ownedMounts: Map? = null + private var ownedItems: Map? = null private val equipEvents = PublishSubject.create() + private var ownsSaddles: Boolean = false + + private var itemList: List = ArrayList() + + fun setItemList(itemList: List) { + this.itemList = itemList + this.notifyDataSetChanged() + } fun getEquipFlowable(): Flowable { return equipEvents.toFlowable(BackpressureStrategy.DROP) } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PetViewHolder { - return PetViewHolder(parent.inflate(R.layout.pet_detail_item)) + var animalIngredientsRetriever: ((Animal) -> Pair)? = null + + private fun canRaiseToMount(pet: Pet): Boolean { + for (mount in existingMounts ?: emptyList()) { + if (mount.key == pet.key) { + return !(ownedMounts?.get(mount.key)?.owned ?: false) + } + } + return false } - override fun onBindViewHolder(holder: PetViewHolder, position: Int) { - data?.let { - holder.bind(it[position], ownedPets?.get(it[position]?.key ?: "")) + private fun eggCount(pet: Pet): Int { + return ownedItems?.get(pet.animal + "-eggs")?.numberOwned ?: 0 + } + private fun potionCount(pet: Pet): Int { + return ownedItems?.get(pet.color + "-hatchingPotions")?.numberOwned ?: 0 + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder = + when (viewType) { + 1 -> SectionViewHolder(parent) + else -> PetViewHolder(parent, equipEvents, animalIngredientsRetriever) + } + + override fun onBindViewHolder(holder: androidx.recyclerview.widget.RecyclerView.ViewHolder, position: Int) { + when (val obj = this.itemList[position]) { + is StableSection -> { + (holder as? SectionViewHolder)?.bind(obj) + } + is Pet -> { + (holder as? PetViewHolder)?.bind(obj, + ownedPets?.get(obj.key ?: "")?.trained ?: 0, + eggCount(obj), + potionCount(obj), + canRaiseToMount(obj), + ownsSaddles, + ownedItems?.get(obj.animal + "-eggs") != null, + ownedItems?.get(obj.color + "-hatchingPotions") != null, + ownedMounts?.containsKey(obj.key) == true + ) + } } } + override fun getItemViewType(position: Int): Int = if (itemList[position] is StableSection) 1 else 2 + + override fun getItemCount(): Int = itemList.size + fun setExistingMounts(existingMounts: RealmResults) { this.existingMounts = existingMounts notifyDataSetChanged() @@ -67,80 +95,15 @@ class PetDetailRecyclerAdapter(data: OrderedRealmCollection?, autoUpdate: B this.ownedPets = ownedPets notifyDataSetChanged() } - inner class PetViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener { - var animal: Pet? = null - var ownedPet: OwnedPet? = null - private val imageView: SimpleDraweeView by bindView(R.id.imageView) - private val trainedProgressbar: ProgressBar by bindView(R.id.trainedProgressBar) + fun setOwnedItems(ownedItems: Map) { + this.ownedItems = ownedItems + ownsSaddles = ownedItems.containsKey("Saddle-food") + notifyDataSetChanged() + } - private val isOwned: Boolean - get() = this.ownedPet?.trained ?: 0 > 0 - - private val canRaiseToMount: Boolean - get() { - for (mount in existingMounts ?: emptyList()) { - if (mount.key == animal?.key) { - return !(ownedMounts?.get(mount.key)?.owned ?: false) - } - } - return false - } - - init { - itemView.setOnClickListener(this) - } - - fun bind(item: Pet, ownedPet: OwnedPet?) { - this.animal = item - this.ownedPet = ownedPet - this.imageView.alpha = 1.0f - val imageName = "social_Pet-$itemType-${item.color}" - if (this.ownedPet?.trained ?: 0 > 0) { - if (this.canRaiseToMount) { - this.trainedProgressbar.visibility = View.VISIBLE - this.trainedProgressbar.progress = ownedPet?.trained ?: 0 - } else { - this.trainedProgressbar.visibility = View.GONE - } - } else { - this.trainedProgressbar.visibility = View.GONE - this.imageView.alpha = 0.1f - } - imageView.background = null - val trained = ownedPet?.trained ?: 0 - DataBindingUtils.loadImage(imageName) { - val drawable = BitmapDrawable(context?.resources, if (trained == 0) it.extractAlpha() else it) - Observable.just(drawable) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(Consumer { - imageView.background = drawable - }, RxErrorHandler.handleEmptyError()) - } - } - - override fun onClick(v: View) { - if (!this.isOwned) { - return - } - val context = context ?: return - val menu = BottomSheetMenu(context) - menu.addMenuItem(BottomSheetMenuItem(itemView.resources.getString(R.string.equip))) - if (canRaiseToMount) { - menu.addMenuItem(BottomSheetMenuItem(itemView.resources.getString(R.string.feed))) - } - menu.setSelectionRunnable { index -> - if (index == 0) { - animal?.let { - equipEvents.onNext(it.key) - } - } else if (index == 1) { - val event = FeedCommand() - event.usingPet = animal - EventBus.getDefault().post(event) - } - } - menu.show() - } + fun setOwnsSaddles(ownsSaddles: Boolean) { + this.ownsSaddles = ownsSaddles + notifyDataSetChanged() } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt index 3949eebde..01c7e6054 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt @@ -84,18 +84,9 @@ class ShopRecyclerAdapter(private val configManager: AppConfigManager) : android override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder = when (viewType) { - 0 -> { - val view = parent.inflate(R.layout.shop_header) - ShopHeaderViewHolder(view) - } - 1 -> { - val view = parent.inflate(R.layout.shop_section_header) - SectionViewHolder(view) - } - 2 -> { - val view = parent.inflate(emptyViewResource) - EmptyStateViewHolder(view) - } + 0 -> ShopHeaderViewHolder(parent) + 1 -> SectionViewHolder(parent.inflate(R.layout.shop_section_header)) + 2 -> EmptyStateViewHolder(parent.inflate(emptyViewResource)) else -> { val view = parent.inflate(R.layout.row_shopitem) val viewHolder = ShopItemViewHolder(view) @@ -221,7 +212,7 @@ class ShopRecyclerAdapter(private val configManager: AppConfigManager) : android this.notifyDataSetChanged() } - internal class ShopHeaderViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView) { + internal class ShopHeaderViewHolder(parent: ViewGroup) : androidx.recyclerview.widget.RecyclerView.ViewHolder(parent.inflate(R.layout.shop_header)) { private val descriptionView: TextView by bindView(itemView, R.id.descriptionView) private val npcBannerView: NPCBannerView by bindView(itemView, R.id.npcBannerView) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/StableRecyclerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/StableRecyclerAdapter.kt index c0d783c4e..e0b46ec18 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/StableRecyclerAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/StableRecyclerAdapter.kt @@ -1,35 +1,39 @@ package com.habitrpg.android.habitica.ui.adapter.inventory -import android.content.Context -import android.graphics.Color -import android.graphics.drawable.BitmapDrawable import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.core.content.ContextCompat import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.facebook.drawee.view.SimpleDraweeView import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.databinding.ShopHeaderBinding import com.habitrpg.android.habitica.extensions.inflate import com.habitrpg.android.habitica.helpers.MainNavigationController -import com.habitrpg.android.habitica.helpers.RxErrorHandler -import com.habitrpg.android.habitica.models.inventory.Animal -import com.habitrpg.android.habitica.ui.activities.MainActivity +import com.habitrpg.android.habitica.models.inventory.* import com.habitrpg.android.habitica.ui.fragments.inventory.stable.StableFragmentDirections -import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils import com.habitrpg.android.habitica.ui.helpers.bindView +import com.habitrpg.android.habitica.ui.helpers.loadImage +import com.habitrpg.android.habitica.ui.viewHolders.MountViewHolder +import com.habitrpg.android.habitica.ui.viewHolders.PetViewHolder import com.habitrpg.android.habitica.ui.viewHolders.SectionViewHolder -import com.habitrpg.android.habitica.ui.views.NPCBannerView -import io.reactivex.Observable -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.functions.Consumer +import io.reactivex.BackpressureStrategy +import io.reactivex.Flowable +import io.reactivex.subjects.PublishSubject -class StableRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter() { +class StableRecyclerAdapter : RecyclerView.Adapter() { + var shopSpriteSuffix: String? = null + private var eggs: Map = mapOf() + var animalIngredientsRetriever: ((Animal) -> Pair)? = null var itemType: String? = null - var context: Context? = null - var activity: MainActivity? = null + private val equipEvents = PublishSubject.create() + + fun getEquipFlowable(): Flowable { + return equipEvents.toFlowable(BackpressureStrategy.DROP) + } private var itemList: List = ArrayList() @@ -38,72 +42,96 @@ class StableRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter< this.notifyDataSetChanged() } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder = - if (viewType == 0) { - val view = parent.inflate(R.layout.shop_header) - StableHeaderViewHolder(view) - } else if (viewType == 1) { - val view = parent.inflate(R.layout.customization_section_header) - SectionViewHolder(view) - } else if (viewType == 2) { - val view = parent.inflate(R.layout.pet_overview_item) - StableViewHolder(view) - } else { - val view = parent.inflate(R.layout.mount_overview_item) - StableViewHolder(view) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = + when (viewType) { + 1 -> SectionViewHolder(parent) + 4 -> StableViewHolder(parent.inflate(R.layout.pet_overview_item)) + 5 -> StableViewHolder(parent.inflate(R.layout.mount_overview_item)) + 2 -> PetViewHolder(parent, equipEvents, animalIngredientsRetriever) + 3 -> MountViewHolder(parent, equipEvents) + else -> StableHeaderViewHolder(parent) } - - override fun onBindViewHolder(holder: androidx.recyclerview.widget.RecyclerView.ViewHolder, position: Int) { - val obj = this.itemList[position] - if (obj == "header") { - (holder as? StableHeaderViewHolder)?.bind() - } else if (obj.javaClass == String::class.java) { - if (obj == "Standard") { - var params = holder.itemView.layoutParams as GridLayoutManager.LayoutParams - params.height = 135 - holder.itemView.layoutParams = params + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (val item = this.itemList[position]) { + "header" -> (holder as? StableHeaderViewHolder)?.bind() + is StableSection -> { + if (item.key == "drop") { + val params = holder.itemView.layoutParams as GridLayoutManager.LayoutParams + params.topMargin = -30 + holder.itemView.layoutParams = params + } + (holder as? SectionViewHolder)?.bind(item) + } + is Animal -> { + val isIndividualAnimal = item.type == "special" || item.type == "wacky" + if (isIndividualAnimal) { + if (item is Pet) { + (holder as? PetViewHolder)?.bind(item, + item.numberOwned, + canRaiseToMount = false, + eggCount = 0, + potionCount = 0, + ownsSaddles = false, + hasUnlockedEgg = false, + hasUnlockedPotion = false, + hasMount = false) + } else if (item is Mount) { + (holder as? MountViewHolder)?.bind(item, item.numberOwned > 0) + } + return + } + (holder as? StableViewHolder)?.bind(item) } - (holder as? SectionViewHolder)?.bind(obj as? String ?: "") - - } else { - (obj as? Animal)?.let { (holder as? StableViewHolder)?.bind(it) } - } } override fun getItemViewType(position: Int): Int { - var item = itemList[position] - + val item = itemList[position] return if (item == "header") { 0 - } - else if (item.javaClass == String::class.java) { + } else if (item is StableSection) { 1 - } - else if (itemType == "pets") { - 2 - } - else { - 3 + } else if (item is Animal) { + val isIndividualAnimal = item.type == "special" || item.type == "wacky" + if (isIndividualAnimal) { + if (itemType == "pets") { + 2 + } else { + 3 + } + } else { + if (itemType == "pets") { + 4 + } else { + 5 + } + } + } else { + 0 } } override fun getItemCount(): Int = itemList.size + fun setEggs(eggs: Map) { + this.eggs = eggs + notifyDataSetChanged() + } - internal class StableHeaderViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView) { + internal inner class StableHeaderViewHolder(parent: ViewGroup) : RecyclerView.ViewHolder(parent.inflate(R.layout.shop_header)) { - private val npcBannerView: NPCBannerView by bindView(itemView, R.id.npcBannerView) - private val namePlate: TextView by bindView(itemView, R.id.namePlate) - private val descriptionView: TextView by bindView(itemView, R.id.descriptionView) + private var binding: ShopHeaderBinding = ShopHeaderBinding.bind(itemView) fun bind() { - npcBannerView.identifier = "stable" - namePlate.setText(R.string.stable_owner) - descriptionView.visibility = View.GONE + binding.npcBannerView.shopSpriteSuffix = shopSpriteSuffix ?: "" + binding.npcBannerView.identifier = "stable" + binding.namePlate.setText(R.string.stable_owner) + binding.descriptionView.visibility = View.GONE } + } - - internal inner class StableViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener { + + internal inner class StableViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener { private var animal: Animal? = null private val imageView: SimpleDraweeView by bindView(itemView, R.id.imageView) @@ -116,15 +144,12 @@ class StableRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter< fun bind(item: Animal) { this.animal = item - titleView.text = if (item.type == "special") { - item.text - } else { - item.animal - } + val context = itemView.context + val egg = eggs[item.animal] + if (egg != null) { + titleView.text = if (item.type == "drop" || itemType == "mounts") egg.mountText else egg.text + } else item.animal ownedTextView.visibility = View.VISIBLE - this.imageView.alpha = 1.0f - this.titleView.alpha = 1.0f - this.ownedTextView.alpha = 1.0f val imageName = if (itemType == "pets") { "Pet_Egg_" + item.animal @@ -132,40 +157,23 @@ class StableRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter< "Mount_Icon_" + item.key } - context?.let { + this.ownedTextView.text = context.getString(R.string.pet_ownership_fraction, item.numberOwned, item.totalNumber) + this.ownedTextView.background = context.getDrawable(R.drawable.layout_rounded_bg_shopitem_price) - var owned = item.numberOwned - var totalNum = item.totalNumber + this.ownedTextView.setTextColor(ContextCompat.getColor(context, R.color.gray_200)) + ownedTextView.visibility = View.VISIBLE + imageView.loadImage(imageName) - this.ownedTextView.text = context?.getString(R.string.pet_ownership_fraction, owned, totalNum) - this.ownedTextView.background = context?.getDrawable(R.drawable.layout_rounded_bg_shopitem_price) + val alpha = if (item.numberOwned <= 0) 0.2f else 1.0f + this.imageView.alpha = alpha + this.titleView.alpha = alpha + this.ownedTextView.alpha = alpha - this.ownedTextView.setTextColor(ContextCompat.getColor(it, R.color.black) ) - - ownedTextView.visibility = if (animal?.type == "special") View.GONE else View.VISIBLE - imageView.background = null - - DataBindingUtils.loadImage(imageName) { - val drawable = BitmapDrawable(context?.resources, it) - Observable.just(drawable) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(Consumer { - imageView.background = drawable - }, RxErrorHandler.handleEmptyError()) - } - if (item.numberOwned <= 0) { - this.imageView.alpha = 0.2f - this.titleView.alpha = 0.2f - this.ownedTextView.alpha = 0.2f - } - - if (item.numberOwned == item.totalNumber) { - this.ownedTextView.background = context?.getDrawable(R.drawable.layout_rounded_bg_animalitem_complete) - this.ownedTextView.setTextColor(ContextCompat.getColor(it, R.color.white)) - } + if (item.numberOwned == item.totalNumber) { + this.ownedTextView.background = context.getDrawable(R.drawable.layout_rounded_bg_animalitem_complete) + this.ownedTextView.setTextColor(ContextCompat.getColor(context, R.color.white)) } - } override fun onClick(v: View) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/challenges/ChallengeTasksRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/challenges/ChallengeTasksRecyclerViewAdapter.kt index d1f32b9e3..07bb34cb9 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/challenges/ChallengeTasksRecyclerViewAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/challenges/ChallengeTasksRecyclerViewAdapter.kt @@ -63,16 +63,16 @@ class ChallengeTasksRecyclerViewAdapter(taskFilterHelper: TaskFilterHelper?, lay override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindableViewHolder { val viewHolder: BindableViewHolder = when (viewType) { - TYPE_HABIT -> HabitViewHolder(getContentView(parent, R.layout.habit_item_card), { _, _ -> }) { task -> + TYPE_HABIT -> HabitViewHolder(getContentView(parent, R.layout.habit_item_card), { _, _ -> }, { }) { task -> taskOpenEventsSubject.onNext(task) } - TYPE_DAILY -> DailyViewHolder(getContentView(parent, R.layout.daily_item_card), { _, _ -> }, { _, _ -> }) { task -> + TYPE_DAILY -> DailyViewHolder(getContentView(parent, R.layout.daily_item_card), { _, _ -> }, { _, _ -> }, { }) { task -> taskOpenEventsSubject.onNext(task) } - TYPE_TODO -> TodoViewHolder(getContentView(parent, R.layout.todo_item_card), { _, _ -> }, { _, _ -> }) { task -> + TYPE_TODO -> TodoViewHolder(getContentView(parent, R.layout.todo_item_card), { _, _ -> }, { _, _ -> }, { }) { task -> taskOpenEventsSubject.onNext(task) } - TYPE_REWARD -> RewardViewHolder(getContentView(parent, R.layout.reward_item_card), { _, _ -> }) { task -> + TYPE_REWARD -> RewardViewHolder(getContentView(parent, R.layout.reward_item_card), { _, _ -> }, { }) { task -> taskOpenEventsSubject.onNext(task) } TYPE_ADD_ITEM -> AddItemViewHolder(getContentView(parent, R.layout.challenge_add_task_item), addItemSubject) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/DailiesRecyclerViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/DailiesRecyclerViewHolder.kt index 147ec2b88..9535f6986 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/DailiesRecyclerViewHolder.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/DailiesRecyclerViewHolder.kt @@ -12,7 +12,9 @@ class DailiesRecyclerViewHolder(data: OrderedRealmCollection?, autoUpdate: override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DailyViewHolder = DailyViewHolder(getContentView(parent), { task, direction -> taskScoreEventsSubject.onNext(Pair(task, direction)) }, - { task, item -> checklistItemScoreSubject.onNext(Pair(task, item))}) { + { task, item -> checklistItemScoreSubject.onNext(Pair(task, item))}, { task -> taskOpenEventsSubject.onNext(task) + }) { + task -> brokenTaskEventsSubject.onNext(task) } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/HabitsRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/HabitsRecyclerViewAdapter.kt index 1e41f002f..715e42566 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/HabitsRecyclerViewAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/HabitsRecyclerViewAdapter.kt @@ -10,7 +10,9 @@ class HabitsRecyclerViewAdapter(data: OrderedRealmCollection?, autoUpdate: override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HabitViewHolder = - HabitViewHolder(getContentView(parent), { task, direction -> taskScoreEventsSubject.onNext(Pair(task, direction)) }) { + HabitViewHolder(getContentView(parent), { task, direction -> taskScoreEventsSubject.onNext(Pair(task, direction)) }, { task -> taskOpenEventsSubject.onNext(task) + }) { + task -> brokenTaskEventsSubject.onNext(task) } } \ No newline at end of file diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RealmBaseTasksRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RealmBaseTasksRecyclerViewAdapter.kt index 40e18dc74..92e8926ae 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RealmBaseTasksRecyclerViewAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RealmBaseTasksRecyclerViewAdapter.kt @@ -112,6 +112,8 @@ abstract class RealmBaseTasksRecyclerViewAdapter( override val checklistItemScoreEvents: Flowable> = checklistItemScoreSubject.toFlowable(BackpressureStrategy.DROP) protected var taskOpenEventsSubject = PublishSubject.create() override val taskOpenEvents: Flowable = taskOpenEventsSubject.toFlowable(BackpressureStrategy.DROP) + protected var brokenTaskEventsSubject = PublishSubject.create() + override val brokenTaskEvents: Flowable = brokenTaskEventsSubject.toFlowable(BackpressureStrategy.DROP) private val isDataValid: Boolean get() = data?.isValid ?: false 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 deda9fe5e..304ef4c40 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RewardsRecyclerViewAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RewardsRecyclerViewAdapter.kt @@ -30,6 +30,8 @@ class RewardsRecyclerViewAdapter(private var customRewards: OrderedRealmCollecti override val checklistItemScoreEvents: Flowable> = checklistItemScoreSubject.toFlowable(BackpressureStrategy.DROP) private var taskOpenEventsSubject = PublishSubject.create() override val taskOpenEvents: Flowable = taskOpenEventsSubject.toFlowable(BackpressureStrategy.LATEST) + protected var brokenTaskEventsSubject = PublishSubject.create() + override val brokenTaskEvents: Flowable = brokenTaskEventsSubject.toFlowable(BackpressureStrategy.DROP) private var purchaseCardSubject = PublishSubject.create() val purchaseCardEvents: Flowable = purchaseCardSubject.toFlowable(BackpressureStrategy.LATEST) @@ -64,8 +66,10 @@ class RewardsRecyclerViewAdapter(private var customRewards: OrderedRealmCollecti override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return if (viewType == VIEWTYPE_CUSTOM_REWARD) { - RewardViewHolder(getContentView(parent), { task, direction -> taskScoreEventsSubject.onNext(Pair(task, direction)) }) { + RewardViewHolder(getContentView(parent), { task, direction -> taskScoreEventsSubject.onNext(Pair(task, direction)) }, { task -> taskOpenEventsSubject.onNext(task) + }) { + task -> brokenTaskEventsSubject.onNext(task) } } else { val viewHolder = ShopItemViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.row_shopitem, parent, false)) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TaskRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TaskRecyclerViewAdapter.kt index 7f9a4b5db..1c2a0707e 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TaskRecyclerViewAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TaskRecyclerViewAdapter.kt @@ -27,4 +27,5 @@ interface TaskRecyclerViewAdapter { val taskScoreEvents: Flowable> val checklistItemScoreEvents: Flowable> val taskOpenEvents: Flowable + val brokenTaskEvents: Flowable } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TodosRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TodosRecyclerViewAdapter.kt index 2afcc131d..f735ca531 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TodosRecyclerViewAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/TodosRecyclerViewAdapter.kt @@ -12,8 +12,10 @@ class TodosRecyclerViewAdapter(data: OrderedRealmCollection?, autoUpdate: override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder = TodoViewHolder(getContentView(parent), { task, direction -> taskScoreEventsSubject.onNext(Pair(task, direction)) }, - { task, item -> checklistItemScoreSubject.onNext(Pair(task, item))}) { - task -> taskOpenEventsSubject.onNext(task) - } + { task, item -> checklistItemScoreSubject.onNext(Pair(task, item))}, { + task -> taskOpenEventsSubject.onNext(task) + }) { + task -> brokenTaskEventsSubject.onNext(task) + } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt index c104e5e6c..f7c1f8ad8 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt @@ -4,6 +4,7 @@ import android.content.Context import android.graphics.drawable.ColorDrawable import android.os.Bundle import android.view.LayoutInflater +import android.view.MenuItem import android.view.View import android.view.ViewGroup import androidx.core.content.ContextCompat @@ -28,6 +29,8 @@ abstract class BaseMainFragment : BaseFragment() { @Inject lateinit var soundManager: SoundManager + protected var showsBackButton: Boolean = false + open val activity get() = getActivity() as? MainActivity val tabLayout get() = activity?.binding?.detailTabs val collapsingToolbar get() = activity?.binding?.toolbar @@ -72,6 +75,12 @@ abstract class BaseMainFragment : BaseFragment() { return null } + override fun onResume() { + super.onResume() + activity?.drawerToggle?.isDrawerIndicatorEnabled = !showsBackButton + activity?.supportActionBar?.setDisplayHomeAsUpEnabled(true) + } + private fun updateTabLayoutVisibility() { if (this.usesTabLayout) { tabLayout?.removeAllTabs() diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarEquipmentFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarEquipmentFragment.kt index 2e55d5d05..f840da8e6 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarEquipmentFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarEquipmentFragment.kt @@ -40,7 +40,8 @@ class AvatarEquipmentFragment : BaseMainFragment() { compositeSubscription.add(adapter.getSelectCustomizationEvents() .flatMap { equipment -> - inventoryRepository.equip(user, if (user?.preferences?.costume == true) "costume" else "equipped", equipment.key ?: "") + val key = (if (equipment.key?.isNotBlank() != true) activeEquipment else equipment.key) ?: "" + inventoryRepository.equip(user, if (user?.preferences?.costume == true) "costume" else "equipped", key) } .subscribe(Consumer { }, RxErrorHandler.handleEmptyError())) compositeSubscription.add(adapter.getUnlockCustomizationEvents() @@ -62,6 +63,7 @@ class AvatarEquipmentFragment : BaseMainFragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + showsBackButton = true super.onViewCreated(view, savedInstanceState) arguments?.let { val args = AvatarEquipmentFragmentArgs.fromBundle(it) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarOverviewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarOverviewFragment.kt index 2cfb1db99..1ef9d578c 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarOverviewFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarOverviewFragment.kt @@ -26,6 +26,7 @@ class AvatarOverviewFragment : BaseMainFragment(), AdapterView.OnItemSelectedLis } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + showsBackButton = true super.onViewCreated(view, savedInstanceState) binding.avatarSizeSpinner.onItemSelectedListener = this diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentDetailFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentDetailFragment.kt index 464830b69..a2bb3eb0e 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentDetailFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentDetailFragment.kt @@ -46,6 +46,7 @@ class EquipmentDetailFragment : BaseMainFragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + showsBackButton = true super.onViewCreated(view, savedInstanceState) arguments?.let { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt index 8e5a66086..389253f8f 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt @@ -220,11 +220,16 @@ class ItemRecyclerFragment : BaseFragment(), androidx.swiperefreshlayout.widget. compositeSubscription.add(inventoryRepository.getOwnedItems(type) .doOnNext { items -> if (items.size > 0) { - adapter?.updateData(items) + val filteredItems = if (isFeeding) { + items.where().notEqualTo("key", "Saddle").findAll() + } else { + items + } + adapter?.updateData(filteredItems) } } .map { items -> items.mapNotNull { it.key } } - .flatMap { inventoryRepository.getItems(itemClass, it.toTypedArray(), user) } + .flatMap { inventoryRepository.getItems(itemClass, it.toTypedArray()) } .map { val itemMap = mutableMapOf() for (item in it) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopFragment.kt index c3e3236c7..6729460b5 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopFragment.kt @@ -234,7 +234,7 @@ class ShopFragment : BaseFragment() { fun onItemPurchased(event: GearPurchasedEvent) { if (Shop.MARKET == shopIdentifier) { loadMarketGear() - } else if (Shop.TIME_TRAVELERS_SHOP == shopIdentifier) { + } else { loadShopInventory() } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopsFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopsFragment.kt index 8280b1510..5dae10324 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopsFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopsFragment.kt @@ -54,7 +54,6 @@ open class ShopsFragment : BaseMainFragment() { this.usesTabLayout = false tabLayout?.visibility = View.GONE viewPager.currentItem = lockTab ?: 0 - viewPager.setOnTouchListener { _, _ -> true } } context?.let { FirebaseAnalytics.getInstance(it).logEvent("open_shop", bundleOf(Pair("shopIndex", lockTab))) } @@ -85,7 +84,7 @@ open class ShopsFragment : BaseMainFragment() { val fragment = ShopFragment() - fragment.shopIdentifier = when (position) { + fragment.shopIdentifier = when (lockTab ?: position) { 0 -> Shop.MARKET 1 -> Shop.QUEST_SHOP 2 -> Shop.SEASONAL_SHOP @@ -97,7 +96,7 @@ open class ShopsFragment : BaseMainFragment() { return fragment } - override fun getCount(): Int = 4 + override fun getCount(): Int = if (lockTab != null) 1 else 4 override fun getPageTitle(position: Int): CharSequence? { return when (position) { @@ -118,6 +117,6 @@ open class ShopsFragment : BaseMainFragment() { private fun updateCurrencyView(user: User) { currencyView.gold = user.stats?.gp ?: 0.0 currencyView.gems = user.gemCount.toDouble() - currencyView.hourglasses = user.hourglassCount?.toDouble() ?: 0.0 + currencyView.hourglasses = user.hourglassCount.toDouble() } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/MountDetailRecyclerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/MountDetailRecyclerFragment.kt index 7da733378..988b5337f 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/MountDetailRecyclerFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/MountDetailRecyclerFragment.kt @@ -8,14 +8,22 @@ import android.view.ViewGroup import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.components.UserComponent import com.habitrpg.android.habitica.data.InventoryRepository +import com.habitrpg.android.habitica.extensions.getTranslatedType import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.models.inventory.Mount +import com.habitrpg.android.habitica.models.inventory.Pet +import com.habitrpg.android.habitica.models.inventory.StableSection import com.habitrpg.android.habitica.models.user.OwnedMount +import com.habitrpg.android.habitica.models.user.OwnedObject +import com.habitrpg.android.habitica.models.user.OwnedPet import com.habitrpg.android.habitica.ui.adapter.inventory.MountDetailRecyclerAdapter import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment import com.habitrpg.android.habitica.ui.helpers.MarginDecoration import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator import com.habitrpg.android.habitica.ui.helpers.bindView +import io.reactivex.functions.BiFunction import io.reactivex.functions.Consumer +import io.realm.RealmResults import javax.inject.Inject @@ -47,24 +55,34 @@ class MountDetailRecyclerFragment : BaseMainFragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + showsBackButton = true super.onViewCreated(view, savedInstanceState) arguments?.let { val args = MountDetailRecyclerFragmentArgs.fromBundle(it) - animalGroup = args.group + if (args.group != "drop") { + animalGroup = args.group + } animalType = args.type animalColor = args.color } layoutManager = androidx.recyclerview.widget.GridLayoutManager(activity, 2) + layoutManager?.spanSizeLookup = object : androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup() { + override fun getSpanSize(position: Int): Int { + return if (adapter?.getItemViewType(position) == 0 || adapter?.getItemViewType(position) == 1) { + layoutManager?.spanCount ?: 1 + } else { + 1 + } + } + } recyclerView.layoutManager = layoutManager recyclerView.addItemDecoration(MarginDecoration(activity)) adapter = recyclerView.adapter as? MountDetailRecyclerAdapter if (adapter == null) { - adapter = MountDetailRecyclerAdapter(null, true) - adapter?.itemType = this.animalType - adapter?.context = context + adapter = MountDetailRecyclerAdapter() recyclerView.adapter = adapter recyclerView.itemAnimator = SafeDefaultItemAnimator() this.loadItems() @@ -89,7 +107,7 @@ class MountDetailRecyclerFragment : BaseMainFragment() { private fun setGridSpanCount(width: Int) { var spanCount = 0 context?.resources?.let { resources - val itemWidth: Float = resources.getDimension(R.dimen.pet_width) + val itemWidth: Float = resources.getDimension(R.dimen.mount_width) spanCount = (width / itemWidth).toInt() } @@ -101,15 +119,38 @@ class MountDetailRecyclerFragment : BaseMainFragment() { } private fun loadItems() { - if (animalType != null && animalGroup != null) { - compositeSubscription.add(inventoryRepository.getOwnedMounts().firstElement() + if (animalType != null || animalGroup != null) { + compositeSubscription.add(inventoryRepository.getMounts(animalType, animalGroup, animalColor) + .zipWith(inventoryRepository.getOwnedMounts() .map { ownedMounts -> val mountMap = mutableMapOf() ownedMounts.forEach { mountMap[it.key ?: ""] = it } return@map mountMap - } - .subscribe(Consumer { adapter?.setOwnedMounts(it) }, RxErrorHandler.handleEmptyError())) - compositeSubscription.add(inventoryRepository.getMounts(animalType!!, animalGroup!!, animalColor).firstElement().subscribe(Consumer { adapter?.updateData(it) }, RxErrorHandler.handleEmptyError())) + }.doOnNext { + adapter?.setOwnedMounts(it) + }, BiFunction, Map, List> { unsortedAnimals, ownedAnimals -> + val items = mutableListOf() + var lastMount: Mount? = null + var currentSection: StableSection? = null + for (mount in unsortedAnimals) { + if (mount.type == "wacky" || mount.type == "special") continue + if (mount.type != lastMount?.type) { + val title = context?.getString(R.string.pet_category, mount.getTranslatedType(context)) + currentSection = StableSection(mount.type, title ?: "") + items.add(currentSection) + } + currentSection?.let { + it.totalCount += 1 + if (ownedAnimals.containsKey(mount.key)) { + it.ownedCount += 1 + } + } + items.add(mount) + lastMount = mount + } + items + }) + .subscribe(Consumer { adapter?.setItemList(it) }, RxErrorHandler.handleEmptyError())) } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/PetDetailRecyclerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/PetDetailRecyclerFragment.kt index 55e96dc9c..a9e9c4f2a 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/PetDetailRecyclerFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/PetDetailRecyclerFragment.kt @@ -8,11 +8,12 @@ import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.components.UserComponent import com.habitrpg.android.habitica.data.InventoryRepository import com.habitrpg.android.habitica.events.commands.FeedCommand +import com.habitrpg.android.habitica.extensions.getTranslatedType import com.habitrpg.android.habitica.helpers.RxErrorHandler -import com.habitrpg.android.habitica.models.inventory.Mount -import com.habitrpg.android.habitica.models.inventory.Pet +import com.habitrpg.android.habitica.models.inventory.* import com.habitrpg.android.habitica.models.user.Items import com.habitrpg.android.habitica.models.user.OwnedMount +import com.habitrpg.android.habitica.models.user.OwnedObject import com.habitrpg.android.habitica.models.user.OwnedPet import com.habitrpg.android.habitica.ui.adapter.inventory.PetDetailRecyclerAdapter import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment @@ -21,9 +22,12 @@ import com.habitrpg.android.habitica.ui.helpers.MarginDecoration import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator import com.habitrpg.android.habitica.ui.helpers.bindView import com.habitrpg.android.habitica.ui.helpers.resetViews +import io.reactivex.functions.BiFunction import io.reactivex.functions.Consumer +import io.reactivex.rxkotlin.combineLatest import io.realm.RealmResults import org.greenrobot.eventbus.Subscribe +import java.util.ArrayList import javax.inject.Inject class PetDetailRecyclerFragment : BaseMainFragment() { @@ -33,9 +37,9 @@ class PetDetailRecyclerFragment : BaseMainFragment() { private val recyclerView: androidx.recyclerview.widget.RecyclerView by bindView(R.id.recyclerView) - var adapter: PetDetailRecyclerAdapter = PetDetailRecyclerAdapter(null, true) - var animalType: String = "" - var animalGroup: String = "" + var adapter: PetDetailRecyclerAdapter = PetDetailRecyclerAdapter() + var animalType: String? = null + var animalGroup: String? = null var animalColor: String? = null internal var layoutManager: androidx.recyclerview.widget.GridLayoutManager? = null @@ -59,23 +63,38 @@ class PetDetailRecyclerFragment : BaseMainFragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + showsBackButton = true super.onViewCreated(view, savedInstanceState) arguments?.let { val args = MountDetailRecyclerFragmentArgs.fromBundle(it) - animalGroup = args.group + if (args.group != "drop") { + animalGroup = args.group + } animalType = args.type animalColor = args.color } + resetViews() layoutManager = androidx.recyclerview.widget.GridLayoutManager(getActivity(), 2) + layoutManager?.spanSizeLookup = object : androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup() { + override fun getSpanSize(position: Int): Int { + return if (adapter.getItemViewType(position) == 0 || adapter.getItemViewType(position) == 1) { + layoutManager?.spanCount ?: 1 + } else { + 1 + } + } + } recyclerView.layoutManager = layoutManager recyclerView.addItemDecoration(MarginDecoration(getActivity())) - - adapter.context = this.getActivity() - adapter.itemType = this.animalType + adapter.animalIngredientsRetriever = { + val egg = inventoryRepository.getItems(Egg::class.java, arrayOf(it.animal)).firstElement().blockingGet().firstOrNull() + val potion = inventoryRepository.getItems(HatchingPotion::class.java, arrayOf(it.color)).firstElement().blockingGet().firstOrNull() + Pair(egg as? Egg, potion as? HatchingPotion) + } recyclerView.adapter = adapter recyclerView.itemAnimator = SafeDefaultItemAnimator() this.loadItems() @@ -84,9 +103,15 @@ class PetDetailRecyclerFragment : BaseMainFragment() { .flatMap { key -> inventoryRepository.equip(user, "pet", key) } .subscribe(Consumer { }, RxErrorHandler.handleEmptyError())) + view.post { setGridSpanCount(view.width) } } + override fun onResume() { + super.onResume() + activity?.title = animalType + } + override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putString(ANIMAL_TYPE_KEY, this.animalType) @@ -95,7 +120,8 @@ class PetDetailRecyclerFragment : BaseMainFragment() { private fun setGridSpanCount(width: Int) { var spanCount = 0 if (context != null && context?.resources != null) { - val itemWidth: Float = context?.resources?.getDimension(R.dimen.pet_width) ?: 120f + val animalWidth = R.dimen.pet_width + val itemWidth: Float = context?.resources?.getDimension(animalWidth) ?: 0.toFloat() spanCount = (width / itemWidth).toInt() } @@ -103,18 +129,10 @@ class PetDetailRecyclerFragment : BaseMainFragment() { spanCount = 1 } layoutManager?.spanCount = spanCount - layoutManager?.requestLayout() } private fun loadItems() { - if (animalType.isNotEmpty() && animalGroup.isNotEmpty()) { - compositeSubscription.add(inventoryRepository.getOwnedPets() - .map { ownedMounts -> - val mountMap = mutableMapOf() - ownedMounts.forEach { mountMap[it.key ?: ""] = it } - return@map mountMap - } - .subscribe(Consumer { adapter.setOwnedPets(it) }, RxErrorHandler.handleEmptyError())) + if (animalType?.isNotEmpty() == true || animalGroup?.isNotEmpty() == true) { compositeSubscription.add(inventoryRepository.getOwnedMounts() .map { ownedMounts -> val mountMap = mutableMapOf() @@ -122,11 +140,41 @@ class PetDetailRecyclerFragment : BaseMainFragment() { return@map mountMap } .subscribe(Consumer { adapter.setOwnedMounts(it) }, RxErrorHandler.handleEmptyError())) - compositeSubscription.add(inventoryRepository.getPets(animalType, animalGroup, animalColor).firstElement().subscribe(Consumer> { adapter.updateData(it) }, RxErrorHandler.handleEmptyError())) + compositeSubscription.add(inventoryRepository.getOwnedItems(true).subscribe(Consumer { adapter.setOwnedItems(it) }, RxErrorHandler.handleEmptyError())) + compositeSubscription.add(inventoryRepository.getPets(animalType, animalGroup, animalColor).combineLatest(inventoryRepository.getOwnedPets() + .map { ownedPets -> + val petMap = mutableMapOf() + ownedPets.forEach { petMap[it.key ?: ""] = it } + return@map petMap + }.doOnNext { + adapter.setOwnedPets(it) + }).map { + val items = mutableListOf() + var lastPet: Pet? = null + var currentSection: StableSection? = null + for (pet in it.first) { + if (pet.type == "wacky" || pet.type == "special") continue + if (pet.type != lastPet?.type) { + val title = context?.getString(R.string.pet_category, pet.getTranslatedType(context)) + currentSection = StableSection(pet.type, title ?: "") + items.add(currentSection) + } + currentSection?.let {section -> + section.totalCount += 1 + if (it.second.containsKey(pet.key)) { + section.ownedCount += 1 + } + } + items.add(pet) + lastPet = pet + } + items + }.subscribe(Consumer { adapter.setItemList(it) }, RxErrorHandler.handleEmptyError())) compositeSubscription.add(inventoryRepository.getMounts(animalType, animalGroup, animalColor).subscribe(Consumer> { adapter.setExistingMounts(it) }, RxErrorHandler.handleEmptyError())) } } + @Subscribe fun showFeedingDialog(event: FeedCommand) { if (event.usingPet == null || event.usingFood == null) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableRecyclerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableRecyclerFragment.kt index b82bf9c92..0fd1eb5c7 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableRecyclerFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableRecyclerFragment.kt @@ -10,12 +10,13 @@ import com.habitrpg.android.habitica.components.UserComponent import com.habitrpg.android.habitica.data.InventoryRepository import com.habitrpg.android.habitica.extensions.getTranslatedType import com.habitrpg.android.habitica.extensions.inflate +import com.habitrpg.android.habitica.helpers.AppConfigManager import com.habitrpg.android.habitica.helpers.RxErrorHandler import com.habitrpg.android.habitica.models.inventory.Animal -import com.habitrpg.android.habitica.models.user.OwnedMount -import com.habitrpg.android.habitica.models.user.OwnedObject -import com.habitrpg.android.habitica.models.user.OwnedPet -import com.habitrpg.android.habitica.models.user.User +import com.habitrpg.android.habitica.models.inventory.Egg +import com.habitrpg.android.habitica.models.inventory.HatchingPotion +import com.habitrpg.android.habitica.models.inventory.StableSection +import com.habitrpg.android.habitica.models.user.* import com.habitrpg.android.habitica.ui.activities.MainActivity import com.habitrpg.android.habitica.ui.adapter.inventory.StableRecyclerAdapter import com.habitrpg.android.habitica.ui.fragments.BaseFragment @@ -31,6 +32,8 @@ class StableRecyclerFragment : BaseFragment() { @Inject lateinit var inventoryRepository: InventoryRepository + @Inject + lateinit var configManager: AppConfigManager private val recyclerView: RecyclerViewEmptySupport? by bindView(R.id.recyclerView) private val emptyView: TextView? by bindView(R.id.emptyView) @@ -47,7 +50,7 @@ class StableRecyclerFragment : BaseFragment() { this.itemType = savedInstanceState.getString(ITEM_TYPE_KEY, "") } - return container?.inflate(R.layout.fragment_recyclerview) + return container?.inflate(R.layout.fragment_recyclerview_stable) } override fun onDestroy() { @@ -86,11 +89,21 @@ class StableRecyclerFragment : BaseFragment() { adapter = recyclerView?.adapter as? StableRecyclerAdapter if (adapter == null) { adapter = StableRecyclerAdapter() - adapter?.activity = this.activity as? MainActivity + adapter?.animalIngredientsRetriever = { + val egg = inventoryRepository.getItems(Egg::class.java, arrayOf(it.animal)).firstElement().blockingGet().firstOrNull() + val potion = inventoryRepository.getItems(HatchingPotion::class.java, arrayOf(it.color)).firstElement().blockingGet().firstOrNull() + Pair(egg as? Egg, potion as? HatchingPotion) + } adapter?.itemType = this.itemType - adapter?.context = context + adapter?.shopSpriteSuffix = configManager.shopSpriteSuffix() recyclerView?.adapter = adapter recyclerView?.itemAnimator = SafeDefaultItemAnimator() + + adapter?.let { + compositeSubscription.add(it.getEquipFlowable() + .flatMap { key -> inventoryRepository.equip(user, if (itemType == "pets") "pet" else "mount", key) } + .subscribe(Consumer { }, RxErrorHandler.handleEmptyError())) + } } this.loadItems() @@ -106,8 +119,8 @@ class StableRecyclerFragment : BaseFragment() { private fun setGridSpanCount(width: Int) { var spanCount = 0 if (context != null && context?.resources != null) { - var animal_width = if (itemType == "pets") R.dimen.pet_width else R.dimen.mount_width - val itemWidth: Float = context?.resources?.getDimension(animal_width) ?: 0.toFloat() + val animalWidth = if (itemType == "pets") R.dimen.pet_width else R.dimen.mount_width + val itemWidth: Float = context?.resources?.getDimension(animalWidth) ?: 0.toFloat() spanCount = (width / itemWidth).toInt() } @@ -136,6 +149,17 @@ class StableRecyclerFragment : BaseFragment() { animalMap } + compositeSubscription.add(inventoryRepository.getItems(Egg::class.java) + .map { + val eggMap = mutableMapOf() + it.forEach { egg -> + eggMap[egg.key] = egg as Egg + } + eggMap + } + .subscribe(Consumer { + adapter?.setEggs(it) + }, RxErrorHandler.handleEmptyError())) compositeSubscription.add(observable.zipWith(ownedObservable, BiFunction, Map, ArrayList> { unsortedAnimals, ownedAnimals -> mapAnimals(unsortedAnimals, ownedAnimals) }).subscribe(Consumer { items -> adapter?.setItemList(items) }, RxErrorHandler.handleEmptyError())) @@ -144,41 +168,53 @@ class StableRecyclerFragment : BaseFragment() { private fun mapAnimals(unsortedAnimals: RealmResults, ownedAnimals: Map): ArrayList { val items = ArrayList() var lastAnimal: Animal = unsortedAnimals[0] ?: return items - var lastSectionTitle = "" + var lastSection: StableSection? = null for (animal in unsortedAnimals) { - val identifier = if (animal.animal.isNotEmpty() && animal.type != "special") animal.animal else animal.key + val identifier = if (animal.animal.isNotEmpty() && (animal.type != "special" && animal.type != "wacky")) animal.animal else animal.key val lastIdentifier = if (lastAnimal.animal.isNotEmpty()) lastAnimal.animal else lastAnimal.key - if (identifier != lastIdentifier || animal === unsortedAnimals[unsortedAnimals.size - 1]) { - if (!((lastAnimal.type == "premium" || lastAnimal.type == "special") && lastAnimal.numberOwned == 0)) { + if (animal.type == "premium") { + if (!items.contains(lastAnimal)) { + items.add(lastAnimal) + } + lastAnimal = items.first { (it as? Animal)?.animal == animal.animal } as Animal + } else if (identifier != lastIdentifier || animal === unsortedAnimals[unsortedAnimals.size - 1]) { + if (!((lastAnimal.type == "special") && lastAnimal.numberOwned == 0) && !items.contains(lastAnimal)) { items.add(lastAnimal) } lastAnimal = animal } - lastAnimal.totalNumber += 1 - if (animal.type != lastSectionTitle) { - if (items.size > 0 && items[items.size - 1].javaClass == String::class.java) { + if (animal.type != lastSection?.key && animal.type != "premium") { + if (items.size > 0 && items[items.size - 1].javaClass == StableSection::class.java) { items.removeAt(items.size - 1) } - items.add(animal.getTranslatedType(context)) - lastSectionTitle = animal.type + val title = if (itemType == "pets") { + context?.getString(R.string.pet_category, animal.getTranslatedType(context)) + } else { + context?.getString(R.string.mount_category, animal.getTranslatedType(context)) + } + val section = StableSection(animal.type, title ?: "") + items.add(section) + lastSection = section } - when (itemType) { + val isOwned = when (itemType) { "pets" -> { val ownedPet = ownedAnimals[animal?.key] as? OwnedPet - if (ownedPet?.trained ?: 0 > 0) { - lastAnimal.numberOwned += 1 - } + ownedPet?.trained ?: 0 > 0 } "mounts" -> { val ownedMount = ownedAnimals[animal?.key] as? OwnedMount - if (ownedMount?.owned == true) { - lastAnimal.numberOwned = lastAnimal.numberOwned + 1 - } + ownedMount?.owned == true } + else -> false + } + lastAnimal.totalNumber += 1 + lastSection?.totalCount = (lastSection?.totalCount ?: 0) + 1 + if (isOwned) { + lastAnimal.numberOwned += 1 + lastSection?.ownedCount = (lastSection?.ownedCount ?: 0) + 1 } - } if (!((lastAnimal.type == "premium" || lastAnimal.type == "special") && lastAnimal.numberOwned == 0)) { items.add(lastAnimal) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeDetailFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeDetailFragment.kt index 77a260823..17bd3a0ae 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeDetailFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeDetailFragment.kt @@ -144,7 +144,9 @@ class ChallengeDetailFragment: BaseMainFragment() { }, RxErrorHandler.handleEmptyError())) } - joinButton?.setOnClickListener { challenge?.let { challenge -> challengeRepository.joinChallenge(challenge).subscribe(Consumer {}, RxErrorHandler.handleEmptyError()) } } + joinButton?.setOnClickListener { challenge?.let { challenge -> challengeRepository.joinChallenge(challenge) + .flatMap { userRepository.retrieveUser(true) } + .subscribe(Consumer {}, RxErrorHandler.handleEmptyError()) } } leaveButton?.setOnClickListener { showChallengeLeaveDialog() } refresh() @@ -353,34 +355,16 @@ class ChallengeDetailFragment: BaseMainFragment() { val context = context ?: return val alert = HabiticaAlertDialog(context) alert.setTitle(this.getString(R.string.challenge_leave_title)) - alert.setMessage(this.getString(R.string.challenge_leave_text, challenge?.name ?: "")) - alert.addButton(R.string.yes, true) { dialog, _ -> - dialog.dismiss() - showRemoveTasksDialog(Consumer { keepTasks -> - val challenge = challenge ?: return@Consumer - challengeRepository.leaveChallenge(challenge, keepTasks).subscribe(Consumer {}, RxErrorHandler.handleEmptyError()) - }) + alert.setMessage(this.getString(R.string.challenge_leave_description)) + alert.addButton(R.string.leave_keep_tasks, true) { dialog, _ -> + val challenge = challenge ?: return@addButton + challengeRepository.leaveChallenge(challenge, "keep-all").subscribe(Consumer {}, RxErrorHandler.handleEmptyError()) } - alert.addButton(R.string.no, false) { dialog, _ -> - dialog.dismiss() + alert.addButton(R.string.leave_delte_tasks, false, true) { dialog, _ -> + val challenge = challenge ?: return@addButton + challengeRepository.leaveChallenge(challenge, "remove-all").subscribe(Consumer {}, RxErrorHandler.handleEmptyError()) } + alert.setExtraCloseButtonVisibility(View.VISIBLE) alert.show() } - - private fun showRemoveTasksDialog(callback: Consumer) { - context?.let { - val alert = HabiticaAlertDialog(it) - alert.setTitle(this.getString(R.string.challenge_remove_tasks_title)) - alert.setMessage(this.getString(R.string.challenge_remove_tasks_text)) - alert.addButton(R.string.remove_tasks, false) { dialog, _ -> - callback.accept("remove-all") - dialog.dismiss() - } - alert.addButton(R.string.keep_tasks, false) { dialog, _ -> - callback.accept("keep-all") - dialog.dismiss() - } - alert.show() - } - } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/RewardsRecyclerviewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/RewardsRecyclerviewFragment.kt index 6fe7dab3e..f8c0aa044 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/RewardsRecyclerviewFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/RewardsRecyclerviewFragment.kt @@ -61,6 +61,7 @@ class RewardsRecyclerviewFragment : TaskRecyclerViewFragment() { val intent = Intent(activity, SkillMemberActivity::class.java) startActivityForResult(intent, 11) }, RxErrorHandler.handleEmptyError())?.let { compositeSubscription.add(it) } + recyclerAdapter?.brokenTaskEvents?.subscribeWithErrorHandler(Consumer { showBrokenChallengeDialog(it) })?.let { compositeSubscription.add(it) } } override fun getLayoutManager(context: Context?): LinearLayoutManager { 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 7308bd439..5e9e0c175 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt @@ -38,6 +38,7 @@ import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator import com.habitrpg.android.habitica.ui.viewHolders.tasks.BaseTaskViewHolder import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar +import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.functions.Consumer import java.util.* @@ -262,6 +263,7 @@ open class TaskRecyclerViewFragment : BaseFragment(), androidx.swiperefreshlayou recyclerAdapter?.checklistItemScoreEvents ?.flatMap { taskRepository.scoreChecklistItem(it.first.id ?: "", it.second.id ?: "") }?.subscribeWithErrorHandler(Consumer {})?.let { compositeSubscription.add(it) } + recyclerAdapter?.brokenTaskEvents?.subscribeWithErrorHandler(Consumer { showBrokenChallengeDialog(it) })?.let { compositeSubscription.add(it) } } val bottomPadding = (binding.recyclerView.paddingBottom + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60f, resources.displayMetrics)).toInt() @@ -287,6 +289,28 @@ open class TaskRecyclerViewFragment : BaseFragment(), androidx.swiperefreshlayou } } + protected fun showBrokenChallengeDialog(task: Task) { + context?.let { + if (!task.isValid) { + return + } + taskRepository.getTasksForChallenge(task.challengeID).subscribe(Consumer { tasks -> + val taskCount = tasks.size + val dialog = HabiticaAlertDialog(it) + dialog.setTitle(R.string.broken_challenge) + dialog.setMessage(it.getString(R.string.broken_challenge_description, taskCount)) + dialog.addButton(it.getString(R.string.keep_x_tasks, taskCount), true) { _, _ -> + taskRepository.unlinkAllTasks(task.challengeID, "keep-all").subscribe(Consumer {}, RxErrorHandler.handleEmptyError()) + } + dialog.addButton(it.getString(R.string.delete_x_tasks, taskCount), false, true) { _, _ -> + taskRepository.unlinkAllTasks(task.challengeID, "remove-all").subscribe(Consumer {}, RxErrorHandler.handleEmptyError()) + } + dialog.setExtraCloseButtonVisibility(View.VISIBLE) + dialog.show() + }, RxErrorHandler.handleEmptyError()) + } + } + private fun setEmptyLabels() { if (this.classType != null) { binding.recyclerView.setEmptyView(binding.emptyView) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/menu/BottomSheetMenu.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/menu/BottomSheetMenu.kt index 7d72eb0de..7c6b0f99b 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/menu/BottomSheetMenu.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/menu/BottomSheetMenu.kt @@ -5,13 +5,15 @@ import android.view.View import android.widget.LinearLayout import com.google.android.material.bottomsheet.BottomSheetDialog import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.databinding.MenuBottomSheetBinding class BottomSheetMenu(context: Context) : BottomSheetDialog(context), View.OnClickListener { - private var contentView = layoutInflater.inflate(R.layout.menu_bottom_sheet, null) as LinearLayout + private var binding = MenuBottomSheetBinding.inflate(layoutInflater) private var runnable: ((Int) -> Unit)? = null init { - setContentView(contentView) + setContentView(binding.root) + binding.titleView.visibility = View.GONE } fun setSelectionRunnable(runnable: (Int) -> Unit) { @@ -24,19 +26,24 @@ class BottomSheetMenu(context: Context) : BottomSheetDialog(context), View.OnCli } } + override fun setTitle(title: CharSequence?) { + binding.titleView.text = title + binding.titleView.visibility = View.VISIBLE + } + fun addMenuItem(menuItem: BottomSheetMenuItem) { - val item = menuItem.inflate(this.context, layoutInflater, this.contentView) + val item = menuItem.inflate(this.context, layoutInflater, this.binding.menuItems) item.setOnClickListener(this) - this.contentView.addView(item) + this.binding.menuItems.addView(item) } fun removeMenuItem(index: Int) { - this.contentView.removeViewAt(index) + this.binding.menuItems.removeViewAt(index) } override fun onClick(v: View) { if (this.runnable != null) { - val index = this.contentView.indexOfChild(v) + val index = this.binding.menuItems.indexOfChild(v) if (index != -1) { runnable?.let { it(index) } this.dismiss() diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/MountViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/MountViewHolder.kt new file mode 100644 index 000000000..11aaeef5f --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/MountViewHolder.kt @@ -0,0 +1,68 @@ +package com.habitrpg.android.habitica.ui.viewHolders + +import android.content.res.Resources +import android.graphics.drawable.BitmapDrawable +import android.view.View +import android.view.ViewGroup +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.databinding.MountOverviewItemBinding +import com.habitrpg.android.habitica.extensions.inflate +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.models.inventory.Mount +import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils +import com.habitrpg.android.habitica.ui.menu.BottomSheetMenu +import com.habitrpg.android.habitica.ui.menu.BottomSheetMenuItem +import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.functions.Consumer +import io.reactivex.subjects.PublishSubject + +class MountViewHolder(parent: ViewGroup, private val equipEvents: PublishSubject) : androidx.recyclerview.widget.RecyclerView.ViewHolder(parent.inflate(R.layout.mount_overview_item)), View.OnClickListener { + private var binding: MountOverviewItemBinding = MountOverviewItemBinding.bind(itemView) + private var owned: Boolean = false + var animal: Mount? = null + + var resources: Resources = itemView.resources + + init { + itemView.setOnClickListener(this) + } + + fun bind(item: Mount, owned: Boolean) { + animal = item + this.owned = owned + if (item.type == "special" || item.type == "wacky") { + binding.titleTextView.text = item.text + } else { + binding.titleTextView.visibility = View.GONE + } + binding.ownedTextView.visibility = View.GONE + val imageName = "Mount_Icon_" + item.animal + "-" + item.color + binding.imageView.alpha = 1.0f + if (!owned) { + binding.imageView.alpha = 0.2f + } + binding.imageView.background = null + DataBindingUtils.loadImage(imageName) { + val drawable = BitmapDrawable(itemView.context.resources, if (owned) it else it.extractAlpha()) + Observable.just(drawable) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(Consumer { + binding.imageView.background = drawable + }, RxErrorHandler.handleEmptyError()) + } + } + + override fun onClick(v: View) { + if (!owned) { + return + } + val menu = BottomSheetMenu(itemView.context) + menu.setTitle(animal?.text) + menu.addMenuItem(BottomSheetMenuItem(resources.getString(R.string.equip))) + menu.setSelectionRunnable { + animal?.let { equipEvents.onNext(it.key) } + } + menu.show() + } +} \ No newline at end of file diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/PetViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/PetViewHolder.kt new file mode 100644 index 000000000..ebe9baf97 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/PetViewHolder.kt @@ -0,0 +1,168 @@ +package com.habitrpg.android.habitica.ui.viewHolders + +import android.graphics.PorterDuff +import android.graphics.drawable.BitmapDrawable +import android.view.View +import android.view.ViewGroup +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.databinding.PetDetailItemBinding +import com.habitrpg.android.habitica.events.commands.FeedCommand +import com.habitrpg.android.habitica.extensions.inflate +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.models.inventory.* +import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils +import com.habitrpg.android.habitica.ui.menu.BottomSheetMenu +import com.habitrpg.android.habitica.ui.menu.BottomSheetMenuItem +import com.habitrpg.android.habitica.ui.views.dialogs.PetSuggestHatchDialog +import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.functions.Consumer +import io.reactivex.subjects.PublishSubject +import org.greenrobot.eventbus.EventBus + +class PetViewHolder(parent: ViewGroup, private val equipEvents: PublishSubject, private val animalIngredientsRetriever: ((Animal) -> Pair)?) : androidx.recyclerview.widget.RecyclerView.ViewHolder(parent.inflate(R.layout.pet_detail_item)), View.OnClickListener { + private var hasMount: Boolean = false + private var hasUnlockedPotion: Boolean = false + private var hasUnlockedEgg: Boolean = false + private var eggCount: Int = 0 + private var potionCount: Int = 0 + private var ownsSaddles = false + private var animal: Pet? = null + + private var binding: PetDetailItemBinding = PetDetailItemBinding.bind(itemView) + + private var isOwned: Boolean = false + + private var canRaiseToMount: Boolean = false + + private val canHatch: Boolean + get() = eggCount > 0 && potionCount > 0 + + init { + itemView.setOnClickListener(this) + } + + fun bind(item: Pet, + trained: Int, + eggCount: Int, + potionCount: Int, + canRaiseToMount: Boolean, + ownsSaddles: Boolean, + hasUnlockedEgg: Boolean, + hasUnlockedPotion: Boolean, + hasMount: Boolean) { + this.animal = item + isOwned = trained > 0 + binding.imageView.alpha = 1.0f + this.canRaiseToMount = canRaiseToMount + this.eggCount = eggCount + this.potionCount = potionCount + this.ownsSaddles = ownsSaddles + this.hasUnlockedEgg = hasUnlockedEgg + this.hasUnlockedPotion = hasUnlockedPotion + this.hasMount = hasMount + binding.imageViewWrapper.visibility = View.VISIBLE + binding.itemWrapper.visibility = View.GONE + binding.checkmarkView.visibility = View.GONE + + if (item.type == "special" || item.type == "wacky") { + binding.titleTextView.text = item.text + binding.titleTextView.visibility =View.VISIBLE + } else { + binding.titleTextView.visibility = View.GONE + } + + val imageName = "social_Pet-${item.animal}-${item.color}" + itemView.setBackgroundResource(R.drawable.layout_rounded_bg_gray_700) + if (trained > 0) { + if (this.canRaiseToMount) { + binding.trainedProgressBar.visibility = View.VISIBLE + binding.trainedProgressBar.progress = trained + } else { + binding.trainedProgressBar.visibility = View.GONE + } + } else { + binding.trainedProgressBar.visibility = View.GONE + binding.imageView.alpha = 0.2f + if (canHatch) { + binding.imageViewWrapper.visibility = View.GONE + binding.itemWrapper.visibility = View.VISIBLE + binding.checkmarkView.visibility = View.VISIBLE + itemView.setBackgroundResource(R.drawable.layout_rounded_bg_gray_700_brand_border) + DataBindingUtils.loadImage(binding.eggView, "Pet_Egg_${item.animal}") + DataBindingUtils.loadImage(binding.hatchingPotionView, "Pet_HatchingPotion_${item.color}") + } + } + + if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.LOLLIPOP_MR1) { + binding.trainedProgressBar.progressBackgroundTintMode = PorterDuff.Mode.SRC_OVER + } + binding.imageView.background = null + DataBindingUtils.loadImage(imageName) { + val resources = itemView.context.resources ?: return@loadImage + val drawable = BitmapDrawable(resources, if (trained == 0) it.extractAlpha() else it) + Observable.just(drawable) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(Consumer { + binding.imageView.background = drawable + }, RxErrorHandler.handleEmptyError()) + } + } + + override fun onClick(v: View) { + if (!isOwned) { + showRequirementsDialog() + return + } + val context = itemView.context + val menu = BottomSheetMenu(context) + menu.setTitle(animal?.text) + menu.addMenuItem(BottomSheetMenuItem(itemView.resources.getString(R.string.equip))) + if (canRaiseToMount) { + menu.addMenuItem(BottomSheetMenuItem(itemView.resources.getString(R.string.feed))) + if (ownsSaddles) { + menu.addMenuItem(BottomSheetMenuItem(itemView.resources.getString(R.string.use_saddle))) + } + } + menu.setSelectionRunnable { index -> + when (index) { + 0 -> { + animal?.let { + equipEvents.onNext(it.key) + } + } + 1 -> { + val event = FeedCommand() + event.usingPet = animal + EventBus.getDefault().post(event) + } + 2 -> { + val event = FeedCommand() + event.usingPet = animal + val saddle = Food() + saddle.key = "Saddle" + event.usingFood = saddle + EventBus.getDefault().post(event) + } + } + } + menu.show() + } + + private fun showRequirementsDialog() { + val context = itemView.context + val dialog = PetSuggestHatchDialog(context) + animal?.let { + val ingredients = animalIngredientsRetriever?.invoke(it) + dialog.configure(it, + ingredients?.first, + ingredients?.second, + eggCount, + potionCount, + hasUnlockedEgg, + hasUnlockedPotion, + hasMount) + } + dialog.show() + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/SectionViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/SectionViewHolder.kt index 59c3f0b14..e40aab43a 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/SectionViewHolder.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/SectionViewHolder.kt @@ -2,12 +2,15 @@ package com.habitrpg.android.habitica.ui.viewHolders import android.content.Context import android.view.View +import android.view.ViewGroup import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.Spinner import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.extensions.inflate +import com.habitrpg.android.habitica.models.inventory.StableSection import com.habitrpg.android.habitica.ui.helpers.bindView class SectionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { @@ -15,10 +18,13 @@ class SectionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private val label: TextView by bindView(itemView, R.id.label) private val selectionSpinner: Spinner? by bindView(itemView, R.id.classSelectionSpinner) internal val notesView: TextView? by bindView(itemView, R.id.headerNotesView) + private val countPill: TextView? by bindView(itemView, R.id.count_pill) var context: Context = itemView.context var spinnerSelectionChanged: (() -> Unit)? = null + constructor(parent: ViewGroup) : this(parent.inflate(R.layout.customization_section_header)) + init { itemView.findViewById(R.id.purchaseSetButton)?.visibility = View.GONE selectionSpinner?.onItemSelectedListener = object: AdapterView.OnItemSelectedListener { @@ -41,6 +47,16 @@ class SectionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { } } + fun bind(section: StableSection) { + label.text = section.text + if (section.key == "special") { + countPill?.visibility = View.GONE + } else { + countPill?.visibility = View.VISIBLE + } + countPill?.text = itemView.context.getString(R.string.pet_ownership_fraction, section.ownedCount, section.totalCount) + } + var spinnerAdapter: ArrayAdapter? = null set(value) { field = value 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 e1d560367..ff9afb9e3 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 @@ -20,7 +20,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch -abstract class BaseTaskViewHolder constructor(itemView: View, var scoreTaskFunc: ((Task, TaskDirection) -> Unit), var openTaskFunc: ((Task) -> Unit)) : BindableViewHolder(itemView), View.OnClickListener { +abstract class BaseTaskViewHolder constructor(itemView: View, var scoreTaskFunc: ((Task, TaskDirection) -> Unit), var openTaskFunc: ((Task) -> Unit), var brokenTaskFunc: ((Task) -> Unit)) : BindableViewHolder(itemView), View.OnClickListener { var task: Task? = null @@ -80,6 +80,11 @@ abstract class BaseTaskViewHolder constructor(itemView: View, var scoreTaskFunc: //titleTextView.movementMethod = LinkMovementMethod.getInstance() expandNotesButton?.setOnClickListener { expandTask() } + iconViewChallenge?.setOnClickListener { + task?.let { t -> + if (task?.challengeBroken?.isNotBlank() == true) brokenTaskFunc(t) + } + } notesTextView?.addEllipsesListener(object : EllipsisTextView.EllipsisListener { override fun ellipsisStateChanged(ellipses: Boolean) { GlobalScope.launch(Dispatchers.Main.immediate) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/ChecklistedViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/ChecklistedViewHolder.kt index 36b582773..64bee280d 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/ChecklistedViewHolder.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/ChecklistedViewHolder.kt @@ -25,7 +25,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.functions.Consumer import io.reactivex.schedulers.Schedulers -abstract class ChecklistedViewHolder(itemView: View, scoreTaskFunc: ((Task, TaskDirection) -> Unit), var scoreChecklistItemFunc: ((Task, ChecklistItem) -> Unit), openTaskFunc: ((Task) -> Unit)) : BaseTaskViewHolder(itemView, scoreTaskFunc, openTaskFunc), CompoundButton.OnCheckedChangeListener { +abstract class ChecklistedViewHolder(itemView: View, scoreTaskFunc: ((Task, TaskDirection) -> Unit), var scoreChecklistItemFunc: ((Task, ChecklistItem) -> Unit), openTaskFunc: ((Task) -> Unit), brokenTaskFunc: ((Task) -> Unit)) : BaseTaskViewHolder(itemView, scoreTaskFunc, openTaskFunc, brokenTaskFunc), CompoundButton.OnCheckedChangeListener { private val checkboxHolder: ViewGroup by bindView(itemView, R.id.checkBoxHolder) internal val checkbox: CheckBox by bindView(itemView, R.id.checkBox) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/DailyViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/DailyViewHolder.kt index 366ec5d80..003fd6884 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/DailyViewHolder.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/DailyViewHolder.kt @@ -10,7 +10,7 @@ import com.habitrpg.android.habitica.ui.helpers.bindView import java.text.DateFormat import java.util.* -class DailyViewHolder(itemView: View, scoreTaskFunc: ((Task, TaskDirection) -> Unit), scoreChecklistItemFunc: ((Task, ChecklistItem) -> Unit), openTaskFunc: ((Task) -> Unit)) : ChecklistedViewHolder(itemView, scoreTaskFunc, scoreChecklistItemFunc, openTaskFunc) { +class DailyViewHolder(itemView: View, scoreTaskFunc: ((Task, TaskDirection) -> Unit), scoreChecklistItemFunc: ((Task, ChecklistItem) -> Unit), openTaskFunc: ((Task) -> Unit), brokenTaskFunc: ((Task) -> Unit)) : ChecklistedViewHolder(itemView, scoreTaskFunc, scoreChecklistItemFunc, openTaskFunc, brokenTaskFunc) { private val streakTextView: TextView by bindView(itemView, R.id.streakTextView) private val reminderTextView: TextView by bindView(itemView, R.id.reminder_textview) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/HabitViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/HabitViewHolder.kt index 3346c61fb..9d5aecf8c 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/HabitViewHolder.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/HabitViewHolder.kt @@ -10,7 +10,7 @@ import com.habitrpg.android.habitica.models.responses.TaskDirection import com.habitrpg.android.habitica.models.tasks.Task import com.habitrpg.android.habitica.ui.helpers.bindView -class HabitViewHolder(itemView: View, scoreTaskFunc: ((Task, TaskDirection) -> Unit), openTaskFunc: ((Task) -> Unit)) : BaseTaskViewHolder(itemView, scoreTaskFunc, openTaskFunc) { +class HabitViewHolder(itemView: View, scoreTaskFunc: ((Task, TaskDirection) -> Unit), openTaskFunc: ((Task) -> Unit), brokenTaskFunc: ((Task) -> Unit)) : BaseTaskViewHolder(itemView, scoreTaskFunc, openTaskFunc, brokenTaskFunc) { private val btnPlusWrapper: FrameLayout by bindView(itemView, R.id.btnPlusWrapper) private val btnPlusIconView: ImageView by bindView(itemView, R.id.btnPlusIconView) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/RewardViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/RewardViewHolder.kt index 9bc563cda..bc2875d51 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/RewardViewHolder.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/RewardViewHolder.kt @@ -13,7 +13,7 @@ import com.habitrpg.android.habitica.ui.ItemDetailDialog import com.habitrpg.android.habitica.ui.helpers.bindView import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper -class RewardViewHolder(itemView: View, scoreTaskFunc: ((Task, TaskDirection) -> Unit), openTaskFunc: ((Task) -> Unit)) : BaseTaskViewHolder(itemView, scoreTaskFunc, openTaskFunc) { +class RewardViewHolder(itemView: View, scoreTaskFunc: ((Task, TaskDirection) -> Unit), openTaskFunc: ((Task) -> Unit), brokenTaskFunc: ((Task) -> Unit)) : BaseTaskViewHolder(itemView, scoreTaskFunc, openTaskFunc, brokenTaskFunc) { private val buyButton: View by bindView(itemView, R.id.buyButton) internal val priceLabel: TextView by bindView(itemView, R.id.priceLabel) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/TodoViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/TodoViewHolder.kt index d7a2fe5f6..240e90203 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/TodoViewHolder.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/TodoViewHolder.kt @@ -8,7 +8,7 @@ import com.habitrpg.android.habitica.models.tasks.Task import java.text.DateFormat -class TodoViewHolder(itemView: View, scoreTaskFunc: ((Task, TaskDirection) -> Unit), scoreChecklistItemFunc: ((Task, ChecklistItem) -> Unit), openTaskFunc: ((Task) -> Unit)) : ChecklistedViewHolder(itemView, scoreTaskFunc, scoreChecklistItemFunc, openTaskFunc) { +class TodoViewHolder(itemView: View, scoreTaskFunc: ((Task, TaskDirection) -> Unit), scoreChecklistItemFunc: ((Task, ChecklistItem) -> Unit), openTaskFunc: ((Task) -> Unit), brokenTaskFunc: ((Task) -> Unit)) : ChecklistedViewHolder(itemView, scoreTaskFunc, scoreChecklistItemFunc, openTaskFunc, brokenTaskFunc) { private val dateFormatter: DateFormat = android.text.format.DateFormat.getDateFormat(context) 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 91a6712fd..f916b9318 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 @@ -20,7 +20,7 @@ class PartyViewModel: GroupViewModel() { get() = getGroupData().value?.quest?.active == true internal val isUserOnQuest: Boolean - get() = getGroupData().value?.quest?.members?.filter { it.key == getUserData().value?.id } != null + get() = !(getGroupData().value?.quest?.members?.filter { it.key == getUserData().value?.id }?.isEmpty()?:true) private val members: MutableLiveData?> by lazy { MutableLiveData?>() 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 16ac411a9..5e21ca8e9 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 @@ -28,7 +28,7 @@ public class HabiticaIconsHelper { if (imageOfExperience != null) return imageOfExperience; - int size = scaleSize(18); + int size = scaleSize(24); imageOfExperience = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(imageOfExperience); canvas.scale(displayDensity, displayDensity); @@ -42,7 +42,7 @@ public class HabiticaIconsHelper { if (imageOfMagic != null) return imageOfMagic; - int size = scaleSize(18); + int size = scaleSize(24); imageOfMagic = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(imageOfMagic); canvas.scale(displayDensity, displayDensity); @@ -164,7 +164,7 @@ public class HabiticaIconsHelper { if (imageOfHeartLightBg != null) return imageOfHeartLightBg; - int size = scaleSize(18); + int size = scaleSize(24); imageOfHeartLightBg = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(imageOfHeartLightBg); canvas.scale(displayDensity, displayDensity); diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/ValueBar.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/ValueBar.kt index 2276e70a9..2e374aaab 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/ValueBar.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/ValueBar.kt @@ -11,6 +11,7 @@ import android.widget.ImageView import android.widget.TextView import androidx.core.content.ContextCompat import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.extensions.dpToPx import com.habitrpg.android.habitica.extensions.getThemeColor import com.habitrpg.android.habitica.ui.helpers.bindView import java.math.RoundingMode @@ -73,7 +74,7 @@ class ValueBar(context: Context, attrs: AttributeSet?) : FrameLayout(context, at progressBar.barPendingColor = attributes?.getColor(R.styleable.ValueBar_barPendingColor, 0) ?: 0 progressBar.barBackgroundColor = attributes?.getColor(R.styleable.ValueBar_barBackgroundColor, 0) ?: 0 - val labelSpacing = attributes?.getDimension(R.styleable.ValueBar_labelSpacing, context.resources.getDimension(R.dimen.spacing_small)) + val labelSpacing = attributes?.getDimension(R.styleable.ValueBar_labelSpacing, 2.dpToPx(context).toFloat()) if (labelSpacing != null) { labelWrapper.setPadding(0, labelSpacing.toInt(), 0, 0) } @@ -142,7 +143,7 @@ class ValueBar(context: Context, attrs: AttributeSet?) : FrameLayout(context, at progressBar.setBackgroundResource(R.drawable.layout_rounded_bg_light_gray) } else { textColor = context.getThemeColor(R.attr.textColorPrimaryDark) - progressBar.setBackgroundResource(R.drawable.layout_rounded_bg_primary) + progressBar.setBackgroundResource(R.drawable.layout_rounded_bg_header_bar) } valueTextView.setTextColor(textColor) descriptionTextView.setTextColor(textColor) 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 17ffb9a41..f8625df1c 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 @@ -265,9 +265,7 @@ open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style. if (dialogQueue.firstOrNull() == currentDialog) { dialogQueue.removeAt(0) } - Log.i("SHOWNEXT", dialogQueue.toString()) if (dialogQueue.size > 0) { - Log.i("FOUNDONE", dialogQueue[0].toString()) if ((dialogQueue[0].context as? Activity) == null || (dialogQueue[0].context as? Activity)?.isFinishing == false) { dialogQueue[0].show() } @@ -277,9 +275,7 @@ open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style. private fun addToQueue(dialog: HabiticaAlertDialog) { if (dialogQueue.isEmpty()) { dialog.show() - Log.i("SHOWIMMEDIATELY", dialog.toString()) } - Log.i("ADDTOQUEUE", dialog.toString()) dialogQueue.add(dialog) } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/PetSuggestHatchDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/PetSuggestHatchDialog.kt new file mode 100644 index 000000000..d53fc7841 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/PetSuggestHatchDialog.kt @@ -0,0 +1,164 @@ +package com.habitrpg.android.habitica.ui.views.dialogs + +import android.content.Context +import android.graphics.drawable.BitmapDrawable +import android.view.LayoutInflater +import android.view.View +import android.widget.FrameLayout +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.content.ContextCompat +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.databinding.DialogPetSuggestHatchBinding +import com.habitrpg.android.habitica.extensions.dpToPx +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.models.inventory.Animal +import com.habitrpg.android.habitica.models.inventory.Egg +import com.habitrpg.android.habitica.models.inventory.HatchingPotion +import com.habitrpg.android.habitica.models.inventory.Item +import com.habitrpg.android.habitica.ui.activities.MainActivity +import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils +import com.habitrpg.android.habitica.ui.views.CurrencyView +import io.reactivex.Flowable +import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.functions.Consumer + + +class PetSuggestHatchDialog(context: Context) : HabiticaAlertDialog(context) { + + + private lateinit var binding: DialogPetSuggestHatchBinding + + init { + val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as? LayoutInflater + inflater?.let { binding = DialogPetSuggestHatchBinding.inflate(it) } + setAdditionalContentView(binding.root) + } + + fun configure(pet: Animal, egg: Egg?, potion: HatchingPotion?, eggCount: Int, potionCount: Int, hasUnlockedEgg: Boolean, hasUnlockedPotion: Boolean, hasMount: Boolean) { + DataBindingUtils.loadImage(binding.eggView, "Pet_Egg_${pet.animal}") + DataBindingUtils.loadImage(binding.hatchingPotionView, "Pet_HatchingPotion_${pet.color}") + binding.petTitleView.text = pet.text + + val hasEgg = eggCount > 0 + val hasPotion = potionCount > 0 + + binding.eggView.alpha = if (hasEgg) 1.0f else 0.5f + binding.hatchingPotionView.alpha = if (hasPotion) 1.0f else 0.5f + + val eggName = egg?.text ?: pet.animal.capitalize() + val potionName = potion?.text ?: pet.color.capitalize() + + if (hasEgg) { + binding.eggCountView.visibility = View.VISIBLE + binding.eggCountView.text = eggCount.toString() + } else { + binding.eggCountView.visibility = View.GONE + } + if (hasPotion) { + binding.potionCountView.visibility = View.VISIBLE + binding.potionCountView.text = potionCount.toString() + } else { + binding.potionCountView.visibility = View.GONE + } + + if (hasEgg && hasPotion) { + binding.descriptionView.text = context.getString(R.string.can_hatch_pet, + eggName, + potionName) + addButton(R.string.hatch, true, false) { _, _ -> + val thisPotion = potion ?: return@addButton + val thisEgg = egg ?: return@addButton + (getActivity() as? MainActivity)?.hatchPet(thisPotion, thisEgg) + } + if (hasMount) { + setTitle(R.string.hatch_your_pet) + } else { + setTitle(R.string.hatch_pet_title) + } + addButton(R.string.close, false) + } else { + if (hasMount) { + if (!hasEgg && !hasPotion) { + binding.descriptionView.text = context.getString(R.string.suggest_pet_hatch_again_missing_both, eggName, potionName) + } else if (!hasEgg) { + binding.descriptionView.text = context.getString(R.string.suggest_pet_hatch_again_missing_egg, eggName) + } else { + binding.descriptionView.text = context.getString(R.string.suggest_pet_hatch_again_missing_potion, potionName) + } + } else { + if (!hasEgg && !hasPotion) { + binding.descriptionView.text = context.getString(R.string.suggest_pet_hatch_missing_both, eggName, potionName) + } else if (!hasEgg) { + binding.descriptionView.text = context.getString(R.string.suggest_pet_hatch_missing_egg, eggName) + } else { + binding.descriptionView.text = context.getString(R.string.suggest_pet_hatch_missing_potion, potionName) + } + } + + var hatchPrice = 0 + if (!hasEgg) { + hatchPrice = getItemPrice(pet, egg, hasUnlockedEgg) + } + + if (!hasPotion) { + hatchPrice = getItemPrice(pet, potion, hasUnlockedPotion) + + } + + addButton(R.string.close, true) + + if (hatchPrice > 0) { + val linearLayout = LinearLayout(context) + val label = TextView(context) + label.setText(R.string.hatch) + label.setTextColor(ContextCompat.getColor(context, R.color.colorPrimary)) + linearLayout.addView(label) + val layoutParams: LinearLayout.LayoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT) + layoutParams.setMargins(0, 0, 4.dpToPx(context), 0) + label.layoutParams = layoutParams + val priceView = CurrencyView(context, "gems", true) + priceView.value = hatchPrice.toDouble() + linearLayout.addView(priceView) + addButton(linearLayout, true) { _, _ -> + val activity = (getActivity() as? MainActivity) ?: return@addButton + val thisPotion = potion ?: return@addButton + val thisEgg = egg ?: return@addButton + var observable: Flowable = Flowable.just("") + if (!hasEgg) { + observable = observable.flatMap { activity.inventoryRepository.purchaseItem("eggs", thisEgg.key, 1) } + } + if (!hasPotion) { + observable = observable.flatMap { activity.inventoryRepository.purchaseItem("hatchingPotions", thisPotion.key, 1) } + } + observable.subscribe(Consumer { + (getActivity() as? MainActivity)?.hatchPet(thisPotion, thisEgg) + }, RxErrorHandler.handleEmptyError()) + } + } + + setTitle(R.string.unhatched_pet) + } + + + val imageName = "social_Pet-${pet.animal}-${pet.color}" + DataBindingUtils.loadImage(imageName) { + val resources = context.resources ?: return@loadImage + val drawable = BitmapDrawable(resources, if (hasMount) it else it.extractAlpha()) + Observable.just(drawable) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(Consumer { + binding.petView.background = drawable + }, RxErrorHandler.handleEmptyError()) + } + } + + private fun getItemPrice(pet: Animal, item: Item?, hasUnlocked: Boolean): Int { + if (pet.type == "drop" || (pet.type == "quest" && hasUnlocked)) { + return item?.value ?: 0 + } + return 0 + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/navigation/BottomNavigationItem.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/navigation/BottomNavigationItem.kt index 7cd9acd0d..132db1afb 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/navigation/BottomNavigationItem.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/navigation/BottomNavigationItem.kt @@ -22,16 +22,19 @@ class BottomNavigationItem @JvmOverloads constructor( private val titleView: TextView by bindView(R.id.title_view) private val badge: TextView by bindView(R.id.badge) + var selectedVisibility = View.VISIBLE + var deselectedVisibility = View.VISIBLE + var isActive = false set(value) { field = value if (isActive) { - selectedTitleView.visibility = View.VISIBLE + selectedTitleView.visibility = selectedVisibility titleView.visibility = View.GONE iconView.drawable.setColorFilter(ContextCompat.getColor(context, R.color.white), PorterDuff.Mode.MULTIPLY ) } else { selectedTitleView.visibility = View.GONE - titleView.visibility = View.VISIBLE + titleView.visibility = deselectedVisibility iconView.drawable.setColorFilter(context.getThemeColor(R.attr.textColorPrimaryDark), PorterDuff.Mode.MULTIPLY ) } } 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 61d53a093..5ac29e960 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 @@ -38,6 +38,7 @@ import com.habitrpg.android.habitica.ui.views.insufficientCurrency.InsufficientG import com.habitrpg.android.habitica.ui.views.insufficientCurrency.InsufficientGoldDialog import com.habitrpg.android.habitica.ui.views.insufficientCurrency.InsufficientHourglassesDialog import com.habitrpg.android.habitica.ui.views.insufficientCurrency.InsufficientSubscriberGemsDialog +import com.habitrpg.android.habitica.ui.views.tasks.form.StepperValueFormView import io.reactivex.Flowable import io.reactivex.Maybe import io.reactivex.disposables.CompositeDisposable @@ -100,37 +101,31 @@ class PurchaseDialog(context: Context, component: UserComponent?, val item: Shop val contentView: PurchaseDialogContent when { - shopItem.isTypeItem -> { - val itemContent = PurchaseDialogItemContent(context) - if (shopItem.canPurchaseBulk) { - itemContent.stepperView.visibility = View.VISIBLE - itemContent.stepperView.onValueChanged = { - purchaseQuantity = it.toInt() - updatePurchaseTotal() - } - } else { - itemContent.stepperView.visibility = View.GONE - } - contentView = itemContent - } - shopItem.isTypeQuest -> { + shopItem.isTypeItem -> contentView = PurchaseDialogItemContent(context) + shopItem.isTypeQuest -> { contentView = PurchaseDialogQuestContent(context) inventoryRepository.getQuestContent(shopItem.key).firstElement().subscribe(Consumer { contentView.setQuestContent(it) }, RxErrorHandler.handleEmptyError()) } - shopItem.isTypeGear -> { + shopItem.isTypeGear -> { contentView = PurchaseDialogGearContent(context) inventoryRepository.getEquipment(shopItem.key).firstElement().subscribe(Consumer { contentView.setEquipment(it) }, RxErrorHandler.handleEmptyError()) checkGearClass() } - "gems" == shopItem.purchaseType -> { - val gemContent = PurchaseDialogGemsContent(context) - gemContent.stepperView.onValueChanged = { + "gems" == shopItem.purchaseType -> contentView = PurchaseDialogGemsContent(context) + else -> contentView = PurchaseDialogBaseContent(context) + } + + val stepperView = contentView.findViewById(R.id.stepper_view) + if (stepperView != null) { + if (shopItem.canPurchaseBulk) { + stepperView.visibility = View.VISIBLE + stepperView.onValueChanged = { purchaseQuantity = it.toInt() updatePurchaseTotal() } - contentView = gemContent + } else { + stepperView.visibility = View.GONE } - else -> contentView = PurchaseDialogBaseContent(context) } amountErrorLabel = contentView.findViewById(R.id.amount_error_label) @@ -299,6 +294,8 @@ class PurchaseDialog(context: Context, component: UserComponent?, val item: Shop } } else if (shopItem.purchaseType == "quests" && shopItem.currency == "gold") { observable = inventoryRepository.purchaseQuest(shopItem.key) + } else if (shopItem.purchaseType == "debuffPotion") { + observable = userRepository.useSkill(user, shopItem.key, null).cast(Any::class.java) } else if (shopItem.purchaseType == "card") { purchaseCardAction?.invoke(shopItem) dismiss() diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/HabitResetStreakButtons.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/HabitResetStreakButtons.kt index 66abc0166..c34184297 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/HabitResetStreakButtons.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/HabitResetStreakButtons.kt @@ -1,6 +1,7 @@ package com.habitrpg.android.habitica.ui.views.tasks.form import android.content.Context +import android.graphics.Typeface import android.util.AttributeSet import android.view.Gravity import android.view.View @@ -66,9 +67,11 @@ class HabitResetStreakButtons @JvmOverloads constructor( if (isActive) { button.background.setTint(tintColor) button.setTextColor(ContextCompat.getColor(context, R.color.white)) + button.typeface = Typeface.create("sans-serif-medium", Typeface.NORMAL) } else { button.background.setTint(ContextCompat.getColor(context, R.color.taskform_gray)) button.setTextColor(ContextCompat.getColor(context, R.color.gray_100)) + button.typeface = Typeface.create("sans-serif", Typeface.NORMAL) } button.setOnClickListener { selectedResetOption = resetOption diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/HabitScoringButtonsView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/HabitScoringButtonsView.kt index 4a6088931..65d9b3e95 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/HabitScoringButtonsView.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/HabitScoringButtonsView.kt @@ -1,6 +1,7 @@ package com.habitrpg.android.habitica.ui.views.tasks.form import android.content.Context +import android.graphics.Typeface import android.util.AttributeSet import android.view.Gravity import android.view.View @@ -28,17 +29,20 @@ class HabitScoringButtonsView @JvmOverloads constructor( var tintColor: Int = ContextCompat.getColor(context, R.color.brand_300) + var textTintColor: Int? = null var isPositive = true set(value) { field = value positiveImageView.setImageDrawable(HabiticaIconsHelper.imageOfHabitControlPlus(tintColor, value).asDrawable(resources)) if (value) { - positiveTextView.setTextColor(tintColor) + positiveTextView.setTextColor(textTintColor ?: tintColor) positiveView.contentDescription = toContentDescription(R.string.positive_habit_form, R.string.on) + positiveTextView.typeface = Typeface.create("sans-serif-medium", Typeface.NORMAL) } else { positiveTextView.setTextColor(ContextCompat.getColor(context, R.color.gray_100)) positiveView.contentDescription = toContentDescription(R.string.positive_habit_form, R.string.off) + positiveTextView.typeface = Typeface.create("sans-serif", Typeface.NORMAL) } } @@ -47,11 +51,13 @@ class HabitScoringButtonsView @JvmOverloads constructor( field = value negativeImageView.setImageDrawable(HabiticaIconsHelper.imageOfHabitControlMinus(tintColor, value).asDrawable(resources)) if (value) { - negativeTextView.setTextColor(tintColor) + negativeTextView.setTextColor(textTintColor ?: tintColor) negativeView.contentDescription = toContentDescription(R.string.negative_habit_form, R.string.on) + negativeTextView.typeface = Typeface.create("sans-serif-medium", Typeface.NORMAL) } else { negativeTextView.setTextColor(ContextCompat.getColor(context, R.color.gray_100)) negativeView.contentDescription = toContentDescription(R.string.negative_habit_form, R.string.off) + negativeTextView.typeface = Typeface.create("sans-serif", Typeface.NORMAL) } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/TaskDifficultyButtons.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/TaskDifficultyButtons.kt index ced10e8d7..50bebcf13 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/TaskDifficultyButtons.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/TaskDifficultyButtons.kt @@ -1,6 +1,7 @@ package com.habitrpg.android.habitica.ui.views.tasks.form import android.content.Context +import android.graphics.Typeface import android.util.AttributeSet import android.view.View import android.view.accessibility.AccessibilityEvent @@ -20,6 +21,7 @@ class TaskDifficultyButtons @JvmOverloads constructor( ) : LinearLayout(context, attrs, defStyleAttr) { var tintColor: Int = ContextCompat.getColor(context, R.color.brand_300) + var textTintColor: Int? = null var selectedDifficulty: Float = 1f set(value) { field = value @@ -57,11 +59,13 @@ class TaskDifficultyButtons @JvmOverloads constructor( var difficultyColor = ContextCompat.getColor(context, R.color.white) if (isActive) { view.findViewById(R.id.image_view).background.mutate().setTint(tintColor) - view.findViewById(R.id.text_view).setTextColor(tintColor) + view.findViewById(R.id.text_view).setTextColor(textTintColor ?: tintColor) + view.findViewById(R.id.text_view).typeface = Typeface.create("sans-serif-medium", Typeface.NORMAL) } else { view.findViewById(R.id.image_view).background.mutate().setTint(ContextCompat.getColor(context, R.color.taskform_gray)) view.findViewById(R.id.text_view).setTextColor(ContextCompat.getColor(context, R.color.gray_100)) difficultyColor = ContextCompat.getColor(context, R.color.gray_400) + view.findViewById(R.id.text_view).typeface = Typeface.create("sans-serif", Typeface.NORMAL) } val drawable = HabiticaIconsHelper.imageOfTaskDifficultyStars(difficultyColor, difficulty.value, true).asDrawable(resources) view.findViewById(R.id.image_view).setImageDrawable(drawable) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/TaskSchedulingControls.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/TaskSchedulingControls.kt index 3e3828e49..131c6e958 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/TaskSchedulingControls.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/TaskSchedulingControls.kt @@ -172,7 +172,7 @@ class TaskSchedulingControls @JvmOverloads constructor( generateSummary() } monthlyRepeatWeeksButton.setOnClickListener { - weeksOfMonth = mutableListOf(startDateCalendar.get(Calendar.WEEK_OF_MONTH)) + weeksOfMonth = mutableListOf(startDateCalendar.get(Calendar.WEEK_OF_MONTH) - 1) daysOfMonth = null generateSummary() } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/utils/ContentDeserializer.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/utils/ContentDeserializer.kt index eb002c423..ebeb26846 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/utils/ContentDeserializer.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/utils/ContentDeserializer.kt @@ -68,6 +68,7 @@ class ContentDeserializer : JsonDeserializer { mount.animal = mountObj.getAsString("egg") mount.color = mountObj.getAsString("potion") mount.key = mountObj.getAsString("key") + mount.text = mountObj.getAsString("text") mount.type = mountObj.getAsString("type") if (mount.type == "special") { mount.animal = mount.key.split("-")[0] diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListFactory.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListFactory.kt index e980ea7ff..2c9ea4ebc 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListFactory.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListFactory.kt @@ -36,6 +36,9 @@ abstract class TaskListFactory internal constructor(val context: Context, intent } private fun loadData() { + if (!this::taskRepository.isInitialized) { + return + } val mainHandler = Handler(context.mainLooper) mainHandler.post { taskRepository.getCurrentUserTasks(taskType) diff --git a/Habitica/staff/release/output.json b/Habitica/staff/release/output.json index de4c44ce8..dd6cdca9a 100644 --- a/Habitica/staff/release/output.json +++ b/Habitica/staff/release/output.json @@ -11,8 +11,8 @@ "type": "SINGLE", "filters": [], "properties": [], - "versionCode": 2459, - "versionName": "2459", + "versionCode": 2500, + "versionName": "2500", "enabled": true, "outputFile": "Habitica-staff-release.apk" } diff --git a/fastlane/README.md b/fastlane/README.md index 33ba19649..305cebbb2 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -12,7 +12,7 @@ Install _fastlane_ using ``` [sudo] gem install fastlane -NV ``` -or alternatively using `brew cask install fastlane` +or alternatively using `brew install fastlane` # Available Actions ## Android diff --git a/fastlane/changelog.txt b/fastlane/changelog.txt index 5a85879e6..25826cb08 100644 --- a/fastlane/changelog.txt +++ b/fastlane/changelog.txt @@ -1,5 +1,3 @@ -This update improves performance in some cases and has various improvements to how markdown is handled (Support for images and opening links from the task list) - -We also made some improvements to how the login screen handles troubles it may encounter. +Thank you for helping us with beta testing! Be sure to download this update now for a better Habitica experience! diff --git a/store_strings.xml b/store_strings.xml index f7ed05bcf..bcc7a4957 100644 --- a/store_strings.xml +++ b/store_strings.xml @@ -3,7 +3,7 @@ Gamify Your Tasks Treat your life like a game to stay motivated and organized! Treat your life like a game to stay motivated and organized! Habitica makes it simple to have fun while accomplishing goals. -Input your Habits, your Daily goals, and your To-Do list, and then create a custom avatar. Check off tasks to level up your avatar and unlock features such as armor, pets, skills, and even quests! Fight monsters with friends to keep each other accountable, and use your gold on in-game rewards, like equipment, or custom awards, like watching an episode of your favorite TV show. Flexible, social, and fun, Habitica is the perfect way to motivate yourself to accomplish anything. +Input your Habits, your Daily goals, and your To Do list, and then create a custom avatar. Check off tasks to level up your avatar and unlock features such as armor, pets, skills, and even quests! Fight monsters with friends to keep each other accountable, and use your gold on in-game rewards, like equipment, or custom awards, like watching an episode of your favorite TV show. Flexible, social, and fun, Habitica is the perfect way to motivate yourself to accomplish anything. If you have any questions, feel free to send feedback to mobile@habitica.com! And if you enjoy our app, we would really appreciate it if you would leave us a review. diff --git a/translations/store_strings-cs.xml b/translations/store_strings-cs.xml index f9f1ded58..6e77e9578 100644 --- a/translations/store_strings-cs.xml +++ b/translations/store_strings-cs.xml @@ -3,7 +3,7 @@ Přeměň své úkoly na hru Berte život jako hru, načerpejte motivaci a mějte vše pod kontrolou! Ber svůj život jako hru, aby jsi zůstal motivovaný a organizovaný! S Habiticou je jednoduché si užívat zábavu a dokončovat své síle zároveň. -Zadej své zvyky, denní cíle a svůj to-do list a poté si vytvoř svou vlastní herní postavu. Odškrtávej si úkoly, aby jsi svou postavu dostal na další úroveň a odemkni si vymoženosti jako brnění, domácí mazlíčky, schopnosti a dokonce i úkoly! Bojuj s monstry společně s kamarády, abyste se navzájem podporovali a použij své zlato na herní odměny, jako je vybavení, nebo na vlastní odměny v reálném životě jako např. sledování episod svého oblíbené seriálu. Přizpůsobivá, společenská a zábavná, Habitica je dokonalým způsobem na to se motivovat k tomu dosáhnout čehokoliv. +Zadej své zvyky, denní cíle a svůj To Do list a poté si vytvoř svou vlastní herní postavu. Odškrtávej si úkoly, aby jsi svou postavu dostal na další úroveň a odemkni si vymoženosti jako brnění, domácí mazlíčky, schopnosti a dokonce i úkoly! Bojuj s monstry společně s kamarády, abyste se navzájem podporovali a použij své zlato na herní odměny, jako je vybavení, nebo na vlastní odměny v reálném životě jako např. sledování episod svého oblíbené seriálu. Přizpůsobivá, společenská a zábavná, Habitica je dokonalým způsobem na to se motivovat k tomu dosáhnout čehokoliv. Máš-li otázky, neboj se poslat zpětnou vazbu na email mobile@habitica.com. A jestli se ti naše aplikace líbí, opravdu bychom ocenili, kdyby jsi nám zanechal hodnocení. diff --git a/translations/store_strings-de.xml b/translations/store_strings-de.xml index 6c31ef553..5af7a741c 100644 --- a/translations/store_strings-de.xml +++ b/translations/store_strings-de.xml @@ -3,7 +3,7 @@ Erledige deine Aufgaben spielerisch Betrachte Dein Leben als ein Spiel, um motiviert und organisiert zu bleiben! Betrachte Dein Leben als ein Spiel, um motiviert und organisiert zu bleiben! Habitica macht es Dir einfach, Spaß dabei zu haben, Deine persönlichen Ziele zu erreichen. -Füge Deine Gewohnheiten, Deine täglichen Ziele und Deine To-Do-Liste hinzu und erstelle Deinen eigenen Avatar. Hake Deine Aufgaben ab, damit Dein Avatar auf ein höheres Level aufsteigen kann und um weitere Funktionen freizuschalten, wie beispielsweise Ausrüstung, Haustiere, Fähigkeiten und auch Quests! Du kannst Monster mit Deinen Freunden bekämpfen und euch so gegenseitig unterstützen. Du kannst Dein Gold dazu nutzen, um spielbezogene Belohnungen zu kaufen, oder für individuelle Belohnungen, wie beispielsweise eine Episode Deiner Lieblings-Fehrnsehserie zu gucken. Habitica ist flexibel, kommunikativ und macht Spaß, es bietet eine tolle Möglichkeit, Dich zu jedem Ziel zu motivieren. +Füge Deine Gewohnheiten, Deine täglichen Ziele und Deine To Do-Liste hinzu und erstelle Deinen eigenen Avatar. Hake Deine Aufgaben ab, damit Dein Avatar auf ein höheres Level aufsteigen kann und um weitere Funktionen freizuschalten, wie beispielsweise Ausrüstung, Haustiere, Fähigkeiten und auch Quests! Du kannst Monster mit Deinen Freunden bekämpfen und euch so gegenseitig unterstützen. Du kannst Dein Gold dazu nutzen, um spielbezogene Belohnungen zu kaufen, oder für individuelle Belohnungen, wie beispielsweise eine Episode Deiner Lieblings-Fehrnsehserie zu gucken. Habitica ist flexibel, kommunikativ und macht Spaß, es bietet eine tolle Möglichkeit, Dich zu jedem Ziel zu motivieren. Falls Du irgendwelche Fragen hast, kannst Du uns gerne Feedback an mobile@habitica.com schicken! Und falls Dir unsere App gefällt, würden wir uns freuen, wenn Du uns bewertest. diff --git a/translations/store_strings-en-rGB.xml b/translations/store_strings-en-rGB.xml index 947be27cc..e140fef18 100644 --- a/translations/store_strings-en-rGB.xml +++ b/translations/store_strings-en-rGB.xml @@ -3,7 +3,7 @@ Gamify Your Tasks Treat your life like a game to stay motivated and organised! Treat your life like a game to stay motivated and organised! Habitica makes it simple to have fun while accomplishing goals. -Input your Habits, your Daily goals, and your To-Do list, and then create a custom avatar. Check off tasks to level up your avatar and unlock features such as armour, pets, skills, and even quests! Fight monsters with friends to keep each other accountable, and use your gold on in-game rewards, like equipment, or custom awards, like watching an episode of your favourite TV show. Flexible, social, and fun, Habitica is the perfect way to motivate yourself to accomplish anything. +Input your Habits, your Daily goals, and your To Do list, and then create a custom avatar. Check off tasks to level up your avatar and unlock features such as armour, pets, skills, and even quests! Fight monsters with friends to keep each other accountable, and use your gold on in-game rewards, like equipment, or custom awards, like watching an episode of your favourite TV show. Flexible, social, and fun, Habitica is the perfect way to motivate yourself to accomplish anything. If you have any questions, feel free to send feedback to mobile@habitica.com! And if you enjoy our app, we would really appreciate it if you would leave us a review. diff --git a/translations/store_strings-fil.xml b/translations/store_strings-fil.xml index 23bd41c6e..b305530db 100644 --- a/translations/store_strings-fil.xml +++ b/translations/store_strings-fil.xml @@ -2,5 +2,5 @@ Isipin mo na ang buhay mo ay isang laro para lalo kang nagaganyak at mas oranisado! Gamify ang iyong Buhay - Tratuhin ang iyong buhay tulad ng isang laro upang manatili motivated at nakaayos! Ginagawang simple ng Habitica na magkaroon ng kasiyahan habang nagagawa ang mga layunin. Ipasok ang iyong Mga gawi, ang iyong Araw-araw na mga layunin, at ang iyong listahan ng To-Do, at pagkatapos ay lumikha ng isang pasadyang avatar. Suriin ang mga gawain sa antas ng iyong avatar at i-unlock ang mga tampok tulad ng armor, mga alagang hayop, kasanayan, at kahit quests! Labanan ang mga monsters sa mga kaibigan upang panatilihing may pananagutan ang isa\'t isa, at gamitin ang iyong ginto sa mga in-game na premyo, tulad ng kagamitan, o pasadyang mga parangal, tulad ng panonood ng isang episode ng iyong paboritong palabas sa TV. Flexible, social, at masaya, Habitica ay ang perpektong paraan upang mag-udyok sa iyong sarili upang makamit ang anumang bagay. Kung mayroon kang anumang mga katanungan, huwag mag-atubiling magpadala ng feedback sa mobile@habitica.com! At kung masiyahan ka sa aming app, talagang pinahahalagahan namin ito kung aalisin mo sa amin ang isang pagsusuri. + Tratuhin ang iyong buhay tulad ng isang laro upang manatili motivated at nakaayos! Ginagawang simple ng Habitica na magkaroon ng kasiyahan habang nagagawa ang mga layunin. Ipasok ang iyong Mga gawi, ang iyong Araw-araw na mga layunin, at ang iyong listahan ng To Do, at pagkatapos ay lumikha ng isang pasadyang avatar. Suriin ang mga gawain sa antas ng iyong avatar at i-unlock ang mga tampok tulad ng armor, mga alagang hayop, kasanayan, at kahit quests! Labanan ang mga monsters sa mga kaibigan upang panatilihing may pananagutan ang isa\'t isa, at gamitin ang iyong ginto sa mga in-game na premyo, tulad ng kagamitan, o pasadyang mga parangal, tulad ng panonood ng isang episode ng iyong paboritong palabas sa TV. Flexible, social, at masaya, Habitica ay ang perpektong paraan upang mag-udyok sa iyong sarili upang makamit ang anumang bagay. Kung mayroon kang anumang mga katanungan, huwag mag-atubiling magpadala ng feedback sa mobile@habitica.com! At kung masiyahan ka sa aming app, talagang pinahahalagahan namin ito kung aalisin mo sa amin ang isang pagsusuri. diff --git a/translations/store_strings-id.xml b/translations/store_strings-id.xml index 3f1c6c258..205455234 100644 --- a/translations/store_strings-id.xml +++ b/translations/store_strings-id.xml @@ -3,7 +3,7 @@ Buat Tugasmu Menjadi Game Perlakukan hidupmu seperti permainan untuk tetap termotivasi dan terorganisir! Perlakukan hidupmu seperti permainan agar tetap termotivasi dan terorganisir! Habitica membantumu untuk bersenang-senang dalam mencapai tujuan. -Masukkan Kebiasaan, sasaran Harian dan To-Do kamu, lalu buat avatarmu sendiri. Centang tugas-tugas untuk naik level dan buka fitur-fitur seperti baju, peliharaan, kemampuan, dan bahkan misi! Bertarung dengan monster bersama teman-teman supaya saling menyemangati, dan gunakan koin emas yang kamu punya untuk membeli hadiah di dalam game, seperti perlengkapan perang, atau hadiah yang kamu buat sendiri, misalnya menonton satu episode siaran TV kesukaanmu. Fleksibel, sosial, menyenangkan, Habitica sempurna untuk memotivasimu mencapai apapun. +Masukkan Kebiasaan, sasaran Harian dan To Do kamu, lalu buat avatarmu sendiri. Centang tugas-tugas untuk naik level dan buka fitur-fitur seperti baju, peliharaan, kemampuan, dan bahkan misi! Bertarung dengan monster bersama teman-teman supaya saling menyemangati, dan gunakan koin emas yang kamu punya untuk membeli hadiah di dalam game, seperti perlengkapan perang, atau hadiah yang kamu buat sendiri, misalnya menonton satu episode siaran TV kesukaanmu. Fleksibel, sosial, menyenangkan, Habitica sempurna untuk memotivasimu mencapai apapun. Kalau kamu punya pertanyaan maupun saran, silakan kirim ke mobile@habitica.com! Dan jika kamu menyukai aplikasi kami, kami akan sangat menghargai ulasanmu. diff --git a/translations/store_strings-it.xml b/translations/store_strings-it.xml index 520543eb4..5eb274833 100644 --- a/translations/store_strings-it.xml +++ b/translations/store_strings-it.xml @@ -4,7 +4,7 @@ Trasforma la tua vita in un gioco di ruolo per affrontarla motivato ed organizzato. Considera la tua vita come un gioco di ruolo per rimanere motivato e organizzato! Habitica la semplifica e ti fa divertire mentre raggiungi i tuoi obiettivi. -Inserisci le tue Abitudini, i tuoi obiettivi giornalieri (Daily) e la tua lista di cose da fare (To-Do), poi crea il tuo avatar personalizzato. Completa le attività per far salire di livello il tuo avatar e sbloccare funzionalità come armature, animali, abilità e persino missioni! Combatti i mostri con i tuoi amici per aiutarvi a vicenda ad essere responsabili, e usa il tuo oro per ricompense in-game come pezzi di equipaggiamento, o ricompense personalizzate, ad esempio guardare un episodio della tua serie TV preferita. Flessibile, sociale e soprattutto divertente, Habitica è il modo perfetto per motivarti a fare qualsiasi cosa. +Inserisci le tue Abitudini, i tuoi obiettivi giornalieri (Daily) e la tua lista di cose da fare (To Do), poi crea il tuo avatar personalizzato. Completa le attività per far salire di livello il tuo avatar e sbloccare funzionalità come armature, animali, abilità e persino missioni! Combatti i mostri con i tuoi amici per aiutarvi a vicenda ad essere responsabili, e usa il tuo oro per ricompense in-game come pezzi di equipaggiamento, o ricompense personalizzate, ad esempio guardare un episodio della tua serie TV preferita. Flessibile, sociale e soprattutto divertente, Habitica è il modo perfetto per motivarti a fare qualsiasi cosa. Se avete una qualsiasi domanda o dei suggerimenti, sentitevi liberi di scrivere a mobile@habitica.com! E se vi piace la nostra app, saremmo davvero felici se ci lasciaste una recensione. diff --git a/translations/store_strings-ja.xml b/translations/store_strings-ja.xml index 6704e72e3..267eba120 100644 --- a/translations/store_strings-ja.xml +++ b/translations/store_strings-ja.xml @@ -4,7 +4,7 @@ 自分の人生をゲーム化して、やる気の維持とやるべきことの整理をしよう! 自分の人生をゲーム化して、やる気の維持とやるべきことの整理をしましょう!Habitica は目標達成を楽しく簡単にします。 -習慣、毎日の目標、そしてTo-Do の一覧を入力し、お好みのアバターをつくりましょう。タスクをこなして、レベルを上げていくと、防具、ペット、スキルやクエストなどの機能をアンロックしていくこともできます! 責任をもちながら友達といっしょにモンスターと戦い、ゲーム内でゴールドを稼いで、アバターの装備や、「好きなテレビ番組の 1 話分」などの自分好みのごほうびを買うこともできます。柔軟で、ソーシャルで、楽しい――Habitica は、あなたがご自身の目標を達成するのを励ますカンペキな方法です。 +習慣、毎日の目標、そしてTo Do の一覧を入力し、お好みのアバターをつくりましょう。タスクをこなして、レベルを上げていくと、防具、ペット、スキルやクエストなどの機能をアンロックしていくこともできます! 責任をもちながら友達といっしょにモンスターと戦い、ゲーム内でゴールドを稼いで、アバターの装備や、「好きなテレビ番組の 1 話分」などの自分好みのごほうびを買うこともできます。柔軟で、ソーシャルで、楽しい――Habitica は、あなたがご自身の目標を達成するのを励ますカンペキな方法です。 質問があったら、ぜひお気軽に mobile@habitica.com あてにフィードバックをお寄せください!そしてもしこのアプリをお楽しみいただけたなら、レビューを残していただけると、たいへんうれしいです。 diff --git a/translations/store_strings-nl.xml b/translations/store_strings-nl.xml index 519c008ba..4f73ca3e5 100644 --- a/translations/store_strings-nl.xml +++ b/translations/store_strings-nl.xml @@ -3,7 +3,7 @@ Maak van je taken een spel Behandel je leven alsof het een spel is en blijf gemotiveerd en georganiseerd! Behandel je leven als een spel om gemotiveerd en georganiseerd te blijven! Habitica houdt het simpel om plezier te maken terwijl je doelen bereikt. -Voer je gewoontes, dagelijkse taken en to-do\'s toe en maak dan een eigen avatar. Vink taken af om je avatar sterker te maken en functies vrij te spelen zoals uitrusting, huisdieren, vaardigheden en zelfs queesten! Vecht tegen monsters met vrienden om samen verantwoordelijk te blijven en gebruik je goud voor beloningen, zoals uitrusting of kies je eigen beloning, zoals een aflevering van je favoriete serie bekijken. Flexibel, sociaal en plezant, Habitica is de perfecte manier om jezelf te motiveren om alles te bereiken. +Voer je gewoontes, dagelijkse taken en To Do\'s toe en maak dan een eigen avatar. Vink taken af om je avatar sterker te maken en functies vrij te spelen zoals uitrusting, huisdieren, vaardigheden en zelfs queesten! Vecht tegen monsters met vrienden om samen verantwoordelijk te blijven en gebruik je goud voor beloningen, zoals uitrusting of kies je eigen beloning, zoals een aflevering van je favoriete serie bekijken. Flexibel, sociaal en plezant, Habitica is de perfecte manier om jezelf te motiveren om alles te bereiken. Als je vragen hebt kan je ze stellen bij mobile@habitica.com! En als je de app leuk vindt, waarderen we het als je ons een recensie nalaat.