Merge branch 'develop' into add-repeatables

This commit is contained in:
Keith Holliday 2017-05-08 10:14:46 -06:00 committed by GitHub
commit 1ca55f4ea9
68 changed files with 1989 additions and 262 deletions

View file

@ -2,7 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.habitrpg.android.habitica"
android:versionCode="179"
android:versionCode="181"
android:versionName="1.0.1"
android:screenOrientation="portrait"
android:installLocation="auto" >
@ -141,6 +141,17 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.activities.MainActivity" />
</activity>
<activity
android:name=".ui.activities.CreateChallengeActivity"
android:theme="@style/AppTheme.ActionBar"
android:parentActivityName=".ui.activities.MainActivity"
android:screenOrientation="portrait"
android:launchMode="singleTask"
tools:ignore="UnusedAttribute">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.activities.MainActivity" />
</activity>
<activity android:name="com.facebook.FacebookActivity"
android:configChanges=
"keyboard|keyboardHidden|screenLayout|screenSize|orientation"

View file

@ -117,13 +117,13 @@ dependencies {
compile 'io.reactivex:rxjava:1.2.0'
//Analytics
compile 'com.amplitude:android-sdk:2.13.1'
compile 'com.amplitude:android-sdk:2.13.3'
// Fresco Image Management Library
compile('com.facebook.fresco:fresco:1.0.1') {
compile('com.facebook.fresco:fresco:1.3.0') {
exclude module: 'bolts-android'
}
compile('com.facebook.fresco:animated-gif:1.0.1') {
compile('com.facebook.fresco:animated-gif:1.3.0') {
exclude module: 'bolts-android'
}
//Tests
@ -162,6 +162,7 @@ android {
defaultConfig {
applicationId "com.habitrpg.android.habitica"
vectorDrawables.useSupportLibrary = true
buildConfigField "String", "STORE", "\"google\""
}
lintOptions {
@ -198,6 +199,10 @@ android {
prod {
}
amazon {
buildConfigField "String", "STORE", "\"amazon\""
}
}
sourceSets {
@ -294,7 +299,7 @@ if (HRPG_RES_FILE.canRead()) {
}
tasks.whenTaskAdded { task ->
if (task.name.equals("lint")) {
if (task.name == "lint") {
task.enabled = false
}
}

View file

@ -1,4 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromYDelta="100%" android:toYDelta="0%"/>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<translate
android:fromYDelta="100%"
android:toYDelta="0%"/>
</set>

View file

@ -1,4 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromYDelta="0%" android:toYDelta="100%"/>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<translate android:fromYDelta="0%"
android:toYDelta="100%"
/>
</set>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp">
<path android:fillColor="#FFFFFF" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/challenge_gem_add_button_disabled" android:state_enabled="false"></item>
<item android:drawable="@drawable/challenge_gem_add_button_enabled" android:state_enabled="true"></item>
</selector>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item >
<bitmap
android:src="@drawable/plus"
android:tint="#e1e0e3" />
</item>
</layer-list>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item >
<bitmap
android:src="@drawable/plus"
android:tint="@color/brand_400" />
</item>
</layer-list>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/challenge_gem_remove_button_disabled" android:state_enabled="false"></item>
<item android:drawable="@drawable/challenge_gem_remove_button_enabled" android:state_enabled="true"></item>
</selector>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item >
<bitmap
android:src="@drawable/minus"
android:tint="#e1e0e3" />
</item>
</layer-list>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item >
<bitmap
android:src="@drawable/minus"
android:tint="@color/brand_400" />
</item>
</layer-list>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp">
<path android:fillColor="#FFFFFF" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp">
<path android:fillColor="#000000" android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
</vector>

View file

@ -0,0 +1,220 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_create_challenge"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.habitrpg.android.habitica.ui.activities.CreateChallengeActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/brand_200"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="22dp">
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColorHint="@color/brand_500"
app:hintTextAppearance="@style/TextAppearance.AppCompat"
android:id="@+id/create_challenge_title_input_layout">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="fill_horizontal"
android:hint="@string/new_challenge_title"
android:id="@+id/create_challenge_title"
android:textColor="@color/white"
android:textColorHighlight="@color/brand_500"
android:textColorHint="@color/brand_500"
tools:text="Get in shape" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColorHint="@color/brand_500"
android:id="@+id/create_challenge_description_input_layout"
app:hintTextAppearance="@style/TextAppearance.AppCompat">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/description_optional"
android:inputType="textMultiLine"
android:id="@+id/create_challenge_description"
android:maxLines="5"
android:minLines="3"
android:textColor="@color/white"
android:textColorHighlight="@color/brand_500"
android:textColorHint="@color/brand_500" />
</android.support.design.widget.TextInputLayout>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="@color/brand_700" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin">
<TextView
style="@style/Subheader2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="23dp"
android:text="@string/gem_reward"
android:textColor="#cc000000" />
<TextView
style="@style/Caption3"
android:id="@+id/create_challenge_gem_error"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="23dp"
android:text="@string/gem_reward"
android:textColor="#f74e52"
android:visibility="gone"
tools:visibility="visible"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="28dp">
<Button
android:id="@+id/challenge_add_gem_btn"
style="@style/Body1_Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="2"
android:drawableLeft="@drawable/challenge_gem_add_button"/>
<ImageView
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_gravity="center"
android:src="@drawable/ic_header_gem" />
<EditText
style="@style/Subheader2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:inputType="number"
android:textColor="#8a000000"
android:text="0"
android:id="@+id/create_challenge_prize"
android:textAlignment="center" />
<Button
android:id="@+id/challenge_remove_gem_btn"
style="@style/Body1_Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="2"
android:drawableRight="@drawable/challenge_gem_remove_button"/>
</LinearLayout>
<TextView
style="@style/Subheader2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="26dp"
android:text="@string/location"
android:textColor="#cc000000" />
<Spinner
android:id="@+id/challenge_location_spinner"
style="@android:style/Widget.Material.Spinner.Underlined"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#8a000000"
android:layout_marginTop="17dp" />
<TextView
style="@style/Subheader2"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:layout_marginTop="39dp"
android:drawablePadding="12dp"
android:drawableEnd="@drawable/ic_info_outline_black_24dp"
android:drawableTint="@color/brand_300"
android:gravity="center"
android:text="Tag"
android:textColor="#cc000000" />
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/create_challenge_tag_input_layout"
app:hintTextAppearance="@style/TextAppearance.AppCompat">
<EditText
android:id="@+id/create_challenge_tag"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/identify_your_challenge_with_a_tag"
android:maxLines="1"
android:textColor="@color/white"
android:textColorHint="#61000000" />
</android.support.design.widget.TextInputLayout>
<TextView
style="@style/Subheader2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/tasks" />
<TextView
style="@style/Caption3"
android:id="@+id/create_challenge_task_error"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="23dp"
android:textColor="#f74e52"
android:text="@string/challenge_create_error_no_tasks"
android:visibility="gone"
tools:visibility="visible"/>
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"
android:id="@+id/create_challenge_task_list">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="#FFF"
android:orientation="vertical"
android:gravity="center">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="22dp"
android:layout_marginBottom="18dp"
android:layout_marginEnd="24dp"
android:layout_marginStart="24dp"
android:textAllCaps="false"
style="@style/Subheader2"
android:textColor="@color/brand_400"
android:background="#f9f9f9"
android:id="@+id/btn_add_task"
tools:text="Add habit">
</Button>
</LinearLayout>

View file

@ -1,24 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
android:gravity="center_horizontal">
<com.facebook.drawee.view.SimpleDraweeView
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_centerHorizontal="true"
android:id="@+id/imageView" />
<TextView
android:text=""
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/imageView"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp"
android:layout_marginLeft="25dp"
android:layout_marginRight="25dp"
@ -28,8 +23,6 @@
android:text=""
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/you_earned_message"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp"
android:id="@+id/next_unlock_message" />
@ -37,10 +30,8 @@
android:text="@string/see_you_tomorrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/next_unlock_message"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp"
android:id="@+id/confirm_button"
style="@style/Widget.AppCompat.Button.Borderless.Colored" />
</RelativeLayout>
</LinearLayout>

View file

@ -18,7 +18,9 @@
fab:menu_animationDelayPerItem="50"
fab:menu_icon="@drawable/fab_add"
fab:menu_buttonSpacing="2dp"
fab:menu_labels_margin="4dp">
fab:menu_labels_margin="4dp"
fab:menu_fab_show_animation="@anim/fab_slide_in"
fab:menu_fab_hide_animation="@anim/fab_slide_out">
<com.github.clans.fab.FloatingActionButton
android:id="@+id/fab.new.habit"

View file

@ -1,4 +1,4 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
@ -42,6 +42,7 @@
android:id="@+id/challenges.refresh.layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
@ -54,62 +55,65 @@
android:scrollbars="vertical"
android:paddingBottom="?attr/actionBarSize" />
</android.support.v4.widget.SwipeRefreshLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/challenges_refresh_empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v4.widget.NestedScrollView
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false">
<LinearLayout
android:id="@+id/challenges.list.empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false">
android:layout_height="wrap_content"
android:layout_marginLeft="48dp"
android:layout_marginRight="48dp"
android:layout_marginTop="65dp"
android:orientation="vertical">
<LinearLayout
android:id="@+id/challenges.list.empty"
<TextView
style="@style/Headline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="48dp"
android:layout_marginRight="48dp"
android:layout_marginTop="65dp"
android:orientation="vertical">
android:layout_height="match_parent"
android:gravity="center"
android:maxLines="2"
android:text="@string/not_part_of_a_challenge"
android:textAlignment="center"
android:textColor="#8a000000" />
<TextView
style="@style/Headline"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:maxLines="2"
android:text="@string/not_part_of_a_challenge"
android:textAlignment="center"
android:textColor="#8a000000" />
<TextView
style="@style/Body2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="22dp"
android:gravity="center"
android:text="@string/join_a_challenge"
android:textAlignment="center"
android:textColor="#66000000" />
<TextView
style="@style/Body2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="22dp"
android:gravity="center"
android:text="@string/join_a_challenge"
android:textAlignment="center"
android:textColor="#66000000" />
<TextView
android:id="@+id/textView5"
style="@style/Body2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="22dp"
android:gravity="center"
android:text="@string/check_the_public_challenge_tab"
android:textAlignment="center"
android:textColor="#66000000" />
<TextView
android:id="@+id/textView5"
style="@style/Body2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="22dp"
android:gravity="center"
android:text="@string/check_the_public_challenge_tab"
android:textAlignment="center"
android:textColor="#66000000" />
</LinearLayout>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.v4.widget.NestedScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>
</FrameLayout>

View file

@ -24,7 +24,25 @@
android:drawableTop="@drawable/ic_heart_large"
android:textSize="14sp"
android:lineSpacingExtra="4dp" />
<TextView
android:id="@+id/notAvailableTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/no_billing_gems"
android:paddingTop="50dp"
android:paddingBottom="10dp"
android:visibility="gone" />
<Button
android:id="@+id/notAvailableButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/visit_habitica_website"
android:layout_marginBottom="50dp"
style="@style/Button.Purple"
android:visibility="gone" />
<LinearLayout
android:id="@+id/gemPurchaseOptions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

View file

@ -150,6 +150,25 @@
android:layout_height="wrap_content"
android:id="@+id/loadingIndicator"/>
<TextView
android:id="@+id/notAvailableTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/no_billing_subscriptions"
android:paddingTop="50dp"
android:paddingBottom="10dp"
android:visibility="gone" />
<Button
android:id="@+id/notAvailableButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/visit_habitica_website"
android:layout_marginBottom="50dp"
style="@style/Button.Purple"
android:visibility="gone" />
<LinearLayout
android:id="@+id/subscriptionOptions"
android:layout_width="match_parent"

View file

@ -5,9 +5,15 @@
<item
android:id="@+id/action_leave"
android:actionViewClass="android.widget.ImageButton"
android:icon="@drawable/leave_light"
android:title="@string/leave"
app:showAsAction="always"
android:actionViewClass="android.widget.ImageButton"/>
app:showAsAction="always" />
<group
android:id="@+id/challenge_edit_action_group">
<item
android:id="@+id/action_edit"
android:title="@string/edit_challenge" />
</group>
</menu>

View file

@ -0,0 +1,11 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.habitrpg.android.habitica.TaskActivity">
<item
android:id="@+id/action_save"
android:title="@string/save"
app:showAsAction="always"/>
</menu>

View file

@ -0,0 +1,10 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.habitrpg.android.habitica.TaskActivity">
<item
android:id="@+id/action_create_challenge"
android:title="@string/create_challenge"/>
</menu>

View file

@ -7,6 +7,7 @@
<string name="tutorial_dailies_1">Создайте ежедневные задачи, которые необходимо выполнять по расписанию.</string>
<string name="tutorial_dailies_2">Будьте осторожны - если вы пропустите раз, ваш аватар в одночасье получит урон . Регулярно выполняя их, вы получите отличные награды!</string>
<string name="tutorial_todos_1">Используйте список дел, чтобы отслеживать задачи, которые нужно выполнять только один раз.</string>
<string name="tutorial_todos_2">Если ваше задание должно быть выполнено к определенному времени, установите срок. Похоже, вы можете выполнить одно - идти впереди!</string>
<string name="tutorial_rewards_1">Покупайте снаряжение для вашего аватара за золото которое вы получаете!</string>
<string name="tutorial_rewards_2">Вы также можете создавать свои вознаграждения в зависимости от того, что вас мотивирует.</string>
<string name="tutorial_tasks_complete">На этом все. Если вы что-то забыли, проверьте раздел часто задаваемых вопросов.</string>

View file

@ -186,6 +186,8 @@
<string name="intro_1_description">Пришло время веселиться, пока вы делаете свою работу. Присоединитесь к %s участникам, уже улучшающих свою жизнь.</string>
<string name="intro_2_subtitle">Прогресс в игре</string>
<string name="intro_2_title">Прогресс в жизни</string>
<string name="intro_3_subtitle">Битва с монстрами</string>
<string name="intro_3_title">Общайтесь</string>
<string name="intro_3_description">Добивайтесь поставленнх целей с помощью друзей. Поддерживайте друг друга в реальной жизни и в сражении, делая свою жизнь лучше!</string>
<string name="intro_finish_button">Давайте начнем!</string>
<string name="previous_button">Назад</string>
@ -237,6 +239,7 @@
<string name="delete">Удалить</string>
<string name="name">Имя</string>
<string name="description">Описание</string>
<string name="add_tag">Добавить новый Тег</string>
<string name="privacy">Приватность</string>
<string name="write_message">Написать сообщение</string>
<string name="post">Отправить</string>
@ -393,6 +396,7 @@
<string name="all_dailies_completed">Вы справились со всеми ежедневными заданиями. Так держать!</string>
<string name="widget_habit_button">Привычки Habitica</string>
<string name="widget_dailies">Ежедневные задания Habitica</string>
<string name="widget_todo_list">Список задач Habitica</string>
<string name="widget_add_task">Добавить задачу Habitica</string>
<string name="google_services_missing">Сервис Google play не найден.</string>
<string name="gem.purchase.toolbartitle">Купить</string>
@ -497,6 +501,7 @@
<string name="login_btn_fb">Зарегистрироваться с Facebook</string>
<string name="login_btn_google">Зарегистрироваться с Google</string>
<string name="action_back">Назад</string>
<string name="welcome_text">О, должно быть ,вы новичок. Я Джастин, я буду твоим проводником в Habitica. \ N \ nЧтобы начать, вам нужно создать аватар.</string>
<string name="avatar_extras">Дополнительно</string>
<string name="avatar_skin_color">Цвет кожи</string>
<string name="avatar_hair_color">Цвет волос</string>
@ -506,11 +511,13 @@
<string name="avatar_wheelchair">Кресло-каталка</string>
<string name="weak">Стройное</string>
<string name="strong">Тучное</string>
<string name="gray">Серый</string>
<string name="completed">Завершить</string>
<string name="setuP_group_other">Другое</string>
<string name="clear">Очистить</string>
<string name="empty_title_habits">У вас нет привычек</string>
<string name="empty_title_todos">У вас нет задач</string>
<string name="empty_title_rewards">У вас нет вознаграждений</string>
<string name="maintenance">Cодержание</string>
<string name="reload_content">Обновить содержимое</string>
</resources>

View file

@ -582,4 +582,21 @@
<string name="dailyDueDefaultView">Set Dailies default to due tab</string>
<string name="dailyDueDefaultViewDescription">With this option set, the Dailies tasks will default to due instead of all</string>
<string name="repeat_summary">"Repeats %1$s every %2$s %3$s %4$s"</string>
<string name="no_billing_gems">Your device does not have any of the supported payment methods. Please use the habitica website if you want to purchase gems.</string>
<string name="no_billing_subscriptions">Your device does not have any of the supported payment methods. Please use the habitica website if you want to purchase a subscription.</string>
<string name="save">Save</string>
<string name="location">Location</string>
<string name="gem_reward">Gem reward</string>
<string name="tasks">Tasks</string>
<string name="create_challenge">Create challenge</string>
<string name="edit_challenge">Edit Challenge</string>
<string name="challenge_create_error_tavern_one_gem">You need at least 1 gem to create a challenge in Tavern.</string>
<string name="challenge_create_error_enough_gems">You don\'t have enough gems to create a challenge.</string>
<string name="identify_your_challenge_with_a_tag">Identify your challenge with a tag ..</string>
<string name="challenge_create_error_tag">You need a tag to create this Challenge.</string>
<string name="challenge_create_error_no_tasks">You need to add at least one task to create this Challenge.</string>
<string name="challenge_create_error_title">You need a title to create this Challenge.</string>
<string name="description_optional">Description (optional)</string>
<string name="new_challenge_title">New challenge title</string>
</resources>

View file

@ -18,6 +18,7 @@ import android.support.multidex.MultiDexApplication;
import android.util.Log;
import com.amplitude.api.Amplitude;
import com.amplitude.api.Identify;
import com.facebook.FacebookSdk;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.habitrpg.android.habitica.components.AppComponent;
@ -140,6 +141,8 @@ public abstract class HabiticaBaseApplication extends MultiDexApplication {
if (!BuildConfig.DEBUG) {
try {
Amplitude.getInstance().initialize(this, getString(R.string.amplitude_app_id)).enableForegroundTracking(this);
Identify identify = new Identify().setOnce("androidStore", BuildConfig.STORE);
Amplitude.getInstance().identify(identify);
} catch (Resources.NotFoundException e) {
//pass
}

View file

@ -287,6 +287,22 @@ public interface ApiService {
@POST("challenges/{challengeId}/leave")
Observable<HabitResponse<Void>> leaveChallenge(@Path("challengeId") String challengeId, @Body LeaveChallengeBody body);
@POST("challenges")
Observable<HabitResponse<Challenge>> createChallenge(@Body Challenge challenge);
@POST("tasks/challenge/{challengeId}")
Observable<HabitResponse<Task>> createChallengeTask(@Path("challengeId") String challengeId, @Body Task task);
@POST("tasks/challenge/{challengeId}")
Observable<HabitResponse<List<Task>>> createChallengeTasks(@Path("challengeId") String challengeId, @Body List<Task> tasks);
@PUT("challenges/{challengeId}")
Observable<HabitResponse<Challenge>> updateChallenge(@Path("challengeId") String challengeId, @Body Challenge challenge);
@DELETE("challenges/{challengeId}")
Observable<HabitResponse<Void>> deleteChallenge(@Path("challengeId") String challengeId);
//DEBUG: These calls only work on a local development server
@POST("debug/add-ten-gems")

View file

@ -19,6 +19,7 @@ import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver;
import com.habitrpg.android.habitica.ui.activities.AboutActivity;
import com.habitrpg.android.habitica.ui.activities.ChallengeDetailActivity;
import com.habitrpg.android.habitica.ui.activities.ClassSelectionActivity;
import com.habitrpg.android.habitica.ui.activities.CreateChallengeActivity;
import com.habitrpg.android.habitica.ui.activities.FullProfileActivity;
import com.habitrpg.android.habitica.ui.activities.GemPurchaseActivity;
import com.habitrpg.android.habitica.ui.activities.GroupFormActivity;
@ -33,6 +34,7 @@ import com.habitrpg.android.habitica.ui.activities.SetupActivity;
import com.habitrpg.android.habitica.ui.activities.SkillMemberActivity;
import com.habitrpg.android.habitica.ui.activities.SkillTasksActivity;
import com.habitrpg.android.habitica.ui.activities.TaskFormActivity;
import com.habitrpg.android.habitica.ui.adapter.social.challenges.ChallengeTasksRecyclerViewAdapter;
import com.habitrpg.android.habitica.ui.adapter.tasks.DailiesRecyclerViewHolder;
import com.habitrpg.android.habitica.ui.adapter.tasks.HabitsRecyclerViewAdapter;
import com.habitrpg.android.habitica.ui.adapter.tasks.RewardsRecyclerViewAdapter;
@ -231,7 +233,7 @@ public interface AppComponent {
void inject(SubscriptionFragment subscriptionFragment);
void inject(ChallengeTasksRecyclerViewFragment.ChallengeTasksRecyclerViewAdapter challengeTasksRecyclerViewAdapter);
void inject(ChallengeTasksRecyclerViewAdapter challengeTasksRecyclerViewAdapter);
void inject(ChallengeTasksRecyclerViewFragment challengeTasksRecyclerViewFragment);
@ -248,4 +250,6 @@ public interface AppComponent {
void inject(HabiticaFirebaseInstanceIDService habiticaFirebaseInstanceIDService);
void inject(HabiticaFirebaseMessagingService habiticaFirebaseMessagingService);
void inject(CreateChallengeActivity createChallengeActivity);
}

View file

@ -200,6 +200,13 @@ public interface ApiClient {
Observable<Void> leaveChallenge(String challengeId, LeaveChallengeBody body);
Observable<Challenge> createChallenge(Challenge challenge);
Observable<Task> createChallengeTask(String challengeId, Task task);
Observable<List<Task>> createChallengeTasks(String challengeId, List<Task> tasks);
Observable<Challenge> updateChallenge(Challenge challenge);
Observable<Void> deleteChallenge(String challengeId);
//DEBUG: These calls only work on a local development server
Observable<Void> debugAddTenGems();

View file

@ -0,0 +1,34 @@
package com.habitrpg.android.habitica.data;
import com.habitrpg.android.habitica.models.social.Challenge;
import com.habitrpg.android.habitica.models.social.Group;
import com.habitrpg.android.habitica.models.tasks.Task;
import com.habitrpg.android.habitica.models.tasks.TaskList;
import java.util.List;
import rx.Observable;
public interface ChallengeRepository extends BaseRepository {
Observable<Challenge> getChallenge(String challengeId);
Observable<TaskList> getChallengeTasks(String challengeId);
Observable<Challenge> createChallenge(Challenge challenge, List<Task> taskList);
/**
*
* @param challenge
* @param fullTaskList lists all tasks of the current challenge, to create the taskOrders
* @param addedTaskList only the tasks to be added online
* @param updatedTaskList only the updated ones
* @param removedTaskList tasks that has be to be removed
* @return
*/
Observable<Challenge> updateChallenge(Challenge challenge, List<Task> fullTaskList,
List<Task> addedTaskList, List<Task> updatedTaskList, List<String> removedTaskList);
Observable<Void> deleteChallenge(String challengeId);
void setUsersGroups(List<Group> groups);
Observable<List<Group>> getLocalGroups();
}

View file

@ -65,7 +65,7 @@ import com.habitrpg.android.habitica.models.tasks.Task;
import com.habitrpg.android.habitica.models.tasks.TaskList;
import com.habitrpg.android.habitica.models.tasks.TaskTag;
import com.habitrpg.android.habitica.utils.BooleanAsIntAdapter;
import com.habitrpg.android.habitica.utils.ChallengeDeserializer;
import com.habitrpg.android.habitica.utils.ChallengeSerializer;
import com.habitrpg.android.habitica.utils.ChatMessageDeserializer;
import com.habitrpg.android.habitica.utils.ChecklistItemSerializer;
import com.habitrpg.android.habitica.utils.ContentDeserializer;
@ -267,7 +267,7 @@ public class ApiClientImpl implements Action1<Throwable>, ApiClient {
.registerTypeAdapter(Task.class, new TaskSerializer())
.registerTypeAdapter(ContentResult.class, new ContentDeserializer())
.registerTypeAdapter(FeedResponse.class, new FeedResponseDeserializer())
.registerTypeAdapter(Challenge.class, new ChallengeDeserializer())
.registerTypeAdapter(Challenge.class, new ChallengeSerializer())
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.create();
return GsonConverterFactory.create(gson);
@ -826,6 +826,32 @@ public class ApiClientImpl implements Action1<Throwable>, ApiClient {
return apiService.leaveChallenge(challengeId, body).compose(configureApiCallObserver());
}
@Override
public Observable<Challenge> createChallenge(Challenge challenge) {
return apiService.createChallenge(challenge).compose(configureApiCallObserver());
}
@Override
public Observable<Task> createChallengeTask(String challengeId, Task task) {
return apiService.createChallengeTask(challengeId, task).compose(configureApiCallObserver());
}
@Override
public Observable<List<Task>> createChallengeTasks(String challengeId, List<Task> tasks) {
return apiService.createChallengeTasks(challengeId, tasks).compose(configureApiCallObserver());
}
@Override
public Observable<Challenge> updateChallenge(Challenge challenge) {
return apiService.updateChallenge(challenge.id, challenge).compose(configureApiCallObserver());
}
@Override
public Observable<Void> deleteChallenge(String challengeId) {
return apiService.deleteChallenge(challengeId).compose(configureApiCallObserver());
}
@Override
public Observable<Void> debugAddTenGems() {
return apiService.debugAddTenGems().compose(configureApiCallObserver());

View file

@ -0,0 +1,121 @@
package com.habitrpg.android.habitica.data.implementation;
import com.github.underscore.$;
import com.habitrpg.android.habitica.data.ApiClient;
import com.habitrpg.android.habitica.data.ChallengeRepository;
import com.habitrpg.android.habitica.data.local.ChallengeLocalRepository;
import com.habitrpg.android.habitica.models.social.Challenge;
import com.habitrpg.android.habitica.models.social.Group;
import com.habitrpg.android.habitica.models.tasks.Task;
import com.habitrpg.android.habitica.models.tasks.TaskList;
import com.habitrpg.android.habitica.models.tasks.TasksOrder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import rx.Observable;
import rx.schedulers.Schedulers;
public class ChallengeRepositoryImpl extends BaseRepositoryImpl<ChallengeLocalRepository> implements ChallengeRepository {
public ChallengeRepositoryImpl(ChallengeLocalRepository localRepository, ApiClient apiClient) {
super(localRepository, apiClient);
}
@Override
public Observable<Challenge> getChallenge(String challengeId) {
return apiClient.getChallenge(challengeId);
}
@Override
public Observable<TaskList> getChallengeTasks(String challengeId) {
return apiClient.getChallengeTasks(challengeId);
}
private TasksOrder getTaskOrders(List<Task> taskList) {
Map<String, List<Task>> stringListMap = $.groupBy(taskList, t -> t.getType());
TasksOrder tasksOrder = new TasksOrder();
for (Map.Entry<String, List<Task>> entry : stringListMap.entrySet()) {
List<String> taskIdList = $.map(entry.getValue(), t -> t.getId());
switch (entry.getKey()) {
case Task.TYPE_HABIT:
tasksOrder.setHabits(taskIdList);
break;
case Task.TYPE_DAILY:
tasksOrder.setDailys(taskIdList);
break;
case Task.TYPE_TODO:
tasksOrder.setTodos(taskIdList);
break;
case Task.TYPE_REWARD:
tasksOrder.setRewards(taskIdList);
break;
}
}
return tasksOrder;
}
private Observable addChallengeTasks(String challengeId, List<Task> addedTaskList) {
if (addedTaskList.size() == 1) {
return apiClient.createChallengeTask(challengeId, addedTaskList.get(0));
} else {
return apiClient.createChallengeTasks(challengeId, addedTaskList);
}
}
@Override
public Observable<Challenge> createChallenge(Challenge challenge, List<Task> taskList) {
challenge.tasksOrder = getTaskOrders(taskList);
return Observable.create(subscriber -> {
apiClient.createChallenge(challenge).subscribe(challenge1 -> {
addChallengeTasks(challenge1.id, taskList).subscribe(task -> {
subscriber.onNext(challenge1);
subscriber.onCompleted();
}, throwable -> subscriber.onError((Throwable) throwable));
}, throwable -> subscriber.onError(throwable));
});
}
@Override
public Observable<Challenge> updateChallenge(Challenge challenge, List<Task> fullTaskList,
List<Task> addedTaskList, List<Task> updatedTaskList, List<String> removedTaskList) {
ArrayList<Observable> observablesToWait = new ArrayList<>($.map(updatedTaskList, t -> apiClient.updateTask(t.getId(), t)));
observablesToWait.addAll($.map(removedTaskList, apiClient::deleteTask));
if (addedTaskList.size() != 0) {
observablesToWait.add(addChallengeTasks(challenge.id, addedTaskList));
}
challenge.tasksOrder = getTaskOrders(fullTaskList);
return Observable.from(observablesToWait)
.flatMap(task -> task.subscribeOn(Schedulers.computation()))
.toList()
.flatMap(tasks -> apiClient.updateChallenge(challenge));
}
@Override
public Observable<Void> deleteChallenge(String challengeId) {
return apiClient.deleteChallenge(challengeId);
}
@Override
public void setUsersGroups(List<Group> groups) {
localRepository.setUsersGroups(groups);
}
@Override
public Observable<List<Group>> getLocalGroups() {
return localRepository.getGroups();
}
}

View file

@ -0,0 +1,19 @@
package com.habitrpg.android.habitica.data.local;
import com.habitrpg.android.habitica.models.social.Challenge;
import com.habitrpg.android.habitica.models.social.Group;
import com.habitrpg.android.habitica.models.tasks.Task;
import java.util.List;
import rx.Observable;
public interface ChallengeLocalRepository extends BaseLocalRepository {
Observable<Challenge> getChallenge(String id);
Observable<List<Task>> getTasks(Challenge challenge);
void setUsersGroups(List<Group> group);
Observable<List<Group>> getGroups();
}

View file

@ -0,0 +1,51 @@
package com.habitrpg.android.habitica.data.local.implementation;
import com.habitrpg.android.habitica.data.local.ChallengeLocalRepository;
import com.habitrpg.android.habitica.models.social.Challenge;
import com.habitrpg.android.habitica.models.social.Group;
import com.habitrpg.android.habitica.models.tasks.Task;
import com.raizlabs.android.dbflow.runtime.transaction.BaseTransaction;
import com.raizlabs.android.dbflow.runtime.transaction.TransactionListener;
import com.raizlabs.android.dbflow.sql.builder.Condition;
import com.raizlabs.android.dbflow.sql.language.Delete;
import com.raizlabs.android.dbflow.sql.language.Select;
import java.util.List;
import rx.Observable;
public class DbFlowChallengeLocalRepository implements ChallengeLocalRepository {
@Override
public Observable<Challenge> getChallenge(String id) {
return Observable.defer(() -> Observable.just(new Select().from(Challenge.class).where(Condition.column("id").is(id)).querySingle()));
}
@Override
public Observable<List<Task>> getTasks(Challenge challenge) {
return null;
}
@Override
public void setUsersGroups(List<Group> groups) {
new Delete().from(Group.class).query();
for (Group group : groups) {
group.isMember = true;
group.async().save();
}
}
@Override
public Observable<List<Group>> getGroups() {
return Observable.defer(() -> Observable.just(new Select().from(Group.class).where(Condition.column("isMember").is(true)).queryList()));
}
@Override
public void close() {
}
}

View file

@ -8,5 +8,5 @@ import com.habitrpg.android.habitica.models.tasks.Task;
public class TaskSaveEvent {
public Task task;
public boolean created;
public boolean ignoreEvent;
}

View file

@ -5,8 +5,14 @@ package com.habitrpg.android.habitica.events.commands;
*/
public class DeleteTaskCommand {
public String TaskIdToDelete;
public boolean ignoreEvent;
public DeleteTaskCommand(String id) {
this(id, false);
}
public DeleteTaskCommand(String id, boolean ignore) {
TaskIdToDelete = id;
ignoreEvent = ignore;
}
}

View file

@ -1,6 +1,7 @@
package com.habitrpg.android.habitica.helpers;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
@ -70,15 +71,18 @@ public class PopupNotificationsManager {
DataBindingUtils.loadImage(imageView, imageKey);
TextView youEarnedTexView = (TextView) view.findViewById(R.id.you_earned_message);
youEarnedTexView.setTextColor(ContextCompat.getColor(context, R.color.textColorLight));
youEarnedTexView.setText(youEarnedMessage);
String message = context.getString(R.string.nextPrizeUnlocks, notification.data.nextRewardAt);
TextView nextUnlockTextView = (TextView) view.findViewById(R.id.next_unlock_message);
nextUnlockTextView.setTextColor(ContextCompat.getColor(context, R.color.textColorLight));
nextUnlockTextView.setText(message);
Button confirmButton = (Button) view.findViewById(R.id.confirm_button);
confirmButton.setTextColor(ContextCompat.getColor(context, R.color.brand_300));
AlertDialog.Builder builder = new AlertDialog.Builder(HabiticaApplication.currentActivity)
AlertDialog.Builder builder = new AlertDialog.Builder(HabiticaApplication.currentActivity, R.style.AlertDialogTheme)
.setTitle(title)
.setView(view)
.setMessage("");

View file

@ -134,7 +134,7 @@ public class ShopItem {
if (getCurrency().equals("gold")) {
return getValue() <= user.getStats().getGp();
} else if (getCurrency().equals("gems")) {
return getValue() <= (user.getBalance() * 4);
return getValue() <= (user.getGemCount());
} else {
return false;
}

View file

@ -1,7 +1,7 @@
package com.habitrpg.android.habitica.models.social;
import com.habitrpg.android.habitica.HabitDatabase;
import com.habitrpg.android.habitica.models.social.Group;
import com.habitrpg.android.habitica.models.tasks.TasksOrder;
import com.habitrpg.android.habitica.models.user.HabitRPGUser;
import com.raizlabs.android.dbflow.annotation.Column;
import com.raizlabs.android.dbflow.annotation.NotNull;
@ -9,8 +9,6 @@ import com.raizlabs.android.dbflow.annotation.PrimaryKey;
import com.raizlabs.android.dbflow.annotation.Table;
import com.raizlabs.android.dbflow.structure.BaseModel;
import java.util.HashMap;
@Table(databaseName = HabitDatabase.NAME)
public class Challenge extends BaseModel {
@ -73,25 +71,5 @@ public class Challenge extends BaseModel {
public HabitRPGUser leader;
public HashMap<String, String[]> getTasksOrder() {
HashMap<String, String[]> map = new HashMap();
if (!dailyList.isEmpty()) {
map.put(TASK_ORDER_DAILYS, dailyList.split(","));
}
if (!habitList.isEmpty()) {
map.put(TASK_ORDER_HABITS, habitList.split(","));
}
if (!rewardList.isEmpty()) {
map.put(TASK_ORDER_REWARDS, rewardList.split(","));
}
if (!todoList.isEmpty()) {
map.put(TASK_ORDER_TODOS, todoList.split(","));
}
return map;
}
public TasksOrder tasksOrder;
}

View file

@ -4,51 +4,66 @@ import com.google.gson.annotations.SerializedName;
import com.habitrpg.android.habitica.models.user.HabitRPGUser;
import com.habitrpg.android.habitica.models.inventory.Quest;
import com.habitrpg.android.habitica.HabitDatabase;
import com.raizlabs.android.dbflow.annotation.Column;
import com.raizlabs.android.dbflow.annotation.NotNull;
import com.raizlabs.android.dbflow.annotation.PrimaryKey;
import com.raizlabs.android.dbflow.annotation.Table;
import com.raizlabs.android.dbflow.structure.BaseModel;
import java.util.List;
/**
* Created by Negue on 16.09.2015.
*/
@Table(databaseName = HabitDatabase.NAME, tableName = "_group")
public class Group extends BaseModel {
@Column
@PrimaryKey
@NotNull
@SerializedName("_id")
public String id;
@Column
public double balance;
@Column
public String description;
@Column
public String leaderID;
@Column
public String leaderName;
@Column
public String name;
@Column
public int memberCount;
@Column
public Boolean isMember;
@Column
public String type;
@Column
public String logo;
public Quest quest;
public String privacy;
public List<ChatMessage> chat;
public List<HabitRPGUser> members;
@Column
public int challengeCount;
@Column
public String leaderMessage;
// TODO Challenges
@Override
public boolean equals(Object o) {
if (this == o) {

View file

@ -1,5 +1,8 @@
package com.habitrpg.android.habitica.models.tasks;
import android.os.Parcel;
import android.os.Parcelable;
import com.habitrpg.android.habitica.HabitDatabase;
import com.raizlabs.android.dbflow.annotation.Column;
import com.raizlabs.android.dbflow.annotation.ModelContainer;
@ -13,7 +16,7 @@ import com.raizlabs.android.dbflow.structure.BaseModel;
*/
@ModelContainer
@Table(databaseName = HabitDatabase.NAME)
public class Days extends BaseModel {
public class Days extends BaseModel implements Parcelable {
@Column
@PrimaryKey
@ -108,4 +111,44 @@ public class Days extends BaseModel {
}
return false;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.task_id);
dest.writeByte(this.m ? (byte) 1 : (byte) 0);
dest.writeByte(this.t ? (byte) 1 : (byte) 0);
dest.writeByte(this.w ? (byte) 1 : (byte) 0);
dest.writeByte(this.th ? (byte) 1 : (byte) 0);
dest.writeByte(this.f ? (byte) 1 : (byte) 0);
dest.writeByte(this.s ? (byte) 1 : (byte) 0);
dest.writeByte(this.su ? (byte) 1 : (byte) 0);
}
protected Days(Parcel in) {
this.task_id = in.readString();
this.m = in.readByte() != 0;
this.t = in.readByte() != 0;
this.w = in.readByte() != 0;
this.th = in.readByte() != 0;
this.f = in.readByte() != 0;
this.s = in.readByte() != 0;
this.su = in.readByte() != 0;
}
public static final Parcelable.Creator<Days> CREATOR = new Parcelable.Creator<Days>() {
@Override
public Days createFromParcel(Parcel source) {
return new Days(source);
}
@Override
public Days[] newArray(int size) {
return new Days[size];
}
};
}

View file

@ -20,6 +20,8 @@ import com.raizlabs.android.dbflow.structure.BaseModel;
import org.greenrobot.eventbus.EventBus;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import java.util.ArrayList;
@ -34,7 +36,7 @@ import java.util.concurrent.TimeUnit;
*/
@ModelContainer
@Table(databaseName = HabitDatabase.NAME)
public class Task extends BaseModel {
public class Task extends BaseModel implements Parcelable {
public static final String TYPE_HABIT = "habit";
public static final String TYPE_TODO = "todo";
public static final String TYPE_DAILY = "daily";
@ -697,4 +699,83 @@ public class Task extends BaseModel {
this.parsedNotes = this.getNotes();
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.user_id);
dest.writeValue(this.priority);
dest.writeString(this.text);
dest.writeString(this.notes);
dest.writeString(this.attribute);
dest.writeString(this.type);
dest.writeDouble(this.value);
dest.writeList(this.tags);
dest.writeLong(this.dateCreated != null ? this.dateCreated.getTime() : -1);
dest.writeInt(this.position);
dest.writeValue(this.up);
dest.writeValue(this.down);
dest.writeByte(this.completed ? (byte) 1 : (byte) 0);
dest.writeList(this.checklist);
dest.writeList(this.reminders);
dest.writeString(this.frequency);
dest.writeValue(this.everyX);
dest.writeValue(this.streak);
dest.writeLong(this.startDate != null ? this.startDate.getTime() : -1);
dest.writeParcelable(this.repeat, flags);
dest.writeLong(this.duedate != null ? this.duedate.getTime() : -1);
dest.writeString(this.specialTag);
dest.writeString(this.id);
}
public Task() {
}
protected Task(Parcel in) {
this.user_id = in.readString();
this.priority = (Float) in.readValue(Float.class.getClassLoader());
this.text = in.readString();
this.notes = in.readString();
this.attribute = in.readString();
this.type = in.readString();
this.value = in.readDouble();
this.tags = new ArrayList<TaskTag>();
in.readList(this.tags, TaskTag.class.getClassLoader());
long tmpDateCreated = in.readLong();
this.dateCreated = tmpDateCreated == -1 ? null : new Date(tmpDateCreated);
this.position = in.readInt();
this.up = (Boolean) in.readValue(Boolean.class.getClassLoader());
this.down = (Boolean) in.readValue(Boolean.class.getClassLoader());
this.completed = in.readByte() != 0;
this.checklist = new ArrayList<ChecklistItem>();
in.readList(this.checklist, ChecklistItem.class.getClassLoader());
this.reminders = new ArrayList<RemindersItem>();
in.readList(this.reminders, RemindersItem.class.getClassLoader());
this.frequency = in.readString();
this.everyX = (Integer) in.readValue(Integer.class.getClassLoader());
this.streak = (Integer) in.readValue(Integer.class.getClassLoader());
long tmpStartDate = in.readLong();
this.startDate = tmpStartDate == -1 ? null : new Date(tmpStartDate);
this.repeat = in.readParcelable(Days.class.getClassLoader());
long tmpDuedate = in.readLong();
this.duedate = tmpDuedate == -1 ? null : new Date(tmpDuedate);
this.specialTag = in.readString();
this.id = in.readString();
}
public static final Parcelable.Creator<Task> CREATOR = new Parcelable.Creator<Task>() {
@Override
public Task createFromParcel(Parcel source) {
return new Task(source);
}
@Override
public Task[] newArray(int size) {
return new Task[size];
}
};
}

View file

@ -220,6 +220,10 @@ public class HabitRPGUser extends BaseModel {
return this.balance;
}
public int getGemCount(){
return (int)(this.balance * 4);
}
public void setBalance(double balance) {
this.balance = balance;
}

View file

@ -1,17 +1,21 @@
package com.habitrpg.android.habitica.modules;
import com.habitrpg.android.habitica.data.ChallengeRepository;
import com.habitrpg.android.habitica.data.SetupCustomizationRepository;
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.implementation.ChallengeRepositoryImpl;
import com.habitrpg.android.habitica.data.implementation.SetupCustomizationRepositoryImpl;
import com.habitrpg.android.habitica.data.implementation.TagRepositoryImpl;
import com.habitrpg.android.habitica.data.implementation.TaskRepositoryImpl;
import com.habitrpg.android.habitica.data.implementation.UserRepositoryImpl;
import com.habitrpg.android.habitica.data.local.ChallengeLocalRepository;
import com.habitrpg.android.habitica.data.local.TagLocalRepository;
import com.habitrpg.android.habitica.data.local.TaskLocalRepository;
import com.habitrpg.android.habitica.data.local.UserLocalRepository;
import com.habitrpg.android.habitica.data.local.implementation.DbFlowChallengeLocalRepository;
import com.habitrpg.android.habitica.data.local.implementation.DbFlowTaskLocalRepository;
import com.habitrpg.android.habitica.data.local.implementation.DbFlowTagLocalRepository;
import com.habitrpg.android.habitica.data.ApiClient;
@ -33,6 +37,12 @@ public class RepositoryModule {
}
@Provides
@Singleton
ChallengeLocalRepository provideChallengeLocalRepository(){
return new DbFlowChallengeLocalRepository();
}
@Provides
TaskLocalRepository providesTaskLocalRepository() {
return new DbFlowTaskLocalRepository();
@ -44,6 +54,13 @@ public class RepositoryModule {
return new TaskRepositoryImpl(localRepository, apiClient);
}
@Provides
@Singleton
ChallengeRepository providesChallengeRepository(ChallengeLocalRepository localRepository, ApiClient apiClient) {
return new ChallengeRepositoryImpl(localRepository, apiClient);
}
@Provides
TagLocalRepository providesTagLocalRepository() {
return new DbFlowTagLocalRepository();

View file

@ -153,8 +153,8 @@ public class AvatarWithBarsViewModel implements View.OnClickListener {
goldText.setText(String.valueOf(gp));
silverText.setText(String.valueOf(sp));
Double gems = user.getBalance() * 4;
gemsText.setText(String.valueOf(gems.intValue()));
int gems = user.getGemCount();
gemsText.setText(String.valueOf(gems));
}
public void setHpBarData(float value, int valueMax) {
@ -186,9 +186,9 @@ public class AvatarWithBarsViewModel implements View.OnClickListener {
@Subscribe
public void onEvent(BoughtGemsEvent gemsEvent) {
Double gems = userObject.getBalance() * 4;
int gems = userObject.getGemCount();
gems += gemsEvent.NewGemsToAdd;
gemsText.setText(String.valueOf(gems.intValue()));
gemsText.setText(String.valueOf(gems));
}
@Override

View file

@ -34,11 +34,13 @@ import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import android.app.AlertDialog;
import android.content.Intent;
import android.databinding.ObservableArrayList;
import android.databinding.ObservableList;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
@ -103,6 +105,7 @@ public class ChallengeDetailActivity extends BaseActivity {
private Challenge challenge;
@Override
protected int getLayoutResId() {
return R.layout.activity_challenge_detail;
@ -112,6 +115,11 @@ public class ChallengeDetailActivity extends BaseActivity {
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_challenge_details, menu);
if(!challenge.leaderId.equals(HabiticaApplication.User.getId())){
menu.setGroupVisible(R.id.challenge_edit_action_group, false);
}
return true;
}
@ -225,6 +233,7 @@ public class ChallengeDetailActivity extends BaseActivity {
ChallengeViewHolder challengeViewHolder = new ChallengeViewHolder(findViewById(R.id.challenge_header));
challengeViewHolder.bind(challenge);
}
@Override
@ -240,11 +249,23 @@ public class ChallengeDetailActivity extends BaseActivity {
return true;
case R.id.action_edit:
openChallengeEditActivity();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void openChallengeEditActivity(){
Intent intent = new Intent(this, CreateChallengeActivity.class);
intent.putExtra(CreateChallengeActivity.CHALLENGE_ID_KEY, challenge.id);
startActivity(intent);
}
private void showChallengeLeaveDialog(){
new AlertDialog.Builder(this)
.setTitle(this.getString(R.string.challenge_leave_title))

View file

@ -0,0 +1,581 @@
package com.habitrpg.android.habitica.ui.activities;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.TextInputLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.AppCompatCheckedTextView;
import android.support.v7.widget.AppCompatTextView;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import com.github.underscore.$;
import com.habitrpg.android.habitica.HabiticaApplication;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.data.ChallengeRepository;
import com.habitrpg.android.habitica.events.TaskSaveEvent;
import com.habitrpg.android.habitica.events.TaskTappedEvent;
import com.habitrpg.android.habitica.events.commands.DeleteTaskCommand;
import com.habitrpg.android.habitica.models.social.Challenge;
import com.habitrpg.android.habitica.models.social.Group;
import com.habitrpg.android.habitica.models.tasks.Task;
import com.habitrpg.android.habitica.ui.adapter.social.challenges.ChallengeTasksRecyclerViewAdapter;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.OnClick;
import rx.Observable;
public class CreateChallengeActivity extends BaseActivity {
public static final String CHALLENGE_ID_KEY = "challengeId";
@BindView(R.id.create_challenge_title_input_layout)
TextInputLayout createChallengeTitleInputLayout;
@BindView(R.id.create_challenge_title)
EditText createChallengeTitle;
@BindView(R.id.create_challenge_description_input_layout)
TextInputLayout createChallengeDescriptionInputLayout;
@BindView(R.id.create_challenge_description)
EditText createChallengeDescription;
@BindView(R.id.create_challenge_prize)
EditText createChallengePrize;
@BindView(R.id.create_challenge_tag_input_layout)
TextInputLayout createChallengeTagInputLayout;
@BindView(R.id.create_challenge_tag)
EditText createChallengeTag;
@BindView(R.id.create_challenge_gem_error)
TextView createChallengeGemError;
@BindView(R.id.create_challenge_task_error)
TextView createChallengeTaskError;
@BindView(R.id.challenge_location_spinner)
Spinner challengeLocationSpinner;
@BindView(R.id.challenge_add_gem_btn)
Button challengeAddGemBtn;
@BindView(R.id.challenge_remove_gem_btn)
Button challengeRemoveGemBtn;
@BindView(R.id.create_challenge_task_list)
RecyclerView createChallengeTaskList;
@Inject
ChallengeRepository challengeRepository;
private ChallengeTasksRecyclerViewAdapter challengeTasks;
private GroupArrayAdapter locationAdapter;
private String challengeId;
private boolean editMode;
private HashMap<String, Task> addedTasks = new HashMap<>();
private HashMap<String, Task> updatedTasks = new HashMap<>();
private HashMap<String, Task> removedTasks = new HashMap<>();
// Add {*} Items
Task addHabit;
Task addDaily;
Task addTodo;
Task addReward;
@Override
protected int getLayoutResId() {
return R.layout.activity_create_challenge;
}
@Override
protected void injectActivity(AppComponent component) {
component.inject(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_create_challenge, menu);
return true;
}
private boolean savingInProgress = false;
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_save && !savingInProgress && validateAllFields()) {
savingInProgress = true;
ProgressDialog dialog = ProgressDialog.show(this, "", "Saving challenge data. Please wait...", true, false);
Observable<Challenge> observable;
if (editMode) {
observable = updateChallenge();
} else {
observable = createChallenge();
}
observable.subscribe(challenge -> {
dialog.dismiss();
savingInProgress = false;
finish();
}, throwable -> {
dialog.dismiss();
savingInProgress = false;
});
} else if(item.getItemId() == android.R.id.home){
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
private boolean validateAllFields() {
ArrayList<String> errorMessages = new ArrayList<>();
if (getEditTextString(createChallengeTitle).isEmpty()) {
String titleEmptyError = getString(R.string.challenge_create_error_title);
createChallengeTitleInputLayout.setError(titleEmptyError);
errorMessages.add(titleEmptyError);
} else {
createChallengeTitleInputLayout.setErrorEnabled(false);
}
if (getEditTextString(createChallengeTag).isEmpty()) {
String tagEmptyError = getString(R.string.challenge_create_error_tag);
createChallengeTagInputLayout.setError(tagEmptyError);
errorMessages.add(tagEmptyError);
} else {
createChallengeTagInputLayout.setErrorEnabled(false);
}
String prizeError = checkPrizeAndMinimumForTavern();
if(!prizeError.isEmpty()){
errorMessages.add(prizeError);
}
// all "Add {*}"-Buttons are one task itself, so we need atleast more than 4
if (challengeTasks.getTaskList().size() <= 4) {
createChallengeTaskError.setVisibility(View.VISIBLE);
errorMessages.add(getString(R.string.challenge_create_error_no_tasks));
} else {
createChallengeTaskError.setVisibility(View.GONE);
}
AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setMessage($.join(errorMessages, "\n"));
AlertDialog alert = builder.create();
alert.show();
return errorMessages.size() == 0;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
if (bundle != null) {
challengeId = bundle.getString(CHALLENGE_ID_KEY, null);
}
EventBus.getDefault().register(this);
fillControls();
if (challengeId != null) {
fillControlsByChallenge();
}
}
@Override
public void onDestroy() {
EventBus.getDefault().unregister(this);
super.onDestroy();
}
@Subscribe
public void onEvent(DeleteTaskCommand deleteTask) {
String taskIdToDelete = deleteTask.TaskIdToDelete;
challengeTasks.removeTask(taskIdToDelete);
if (editMode) {
if (addedTasks.containsKey(taskIdToDelete)) {
addedTasks.remove(taskIdToDelete);
} else {
removedTasks.put(taskIdToDelete, null);
if (updatedTasks.containsKey(taskIdToDelete)) {
updatedTasks.remove(taskIdToDelete);
}
}
}
}
@Subscribe
public void onEvent(TaskTappedEvent tappedEvent) {
openNewTaskActivity(null, tappedEvent.Task);
}
@Subscribe
public void onEvent(TaskSaveEvent saveEvent) {
if (saveEvent.task.getId() == null) {
saveEvent.task.setId(UUID.randomUUID().toString());
}
addOrUpdateTaskInList(saveEvent.task);
}
@OnClick(R.id.challenge_add_gem_btn)
public void onAddGem() {
int currentVal = Integer.parseInt(createChallengePrize.getText().toString());
currentVal++;
createChallengePrize.setText("" + currentVal);
checkPrizeAndMinimumForTavern();
}
@OnClick(R.id.challenge_remove_gem_btn)
public void onRemoveGem() {
int currentVal = Integer.parseInt(createChallengePrize.getText().toString());
currentVal--;
createChallengePrize.setText("" + currentVal);
checkPrizeAndMinimumForTavern();
}
private String checkPrizeAndMinimumForTavern() {
String errorResult = "";
String inputValue = createChallengePrize.getText().toString();
if (inputValue.isEmpty()) {
inputValue = "0";
}
int currentVal = Integer.parseInt(inputValue);
// 0 is Tavern
int selectedLocation = challengeLocationSpinner.getSelectedItemPosition();
double gemCount = HabiticaApplication.User.getGemCount();
if (selectedLocation == 0 && currentVal == 0) {
createChallengeGemError.setVisibility(View.VISIBLE);
String error = getString(R.string.challenge_create_error_tavern_one_gem);
createChallengeGemError.setText(error);
errorResult = error;
} else if (currentVal > gemCount) {
createChallengeGemError.setVisibility(View.VISIBLE);
String error = getString(R.string.challenge_create_error_enough_gems);
createChallengeGemError.setText(error);
errorResult = error;
} else {
createChallengeGemError.setVisibility(View.GONE);
}
challengeRemoveGemBtn.setEnabled(currentVal != 0);
return errorResult;
}
private void fillControls() {
Resources resources = getResources();
ActionBar supportActionBar = getSupportActionBar();
if (supportActionBar != null) {
supportActionBar.setDisplayShowHomeEnabled(true);
supportActionBar.setHomeAsUpIndicator(R.drawable.ic_close_white_24dp);
supportActionBar.setTitle("");
supportActionBar.setBackgroundDrawable(new ColorDrawable(resources.getColor(R.color.brand_200)));
supportActionBar.setElevation(0);
}
locationAdapter = new GroupArrayAdapter(this);
locationAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
challengeRepository.getLocalGroups().subscribe(groups -> {
Group tavern = new Group();
tavern.id = "00000000-0000-4000-A000-000000000000";
tavern.name = getString(R.string.sidebar_tavern);
locationAdapter.add(tavern);
groups.forEach(group -> locationAdapter.add(group));
}, Throwable::printStackTrace);
challengeLocationSpinner.setAdapter(locationAdapter);
challengeLocationSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
checkPrizeAndMinimumForTavern();
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
createChallengePrize.setOnKeyListener((view, i, keyEvent) -> {
checkPrizeAndMinimumForTavern();
return false;
});
addHabit = createTask(ChallengeTasksRecyclerViewAdapter.TASK_TYPE_ADD_ITEM, resources.getString(R.string.add_habit));
addDaily = createTask(ChallengeTasksRecyclerViewAdapter.TASK_TYPE_ADD_ITEM, resources.getString(R.string.add_daily));
addTodo = createTask(ChallengeTasksRecyclerViewAdapter.TASK_TYPE_ADD_ITEM, resources.getString(R.string.add_todo));
addReward = createTask(ChallengeTasksRecyclerViewAdapter.TASK_TYPE_ADD_ITEM, resources.getString(R.string.add_reward));
ArrayList<Task> taskList = new ArrayList<>();
taskList.add(addHabit);
taskList.add(addDaily);
taskList.add(addTodo);
taskList.add(addReward);
challengeTasks = new ChallengeTasksRecyclerViewAdapter(null, 0, this, "", null, false, true);
challengeTasks.setTasks(taskList);
challengeTasks.addItemObservable().subscribe(t -> {
if (t.equals(addHabit)) {
openNewTaskActivity(Task.TYPE_HABIT, null);
} else if (t.equals(addDaily)) {
openNewTaskActivity(Task.TYPE_DAILY, null);
} else if (t.equals(addTodo)) {
openNewTaskActivity(Task.TYPE_TODO, null);
} else if (t.equals(addReward)) {
openNewTaskActivity(Task.TYPE_REWARD, null);
}
});
createChallengeTaskList.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() {
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
// Stop only scrolling.
return rv.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING;
}
});
createChallengeTaskList.setAdapter(challengeTasks);
createChallengeTaskList.setLayoutManager(new LinearLayoutManager(this));
}
private static Task createTask(String taskType, String taskName) {
Task t = new Task();
t.setId(UUID.randomUUID().toString());
t.setType(taskType);
t.setText(taskName);
if (taskType.equals(Task.TYPE_HABIT)) {
t.setUp(true);
t.setDown(false);
}
return t;
}
private void fillControlsByChallenge() {
challengeRepository.getChallenge(challengeId).subscribe(challenge -> {
createChallengeTitle.setText(challenge.name);
createChallengeDescription.setText(challenge.description);
createChallengeTag.setText(challenge.shortName);
createChallengePrize.setText(challenge.prize + "");
for (int i = 0; i < locationAdapter.getCount(); i++) {
Group group = locationAdapter.getItem(i);
if (group.id == challenge.groupId) {
challengeLocationSpinner.setSelection(i);
break;
}
}
checkPrizeAndMinimumForTavern();
challengeRepository.getChallengeTasks(challengeId).subscribe(tasks -> {
tasks.tasks.forEach((s, task) -> addOrUpdateTaskInList(task));
}, Throwable::printStackTrace, () -> {
// activate editMode to track taskChanges
editMode = true;
});
});
}
private void openNewTaskActivity(String type, Task task) {
Bundle bundle = new Bundle();
if (task == null) {
bundle.putString(TaskFormActivity.TASK_TYPE_KEY, type);
} else {
bundle.putParcelable(TaskFormActivity.PARCELABLE_TASK, task);
}
bundle.putBoolean(TaskFormActivity.SAVE_TO_DB, false);
bundle.putBoolean(TaskFormActivity.SET_IGNORE_FLAG, true);
bundle.putBoolean(TaskFormActivity.SHOW_TAG_SELECTION, false);
bundle.putBoolean(TaskFormActivity.SHOW_CHECKLIST, false);
if (HabiticaApplication.User != null && HabiticaApplication.User.getPreferences() != null) {
String allocationMode = HabiticaApplication.User.getPreferences().getAllocationMode();
bundle.putString(TaskFormActivity.USER_ID_KEY, HabiticaApplication.User.getId());
bundle.putString(TaskFormActivity.ALLOCATION_MODE_KEY, allocationMode);
}
Intent intent = new Intent(this, TaskFormActivity.class);
intent.putExtras(bundle);
startActivityForResult(intent, 1);
}
private Challenge getChallengeData() {
Challenge c = new Challenge();
int locationPos = challengeLocationSpinner.getSelectedItemPosition();
Group locationGroup = locationAdapter.getItem(locationPos);
if (challengeId != null) {
c.id = challengeId;
}
c.groupId = locationGroup.id;
c.name = createChallengeTitle.getText().toString();
c.description = createChallengeDescription.getText().toString();
c.shortName = createChallengeTag.getText().toString();
c.prize = Integer.parseInt(createChallengePrize.getText().toString());
return c;
}
private Observable<Challenge> createChallenge() {
Challenge c = getChallengeData();
List<Task> taskList = challengeTasks.getTaskList();
taskList.remove(addHabit);
taskList.remove(addDaily);
taskList.remove(addTodo);
taskList.remove(addReward);
return challengeRepository.createChallenge(c, taskList);
}
private Observable<Challenge> updateChallenge() {
Challenge c = getChallengeData();
List<Task> taskList = challengeTasks.getTaskList();
taskList.remove(addHabit);
taskList.remove(addDaily);
taskList.remove(addTodo);
taskList.remove(addReward);
return challengeRepository.updateChallenge(c, taskList, new ArrayList<>(addedTasks.values()),
new ArrayList<>(updatedTasks.values()),
new ArrayList<>(removedTasks.keySet())
);
}
private void addOrUpdateTaskInList(Task task) {
if (!challengeTasks.replaceTask(task)) {
Task taskAbove;
switch (task.getType()) {
case Task.TYPE_HABIT:
taskAbove = addHabit;
break;
case Task.TYPE_DAILY:
taskAbove = addDaily;
break;
case Task.TYPE_TODO:
taskAbove = addTodo;
break;
default:
taskAbove = addReward;
break;
}
challengeTasks.addTaskUnder(task, taskAbove);
if (editMode) {
addedTasks.put(task.getId(), task);
}
} else {
// don't need to add the task to updatedTasks if its already been added right now
if (editMode && !addedTasks.containsKey(task.getId())) {
updatedTasks.put(task.getId(), task);
}
}
}
private String getEditTextString(EditText editText) {
return editText.getText().toString();
}
private class GroupArrayAdapter extends ArrayAdapter<Group> {
public GroupArrayAdapter(@NonNull Context context) {
super(context, android.R.layout.simple_spinner_item);
}
@NonNull
@Override
public View getView(int position, View convertView, ViewGroup parent) {
AppCompatTextView checkedTextView = (AppCompatTextView) super.getView(position, convertView, parent);
checkedTextView.setText(getItem(position).name);
return checkedTextView;
}
@Override
public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
AppCompatCheckedTextView checkedTextView = (AppCompatCheckedTextView) super.getDropDownView(position, convertView, parent);
checkedTextView.setText(getItem(position).name);
return checkedTextView;
}
}
}

View file

@ -853,6 +853,10 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
@Subscribe
public void onEvent(final DeleteTaskCommand cmd) {
if(cmd.ignoreEvent) {
return;
}
taskRepository.deleteTask(cmd.TaskIdToDelete)
.subscribe(aVoid -> EventBus.getDefault().post(new TaskRemovedEvent(cmd.TaskIdToDelete)), throwable -> {});
}
@ -1213,6 +1217,9 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
@Subscribe
public void onEvent(final TaskSaveEvent event) {
if(event.ignoreEvent)
return;
Task task = event.task;
if (event.created) {
this.taskRepository.createTask(task).subscribe(task1 -> {}, throwable -> {});

View file

@ -116,7 +116,14 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
public static final String TASK_ID_KEY = "taskId";
public static final String USER_ID_KEY = "userId";
public static final String TASK_TYPE_KEY = "type";
public static final String SHOW_TAG_SELECTION = "show_tag_selection";
public static final String SHOW_CHECKLIST = "show_checklist";
public static final String PARCELABLE_TASK = "parcelable_task";
public static final String ALLOCATION_MODE_KEY = "allocationModeKey";
public static final String SAVE_TO_DB = "saveToDb";
// in order to disable the event handler in MainActivity
public static final String SET_IGNORE_FLAG = "ignoreFlag";
@BindView(R.id.task_value_edittext)
EditText taskValue;
@ -257,6 +264,9 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
private String taskType;
private String taskId;
private String userId;
private boolean showTagSelection;
private boolean showChecklist;
private boolean setIgnoreFlag;
private Task task;
private String allocationMode;
private List<CheckBox> weekdayCheckboxes = new ArrayList<>();
@ -270,6 +280,7 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
private RemindersManager remindersManager;
private FirstDayOfTheWeekHelper firstDayOfTheWeekHelper;
private boolean saveToDb;
@Override
protected int getLayoutResId() {
@ -282,10 +293,23 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
taskType = bundle.getString(TASK_TYPE_KEY);
taskId = bundle.getString(TASK_ID_KEY);
userId = bundle.getString(USER_ID_KEY);
showTagSelection = bundle.getBoolean(SHOW_TAG_SELECTION, true);
showChecklist = bundle.getBoolean(SHOW_CHECKLIST, true);
allocationMode = bundle.getString(ALLOCATION_MODE_KEY);
saveToDb = bundle.getBoolean(SAVE_TO_DB, true);
setIgnoreFlag = bundle.getBoolean(SET_IGNORE_FLAG, false);
tagsWrapper.setVisibility(showTagSelection ? View.VISIBLE : View.GONE);
if(bundle.containsKey(PARCELABLE_TASK)){
task = bundle.getParcelable(PARCELABLE_TASK);
taskType = task.type;
}
tagCheckBoxList = new ArrayList<>();
selectedTags = new ArrayList<>();
if (taskType == null) {
@ -309,7 +333,13 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
finish();
dismissKeyboard();
EventBus.getDefault().post(new DeleteTaskCommand(taskId));
String taskToDelete = this.taskId;
if(taskToDelete == null && task != null){
taskToDelete = task.getId();
}
EventBus.getDefault().post(new DeleteTaskCommand(taskToDelete, setIgnoreFlag));
}).setNegativeButton(getString(R.string.no), (dialog, which) -> {
dialog.dismiss();
}).show());
@ -378,6 +408,10 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
attributeWrapper.setVisibility(View.GONE);
}
if(!showChecklist){
mainWrapper.removeView(checklistWrapper);
}
if (taskId != null) {
Task task = new Select().from(Task.class).byIds(taskId).querySingle();
this.task = task;
@ -387,6 +421,11 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
setTitle(task);
btnDelete.setEnabled(true);
} else if(task != null) {
populate(task);
taskText.requestFocus();
btnDelete.setEnabled(true);
} else {
setTitle((Task) null);
@ -407,7 +446,10 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
// If it's a to-do, change the emojiToggle2 to the actual emojiToggle2 (prevents NPEs when not a to-do task)
if (isTodo) {
emojiToggle2 = (ImageButton) findViewById(R.id.emoji_toggle_btn2);
} else {
}
// if showChecklist is inactive the wrapper is wrapper, so the reference can't be found
if(emojiToggle2 == null) {
emojiToggle2 = emojiToggle0;
}
@ -1179,12 +1221,16 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
}
//save
this.task.setTags(taskTags);
this.task.save();
if(saveToDb){
this.task.save();
}
//send back to other elements.
TaskSaveEvent event = new TaskSaveEvent();
if (TaskFormActivity.this.task.getId() == null) {
event.created = true;
}
event.ignoreEvent = setIgnoreFlag;
event.task = TaskFormActivity.this.task;
EventBus.getDefault().post(event);

View file

@ -1,18 +1,5 @@
package com.habitrpg.android.habitica.ui.adapter.social;
import com.github.underscore.$;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.events.commands.ShowChallengeDetailActivityCommand;
import com.habitrpg.android.habitica.events.commands.ShowChallengeDetailDialogCommand;
import com.habitrpg.android.habitica.ui.fragments.social.challenges.ChallengeFilterOptions;
import com.habitrpg.android.habitica.models.social.Challenge;
import com.habitrpg.android.habitica.models.user.HabitRPGUser;
import net.pherth.android.emoji_library.EmojiParser;
import net.pherth.android.emoji_library.EmojiTextView;
import org.greenrobot.eventbus.EventBus;
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
@ -23,19 +10,28 @@ import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.github.underscore.$;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.events.commands.ShowChallengeDetailActivityCommand;
import com.habitrpg.android.habitica.events.commands.ShowChallengeDetailDialogCommand;
import com.habitrpg.android.habitica.models.social.Challenge;
import com.habitrpg.android.habitica.models.user.HabitRPGUser;
import com.habitrpg.android.habitica.ui.fragments.social.challenges.ChallengeFilterOptions;
import net.pherth.android.emoji_library.EmojiParser;
import net.pherth.android.emoji_library.EmojiTextView;
import org.greenrobot.eventbus.EventBus;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import butterknife.BindView;
import butterknife.ButterKnife;
import static com.raizlabs.android.dbflow.config.FlowManager.getContext;
public class ChallengesListViewAdapter extends RecyclerView.Adapter<ChallengesListViewAdapter.ChallengeViewHolder> {
private List<Challenge> challenges = new ArrayList<>();
private List<Challenge> challengesSource = new ArrayList<>();

View file

@ -0,0 +1,195 @@
package com.habitrpg.android.habitica.ui.adapter.social.challenges;
import android.content.Context;
import android.support.annotation.Nullable;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import com.github.underscore.$;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.helpers.TaskFilterHelper;
import com.habitrpg.android.habitica.models.tasks.Task;
import com.habitrpg.android.habitica.ui.adapter.tasks.SortableTasksRecyclerViewAdapter;
import com.habitrpg.android.habitica.ui.viewHolders.tasks.BaseTaskViewHolder;
import com.habitrpg.android.habitica.ui.viewHolders.tasks.DailyViewHolder;
import com.habitrpg.android.habitica.ui.viewHolders.tasks.HabitViewHolder;
import com.habitrpg.android.habitica.ui.viewHolders.tasks.RewardViewHolder;
import com.habitrpg.android.habitica.ui.viewHolders.tasks.TodoViewHolder;
import java.util.List;
import rx.Observable;
import rx.subjects.PublishSubject;
public class ChallengeTasksRecyclerViewAdapter
extends SortableTasksRecyclerViewAdapter<BaseTaskViewHolder> {
public static final String TASK_TYPE_ADD_ITEM = "ADD_ITEM";
private static final int TYPE_HEADER = 0;
private static final int TYPE_HABIT = 1;
private static final int TYPE_DAILY = 2;
private static final int TYPE_TODO = 3;
private static final int TYPE_REWARD = 4;
private static final int TYPE_ADD_ITEM = 5;
private int dailyResetOffset = 0;
private PublishSubject<Task> addItemSubject = PublishSubject.create();
private boolean openTaskDisabled;
private boolean taskActionsDisabled;
public ChallengeTasksRecyclerViewAdapter(@Nullable TaskFilterHelper taskFilterHelper, int layoutResource,
Context newContext, String userID, @Nullable SortTasksCallback sortCallback,
boolean openTaskDisabled, boolean taskActionsDisabled) {
super("", taskFilterHelper, layoutResource, newContext, userID, sortCallback);
this.openTaskDisabled = openTaskDisabled;
this.taskActionsDisabled = taskActionsDisabled;
}
public void setDailyResetOffset(int newResetOffset) {
dailyResetOffset = newResetOffset;
}
@Override
protected void injectThis(AppComponent component) {
component.inject(this);
}
@Override
public boolean loadFromDatabase() {
return false;
}
@Override
public int getItemViewType(int position) {
Task task = this.filteredContent.get(position);
if (task.type.equals(Task.TYPE_HABIT))
return TYPE_HABIT;
if (task.type.equals(Task.TYPE_DAILY))
return TYPE_DAILY;
if (task.type.equals(Task.TYPE_TODO))
return TYPE_TODO;
if (task.type.equals(Task.TYPE_REWARD))
return TYPE_REWARD;
if (addItemSubject.hasObservers() && task.type.equals(TASK_TYPE_ADD_ITEM))
return TYPE_ADD_ITEM;
return TYPE_HEADER;
}
public Observable<Task> addItemObservable(){
return addItemSubject;
}
public int addTaskUnder(Task taskToAdd, Task taskAbove) {
int position = $.findIndex(this.content, t -> t.getId().equals(taskAbove.getId()));
content.add(position + 1, taskToAdd);
filter();
return position;
}
@Override
public BaseTaskViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
BaseTaskViewHolder viewHolder = null;
switch (viewType) {
case TYPE_HABIT:
viewHolder = new HabitViewHolder(getContentView(parent, R.layout.habit_item_card));
break;
case TYPE_DAILY:
viewHolder = new DailyViewHolder(getContentView(parent, R.layout.daily_item_card), dailyResetOffset);
break;
case TYPE_TODO:
viewHolder = new TodoViewHolder(getContentView(parent, R.layout.todo_item_card));
break;
case TYPE_REWARD:
viewHolder = new RewardViewHolder(getContentView(parent, R.layout.reward_item_card));
break;
case TYPE_ADD_ITEM:
viewHolder = new AddItemViewHolder(getContentView(parent, R.layout.challenge_add_task_item), addItemSubject);
break;
default:
viewHolder = new DividerViewHolder(getContentView(parent, R.layout.challenge_task_divider));
break;
}
viewHolder.setDisabled(openTaskDisabled, taskActionsDisabled);
return viewHolder;
}
public List<Task> getTaskList(){
return $.map(content, t -> t);
}
/**
* @param task
* @return true if task found&updated
*/
public boolean replaceTask(Task task) {
int i;
for (i = 0; i < this.content.size(); ++i) {
if (content.get(i).getId().equals(task.getId())) {
break;
}
}
if (i < content.size()) {
content.set(i, task);
filter();
return true;
}
return false;
}
public class AddItemViewHolder extends BaseTaskViewHolder {
private Button addBtn;
private PublishSubject<Task> callback;
private Task newTask;
public AddItemViewHolder(View itemView, PublishSubject<Task> callback) {
super(itemView, false);
this.callback = callback;
addBtn = (Button) itemView.findViewById(R.id.btn_add_task);
addBtn.setClickable(true);
addBtn.setOnClickListener(view -> callback.onNext(newTask));
context = itemView.getContext();
}
@Override
public void bindHolder(Task newTask, int position) {
this.newTask = newTask;
addBtn.setText(newTask.text);
}
}
private class DividerViewHolder extends BaseTaskViewHolder {
private TextView divider_name;
public DividerViewHolder(View itemView) {
super(itemView, false);
divider_name = (TextView) itemView.findViewById(R.id.divider_name);
context = itemView.getContext();
}
@Override
public void bindHolder(Task newTask, int position) {
divider_name.setText(newTask.text);
}
}
}

View file

@ -1,5 +1,6 @@
package com.habitrpg.android.habitica.ui.fragments;
import com.habitrpg.android.habitica.BuildConfig;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.helpers.PurchaseTypes;
@ -14,6 +15,9 @@ import org.solovyev.android.checkout.ProductTypes;
import org.solovyev.android.checkout.RequestListener;
import org.solovyev.android.checkout.Sku;
import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@ -21,10 +25,13 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.OnClick;
public class GemsPurchaseFragment extends BaseFragment implements GemPurchaseActivity.CheckoutFragment {
@ -36,12 +43,20 @@ public class GemsPurchaseFragment extends BaseFragment implements GemPurchaseAct
GemPurchaseOptionsView gems42View;
@BindView(R.id.gems_84_view)
GemPurchaseOptionsView gems84View;
@BindView(R.id.gemPurchaseOptions)
ViewGroup gemPurchaseOptions;
@BindView(R.id.notAvailableTextView)
TextView billingNotAvailableTextView;
@BindView(R.id.notAvailableButton)
Button billingNotAvailableButton;
@Inject
CrashlyticsProxy crashlyticsProxy;
private GemPurchaseActivity listener;
private BillingRequests billingRequests;
private boolean billingNotSupported;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@ -67,6 +82,10 @@ public class GemsPurchaseFragment extends BaseFragment implements GemPurchaseAct
gems84View.setOnPurchaseClickListener(v -> purchaseGems(PurchaseTypes.Purchase84Gems));
gems84View.seedsImageButton.setOnClickListener(v -> ((GemPurchaseActivity) this.getActivity()).showSeedsPromo(getString(R.string.seeds_interstitial_gems), "store"));
if (billingNotSupported) {
setBillingNotSupported();
}
}
@Override
@ -80,7 +99,7 @@ public class GemsPurchaseFragment extends BaseFragment implements GemPurchaseAct
products -> {
Inventory.Product gems = products.get(ProductTypes.IN_APP);
if (!gems.supported) {
// billing is not supported, user can't purchase anything
setBillingNotSupported();
return;
}
java.util.List<Sku> skus = gems.getSkus();
@ -91,6 +110,15 @@ public class GemsPurchaseFragment extends BaseFragment implements GemPurchaseAct
}
}
private void setBillingNotSupported() {
billingNotSupported = true;
if (gemPurchaseOptions != null) {
gemPurchaseOptions.setVisibility(View.GONE);
billingNotAvailableButton.setVisibility(View.VISIBLE);
billingNotAvailableTextView.setVisibility(View.VISIBLE);
}
}
@Override
public void setListener(GemPurchaseActivity listener) {
this.listener = listener;
@ -136,6 +164,11 @@ public class GemsPurchaseFragment extends BaseFragment implements GemPurchaseAct
crashlyticsProxy.fabricLogE("Purchase", "Error", e);
}
});
}
@OnClick(R.id.notAvailableButton)
public void openWebsite() {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(BuildConfig.BASE_URL + "/"));
getContext().startActivity(intent);
}
}

View file

@ -97,6 +97,11 @@ public class SubscriptionFragment extends BaseFragment implements GemPurchaseAct
@BindView(R.id.subscribeBenefitsTitle)
TextView subscribeBenefitsTitle;
@BindView(R.id.notAvailableTextView)
TextView billingNotAvailableTextView;
@BindView(R.id.notAvailableButton)
Button billingNotAvailableButton;
@Nullable
Sku selectedSubscriptionSku;
List<Sku> skus;
@ -106,6 +111,7 @@ public class SubscriptionFragment extends BaseFragment implements GemPurchaseAct
private HabitRPGUser user;
private boolean hasLoadedSubscriptionOptions;
private boolean billingNotSupported;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@ -145,6 +151,10 @@ public class SubscriptionFragment extends BaseFragment implements GemPurchaseAct
this.subscribeListitem2Box.setOnClickListener(view1 -> toggleDescriptionView(this.subscribeListitem2Button, this.subscribeListItem2Description));
this.subscribeListitem3Box.setOnClickListener(view1 -> toggleDescriptionView(this.subscribeListitem3Button, this.subscribeListItem3Description));
this.subscribeListitem4Box.setOnClickListener(view1 -> toggleDescriptionView(this.subscribeListitem4Button, this.subscribeListItem4Description));
if (billingNotSupported) {
setBillingNotSupported();
}
}
private void toggleDescriptionView(ImageView button, TextView descriptionView) {
@ -173,6 +183,11 @@ public class SubscriptionFragment extends BaseFragment implements GemPurchaseAct
products -> {
Inventory.Product subscriptions = products.get(ProductTypes.SUBSCRIPTION);
if (!subscriptions.supported) {
setBillingNotSupported();
return;
}
skus = subscriptions.getSkus();
for (Sku sku : skus) {
@ -185,6 +200,16 @@ public class SubscriptionFragment extends BaseFragment implements GemPurchaseAct
}
}
private void setBillingNotSupported() {
billingNotSupported = true;
if (subscriptionOptions != null) {
subscriptionOptions.setVisibility(View.GONE);
loadingIndicator.setVisibility(View.GONE);
billingNotAvailableButton.setVisibility(View.VISIBLE);
billingNotAvailableTextView.setVisibility(View.VISIBLE);
}
}
private void updateButtonLabel(Sku sku, String price, Inventory.Product subscriptions) {
SubscriptionOptionView matchingView = buttonForSku(sku);
if (matchingView != null) {
@ -288,6 +313,8 @@ public class SubscriptionFragment extends BaseFragment implements GemPurchaseAct
this.subscriptionDetailsView.setPlan(plan);
this.subscribeBenefitsTitle.setText(R.string.subscribe_prompt_thanks);
this.subscriptionOptions.setVisibility(View.GONE);
billingNotAvailableButton.setVisibility(View.GONE);
billingNotAvailableTextView.setVisibility(View.GONE);
} else {
if (!hasLoadedSubscriptionOptions) {
return;

View file

@ -72,7 +72,7 @@ public class AvatarCustomizationFragment extends BaseMainFragment {
this.updateActiveCustomization();
this.adapter.userSize = this.user.getPreferences().getSize();
this.adapter.hairColor = this.user.getPreferences().getHair().getColor();
this.adapter.gemBalance = user.getBalance() * 4;
this.adapter.gemBalance = user.getGemCount();
return view;
}
@ -134,7 +134,7 @@ public class AvatarCustomizationFragment extends BaseMainFragment {
@Override
public void updateUserData(HabitRPGUser user) {
super.updateUserData(user);
this.adapter.gemBalance = user.getBalance() * 4;
this.adapter.gemBalance = user.getGemCount();
this.updateActiveCustomization();
if (adapter.getCustomizationList() != null) {
List<String> ownedCustomizations = new ArrayList<>();

View file

@ -2,6 +2,8 @@ package com.habitrpg.android.habitica.ui.fragments.social;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.data.ChallengeRepository;
import com.habitrpg.android.habitica.data.local.ChallengeLocalRepository;
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment;
import com.habitrpg.android.habitica.models.social.Group;
@ -18,6 +20,8 @@ import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
@ -32,6 +36,9 @@ public class GuildsOverviewFragment extends BaseMainFragment implements View.OnC
@BindView(R.id.chat_refresh_layout)
SwipeRefreshLayout swipeRefreshLayout;
@Inject
ChallengeRepository challengeRepository;
private List<Group> guilds;
private ArrayList<String> guildIDs;
@ -77,6 +84,8 @@ public class GuildsOverviewFragment extends BaseMainFragment implements View.OnC
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(false);
}
challengeRepository.setUsersGroups(groups);
}, throwable -> {
});
}

View file

@ -1,17 +1,23 @@
package com.habitrpg.android.habitica.ui.fragments.social.challenges;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.ui.activities.CreateChallengeActivity;
import com.habitrpg.android.habitica.ui.activities.PartyInviteActivity;
import com.habitrpg.android.habitica.ui.adapter.social.ChallengesListViewAdapter;
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment;
import com.habitrpg.android.habitica.models.social.Challenge;
@ -50,7 +56,7 @@ public class ChallengeListFragment extends BaseMainFragment implements SwipeRefr
private Action0 refreshCallback;
private boolean withFilter;
public void setWithFilter(boolean withFilter){
public void setWithFilter(boolean withFilter) {
this.withFilter = withFilter;
}
@ -121,12 +127,12 @@ public class ChallengeListFragment extends BaseMainFragment implements SwipeRefr
swipeRefreshLayout.setOnRefreshListener(this);
swipeRefreshEmptyLayout.setOnRefreshListener(this);
challengeFilterLayout.setVisibility(withFilter?View.VISIBLE:View.GONE);
challengeFilterLayout.setVisibility(withFilter ? View.VISIBLE : View.GONE);
challengeFilterLayout.setClickable(true);
challengeFilterLayout.setOnClickListener(view -> ChallengeFilterDialogHolder.showDialog(getActivity(), currentChallengesInView, lastFilterOptions, filterOptions -> {
challengeAdapter.setFilterByGroups(filterOptions);
this.lastFilterOptions = filterOptions;
}));
challengeAdapter.setFilterByGroups(filterOptions);
this.lastFilterOptions = filterOptions;
}));
recyclerView.setLayoutManager(new LinearLayoutManager(this.activity));
recyclerView.setAdapter(challengeAdapter);
@ -215,4 +221,27 @@ public class ChallengeListFragment extends BaseMainFragment implements SwipeRefr
public String customTitle() {
return getString(R.string.sidebar_challenges);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_list_challenges, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
switch (id) {
case R.id.action_create_challenge:
Intent intent = new Intent(getActivity(), CreateChallengeActivity.class);
startActivity(intent);
return true;
}
return super.onOptionsItemSelected(item);
}
}

View file

@ -5,6 +5,7 @@ import com.habitrpg.android.habitica.helpers.TaskFilterHelper;
import com.habitrpg.android.habitica.data.ApiClient;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.ui.adapter.social.challenges.ChallengeTasksRecyclerViewAdapter;
import com.habitrpg.android.habitica.ui.adapter.tasks.BaseTasksRecyclerViewAdapter;
import com.habitrpg.android.habitica.ui.adapter.tasks.SortableTasksRecyclerViewAdapter;
import com.habitrpg.android.habitica.ui.fragments.BaseFragment;
@ -86,7 +87,10 @@ public class ChallengeTasksRecyclerViewFragment extends BaseFragment {
}
public void setInnerAdapter() {
this.recyclerAdapter = new ChallengeTasksRecyclerViewAdapter(null, 0, getContext(), userID, null);
ChallengeTasksRecyclerViewAdapter challengeTasksRecyclerViewAdapter = new ChallengeTasksRecyclerViewAdapter(null, 0, getContext(), userID, null, true, true);
this.recyclerAdapter = challengeTasksRecyclerViewAdapter;
challengeTasksRecyclerViewAdapter.setDailyResetOffset(user.getPreferences().getDayStart());
if (tasksOnInitialize != null && tasksOnInitialize.size() != 0 && recyclerAdapter != null && recyclerAdapter.getItemCount() == 0) {
recyclerAdapter.setTasks(tasksOnInitialize);
@ -137,96 +141,8 @@ public class ChallengeTasksRecyclerViewFragment extends BaseFragment {
// region Challenge specific RecyclerViewAdapters
public class ChallengeTasksRecyclerViewAdapter extends SortableTasksRecyclerViewAdapter<BaseTaskViewHolder> {
private static final int TYPE_HEADER = 0;
private static final int TYPE_HABIT = 1;
private static final int TYPE_DAILY = 2;
private static final int TYPE_TODO = 3;
private static final int TYPE_REWARD = 4;
private int dailyResetOffset = 0;
public ChallengeTasksRecyclerViewAdapter(@Nullable TaskFilterHelper taskFilterHelper, int layoutResource, Context newContext, String userID, @Nullable SortTasksCallback sortCallback) {
super("", taskFilterHelper, layoutResource, newContext, userID, sortCallback);
if (user != null) {
dailyResetOffset = user.getPreferences().getDayStart();
}
}
@Override
protected void injectThis(AppComponent component) {
component.inject(this);
}
@Override
public boolean loadFromDatabase() {
return false;
}
@Override
public int getItemViewType(int position) {
Task task = this.filteredContent.get(position);
if (task.type.equals(Task.TYPE_HABIT))
return TYPE_HABIT;
if (task.type.equals(Task.TYPE_DAILY))
return TYPE_DAILY;
if (task.type.equals(Task.TYPE_TODO))
return TYPE_TODO;
if (task.type.equals(Task.TYPE_REWARD))
return TYPE_REWARD;
return TYPE_HEADER;
}
@Override
public BaseTaskViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
BaseTaskViewHolder viewHolder;
switch (viewType) {
case TYPE_HABIT:
viewHolder = new HabitViewHolder(getContentView(parent, R.layout.habit_item_card));
break;
case TYPE_DAILY:
viewHolder = new DailyViewHolder(getContentView(parent, R.layout.daily_item_card), dailyResetOffset);
break;
case TYPE_TODO:
viewHolder = new TodoViewHolder(getContentView(parent, R.layout.todo_item_card));
break;
case TYPE_REWARD:
viewHolder = new RewardViewHolder(getContentView(parent, R.layout.reward_item_card));
break;
default:
viewHolder = new DividerViewHolder(getContentView(parent, R.layout.challenge_task_divider));
break;
}
//viewHolder.setDisabled(true);
return viewHolder;
}
}
private class DividerViewHolder extends BaseTaskViewHolder {
private TextView divider_name;
public DividerViewHolder(View itemView) {
super(itemView, false);
divider_name = (TextView) itemView.findViewById(R.id.divider_name);
context = itemView.getContext();
}
@Override
public void bindHolder(Task newTask, int position) {
divider_name.setText(newTask.text);
}
}
// endregion
}

View file

@ -138,17 +138,10 @@ public class FloatingActionMenuBehavior extends CoordinatorLayout.Behavior {
}
private void slideFabOffScreen(FloatingActionMenu view) {
Animation slideOff = AnimationUtils.loadAnimation(context, R.anim.fab_slide_out);
slideOff.setDuration(FAB_ANIMATION_DURATION);
slideOff.setFillAfter(true);
view.startAnimation(slideOff);
view.hideMenu(true);
}
private void slideFabOnScreen(FloatingActionMenu view) {
Animation slideIn = AnimationUtils.loadAnimation(context, R.anim.fab_slide_in);
slideIn.setDuration(FAB_ANIMATION_DURATION);
slideIn.setFillAfter(true);
view.startAnimation(slideIn);
view.showMenu(false);
view.showMenu(true);
}
}

View file

@ -63,7 +63,6 @@ public abstract class BaseTaskViewHolder extends RecyclerView.ViewHolder impleme
@BindView(R.id.approvalRequiredTextField)
TextView approvalRequiredTextView;
boolean disabled;
public BaseTaskViewHolder(View itemView) {
this(itemView, true);
@ -187,7 +186,7 @@ public abstract class BaseTaskViewHolder extends RecyclerView.ViewHolder impleme
@Override
public void onClick(View v) {
if (v != itemView || isDisabled()) {
if (v != itemView || this.openTaskDisabled) {
return;
}
@ -201,13 +200,11 @@ public abstract class BaseTaskViewHolder extends RecyclerView.ViewHolder impleme
return true;
}
public boolean isDisabled() {
return disabled;
}
protected boolean openTaskDisabled, taskActionsDisabled;
public void setDisabled(boolean disabled) {
this.disabled = disabled;
public void setDisabled(boolean openTaskDisabled, boolean taskActionsDisabled) {
this.openTaskDisabled = openTaskDisabled;
this.taskActionsDisabled = taskActionsDisabled;
itemView.setEnabled(!disabled);
}
}

View file

@ -167,9 +167,9 @@ public abstract class ChecklistedViewHolder extends BaseTaskViewHolder implement
}
@Override
public void setDisabled(boolean disabled) {
super.setDisabled(disabled);
public void setDisabled(boolean openTaskDisabled, boolean taskActionsDisabled) {
super.setDisabled(openTaskDisabled, taskActionsDisabled);
this.checkbox.setEnabled(!disabled);
this.checkbox.setEnabled(!taskActionsDisabled);
}
}

View file

@ -88,10 +88,10 @@ public class HabitViewHolder extends BaseTaskViewHolder {
}
@Override
public void setDisabled(boolean disabled) {
super.setDisabled(disabled);
public void setDisabled(boolean openTaskDisabled, boolean taskActionsDisabled) {
super.setDisabled(openTaskDisabled, taskActionsDisabled);
this.btnPlus.setEnabled(!disabled);
this.btnMinus.setEnabled(!disabled);
this.btnPlus.setEnabled(!taskActionsDisabled);
this.btnMinus.setEnabled(!taskActionsDisabled);
}
}

View file

@ -89,10 +89,10 @@ public class RewardViewHolder extends BaseTaskViewHolder {
}
@Override
public void setDisabled(boolean disabled) {
super.setDisabled(disabled);
public void setDisabled(boolean openTaskDisabled, boolean taskActionsDisabled) {
super.setDisabled(openTaskDisabled, taskActionsDisabled);
this.rewardButton.setEnabled(!disabled);
this.rewardButton.setEnabled(!taskActionsDisabled);
}
}

View file

@ -8,7 +8,9 @@ import android.animation.ValueAnimator;
import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.AnticipateInterpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.FrameLayout;
@ -44,6 +46,11 @@ public class LoginBackgroundView extends RelativeLayout {
public LoginBackgroundView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.random = new Random();
DisplayMetrics metrics = new DisplayMetrics();
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getMetrics(metrics);
height = (int) (metrics.heightPixels*SIZE_FACTOR);
}
@Override
@ -69,10 +76,7 @@ public class LoginBackgroundView extends RelativeLayout {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.width = parentWidth;
this.height = (int)(parentHeight*SIZE_FACTOR);
this.width = MeasureSpec.getSize(widthMeasureSpec);
this.setMeasuredDimension(width, height);
params.width = width;
params.height = height;

View file

@ -6,13 +6,15 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.habitrpg.android.habitica.models.social.Challenge;
import android.text.TextUtils;
import java.lang.reflect.Type;
public class ChallengeDeserializer implements JsonDeserializer<Challenge> {
public class ChallengeSerializer implements JsonDeserializer<Challenge>, JsonSerializer<Challenge> {
@Override
public Challenge deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
@ -43,7 +45,15 @@ public class ChallengeDeserializer implements JsonDeserializer<Challenge> {
if (profile != null) {
challenge.leaderName = profile.get("name").getAsString();
challenge.leaderId = leaderObj.get("id").getAsString();
JsonElement id = leaderObj.get("id");
if (id == null) {
id = leaderObj.get("_id");
}
if (id != null) {
challenge.leaderId = id.getAsString();
}
}
}
}
@ -85,4 +95,22 @@ public class ChallengeDeserializer implements JsonDeserializer<Challenge> {
return "";
}
@Override
public JsonElement serialize(Challenge src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject object = new JsonObject();
object.addProperty("id", src.id);
object.addProperty("name", src.name);
object.addProperty("shortName", src.shortName);
object.addProperty("description", src.description);
object.addProperty("memberCount", src.memberCount);
object.addProperty("prize", src.prize);
object.addProperty("official", src.official);
object.addProperty("group", src.groupId);
object.add("tasksOrder", context.serialize(src.tasksOrder));
return object;
}
}

View file

@ -122,7 +122,7 @@ public class AvatarStatsWidgetProvider extends BaseWidgetProvider {
int sp = (int) ((stats.getGp() - gp) * 100);
remoteViews.setTextViewText(R.id.gold_tv, String.valueOf(gp));
remoteViews.setTextViewText(R.id.silver_tv, String.valueOf(sp));
remoteViews.setTextViewText(R.id.gems_tv, String.valueOf((int) (user.getBalance() * 4)));
remoteViews.setTextViewText(R.id.gems_tv, String.valueOf(user.getGemCount()));
remoteViews.setTextViewText(R.id.lvl_tv, context.getString(R.string.user_level, user.getStats().getLvl()));
AvatarView avatarView = new AvatarView(context, true, true, true);

View file

@ -2,6 +2,7 @@ package com.habitrpg.android.habitica.api;
import com.habitrpg.android.habitica.data.implementation.ApiClientImpl;
import com.habitrpg.android.habitica.helpers.PopupNotificationsManager;
import com.habitrpg.android.habitica.proxy.impl.EmptyCrashlyticsProxy;
import com.habitrpg.android.habitica.data.ApiClient;
import com.habitrpg.android.habitica.BuildConfig;
@ -39,7 +40,7 @@ public class BaseAPITests {
BuildConfig.PORT,
"",
"");
apiClient = new ApiClientImpl(ApiClientImpl.createGsonFactory(), hostConfig, new EmptyCrashlyticsProxy(), context);
apiClient = new ApiClientImpl(ApiClientImpl.createGsonFactory(), hostConfig, new EmptyCrashlyticsProxy(), new PopupNotificationsManager(context), context);
generateUser();
}

View file

@ -1,4 +1,9 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="store_title">Игронезируйте ваши задачи</string>
<string name="store_short_description">Живите играючи и развивайте мотивацию и организованность!</string>
<string name="store_description">Относитесь к своей жизни, как к игре, чтобы оставаться мотивированным и организованным! Habitica позволяет легко развлекаться при достижении целей.
Введите свои привычки, свои ежедневные цели и список дел, а затем создайте собственный аватар. Выполняйте задачи, чтобы повысить уровень вашего аватара и разблокировать такие функции, как снаряжение, питомцы, навыки и даже квесты! Сражайтесь против монстрами с друзьями, чтобы поддержать друг друга , и использовать свое золото в игре в качестве поощрения, например, на экипировку или специальные награды, или даже на просмотр эпизода вашего любимого телешоу. Гибкая, социальная и веселая, Habitica - это идеальный способ мотивировать себя к чему-либо ».
Если у вас есть какие-либо вопросы, не стесняйтесь отправлять их на mobile@habitica.com! А если вам понравится наше приложение, мы будем очень признательны, если вы оставите нам отзыв ».
</string>
</resources>