Merge branch 'develop' into resize-petnmounts-cards

This commit is contained in:
Phillip Thelen 2020-06-10 14:07:50 +02:00 committed by GitHub
commit 9c931973c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
79 changed files with 882 additions and 259 deletions

View file

@ -8,7 +8,7 @@ assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
A clear and concise description of what the bug is. If your issue is not related to a specific bug, please submit it as a suggestion through our form: https://forms.gle/Fa6oztkDsbzLqSvE9
**To Reproduce**
Steps to reproduce the behavior:

26
.github/ISSUE_TEMPLATE/crash_report.md vendored Normal file
View file

@ -0,0 +1,26 @@
---
name: Crash report
about: Create a report for app crash instances
title: ''
labels: 'Type: Crash'
assignees: ''
---
**Describe the crash**
What were you doing before the issue? Was there an error, did the app stall, or did it fully close?
**To Reproduce**
Can you reliably reproduce the crash? If so, list steps.
**Screenshots/Video**
If applicable, add screenshots or video to help show the problem
**Smartphone (please complete the following information):**
- Device: [e.g. Moto G5]
- OS: [e.g. Android 9]
- App Version: [e.g. 2.2]
- Habitica Username:
**Additional context**
Add any other context about the problem here.

View file

@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: 'Type: Feature'
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View file

@ -5,11 +5,8 @@
android:screenOrientation="portrait"
android:installLocation="auto" >
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

View file

@ -18,7 +18,7 @@ buildscript {
}
dependencies {
classpath 'io.fabric.tools:gradle:1.+'
classpath 'com.android.tools.build:gradle:3.6.2'
classpath 'com.android.tools.build:gradle:4.0.0'
classpath('com.noveogroup.android:check:1.2.5') {
exclude module: 'checkstyle'
exclude module: 'pmd-java'
@ -129,12 +129,12 @@ dependencies {
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'com.nex3z:flow-layout:1.2.2'
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.core:core-ktx:1.3.0'
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.2.1'
implementation "androidx.paging:paging-runtime-ktx:2.1.1"
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'
implementation "androidx.paging:paging-runtime-ktx:2.1.2"
implementation 'com.plattysoft.leonids:LeonidsLib:1.3.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
@ -162,8 +162,8 @@ android {
multiDexEnabled true
resConfigs "en", "bg", "de", "en-rGB", "es", "fr", "hr-rHR", "in", "it", "iw", "ja", "ko", "lt", "nl", "pl", "pt-rBR", "pt-rPT", "ru", "tr", "zh", "zh-rTW"
versionCode 2404
versionName "2.5"
versionCode 2421
versionName "2.6.2"
}
viewBinding {
@ -213,6 +213,12 @@ android {
dimension "buildType"
}
staff {
dimension "buildType"
buildConfigField "String", "TESTING_LEVEL", "\"staff\""
resValue "string", "app_name", "Habitica Staff"
}
alpha {
dimension "buildType"
buildConfigField "String", "TESTING_LEVEL", "\"alpha\""

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 646 B

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape android:shape="rectangle">
<solid android:color="@color/brand_200" />
</shape>
</item>
<item>
<bitmap
android:gravity="left|top"
android:src="@drawable/gold_coins_left" />
</item>
<item>
<bitmap
android:gravity="right|bottom"
android:src="@drawable/gold_coins_right" />
</item>
</layer-list>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@color/white" />
<corners android:radius="8dp" />
</shape>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<size android:width="24dp" android:height="80dp" />
<solid android:color="@color/gray_700" />
<corners android:topRightRadius="8dp" android:bottomRightRadius="8dp" />
</shape>

View file

@ -35,72 +35,266 @@
android:background="@color/gray_700"
android:layout_marginBottom="16dp"
/>
<com.habitrpg.android.habitica.ui.views.settings.FixValuesEditText
android:id="@+id/healthEditText"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/HP_default"
app:fixIconName="health"
app:hintStyle="@style/RedTextLabel"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="12dp"
app:iconBgColor="@color/red_500"/>
<com.habitrpg.android.habitica.ui.views.settings.FixValuesEditText
android:id="@+id/experienceEditText"
android:layout_marginBottom="12dp" >
<View
android:id="@+id/healthIconBackgroundView"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:background="@color/red_500"/>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/editTextWrapper"
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1"
android:background="@drawable/layout_rounded_bg_gray_700"
android:paddingTop="8dp"
android:hint="@string/health"
android:paddingStart="16dp">
<EditText
android:id="@+id/healthEditText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:inputType="numberDecimal"
android:background="@android:color/transparent"/>
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:id="@+id/healthIconView"
android:layout_width="56dp"
android:layout_height="56dp"
android:scaleType="center"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_gravity="bottom"
android:background="@color/gray_500"
android:layout_marginEnd="56dp"/>
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/XP_default"
app:fixIconName="experience"
app:hintStyle="@style/YellowTextLabel"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="12dp"
app:iconBgColor="@color/yellow_500"/>
<com.habitrpg.android.habitica.ui.views.settings.FixValuesEditText
android:id="@+id/manaEditText"
android:layout_marginBottom="12dp" >
<View
android:id="@+id/experienceIconBackgroundView"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:background="@color/yellow_500"/>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent">
<com.google.android.material.textfield.TextInputLayout
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1"
android:background="@drawable/layout_rounded_bg_gray_700"
android:paddingTop="8dp"
android:hint="@string/experience_points"
android:paddingStart="16dp">
<EditText
android:id="@+id/experienceEditText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:inputType="numberDecimal"
android:background="@android:color/transparent"/>
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:id="@+id/experienceIconView"
android:layout_width="56dp"
android:layout_height="56dp"
android:scaleType="center"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_gravity="bottom"
android:background="@color/gray_500"
android:layout_marginEnd="56dp"/>
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/MP_default"
app:fixIconName="mana"
app:hintStyle="@style/BlueTextLabel"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="12dp"
app:iconBgColor="@color/blue_500"/>
<com.habitrpg.android.habitica.ui.views.settings.FixValuesEditText
android:id="@+id/goldEditText"
android:layout_marginBottom="12dp" >
<View
android:id="@+id/manaIconBackgroundView"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:background="@color/blue_500"/>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent">
<com.google.android.material.textfield.TextInputLayout
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1"
android:background="@drawable/layout_rounded_bg_gray_700"
android:paddingTop="8dp"
android:hint="@string/mana_points"
android:paddingStart="16dp">
<EditText
android:id="@+id/manaEditText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:inputType="numberDecimal"
android:background="@android:color/transparent"/>
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:id="@+id/manaIconView"
android:layout_width="56dp"
android:layout_height="56dp"
android:scaleType="center"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_gravity="bottom"
android:background="@color/gray_500"
android:layout_marginEnd="56dp"/>
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/gold_plural"
app:fixIconName="gold"
app:hintStyle="@style/YellowTextLabel"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="12dp"
app:iconBgColor="@color/yellow_500"/>
<com.habitrpg.android.habitica.ui.views.settings.FixValuesEditText
android:id="@+id/levelEditText"
android:layout_marginBottom="12dp" >
<View
android:id="@+id/goldIconBackgroundView"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:background="@color/yellow_500"/>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent">
<com.google.android.material.textfield.TextInputLayout
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1"
android:background="@drawable/layout_rounded_bg_gray_700"
android:paddingTop="8dp"
android:hint="@string/gold_capitalilzed"
android:paddingStart="16dp">
<EditText
android:id="@+id/goldEditText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:inputType="numberDecimal"
android:background="@android:color/transparent"/>
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:id="@+id/goldIconView"
android:layout_width="56dp"
android:layout_height="56dp"
android:scaleType="center"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_gravity="bottom"
android:background="@color/gray_500"
android:layout_marginEnd="56dp"/>
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/character_level"
app:fixIconName="level"
app:hintStyle="@style/PurpleTextLabel"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="12dp"
app:iconBgColor="@color/brand_300"/>
<com.habitrpg.android.habitica.ui.views.settings.FixValuesEditText
android:id="@+id/streakEditText"
android:layout_marginBottom="12dp" >
<View
android:id="@+id/levelIconBackgroundView"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:background="@color/red_500"/>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent">
<com.google.android.material.textfield.TextInputLayout
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1"
android:background="@drawable/layout_rounded_bg_gray_700"
android:paddingTop="8dp"
android:hint="@string/character_level"
android:paddingStart="16dp">
<EditText
android:id="@+id/levelEditText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:inputType="number"
android:background="@android:color/transparent"/>
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:id="@+id/levelIconView"
android:layout_width="56dp"
android:layout_height="56dp"
android:scaleType="center"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_gravity="bottom"
android:background="@color/gray_500"
android:layout_marginEnd="56dp"/>
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/streak_label"
app:fixIconName="streak"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="12dp"
app:iconBgColor="@color/gray_400"/>
android:layout_marginBottom="12dp" >
<View
android:id="@+id/streakIconBackgroundView"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:background="@color/gray_400"/>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/streakEditTextWrapper"
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1"
android:background="@drawable/layout_rounded_bg_gray_700"
android:paddingTop="8dp"
android:hint="@string/streak_label"
android:paddingStart="16dp">
<EditText
android:id="@+id/streakEditText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:inputType="number"
android:background="@android:color/transparent"/>
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:id="@+id/streakIconView"
android:layout_width="56dp"
android:layout_height="56dp"
android:scaleType="center"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_gravity="bottom"
android:background="@color/gray_500"
android:layout_marginEnd="56dp"/>
</FrameLayout>
</LinearLayout>
</ScrollView>
</LinearLayout>

View file

@ -220,8 +220,7 @@
android:layout_height="@dimen/diamond_button_height"
android:text="@string/login_btn_apple"
android:drawableStart="@drawable/apple_icon"
style="@style/LoginButton"
android:visibility="gone"/>
style="@style/LoginButton"/>
<Button
android:id="@+id/forgot_password"

View file

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/adventure_guide_menu_bg"
tools:parentTag="android.widget.FrameLayout">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_marginStart="8dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="12dp"
android:background="@drawable/adventure_guide_menu_card_bg">
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="16dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="12dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
style="@style/Caption1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/onboarding_tasks" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/complete_for_gold"
android:textColor="@color/gray_50"
android:textSize="12sp" />
<TextView
android:id="@+id/count_view"
style="@style/Subheader1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/yellow_5"
tools:text="2/5" />
</LinearLayout>
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="match_parent"
android:layout_height="4dp"
android:progressTint="@color/yellow_50"
style="@android:style/Widget.ProgressBar.Horizontal"
android:progressBackgroundTint="@color/gray_600"
tools:progress="2"
android:max="5"/>
</LinearLayout>
<FrameLayout
android:layout_width="24dp"
android:layout_height="match_parent"
android:background="@drawable/adventure_guide_menu_card_gray">
<ImageView
android:layout_width="24dp"
android:layout_height="match_parent"
android:background="@drawable/adventure_guide_menu_card_gray"
android:src="@drawable/ic_keyboard_arrow_right"
android:scaleType="center"
android:alpha="0.25"
/>
</FrameLayout>
</LinearLayout>
</merge>

View file

@ -24,7 +24,7 @@
android:background="@drawable/layout_rounded_bg_gray_700"
android:paddingTop="8dp"
android:paddingStart="16dp">
<EditText
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="match_parent"

View file

@ -130,7 +130,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="@color/gray_50"
android:textColor="@color/gray_100"
android:gravity="center"/>
</LinearLayout>
</LinearLayout>

View file

@ -1,8 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white">
android:background="@color/white"
android:layout_marginBottom="@dimen/spacing_large">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -107,4 +111,5 @@
android:elevation="0dp"
android:layout_marginStart="@dimen/spacing_large"
android:layout_marginEnd="@dimen/spacing_large" />
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View file

@ -1,4 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
@ -38,7 +42,7 @@
android:text="@string/using_habitica_description"
style="@style/Body2"
android:lineSpacingExtra="3sp"
android:textColor="@color/gray_50"
android:textColor="@color/gray_100"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:gravity="center"
@ -56,7 +60,7 @@
android:layout_marginBottom="@dimen/spacing_medium"
android:background="@drawable/support_info_lower_bg"
android:textColor="@color/white"
android:elevation="2dp"
android:elevation="4dp"
android:textAllCaps="false"
style="@style/Subheader1"
/>
@ -88,7 +92,7 @@
android:text="@string/bugs_fixes_description"
style="@style/Body2"
android:lineSpacingExtra="3sp"
android:textColor="@color/gray_50"
android:textColor="@color/gray_100"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:gravity="center"
@ -106,7 +110,7 @@
android:layout_marginBottom="@dimen/spacing_medium"
android:background="@drawable/support_info_lower_bg"
android:textColor="@color/white"
android:elevation="2dp"
android:elevation="4dp"
android:textAllCaps="false"
style="@style/Subheader1"
/>
@ -138,7 +142,7 @@
android:text="@string/suggestions_feedback_description"
style="@style/Body2"
android:lineSpacingExtra="3sp"
android:textColor="@color/gray_50"
android:textColor="@color/gray_100"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:gravity="center"
@ -155,7 +159,7 @@
android:layout_marginEnd="@dimen/spacing_medium"
android:layout_marginBottom="@dimen/spacing_medium"
android:background="@drawable/support_info_lower_bg"
android:elevation="2dp"
android:elevation="4dp"
android:textColor="@color/white"
style="@style/Subheader1"
/>
@ -173,4 +177,5 @@
android:textColor="@color/gray_50"
android:text="@string/reset_walkthrough"
android:layout_margin="@dimen/spacing_large"/>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View file

@ -1016,4 +1016,7 @@
<string name="still_questions">Still have a question?</string>
<string name="delete_checklist_entry">Delete Checklist entry</string>
<string name="pet_ownership_fraction">%1$d/%2$d</string>
<string name="task_display">Task list display</string>
<string name="onboarding_tasks">Onboarding Tasks</string>
<string name="complete_for_gold">Complete to earn 100 Gold!</string>
</resources>

View file

@ -299,7 +299,7 @@
<item name="android:textColor">@color/gray_200</item>
<item name="android:textAllCaps">true</item>
<item name="android:layout_marginStart">@dimen/spacing_large</item>
<item name="android:letterSpacing">0.1</item>
<item name="android:letterSpacing">0.15</item>
</style>
<style name="BottomMenu">

View file

@ -33,6 +33,18 @@
<item>@string/avatar_size_broad</item>
</string-array>
<string-array name="TaskDisplayTypes">
<item>Standard</item>
<item>Compact</item>
<item>Minimal</item>
</string-array>
<string-array name="TaskDisplayValues">
<item>standard</item>
<item>compact</item>
<item>minimal</item>
</string-array>
<string-array name="Language">
<item>English</item>
<item>Български</item>

View file

@ -131,11 +131,12 @@
<PreferenceCategory
android:title="@string/app_settings"
android:layout="@layout/preference_category">
<ListPreference android:title="@string/pref_first_day_of_the_week_title"
android:key="@string/pref_first_day_of_the_week_key"
android:entries="@array/weekdays"
android:entryValues="@array/weekdayValues"
android:summary="@string/pref_first_day_of_the_week_summary"
<ListPreference
android:title="@string/task_display"
android:key="task_display"
android:entries="@array/TaskDisplayTypes"
android:entryValues="@array/TaskDisplayValues"
android:defaultValue="standard"
android:layout="@layout/preference_child_summary"/>
<ListPreference android:title="@string/Language_title"
android:key="language"
@ -159,6 +160,13 @@
android:summary="@string/dailyDueDefaultViewDescription"
android:layout="@layout/preference_child_summary"
/>
<ListPreference
android:entries="@array/weekdays"
android:entryValues="@array/weekdayValues"
android:key="@string/pref_first_day_of_the_week_key"
android:layout="@layout/preference_child_summary"
android:summary="@string/pref_first_day_of_the_week_summary"
android:title="@string/pref_first_day_of_the_week_title" />
</PreferenceCategory>
<PreferenceCategory

View file

@ -76,5 +76,9 @@
<key>raiseShops</key>
<value>false</value>
</entry>
<entry>
<key>enableTaskDisplayMode</key>
<value>true</value>
</entry>
</defaultsMap>
<!-- END xml_defaults -->

View file

@ -1,9 +1,8 @@
package com.habitrpg.android.habitica.helpers
enum class AppTestingLevel(identifier: String) {
STAFF("staff"),
ALPHA("alpha"),
BETA("beta"),
PRODUCTION("production")
}

View file

@ -1,5 +1,7 @@
package com.habitrpg.android.habitica.helpers
import android.content.Context
import androidx.preference.PreferenceManager
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
@ -82,4 +84,17 @@ class AppConfigManager {
fun raiseShops(): Boolean {
return remoteConfig.getBoolean("raiseShops")
}
fun enableTaskDisplayMode(): Boolean {
return remoteConfig.getBoolean("enableTaskDisplayMode")
}
fun taskDisplayMode(context: Context): String {
return if (remoteConfig.getBoolean("enableTaskDisplayMode")) {
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
preferences.getString("task_display", "standard") ?: "standard"
} else {
"standard"
}
}
}

View file

@ -53,6 +53,9 @@ open class User : RealmObject(), Avatar, VersionedObject {
for (test in abTests ?: emptyList<ABTest>()) {
test.userID = id
}
for (achievement in achievements ?: emptyList<UserAchievement>()) {
achievement.userId = id
}
}
@SerializedName("_v")
@ -127,6 +130,7 @@ open class User : RealmObject(), Avatar, VersionedObject {
}
var tags = RealmList<Tag>()
var achievements = RealmList<UserAchievement>()
var questAchievements = RealmList<QuestAchievement>()
set(value) {
field = value
@ -238,4 +242,30 @@ open class User : RealmObject(), Avatar, VersionedObject {
}
return isSubscribed
}
val onboardingAchievements: List<UserAchievement>
get() {
val onboarding = mutableMapOf<String, UserAchievement>()
for (key in ONBOARDING_ACHIEVEMENT_KEYS) {
val achievement = UserAchievement()
achievement.key = key
onboarding[key] = achievement
}
for (achievement in achievements) {
if (achievement.key in ONBOARDING_ACHIEVEMENT_KEYS) {
onboarding[achievement.key ?: ""] = achievement
}
}
return onboarding.values.toList()
}
val hasCompletedOnboarding: Boolean
get() {
val onboarding = onboardingAchievements
return onboarding.count { it.earned } == onboarding.size
}
companion object {
val ONBOARDING_ACHIEVEMENT_KEYS = listOf("createdTask", "completedTask", "hatchedPet", "fedPet", "purchasedEquipment")
}
}

View file

@ -0,0 +1,24 @@
package com.habitrpg.android.habitica.models.user
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class UserAchievement : RealmObject {
@PrimaryKey
var combinedKey: String? = null
var userId: String? = null
set(value) {
field = value
combinedKey = field + key
}
var key: String? = null
set(value) {
field = value
combinedKey = userId + field
}
var earned: Boolean = false
constructor()
}

View file

@ -1,10 +1,12 @@
package com.habitrpg.android.habitica.ui.activities
import android.app.ProgressDialog
import android.graphics.PorterDuff
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.EditText
import androidx.core.content.ContextCompat
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
@ -15,9 +17,9 @@ import com.habitrpg.android.habitica.models.user.Stats
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.modules.AppModule
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
import com.habitrpg.android.habitica.ui.views.settings.FixValuesEditText
import io.reactivex.functions.Action
import io.reactivex.functions.Consumer
import kotlinx.android.synthetic.main.activity_fixcharacter.*
import javax.inject.Inject
import javax.inject.Named
@ -50,6 +52,19 @@ class FixCharacterValuesActivity: BaseActivity() {
compositeSubscription.add(repository.getUser(userId).firstElement().subscribe(Consumer {
user = it
}, RxErrorHandler.handleEmptyError()))
setIconBackground(binding.healthIconBackgroundView, ContextCompat.getColor(this, R.color.red_500))
setIconBackground(binding.experienceIconBackgroundView, ContextCompat.getColor(this, R.color.yellow_500))
setIconBackground(binding.manaIconBackgroundView, ContextCompat.getColor(this, R.color.blue_500))
setIconBackground(binding.goldIconBackgroundView, ContextCompat.getColor(this, R.color.yellow_500))
setIconBackground(binding.streakIconBackgroundView, ContextCompat.getColor(this, R.color.gray_400))
binding.healthIconView.setImageBitmap(HabiticaIconsHelper.imageOfHeartLightBg())
binding.experienceIconView.setImageBitmap(HabiticaIconsHelper.imageOfExperience())
binding.manaIconView.setImageBitmap(HabiticaIconsHelper.imageOfMagic())
binding.goldIconView.setImageBitmap(HabiticaIconsHelper.imageOfGold())
binding.levelIconView.setImageBitmap(HabiticaIconsHelper.imageOfRogueLightBg())
binding.streakIconView.setImageResource(R.drawable.achievement_thermometer)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
@ -90,35 +105,42 @@ class FixCharacterValuesActivity: BaseActivity() {
private fun updateFields(user: User) {
val stats = user.stats ?: return
binding.healthEditText.text = stats.hp.toString()
binding.experienceEditText.text = stats.exp.toString()
binding.goldEditText.text = stats.gp.toString()
binding.manaEditText.text = stats.mp.toString()
binding.levelEditText.text = stats.lvl.toString()
binding.streakEditText.text = user.streakCount.toString()
binding.healthEditText.setText(stats.hp.toString())
binding.experienceEditText.setText(stats.exp.toString())
binding.goldEditText.setText(stats.gp.toString())
binding.manaEditText.setText(stats.mp.toString())
binding.levelEditText.setText(stats.lvl.toString())
binding.streakEditText.setText(user.streakCount.toString())
when (stats.habitClass) {
Stats.WARRIOR -> {
binding.levelEditText.iconBackgroundColor = ContextCompat.getColor(this, R.color.red_500)
binding.levelEditText.setIconBitmap(HabiticaIconsHelper.imageOfWarriorLightBg())
setIconBackground(levelIconBackgroundView, ContextCompat.getColor(this, R.color.red_500))
binding.levelIconView.setImageBitmap(HabiticaIconsHelper.imageOfWarriorLightBg())
}
Stats.MAGE -> {
binding.levelEditText.iconBackgroundColor = ContextCompat.getColor(this, R.color.blue_500)
binding.levelEditText.setIconBitmap(HabiticaIconsHelper.imageOfMageLightBg())
setIconBackground(levelIconBackgroundView, ContextCompat.getColor(this, R.color.blue_500))
binding.levelIconView.setImageBitmap(HabiticaIconsHelper.imageOfMageLightBg())
}
Stats.HEALER -> {
binding.levelEditText.iconBackgroundColor = ContextCompat.getColor(this, R.color.yellow_500)
binding.levelEditText.setIconBitmap(HabiticaIconsHelper.imageOfHealerLightBg())
setIconBackground(levelIconBackgroundView, ContextCompat.getColor(this, R.color.yellow_500))
binding.levelIconView.setImageBitmap(HabiticaIconsHelper.imageOfHealerLightBg())
}
Stats.ROGUE -> {
binding.levelEditText.iconBackgroundColor = ContextCompat.getColor(this, R.color.brand_500)
binding.levelEditText.setIconBitmap(HabiticaIconsHelper.imageOfRogueLightBg())
setIconBackground(levelIconBackgroundView, ContextCompat.getColor(this, R.color.brand_500))
binding.levelIconView.setImageBitmap(HabiticaIconsHelper.imageOfRogueLightBg())
}
}
}
private fun FixValuesEditText.getDoubleValue(): Double {
val stringValue = this.text
private fun setIconBackground(view: View, color: Int) {
val backgroundDrawable = ContextCompat.getDrawable(this, R.drawable.layout_rounded_bg)
backgroundDrawable?.setColorFilter(color, PorterDuff.Mode.MULTIPLY)
backgroundDrawable?.alpha = 50
view.background = backgroundDrawable
}
private fun EditText.getDoubleValue(): Double {
val stringValue = this.text.toString()
return try {
stringValue.toDouble()
} catch (_: NumberFormatException) {

View file

@ -24,7 +24,6 @@ import com.habitrpg.android.habitica.ui.helpers.dismissKeyboard
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.Companion.showSnackbar
import io.reactivex.functions.Consumer
import kotlinx.android.synthetic.main.activity_prefs.*
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -54,7 +53,7 @@ class GroupInviteActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupToolbar(toolbar)
setupToolbar(findViewById(R.id.toolbar))
viewPager.currentItem = 0
supportActionBar?.title = null

View file

@ -6,7 +6,6 @@ import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.ui.helpers.setMarkdown
import kotlinx.android.synthetic.main.activity_prefs.*
import okhttp3.*
import java.io.BufferedReader
import java.io.IOException
@ -20,7 +19,7 @@ class GuidelinesActivity: BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupToolbar(toolbar)
setupToolbar(findViewById(R.id.toolbar))
val client = OkHttpClient()
val request = Request.Builder().url("https://s3.amazonaws.com/habitica-assets/mobileApp/endpoint/community-guidelines.md").build()

View file

@ -3,46 +3,47 @@ package com.habitrpg.android.habitica.ui.activities
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.viewpager.widget.ViewPager
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.ContentRepository
import com.habitrpg.android.habitica.databinding.ActivityIntroBinding
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.ui.fragments.setup.IntroFragment
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.viewpagerindicator.IconPageIndicator
import com.viewpagerindicator.IconPagerAdapter
import io.reactivex.functions.Consumer
import javax.inject.Inject
class IntroActivity : BaseActivity(), View.OnClickListener, ViewPager.OnPageChangeListener {
private lateinit var binding: ActivityIntroBinding
@Inject
lateinit var contentRepository: ContentRepository
private val pager: ViewPager by bindView(R.id.viewPager)
private val indicator: IconPageIndicator by bindView(R.id.view_pager_indicator)
private val skipButton: Button by bindView(R.id.skipButton)
private val finishButton: Button by bindView(R.id.finishButton)
override fun getLayoutResId(): Int {
return R.layout.activity_intro
}
override fun getContentView(): View {
binding = ActivityIntroBinding.inflate(layoutInflater)
return binding.root
}
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupIntro()
indicator.setViewPager(pager)
//binding.viewPagerIndicator.setViewPager(binding.viewPager)
this.skipButton.setOnClickListener(this)
this.finishButton.setOnClickListener(this)
binding.skipButton.setOnClickListener(this)
binding.finishButton.setOnClickListener(this)
compositeSubscription.add(contentRepository.retrieveContent(this).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
}
@ -52,11 +53,9 @@ class IntroActivity : BaseActivity(), View.OnClickListener, ViewPager.OnPageChan
}
private fun setupIntro() {
val fragmentManager = supportFragmentManager
binding.viewPager.adapter = PagerAdapter(supportFragmentManager)
pager.adapter = PagerAdapter(fragmentManager)
pager.addOnPageChangeListener(this)
binding.viewPager.addOnPageChangeListener(this)
}
override fun onClick(v: View) {
@ -76,43 +75,19 @@ class IntroActivity : BaseActivity(), View.OnClickListener, ViewPager.OnPageChan
override fun onPageSelected(position: Int) {
if (position == 2) {
this.finishButton.visibility = View.VISIBLE
binding.finishButton.visibility = View.VISIBLE
} else {
this.finishButton.visibility = View.GONE
binding.finishButton.visibility = View.GONE
}
}
override fun onPageScrollStateChanged(state: Int) { /* no-on */ }
private inner class PagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT), IconPagerAdapter {
private inner class PagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT), IconPagerAdapter {
override fun getItem(position: Int): Fragment {
val fragment = IntroFragment()
when (position) {
0 -> {
fragment.setImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_1, null))
fragment.setSubtitle(getString(R.string.intro_1_subtitle))
fragment.setTitleImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_1_title, null))
fragment.setDescription(getString(R.string.intro_1_description, getString(R.string.habitica_user_count)))
fragment.setBackgroundColor(ContextCompat.getColor(this@IntroActivity, R.color.brand_300))
}
1 -> {
fragment.setImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_2, null))
fragment.setSubtitle(getString(R.string.intro_2_subtitle))
fragment.setTitle(getString(R.string.intro_2_title))
fragment.setDescription(getString(R.string.intro_2_description))
fragment.setBackgroundColor(ContextCompat.getColor(this@IntroActivity, R.color.blue_10))
}
2 -> {
fragment.setImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_3, null))
fragment.setSubtitle(getString(R.string.intro_3_subtitle))
fragment.setTitle(getString(R.string.intro_3_title))
fragment.setDescription(getString(R.string.intro_3_description))
fragment.setBackgroundColor(ContextCompat.getColor(this@IntroActivity, R.color.red_100))
}
}
configureFragment(fragment, position)
return fragment
}
@ -123,5 +98,39 @@ class IntroActivity : BaseActivity(), View.OnClickListener, ViewPager.OnPageChan
override fun getCount(): Int {
return 3
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val item = super.instantiateItem(container, position)
if (item is IntroFragment) {
configureFragment(item, position)
}
return item
}
}
private fun configureFragment(fragment: IntroFragment, position: Int) {
when (position) {
0 -> {
fragment.setImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_1, null))
fragment.setSubtitle(getString(R.string.intro_1_subtitle))
fragment.setTitleImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_1_title, null))
fragment.setDescription(getString(R.string.intro_1_description, getString(R.string.habitica_user_count)))
fragment.setBackgroundColor(ContextCompat.getColor(this@IntroActivity, R.color.brand_300))
}
1 -> {
fragment.setImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_2, null))
fragment.setSubtitle(getString(R.string.intro_2_subtitle))
fragment.setTitle(getString(R.string.intro_2_title))
fragment.setDescription(getString(R.string.intro_2_description))
fragment.setBackgroundColor(ContextCompat.getColor(this@IntroActivity, R.color.blue_10))
}
2 -> {
fragment.setImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_3, null))
fragment.setSubtitle(getString(R.string.intro_3_subtitle))
fragment.setTitle(getString(R.string.intro_3_title))
fragment.setDescription(getString(R.string.intro_3_description))
fragment.setBackgroundColor(ContextCompat.getColor(this@IntroActivity, R.color.red_100))
}
}
}
}

View file

@ -13,6 +13,8 @@ import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.data.SocialRepository
import com.habitrpg.android.habitica.databinding.ActivityIntroBinding
import com.habitrpg.android.habitica.databinding.ActivityNotificationsBinding
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.Notification
import com.habitrpg.android.habitica.models.inventory.QuestContent
@ -20,11 +22,11 @@ import com.habitrpg.android.habitica.models.notifications.*
import com.habitrpg.android.habitica.ui.activities.MainActivity.Companion.NOTIFICATION_CLICK
import com.habitrpg.android.habitica.ui.viewmodels.NotificationsViewModel
import io.reactivex.functions.Consumer
import kotlinx.android.synthetic.main.activity_notifications.*
import javax.inject.Inject
class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener {
private lateinit var binding: ActivityNotificationsBinding
@Inject
lateinit var inventoryRepository: InventoryRepository
@Inject
@ -36,12 +38,17 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
override fun getLayoutResId(): Int = R.layout.activity_notifications
override fun getContentView(): View {
binding = ActivityNotificationsBinding.inflate(layoutInflater)
return binding.root
}
private var notifications: List<Notification> = emptyList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupToolbar(toolbar)
setupToolbar(binding.toolbar)
inflater = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as? LayoutInflater
@ -53,7 +60,7 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
viewModel.markNotificationsAsSeen(it)
}, RxErrorHandler.handleEmptyError()))
notifications_refresh_layout?.setOnRefreshListener(this)
binding.notificationsRefreshLayout.setOnRefreshListener(this)
}
override fun injectActivity(component: UserComponent?) {
@ -69,21 +76,21 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
}
override fun onRefresh() {
notifications_refresh_layout.isRefreshing = true
binding.notificationsRefreshLayout.isRefreshing = true
compositeSubscription.add(viewModel.refreshNotifications().subscribe(Consumer {
notifications_refresh_layout.isRefreshing = false
binding.notificationsRefreshLayout.isRefreshing = false
}, RxErrorHandler.handleEmptyError()))
}
private fun setNotifications(notifications: List<Notification>) {
this.notifications = notifications
if (notification_items == null) {
if (binding.notificationItems == null) {
return
}
notification_items.removeAllViewsInLayout()
binding.notificationItems.removeAllViewsInLayout()
if (notifications.isEmpty()) {
displayNoNotificationsView()
@ -93,15 +100,15 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
}
private fun displayNoNotificationsView() {
notification_items.showDividers = LinearLayout.SHOW_DIVIDER_NONE
binding.notificationItems.showDividers = LinearLayout.SHOW_DIVIDER_NONE
notification_items.addView(inflater?.inflate(R.layout.no_notifications, notification_items, false))
binding.notificationItems.addView(inflater?.inflate(R.layout.no_notifications, binding.notificationItems, false))
}
private fun displayNotificationsListView(notifications: List<Notification>) {
notification_items.showDividers = LinearLayout.SHOW_DIVIDER_MIDDLE or LinearLayout.SHOW_DIVIDER_END
binding.notificationItems.showDividers = LinearLayout.SHOW_DIVIDER_MIDDLE or LinearLayout.SHOW_DIVIDER_END
notification_items.addView(
binding.notificationItems.addView(
createNotificationsHeaderView(notifications.count())
)
@ -121,13 +128,13 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
}
if (item != null) {
notification_items.addView(item)
binding.notificationItems.addView(item)
}
}
}
private fun createNotificationsHeaderView(notificationCount: Int): View? {
val header = inflater?.inflate(R.layout.notifications_header, notification_items, false)
val header = inflater?.inflate(R.layout.notifications_header, binding.notificationItems, false)
val badge = header?.findViewById(R.id.notifications_title_badge) as? TextView
badge?.text = notificationCount.toString()
@ -237,7 +244,7 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
imageResourceId: Int? = null,
textColor: Int? = null
): View? {
val item = inflater?.inflate(R.layout.notification_item, notification_items, false)
val item = inflater?.inflate(R.layout.notification_item, binding.notificationItems, false)
val container = item?.findViewById(R.id.notification_item) as? View
container?.setOnClickListener {
@ -337,7 +344,7 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
notification: Notification,
messageText: CharSequence,
openable: Boolean = false): View? {
val item = inflater?.inflate(R.layout.notification_item_actionable, notification_items, false)
val item = inflater?.inflate(R.layout.notification_item_actionable, binding.notificationItems, false)
if (openable) {
val container = item?.findViewById(R.id.notification_item) as? View

View file

@ -6,7 +6,6 @@ import androidx.preference.PreferenceScreen
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.ui.fragments.preferences.*
import kotlinx.android.synthetic.main.activity_prefs.*
class PrefsActivity : BaseActivity(), PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
@ -15,7 +14,7 @@ class PrefsActivity : BaseActivity(), PreferenceFragmentCompat.OnPreferenceStart
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupToolbar(toolbar)
setupToolbar(findViewById(R.id.toolbar))
supportFragmentManager.beginTransaction()
.add(R.id.fragment_container, PreferencesFragment())

View file

@ -6,6 +6,7 @@ import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.Button
import androidx.appcompat.content.res.AppCompatResources
@ -61,6 +62,7 @@ class SetupActivity : BaseActivity(), ViewPager.OnPageChangeListener {
internal var taskSetupFragment: TaskSetupFragment? = null
internal var user: User? = null
private var completedSetup = false
private var createdTasks = false
private val isLastPage: Boolean
get() = this.pager.adapter == null || this.pager.currentItem == (this.pager.adapter?.count ?: 0) - 1
@ -127,8 +129,13 @@ class SetupActivity : BaseActivity(), ViewPager.OnPageChangeListener {
if (this.taskSetupFragment == null) {
return
}
if (createdTasks) {
onUserReceived(user)
return
}
val newTasks = this.taskSetupFragment?.createSampleTasks()
this.completedSetup = true
createdTasks = true
newTasks?.let {
this.taskRepository.createTasks(it).subscribe(Consumer { onUserReceived(user) }, RxErrorHandler.handleEmptyError())
}
@ -194,14 +201,14 @@ class SetupActivity : BaseActivity(), ViewPager.OnPageChangeListener {
private fun onUserReceived(user: User?) {
if (completedSetup) {
if (!compositeSubscription.isDisposed) {
compositeSubscription.dispose()
}
val additionalData = HashMap<String, Any>()
additionalData["status"] = "completed"
AmplitudeManager.sendEvent("setup", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData)
compositeSubscription.add(userRepository.updateUser(user, "flags.welcomed", true).subscribe(Consumer {
if (!compositeSubscription.isDisposed) {
compositeSubscription.dispose()
}
startMainActivity()
}, RxErrorHandler.handleEmptyError()))
return
@ -256,6 +263,29 @@ class SetupActivity : BaseActivity(), ViewPager.OnPageChangeListener {
}
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val item = super.instantiateItem(container, position)
when (item) {
is AvatarSetupFragment -> {
avatarSetupFragment = item
item.activity = this@SetupActivity
item.setUser(user)
item.width = pager.width
}
is TaskSetupFragment -> {
taskSetupFragment = item
item.setUser(user)
}
is WelcomeFragment -> {
welcomeFragment = item
item.nameValidEvents.toFlowable(BackpressureStrategy.DROP)?.subscribe {
setNextButtonEnabled(it)
}
}
}
return item
}
override fun getCount(): Int {
return 3
}

View file

@ -13,7 +13,6 @@ import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.ui.adapter.social.PartyMemberRecyclerViewAdapter
import com.habitrpg.android.habitica.ui.helpers.bindView
import io.reactivex.functions.Consumer
import kotlinx.android.synthetic.main.activity_prefs.*
import javax.inject.Inject
class SkillMemberActivity : BaseActivity() {
@ -36,7 +35,7 @@ class SkillMemberActivity : BaseActivity() {
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupToolbar(toolbar)
setupToolbar(findViewById(R.id.toolbar))
loadMemberList()
}

View file

@ -5,6 +5,7 @@ import android.content.Intent
import android.os.Bundle
import android.util.SparseArray
import android.view.MenuItem
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentPagerAdapter
import androidx.viewpager.widget.ViewPager
@ -18,7 +19,6 @@ import com.habitrpg.android.habitica.modules.AppModule
import com.habitrpg.android.habitica.ui.fragments.skills.SkillTasksRecyclerViewFragment
import com.habitrpg.android.habitica.ui.helpers.bindView
import io.reactivex.functions.Consumer
import kotlinx.android.synthetic.main.activity_prefs.*
import javax.inject.Inject
import javax.inject.Named
@ -40,7 +40,7 @@ class SkillTasksActivity : BaseActivity() {
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupToolbar(toolbar)
setupToolbar(findViewById(R.id.toolbar))
loadTaskLists()
}
@ -68,6 +68,21 @@ class SkillTasksActivity : BaseActivity() {
return fragment
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val item = super.instantiateItem(container, position)
if (item is SkillTasksRecyclerViewFragment) {
item.taskType = when (position) {
0 -> Task.TYPE_HABIT
1 -> Task.TYPE_DAILY
else -> Task.TYPE_TODO
}
compositeSubscription.add(item.getTaskSelectionEvents().subscribe(Consumer { task -> taskSelected(task) }, RxErrorHandler.handleEmptyError()))
viewFragmentsDictionary.put(position, item)
}
return item
}
override fun getCount(): Int {
return 3
}

View file

@ -11,6 +11,7 @@ import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.ui.helpers.bindOptionalView
import com.habitrpg.android.habitica.ui.menu.HabiticaDrawerItem
import com.habitrpg.android.habitica.ui.viewHolders.GiftOneGetOnePromoMenuView
import com.habitrpg.android.habitica.ui.views.adventureGuide.AdventureGuideMenuBanner
import com.habitrpg.android.habitica.ui.views.promo.SubscriptionBuyGemsPromoView
import com.habitrpg.android.habitica.ui.views.promo.SubscriptionBuyGemsPromoViewHolder
import io.reactivex.BackpressureStrategy
@ -78,6 +79,8 @@ class NavigationDrawerAdapter(tintColor: Int, backgroundTintColor: Int): Recycle
} else if (getItemViewType(position) == 1) {
(holder as? SectionHeaderViewHolder)?.backgroundTintColor = backgroundTintColor
(holder as? SectionHeaderViewHolder)?.bind(drawerItem)
} else if (getItemViewType(position) == 4) {
drawerItem.user?.let { (holder.itemView as? AdventureGuideMenuBanner)?.updateData(it) }
}
}
@ -111,6 +114,14 @@ class NavigationDrawerAdapter(tintColor: Int, backgroundTintColor: Int): Recycle
)
SubscriptionBuyGemsPromoViewHolder(itemView)
}
4 -> {
val itemView = AdventureGuideMenuBanner(parent.context)
itemView.layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
104.dpToPx(parent.context)
)
SubscriptionBuyGemsPromoViewHolder(itemView)
}
1 -> SectionHeaderViewHolder(parent.inflate(R.layout.drawer_main_section_header))
else -> DrawerItemViewHolder(parent.inflate(R.layout.drawer_main_item))
}

View file

@ -116,7 +116,7 @@ class ChallengeTasksRecyclerViewAdapter(taskFilterHelper: TaskFilterHelper?, lay
addBtn.setOnClickListener { newTask?.let { callback.onNext(it) } }
}
override fun bind(data: Task, position: Int) {
override fun bind(data: Task, position: Int, displayMode: String) {
this.newTask = data
addBtn.text = data.text
}
@ -126,7 +126,7 @@ class ChallengeTasksRecyclerViewAdapter(taskFilterHelper: TaskFilterHelper?, lay
private val dividerName: TextView = itemView.findViewById(R.id.divider_name)
override fun bind(data: Task, position: Int) {
override fun bind(data: Task, position: Int, displayMode: String) {
dividerName.text = data.text
}
}

View file

@ -46,7 +46,7 @@ abstract class BaseTasksRecyclerViewAdapter<VH : BindableViewHolder<Task>>(var t
override fun onBindViewHolder(holder: VH, position: Int) {
val item = filteredContent?.get(position)
if (item != null) {
holder.bind(item, position)
holder.bind(item, position, "normal")
}
/*if (this.displayedChecklist != null && ChecklistedViewHolder.class.isAssignableFrom(holder.getClass())) {
ChecklistedViewHolder checklistedHolder = (ChecklistedViewHolder) holder;

View file

@ -24,6 +24,14 @@ abstract class RealmBaseTasksRecyclerViewAdapter<VH : BaseTaskViewHolder>(
private var updateOnModification: Boolean = false
override var ignoreUpdates: Boolean = false
override var taskDisplayMode: String = "standard"
set(value) {
if (field != value) {
field = value
notifyDataSetChanged()
}
}
private val resultsListener: OrderedRealmCollectionChangeListener<RealmResults<Task>> by lazy {
OrderedRealmCollectionChangeListener<RealmResults<Task>> { _, changeSet ->
buildChangeSet(changeSet)
@ -128,7 +136,7 @@ abstract class RealmBaseTasksRecyclerViewAdapter<VH : BaseTaskViewHolder>(
override fun onBindViewHolder(holder: VH, position: Int) {
val item = getItem(position)
if (item != null) {
holder.bind(item, position)
holder.bind(item, position, taskDisplayMode)
holder.errorButtonClicked = Action {
errorButtonEventsSubject.onNext("")
}

View file

@ -33,6 +33,15 @@ class RewardsRecyclerViewAdapter(private var customRewards: OrderedRealmCollecti
private var purchaseCardSubject = PublishSubject.create<ShopItem>()
val purchaseCardEvents: Flowable<ShopItem> = purchaseCardSubject.toFlowable(BackpressureStrategy.LATEST)
override var taskDisplayMode: String = "standard"
set(value) {
if (field != value) {
field = value
notifyDataSetChanged()
}
}
private val inAppRewardCount: Int
get() {
if (inAppRewards?.isValid != true) return 0
@ -71,7 +80,7 @@ class RewardsRecyclerViewAdapter(private var customRewards: OrderedRealmCollecti
if (customRewards != null && position < customRewardCount) {
val reward = customRewards?.get(position) ?: return
val gold = user?.stats?.gp ?: 0.0
(holder as? RewardViewHolder)?.bind(reward, position, reward.value < gold)
(holder as? RewardViewHolder)?.bind(reward, position, reward.value < gold, taskDisplayMode)
} else if (inAppRewards != null) {
val item = inAppRewards?.get(position - customRewardCount) ?: return
if (holder is ShopItemViewHolder) {

View file

@ -11,6 +11,8 @@ interface TaskRecyclerViewAdapter {
val errorButtonEvents: Flowable<String>
var taskDisplayMode: String
fun updateData(tasks: OrderedRealmCollection<Task>?)
fun filter()

View file

@ -282,6 +282,10 @@ class NavigationDrawerFragment : DialogFragment() {
partyMenuItem?.transitionId = R.id.noPartyFragment
partyMenuItem?.bundle = null
}
val adventureGuideItem = getItemWithIdentifier(SIDEBAR_ADVENTURE_GUIDE)
adventureGuideItem?.isVisible = !user.hasCompletedOnboarding
adventureGuideItem?.user = user
}
override fun onDestroy() {
@ -295,6 +299,9 @@ class NavigationDrawerFragment : DialogFragment() {
private fun initializeMenuItems() {
val items = ArrayList<HabiticaDrawerItem>()
context?.let {context ->
val adventureItem = HabiticaDrawerItem(R.id.tasksFragment, SIDEBAR_ADVENTURE_GUIDE)
adventureItem.itemViewType = 4
items.add(adventureItem)
items.add(HabiticaDrawerItem(R.id.tasksFragment, SIDEBAR_TASKS, context.getString(R.string.sidebar_tasks)))
items.add(HabiticaDrawerItem(R.id.skillsFragment, SIDEBAR_SKILLS, context.getString(R.string.sidebar_skills)))
items.add(HabiticaDrawerItem(R.id.statsFragment, SIDEBAR_STATS, context.getString(R.string.sidebar_stats)))
@ -304,17 +311,13 @@ class NavigationDrawerFragment : DialogFragment() {
items.add(HabiticaDrawerItem(R.id.partyFragment, SIDEBAR_PARTY, context.getString(R.string.sidebar_party)))
items.add(HabiticaDrawerItem(R.id.guildsOverviewFragment, SIDEBAR_GUILDS, context.getString(R.string.sidebar_guilds)))
items.add(HabiticaDrawerItem(R.id.challengesOverviewFragment, SIDEBAR_CHALLENGES, context.getString(R.string.sidebar_challenges)))
if (configManager.raiseShops()) {
items.add(HabiticaDrawerItem(0, SIDEBAR_INVENTORY, context.getString(R.string.sidebar_shops), true))
items.add(HabiticaDrawerItem(R.id.marketFragment, SIDEBAR_SHOPS_MARKET, context.getString(R.string.market)))
items.add(HabiticaDrawerItem(R.id.questShopFragment, SIDEBAR_SHOPS_QUEST, context.getString(R.string.questShop)))
items.add(HabiticaDrawerItem(R.id.seasonalShopFragment, SIDEBAR_SHOPS_SEASONAL, context.getString(R.string.seasonalShop)))
items.add(HabiticaDrawerItem(R.id.timeTravelersShopFragment, SIDEBAR_SHOPS_TIMETRAVEL, context.getString(R.string.timeTravelers)))
}
items.add(HabiticaDrawerItem(0, SIDEBAR_INVENTORY, context.getString(R.string.sidebar_shops), true))
items.add(HabiticaDrawerItem(R.id.marketFragment, SIDEBAR_SHOPS_MARKET, context.getString(R.string.market)))
items.add(HabiticaDrawerItem(R.id.questShopFragment, SIDEBAR_SHOPS_QUEST, context.getString(R.string.questShop)))
items.add(HabiticaDrawerItem(R.id.seasonalShopFragment, SIDEBAR_SHOPS_SEASONAL, context.getString(R.string.seasonalShop)))
items.add(HabiticaDrawerItem(R.id.timeTravelersShopFragment, SIDEBAR_SHOPS_TIMETRAVEL, context.getString(R.string.timeTravelers)))
items.add(HabiticaDrawerItem(0, SIDEBAR_INVENTORY, context.getString(R.string.sidebar_section_inventory), true))
if (!configManager.raiseShops()) {
items.add(HabiticaDrawerItem(R.id.shopsFragment, SIDEBAR_SHOPS, context.getString(R.string.sidebar_shops)))
}
items.add(HabiticaDrawerItem(R.id.avatarOverviewFragment, SIDEBAR_AVATAR, context.getString(R.string.sidebar_avatar)))
items.add(HabiticaDrawerItem(R.id.equipmentOverviewFragment, SIDEBAR_EQUIPMENT, context.getString(R.string.sidebar_equipment)))
items.add(HabiticaDrawerItem(R.id.itemsFragment, SIDEBAR_ITEMS, context.getString(R.string.sidebar_items)))
@ -498,6 +501,7 @@ class NavigationDrawerFragment : DialogFragment() {
const val SIDEBAR_SUBSCRIPTION = "subscription"
const val SIDEBAR_SUBSCRIPTION_PROMO = "subscriptionpromo"
const val SIDEBAR_G1G1_PROMO = "g1g1promo"
const val SIDEBAR_ADVENTURE_GUIDE = "adventureguide"
const val SIDEBAR_ABOUT_HEADER = "about_header"
const val SIDEBAR_NEWS = "news"
const val SIDEBAR_HELP = "help"

View file

@ -78,6 +78,14 @@ class PreferencesFragment : BasePreferencesFragment(), SharedPreferences.OnShare
val themePreference = findPreference("theme_name") as? ListPreference
themePreference?.isVisible = configManager.testingLevel() == AppTestingLevel.ALPHA || BuildConfig.DEBUG
themePreference?.summary = themePreference?.entry
val taskDisplayPreference = findPreference("task_display") as? ListPreference
if (configManager.enableTaskDisplayMode()) {
taskDisplayPreference?.summary = taskDisplayPreference?.entry
} else {
taskDisplayPreference?.isVisible = false
}
}
override fun onResume() {
@ -224,6 +232,10 @@ class PreferencesFragment : BasePreferencesFragment(), SharedPreferences.OnShare
apiClient.updateServerUrl(sharedPreferences.getString(key, ""))
findPreference(key).summary = sharedPreferences.getString(key, "")
}
"task_display" -> {
val preference = findPreference(key) as ListPreference
preference.summary = preference.entry
}
}
}

View file

@ -77,30 +77,30 @@ class BugFixFragment: BaseMainFragment() {
val deviceName = deviceInfo?.name ?: DeviceName.getDeviceName()
val manufacturer = deviceInfo?.manufacturer ?: Build.MANUFACTURER
var bodyOfEmail = "Device: $manufacturer $deviceName" +
" \nAndroid Version: $version"+
" \nAppVersion: " + getString(R.string.version_info, versionName, versionCode)
" %0AAndroid Version: $version"+
" %0AAppVersion: " + getString(R.string.version_info, versionName, versionCode)
if (appConfigManager.testingLevel().name != AppTestingLevel.PRODUCTION.name) {
bodyOfEmail += " ${appConfigManager.testingLevel().name}"
}
bodyOfEmail += " \nUser ID: $userId"
bodyOfEmail += " %0AUser ID: $userId"
val user = this.user
if (user != null) {
bodyOfEmail += " \nLevel: " + (user.stats?.lvl ?: 0) +
" \nClass: " + (if (user.preferences?.disableClasses == true) "Disabled" else (user.stats?.habitClass ?: "None")) +
" \nIs in Inn: " + (user.preferences?.sleep ?: false) +
" \nUses Costume: " + (user.preferences?.costume ?: false) +
" \nCustom Day Start: " + (user.preferences?.dayStart ?: 0) +
" \nTimezone Offset: " + (user.preferences?.timezoneOffset ?: 0)
bodyOfEmail += " %0ALevel: " + (user.stats?.lvl ?: 0) +
" %0AClass: " + (if (user.preferences?.disableClasses == true) "Disabled" else (user.stats?.habitClass ?: "None")) +
" %0AIs in Inn: " + (user.preferences?.sleep ?: false) +
" %0AUses Costume: " + (user.preferences?.costume ?: false) +
" %0ACustom Day Start: " + (user.preferences?.dayStart ?: 0) +
" %0ATimezone Offset: " + (user.preferences?.timezoneOffset ?: 0)
}
bodyOfEmail += " \nDetails:\n"
bodyOfEmail += " %0ADetails:%0A"
activity?.let {
val emailIntent = Intent(Intent.ACTION_SENDTO)
val mailto = "mailto:" + appConfigManager.supportEmail() +
"&subject=" + Uri.encode(subject) +
"?subject=" + Uri.encode(subject) +
"&body=" + Uri.encode(bodyOfEmail)
emailIntent.data = Uri.parse(mailto);

View file

@ -118,7 +118,7 @@ class SupportMainFragment : BaseMainFragment() {
activity?.let {
val emailIntent = Intent(Intent.ACTION_SENDTO)
val mailto = "mailto:" + appConfigManager.supportEmail() +
"&subject=" + Uri.encode(subject) +
"?subject=" + Uri.encode(subject) +
"&body=" + Uri.encode(bodyOfEmail)
emailIntent.data = Uri.parse(mailto);

View file

@ -102,6 +102,8 @@ open class TaskRecyclerViewFragment : BaseFragment(), androidx.swiperefreshlayou
this.recyclerAdapter?.filter()
}, RxErrorHandler.handleEmptyError()))
}
context?.let { recyclerAdapter?.taskDisplayMode = configManager.taskDisplayMode(it) }
}
private fun handleTaskResult(result: TaskScoringResult, value: Int) {
@ -359,6 +361,11 @@ open class TaskRecyclerViewFragment : BaseFragment(), androidx.swiperefreshlayou
}.subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
}
override fun onResume() {
super.onResume()
context?.let { recyclerAdapter?.taskDisplayMode = configManager.taskDisplayMode(it) }
}
fun setActiveFilter(activeFilter: String) {
taskFilterHelper.setActiveFilter(classType ?: "", activeFilter)
recyclerAdapter?.filter()

View file

@ -2,6 +2,7 @@ package com.habitrpg.android.habitica.ui.menu
import android.graphics.drawable.Drawable
import android.os.Bundle
import com.habitrpg.android.habitica.models.user.User
data class HabiticaDrawerItem(var transitionId: Int, val identifier: String, val text: String, val isHeader: Boolean = false) {
constructor(transitionId: Int, identifier: String) : this(transitionId, identifier, "")
@ -15,4 +16,6 @@ data class HabiticaDrawerItem(var transitionId: Int, val identifier: String, val
var showBubble: Boolean = false
var isVisible: Boolean = true
var isEnabled: Boolean = true
var user: User? = null
}

View file

@ -5,5 +5,5 @@ import androidx.recyclerview.widget.RecyclerView
abstract class BindableViewHolder<T>(itemView: View) : RecyclerView.ViewHolder(itemView) {
abstract fun bind(data: T, position: Int)
abstract fun bind(data: T, position: Int, displayMode: String)
}

View file

@ -104,7 +104,7 @@ abstract class BaseTaskViewHolder constructor(itemView: View, var scoreTaskFunc:
}
}
override fun bind(data: Task, position: Int) {
override fun bind(data: Task, position: Int, displayMode: String) {
task = data
itemView.setBackgroundResource(R.color.white)
@ -134,40 +134,53 @@ abstract class BaseTaskViewHolder constructor(itemView: View, var scoreTaskFunc:
titleTextView.setParsedMarkdown(parsedText)
}, RxErrorHandler.handleEmptyError())
}
if (data.parsedNotes != null) {
notesTextView?.setParsedMarkdown(data.parsedText)
} else {
notesTextView?.text = data.notes
notesTextView?.setSpannableFactory(NoCopySpannableFactory.getInstance());
data.notes?.let {notes ->
if (notes.isEmpty()) {
return@let
if (displayMode != "minimal") {
if (data.parsedNotes != null) {
notesTextView?.setParsedMarkdown(data.parsedText)
} else {
notesTextView?.text = data.notes
notesTextView?.setSpannableFactory(NoCopySpannableFactory.getInstance());
data.notes?.let {notes ->
if (notes.isEmpty()) {
return@let
}
Single.just(notes)
.map { MarkdownParser.parseMarkdown(it) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(Consumer { parsedNotes ->
notesTextView?.text = parsedNotes
notesTextView?.setParsedMarkdown(parsedNotes)
}, RxErrorHandler.handleEmptyError())
}
}
Single.just(notes)
.map { MarkdownParser.parseMarkdown(it) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(Consumer { parsedNotes ->
notesTextView?.text = parsedNotes
notesTextView?.setParsedMarkdown(parsedNotes)
}, RxErrorHandler.handleEmptyError())
} else {
notesTextView?.visibility = View.GONE
}
}
}
} else {
titleTextView.text = data.text
notesTextView?.text = data.notes
if (displayMode != "minimal") {
notesTextView?.text = data.notes
} else {
notesTextView?.visibility = View.GONE
}
}
rightBorderView?.setBackgroundResource(data.lightTaskColor)
iconViewReminder?.visibility = if (data.reminders?.size ?: 0 > 0) View.VISIBLE else View.GONE
iconViewTag?.visibility = if (data.tags?.size ?: 0 > 0) View.VISIBLE else View.GONE
if (displayMode == "standard") {
iconViewReminder?.visibility = if (data.reminders?.size ?: 0 > 0) View.VISIBLE else View.GONE
iconViewTag?.visibility = if (data.tags?.size ?: 0 > 0) View.VISIBLE else View.GONE
iconViewChallenge?.visibility = if (task?.challengeID != null) View.VISIBLE else View.GONE
iconViewChallenge?.visibility = if (task?.challengeID != null) View.VISIBLE else View.GONE
configureSpecialTaskTextView(data)
configureSpecialTaskTextView(data)
taskIconWrapper?.visibility = if (taskIconWrapperIsVisible) View.VISIBLE else View.GONE
} else {
taskIconWrapper?.visibility = View.GONE
}
taskIconWrapper?.visibility = if (taskIconWrapperIsVisible) View.VISIBLE else View.GONE
if (data.isPendingApproval) {
approvalRequiredTextView?.visibility = View.VISIBLE

View file

@ -42,7 +42,7 @@ abstract class ChecklistedViewHolder(itemView: View, scoreTaskFunc: ((Task, Task
expandCheckboxTouchArea(checkboxHolder, checkbox)
}
override fun bind(newTask: Task, position: Int) {
override fun bind(newTask: Task, position: Int, displayMode: String) {
var completed = newTask.completed
if (newTask.isPendingApproval) {
completed = false
@ -66,7 +66,7 @@ abstract class ChecklistedViewHolder(itemView: View, scoreTaskFunc: ((Task, Task
} else {
this.rightBorderView?.setBackgroundColor(this.taskGray)
}
super.bind(newTask, position)
super.bind(newTask, position, displayMode)
}
abstract fun shouldDisplayAsActive(newTask: Task): Boolean

View file

@ -25,7 +25,7 @@ class DailyViewHolder(itemView: View, scoreTaskFunc: ((Task, TaskDirection) -> U
return isVisible
}
override fun bind(newTask: Task, position: Int) {
override fun bind(newTask: Task, position: Int, displayMode: String) {
this.task = newTask
if (newTask.isChecklistDisplayActive) {
this.checklistIndicatorWrapper.setBackgroundResource(newTask.lightTaskColor)
@ -55,7 +55,7 @@ class DailyViewHolder(itemView: View, scoreTaskFunc: ((Task, TaskDirection) -> U
reminderTextView.text = reminderString
}
super.bind(newTask, position)
super.bind(newTask, position, displayMode)
}
override fun shouldDisplayAsActive(newTask: Task): Boolean {

View file

@ -34,7 +34,7 @@ class HabitViewHolder(itemView: View, scoreTaskFunc: ((Task, TaskDirection) -> U
btnMinus.setOnClickListener { onMinusButtonClicked() }
}
override fun bind(data: Task, position: Int) {
override fun bind(data: Task, position: Int, displayMode: String) {
this.task = data
if (data.up == true) {
this.btnPlusWrapper.setBackgroundResource(data.lightTaskColor)
@ -82,7 +82,7 @@ class HabitViewHolder(itemView: View, scoreTaskFunc: ((Task, TaskDirection) -> U
} else {
streakTextView.visibility = View.GONE
}
super.bind(data, position)
super.bind(data, position, displayMode)
}
private fun onPlusButtonClicked() {

View file

@ -63,9 +63,9 @@ class RewardViewHolder(itemView: View, scoreTaskFunc: ((Task, TaskDirection) ->
this.buyButton.isEnabled = !taskActionsDisabled
}
fun bind(reward: Task, position: Int, canBuy: Boolean) {
fun bind(reward: Task, position: Int, canBuy: Boolean, displayMode: String) {
this.task = reward
super.bind(reward, position)
super.bind(reward, position, displayMode)
this.priceLabel.text = NumberAbbreviator.abbreviate(itemView.context, this.task?.value ?: 0.0)
if (canBuy) {

View file

@ -12,14 +12,14 @@ class TodoViewHolder(itemView: View, scoreTaskFunc: ((Task, TaskDirection) -> Un
private val dateFormatter: DateFormat = android.text.format.DateFormat.getDateFormat(context)
override fun bind(newTask: Task, position: Int) {
override fun bind(newTask: Task, position: Int, displayMode: String) {
this.task = newTask
if (newTask.completed) {
checklistIndicatorWrapper.setBackgroundColor(taskGray)
} else {
checklistIndicatorWrapper.setBackgroundColor(newTask.lightTaskColor)
}
super.bind(newTask, position)
super.bind(newTask, position, displayMode)
}
override fun configureSpecialTaskTextView(task: Task) {

View file

@ -0,0 +1,30 @@
package com.habitrpg.android.habitica.ui.views.adventureGuide
import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout
import androidx.core.content.ContextCompat
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.AdventureGuideMenuBannerBinding
import com.habitrpg.android.habitica.databinding.EquipmentOverviewItemBinding
import com.habitrpg.android.habitica.extensions.layoutInflater
import com.habitrpg.android.habitica.models.user.User
class AdventureGuideMenuBanner @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
private var binding = AdventureGuideMenuBannerBinding.inflate(context.layoutInflater, this)
init {
background = ContextCompat.getDrawable(context, R.drawable.adventure_guide_menu_bg)
}
fun updateData(user: User) {
val achievements = user.onboardingAchievements
val completed = achievements.count { it.earned }
binding.progressBar.max = achievements.size
binding.progressBar.progress = completed / achievements.size
binding.countView.text = "${completed} / ${achievements.size}"
}
}

View file

@ -31,7 +31,7 @@ open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style.
private var buttonsWrapper: LinearLayout
private var noticeTextView: TextView
private var additionalContentView: View? = null
internal var additionalContentView: View? = null
var isScrollingLayout: Boolean = false
get() {

View file

@ -14,11 +14,12 @@ import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
class FixValuesEditText(context: Context, attrs: AttributeSet) : FrameLayout(context, attrs) {
private val binding: FixvaluesEdittextBinding = FixvaluesEdittextBinding.inflate(context.layoutInflater, this)
private var binding: FixvaluesEdittextBinding
var text: String
get() = binding.editText.text.toString()
set(value) {
binding.editText.setText(value)
binding.editText.hint = value
}
@ColorRes
@ -37,13 +38,15 @@ class FixValuesEditText(context: Context, attrs: AttributeSet) : FrameLayout(con
R.styleable.FixValuesEditText,
0, 0)
val view = context.layoutInflater.inflate(R.layout.fixvalues_edittext, this, true)
binding = FixvaluesEdittextBinding.bind(view)
binding.editText.hint = attributes.getString(R.styleable.FixValuesEditText_title)
binding.editTextWrapper.hint = binding.editText.hint
binding.editTextWrapper.setHintTextAppearance(attributes.getResourceId(R.styleable.FixValuesEditText_hintStyle, R.style.PurpleTextLabel))
iconBackgroundColor = attributes.getColor(R.styleable.FixValuesEditText_iconBgColor, 0)
val iconName = attributes.getString(R.styleable.FixValuesEditText_fixIconName)
when (iconName) {
when (attributes.getString(R.styleable.FixValuesEditText_fixIconName)) {
"health" -> binding.iconView.setImageBitmap(HabiticaIconsHelper.imageOfHeartLightBg())
"experience" -> binding.iconView.setImageBitmap(HabiticaIconsHelper.imageOfExperience())
"mana" -> binding.iconView.setImageBitmap(HabiticaIconsHelper.imageOfMagic())

View file

@ -213,6 +213,8 @@ class PurchaseDialog(context: Context, component: UserComponent?, val item: Shop
} else {
limitedTextView.setBackgroundColor(ContextCompat.getColor(context, R.color.green_10))
}
val gemContent = additionalContentView as? PurchaseDialogGemsContent
gemContent?.stepperView?.maxValue = (user?.purchased?.plan?.numberOfGemsLeft() ?: 1).toDouble()
}
buyButton.elevation = 0f

View file

@ -25,7 +25,5 @@ internal class PurchaseDialogGemsContent(context: Context) : PurchaseDialogConte
super.setItem(item)
notesTextView.text = item.notes
stepperView.maxValue = item.limitedNumberLeft?.toDouble()
}
}

View file

@ -54,17 +54,21 @@ class StepperValueFormView @JvmOverloads constructor(
private var valueString = ""
set(value) {
if (value.isEmpty()) return
val hasChanged = field != value || editText.text.toString() != field
field = value
if (value.isEmpty()) {
onValueChanged?.invoke(0.0)
return
}
if (editText.text.toString() != field) {
if (hasChanged) {
editText.setText(field)
if (editTextIsFocused) {
editText.setSelection(field.length)
}
}
val newValue = field.toDoubleOrNull() ?: 0.0
if (this.value != newValue) {
if (this.value != newValue || hasChanged) {
this.value = newValue
}
}

View file

@ -120,6 +120,22 @@ class UserDeserializer : JsonDeserializer<User> {
tag.userId = user.id
}
}
if (obj.has("achievements")) {
val achievements = RealmList<UserAchievement>()
for (entry in obj.getAsJsonObject("achievements").entrySet()) {
if (!entry.value.isJsonPrimitive) {
continue
}
val achievement = UserAchievement()
achievement.key = entry.key
achievement.earned = entry.value.asBoolean
achievements.add(achievement)
}
user.achievements = achievements
for (achievement in user.achievements) {
achievement.userId = user.id
}
}
if (obj.has("tasksOrder")) {
user.tasksOrder = context.deserialize(obj.get("tasksOrder"), TasksOrder::class.java)
}

View file

@ -11,7 +11,7 @@ buildscript {
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.1'
classpath 'com.android.tools.build:gradle:4.0.0'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
classpath 'com.google.gms:google-services:4.3.3'
classpath "io.realm:realm-gradle-plugin:6.0.2"

View file

@ -21,6 +21,11 @@ or alternatively using `brew cask install fastlane`
fastlane android test
```
Runs all the tests
### android staff
```
fastlane android staff
```
Submit a new Staff Build to Google Play
### android alpha
```
fastlane android alpha

View file

@ -2,7 +2,6 @@ android.enableJetifier=true
android.useAndroidX=true
android.enableR8=true
android.debug.obsoleteApi=true
android.enableUnitTestBinaryResources=true
org.gradle.configureondemand=true
org.gradle.daemon=true
org.gradle.jvmargs=-Xmx6656M

View file

@ -1,6 +1,6 @@
#Sun Feb 02 18:26:34 CET 2020
#Wed Jun 03 15:04:01 CEST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-rc-1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip