mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-04-14 11:46:32 +00:00
Various settings fixes
This commit is contained in:
parent
af01b9738a
commit
ea6adb6f16
40 changed files with 523 additions and 272 deletions
|
|
@ -50,9 +50,8 @@ dependencies {
|
|||
implementation 'com.google.dagger:dagger:2.39.1'
|
||||
kapt 'com.google.dagger:dagger-compiler:2.39.1'
|
||||
compileOnly 'javax.annotation:javax.annotation-api:1.3.2'
|
||||
compileOnly 'com.github.pengrad:jdk9-deps:1.0'
|
||||
//App Compatibility and Material Design
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.4.0'
|
||||
implementation 'com.google.android.material:material:1.4.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||
implementation "androidx.preference:preference-ktx:1.1.1"
|
||||
|
|
@ -103,14 +102,13 @@ dependencies {
|
|||
//Leak Detection
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
|
||||
//Push Notifications
|
||||
implementation platform('com.google.firebase:firebase-bom:28.3.0')
|
||||
implementation 'com.google.firebase:firebase-crashlytics'
|
||||
implementation platform('com.google.firebase:firebase-bom:29.0.0')
|
||||
implementation 'com.google.firebase:firebase-crashlytics-ktx'
|
||||
implementation 'com.google.firebase:firebase-core'
|
||||
implementation 'com.google.firebase:firebase-messaging'
|
||||
implementation 'com.google.firebase:firebase-config'
|
||||
implementation 'com.google.firebase:firebase-perf'
|
||||
implementation 'com.google.firebase:firebase-messaging-ktx'
|
||||
implementation 'com.google.firebase:firebase-config-ktx'
|
||||
implementation 'com.google.firebase:firebase-perf-ktx'
|
||||
implementation 'com.google.android.gms:play-services-auth:19.2.0'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.31"
|
||||
implementation 'com.nex3z:flow-layout:1.2.2'
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.7.0'
|
||||
|
|
@ -119,9 +117,9 @@ dependencies {
|
|||
implementation "androidx.lifecycle:lifecycle-common-java8:2.4.0"
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
|
||||
implementation "androidx.fragment:fragment-ktx:1.3.6"
|
||||
implementation "androidx.paging:paging-runtime-ktx:3.0.1"
|
||||
implementation 'com.plattysoft.leonids:LeonidsLib:1.3.2'
|
||||
implementation "androidx.fragment:fragment-ktx:1.4.0"
|
||||
implementation "androidx.paging:paging-runtime-ktx:3.1.0"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
|
||||
|
||||
|
|
@ -153,8 +151,8 @@ android {
|
|||
buildConfigField "String", "TESTING_LEVEL", "\"production\""
|
||||
resConfigs "en", "bg", "de", "en-rGB", "es", "fr", "hr-rHR", "in", "it", "iw", "ja", "ko", "lt", "nl", "pl", "pt-rBR", "pt-rPT", "ru", "tr", "zh", "zh-rTW"
|
||||
|
||||
versionCode 3093
|
||||
versionName "3.4.1.1"
|
||||
versionCode 3102
|
||||
versionName "3.4.2"
|
||||
|
||||
targetSdkVersion 31
|
||||
|
||||
|
|
|
|||
|
|
@ -1,55 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical" android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingHorizontal="@dimen/spacing_medium">
|
||||
<com.habitrpg.android.habitica.ui.views.ValidatingEditText
|
||||
android:id="@+id/email_edit_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/TextInputLayoutAppearanceTheme"
|
||||
style="@style/TextInputLayoutAppearance"
|
||||
android:hint="@string/email">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/emailTitleTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textEmailAddress"
|
||||
android:paddingBottom="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingStart="@dimen/spacing_large"
|
||||
android:paddingEnd="@dimen/spacing_large" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:inputType="textEmailAddress"
|
||||
app:hint="@string/email" />
|
||||
<com.habitrpg.android.habitica.ui.views.ValidatingEditText
|
||||
android:id="@+id/password_edit_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/TextInputLayoutAppearanceTheme"
|
||||
style="@style/TextInputLayoutAppearance"
|
||||
android:hint="@string/password"
|
||||
android:layout_marginTop="@dimen/content_section_spacing">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/passwordEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword"
|
||||
android:paddingBottom="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingStart="@dimen/spacing_large"
|
||||
android:paddingEnd="@dimen/spacing_large"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:inputType="textPassword"
|
||||
app:hint="@string/new_password"
|
||||
android:layout_marginTop="@dimen/content_section_spacing" />
|
||||
<com.habitrpg.android.habitica.ui.views.ValidatingEditText
|
||||
android:id="@+id/password_repeat_edit_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/TextInputLayoutAppearanceTheme"
|
||||
style="@style/TextInputLayoutAppearance"
|
||||
android:hint="@string/confirmpassword"
|
||||
android:layout_marginTop="@dimen/content_section_spacing">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/passwordRepeatEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword"
|
||||
android:paddingBottom="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingStart="@dimen/spacing_large"
|
||||
android:paddingEnd="@dimen/spacing_large" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
android:inputType="textPassword"
|
||||
app:hint="@string/new_password_repeat"
|
||||
android:layout_marginTop="@dimen/content_section_spacing" />
|
||||
</LinearLayout>
|
||||
|
|
@ -1,43 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical" android:layout_width="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
<com.habitrpg.android.habitica.ui.views.ValidatingEditText
|
||||
android:id="@+id/old_password_edit_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/old_password"
|
||||
android:theme="@style/TextInputLayoutAppearanceTheme"
|
||||
style="@style/TextInputLayoutAppearance">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/editText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:inputType="textPassword"
|
||||
app:hint="@string/old_password"/>
|
||||
<com.habitrpg.android.habitica.ui.views.ValidatingEditText
|
||||
android:id="@+id/new_password_edit_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/TextInputLayoutAppearanceTheme"
|
||||
style="@style/TextInputLayoutAppearance"
|
||||
android:hint="@string/new_password"
|
||||
android:layout_marginTop="@dimen/content_section_spacing">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/passwordEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:inputType="textPassword"
|
||||
app:hint="@string/new_password"
|
||||
android:layout_marginTop="@dimen/content_section_spacing" />
|
||||
<com.habitrpg.android.habitica.ui.views.ValidatingEditText
|
||||
android:id="@+id/new_password_repeat_edit_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/TextInputLayoutAppearanceTheme"
|
||||
style="@style/TextInputLayoutAppearance"
|
||||
android:hint="@string/new_password_repeat"
|
||||
android:layout_marginTop="@dimen/content_section_spacing">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/passwordRepeatEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
android:inputType="textPassword"
|
||||
app:hint="@string/new_password_repeat"
|
||||
android:layout_marginTop="@dimen/content_section_spacing" />
|
||||
</LinearLayout>
|
||||
|
|
@ -1,32 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical" android:layout_width="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/input_layout"
|
||||
<com.habitrpg.android.habitica.ui.views.ValidatingEditText
|
||||
android:id="@+id/email_edit_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/TextInputLayoutAppearanceTheme"
|
||||
style="@style/TextInputLayoutAppearance">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/editText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/spacing_large"
|
||||
android:paddingEnd="@dimen/spacing_large" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:inputType="textEmailAddress"
|
||||
app:hint="@string/email" />
|
||||
<com.habitrpg.android.habitica.ui.views.ValidatingEditText
|
||||
android:id="@+id/password_edit_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/password"
|
||||
android:theme="@style/TextInputLayoutAppearanceTheme"
|
||||
style="@style/TextInputLayoutAppearance"
|
||||
android:layout_marginTop="@dimen/content_section_spacing">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/passwordEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/spacing_large"
|
||||
android:paddingEnd="@dimen/spacing_large" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
android:inputType="textPassword"
|
||||
app:hint="@string/password"
|
||||
android:layout_marginTop="@dimen/content_section_spacing" />
|
||||
</LinearLayout>
|
||||
51
Habitica/res/layout/preference_child_summary_danger.xml
Normal file
51
Habitica/res/layout/preference_child_summary_danger.xml
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Layout for a visually child-like Preference in a PreferenceActivity. -->
|
||||
<!--suppress AndroidDomInspection -->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:baselineAligned="false"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:paddingEnd="16dip"
|
||||
android:paddingStart="16dip"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="6dip"
|
||||
android:layout_marginStart="16dip"
|
||||
android:layout_marginEnd="6dip"
|
||||
android:layout_marginTop="6dip"
|
||||
android:layout_weight="1" >
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textColor="@color/text_red"
|
||||
tools:text="Title"/>
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/summary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignStart="@android:id/title"
|
||||
android:layout_below="@android:id/title"
|
||||
android:maxLines="4"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
</RelativeLayout>
|
||||
<LinearLayout android:id="@android:id/widget_frame"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="16dp" />
|
||||
</LinearLayout>
|
||||
|
|
@ -38,6 +38,6 @@
|
|||
android:layout_below="@android:id/title"
|
||||
android:maxLines="4"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="@color/red_10" />
|
||||
android:textColor="@color/text_red" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
31
Habitica/res/layout/validating_edit_text.xml
Normal file
31
Habitica/res/layout/validating_edit_text.xml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:parentTag="LinearLayout"
|
||||
android:orientation="vertical">
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/input_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/TextInputLayoutAppearanceTheme"
|
||||
style="@style/TextInputLayoutAppearance">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/edit_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textEmailAddress" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
<TextView
|
||||
android:id="@+id/error_text"
|
||||
android:layout_gravity="center"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Caption2.Regular"
|
||||
android:layout_marginTop="@dimen/spacing_small"
|
||||
android:textColor="@color/text_red"
|
||||
android:visibility="gone"
|
||||
tools:text="Error text"
|
||||
tools:visibility="visible"/>
|
||||
</merge>
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
<color name="text_inverted">@color/gray_10</color>
|
||||
<color name="text_brand">@color/brand_400</color>
|
||||
<color name="text_brand_neon">@color/brand_500</color>
|
||||
<color name="text_red">@color/red_10</color>
|
||||
<color name="text_red">@color/red_100</color>
|
||||
<color name="text_orange">@color/orange_100</color>
|
||||
<color name="text_yellow">@color/yellow_100</color>
|
||||
<color name="text_green">@color/green_100</color>
|
||||
|
|
|
|||
|
|
@ -1147,7 +1147,7 @@
|
|||
<string name="leave_guild_confirmation">Вы уверены, что хотите покинуть команду\?</string>
|
||||
<string name="report_explanation">Жалуйтесь ** только ** на те публикации, которые нарушают [Принципы сообщества] (https://habitica.com/static/community-guidelines) и / или [Условия предоставления услуг] (https://habitica.com/static/terms ). Ложное сообщение о нарушении может привести к наказанию.</string>
|
||||
<string name="gift_gems_subtitle">Выберите ниже набор самоцветов, который хотите подарить!</string>
|
||||
<string name="available_for">Доступно для %@</string>
|
||||
<string name="available_for">Доступно для %s</string>
|
||||
<string name="reloaded_content">Обновить содержимое</string>
|
||||
<string name="rejoin_party">Вы не сможете снова присоединиться к этой команде, пока вас не пригласят.</string>
|
||||
<string name="leave_party_challenges_confirmation">Вы хотите продолжить участвовать в испытании, покинув команду\?</string>
|
||||
|
|
|
|||
|
|
@ -147,4 +147,8 @@
|
|||
<attr name="dayTextColor" format="color" />
|
||||
<attr name="nightTextColor" format="color" />
|
||||
</declare-styleable>
|
||||
<declare-styleable name="ValidatingEditText">
|
||||
<attr name="hint" format="string" />
|
||||
<attr name="android:inputType" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@
|
|||
<color name="text_inverted">@color/gray_700</color>
|
||||
<color name="text_brand">@color/brand_300</color>
|
||||
<color name="text_brand_neon">@color/brand_400</color>
|
||||
<color name="text_red">@color/red_10</color>
|
||||
<color name="text_red">@color/maroon_100</color>
|
||||
<color name="text_orange">@color/orange_10</color>
|
||||
<color name="text_yellow">@color/yellow_10</color>
|
||||
<color name="text_green">@color/green_10</color>
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@
|
|||
<dimen name="row_title_size">16sp</dimen>
|
||||
<dimen name="row_text_size">14sp</dimen>
|
||||
<dimen name="row_title_bottommargin">2dp</dimen>
|
||||
<dimen name="pet_width">84dp</dimen>
|
||||
<dimen name="pet_width">88dp</dimen>
|
||||
<dimen name="mount_width">120dp</dimen>
|
||||
<dimen name="bottom_menu_padding">20dp</dimen>
|
||||
<dimen name="pet_image_width">68dp</dimen>
|
||||
|
|
|
|||
|
|
@ -1196,4 +1196,12 @@
|
|||
<string name="connect">Connect</string>
|
||||
<string name="disconnect">Disconnect</string>
|
||||
<string name="add">Add</string>
|
||||
<string name="apitoken_summary">Copy Token. Be careful, this is a password!</string>
|
||||
<string name="added_social_auth">Added %s authentication</string>
|
||||
<string name="copied_to_clipboard">Copied %s to clipboard</string>
|
||||
<string name="removed_social_auth">Disconnected %s</string>
|
||||
<string name="password_added">Password saved</string>
|
||||
<string name="add_email_and_password">Add Email and Password</string>
|
||||
<string name="password_not_matching">Password needs to be typed correctly twice</string>
|
||||
<string name="email_invalid">Invalid Email address</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -63,23 +63,20 @@
|
|||
android:title="@string/public_profile"
|
||||
android:key="public_profile"
|
||||
android:layout="@layout/preference_category">
|
||||
<EditTextPreference
|
||||
<Preference
|
||||
android:key="display_name"
|
||||
android:layout="@layout/preference_child_summary"
|
||||
android:negativeButtonText="@string/cancel"
|
||||
android:positiveButtonText="@string/save"
|
||||
android:persistent="false"
|
||||
android:title="@string/display_name" />
|
||||
<EditTextPreference
|
||||
<Preference
|
||||
android:key="about"
|
||||
android:layout="@layout/preference_child_summary"
|
||||
android:negativeButtonText="@string/cancel"
|
||||
android:positiveButtonText="@string/save"
|
||||
android:persistent="false"
|
||||
android:title="@string/about_me" />
|
||||
<EditTextPreference
|
||||
<Preference
|
||||
android:key="photo_url"
|
||||
android:layout="@layout/preference_child_summary"
|
||||
android:negativeButtonText="@string/cancel"
|
||||
android:positiveButtonText="@string/save"
|
||||
android:persistent="false"
|
||||
android:title="@string/photo_url" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
|
|
@ -87,21 +84,23 @@
|
|||
android:key="api"
|
||||
android:layout="@layout/preference_category">
|
||||
<Preference
|
||||
android:key="user_id"
|
||||
android:key="@string/SP_userID"
|
||||
android:title="@string/SP_userID_title"
|
||||
android:selectable="true"
|
||||
android:persistent="false"
|
||||
android:shouldDisableView="false"
|
||||
android:summary="@string/SP_userID_summary"
|
||||
android:layout="@layout/preference_child_summary"/>
|
||||
|
||||
<Preference
|
||||
android:key="api_token"
|
||||
android:key="@string/SP_APIToken"
|
||||
android:title="@string/SP_APIToken_title"
|
||||
android:selectable="true"
|
||||
android:persistent="false"
|
||||
android:shouldDisableView="false"
|
||||
android:summary="@string/SP_APIToken_summary"
|
||||
android:summary="@string/apitoken_summary"
|
||||
android:layout="@layout/preference_child_summary"/>
|
||||
<Preference android:title="@string/fix_character_values"
|
||||
app:key="fixCharacterValues"
|
||||
android:layout="@layout/preference_child_summary"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/danger_zone"
|
||||
|
|
@ -109,11 +108,11 @@
|
|||
<Preference android:title="@string/reset_account"
|
||||
android:persistent="false"
|
||||
android:key="reset_account"
|
||||
android:layout="@layout/preference_child_summary" />
|
||||
android:layout="@layout/preference_child_summary_danger" />
|
||||
<Preference android:title="@string/delete_account"
|
||||
android:persistent="false"
|
||||
android:key="delete_account"
|
||||
android:layout="@layout/preference_child_summary" />
|
||||
android:layout="@layout/preference_child_summary_danger" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
|
||||
|
|
@ -122,13 +121,9 @@
|
|||
tools:title="Change Class"
|
||||
android:layout="@layout/preference_child_summary"
|
||||
app:isPreferenceVisible="false"/>
|
||||
<Preference android:title="@string/fix_character_values"
|
||||
app:key="fixCharacterValues"
|
||||
android:layout="@layout/preference_child_summary"/>
|
||||
<Preference android:title="@string/logout"
|
||||
android:key="logout"
|
||||
android:summary="@string/logout_description"
|
||||
android:layout="@layout/preference_child_summary"/>
|
||||
android:layout="@layout/preference_child_summary_danger"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import com.habitrpg.android.habitica.models.user.OwnedMount;
|
|||
import com.habitrpg.android.habitica.models.user.OwnedPet;
|
||||
import com.habitrpg.android.habitica.models.user.Purchases;
|
||||
import com.habitrpg.android.habitica.models.user.User;
|
||||
import com.habitrpg.android.habitica.models.user.auth.SocialAuthentication;
|
||||
import com.habitrpg.android.habitica.utils.AchievementListDeserializer;
|
||||
import com.habitrpg.android.habitica.utils.BooleanAsIntAdapter;
|
||||
import com.habitrpg.android.habitica.utils.ChallengeDeserializer;
|
||||
|
|
@ -51,6 +52,7 @@ import com.habitrpg.android.habitica.utils.QuestCollectDeserializer;
|
|||
import com.habitrpg.android.habitica.utils.QuestDeserializer;
|
||||
import com.habitrpg.android.habitica.utils.QuestDropItemsListSerialization;
|
||||
import com.habitrpg.android.habitica.utils.SkillDeserializer;
|
||||
import com.habitrpg.android.habitica.utils.SocialAuthenticationDeserializer;
|
||||
import com.habitrpg.android.habitica.utils.TaskListDeserializer;
|
||||
import com.habitrpg.android.habitica.utils.TaskSerializer;
|
||||
import com.habitrpg.android.habitica.utils.TaskTagDeserializer;
|
||||
|
|
@ -84,7 +86,6 @@ public class GSonFactoryCreator {
|
|||
Type ownedMountListType = new TypeToken<RealmList<OwnedMount>>() {}.getType();
|
||||
Type achievementsListType = new TypeToken<List<Achievement>>() {}.getType();
|
||||
|
||||
|
||||
Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(taskTagClassListType, new TaskTagDeserializer())
|
||||
.registerTypeAdapter(Boolean.class, new BooleanAsIntAdapter())
|
||||
|
|
@ -117,6 +118,7 @@ public class GSonFactoryCreator {
|
|||
.registerTypeAdapter(WorldState.class, new WorldStateSerialization())
|
||||
.registerTypeAdapter(FindUsernameResult.class, new FindUsernameResultDeserializer())
|
||||
.registerTypeAdapter(Notification.class, new NotificationDeserializer())
|
||||
.registerTypeAdapter(SocialAuthentication.class, new SocialAuthenticationDeserializer())
|
||||
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
|
||||
.create();
|
||||
return GsonConverterFactory.create(gson);
|
||||
|
|
|
|||
|
|
@ -3,16 +3,23 @@ package com.habitrpg.android.habitica.events
|
|||
import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.SnackbarDisplayType
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
||||
/**
|
||||
* Created by phillip on 26.06.17.
|
||||
*/
|
||||
class ShowSnackbarEvent {
|
||||
constructor(title: String, type: SnackbarDisplayType) {
|
||||
constructor(title: String?, type: SnackbarDisplayType) {
|
||||
this.title = title
|
||||
this.type = type
|
||||
}
|
||||
|
||||
constructor(title: String?, text: String?, type: SnackbarDisplayType) {
|
||||
this.title = title
|
||||
this.text = text
|
||||
this.type = type
|
||||
}
|
||||
|
||||
constructor()
|
||||
|
||||
var leftImage: Drawable? = null
|
||||
|
|
@ -23,4 +30,8 @@ class ShowSnackbarEvent {
|
|||
var rightIcon: Drawable? = null
|
||||
var rightTextColor = 0
|
||||
var rightText: String? = null
|
||||
|
||||
fun post() {
|
||||
EventBus.getDefault().post(this)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
package com.habitrpg.android.habitica.extensions
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
|
||||
fun withImmutableFlag(flags: Int): Int {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
flags + PendingIntent.FLAG_IMMUTABLE
|
||||
} else {
|
||||
flags
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import android.content.Intent
|
|||
import android.os.Build
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.habitrpg.android.habitica.data.TaskRepository
|
||||
import com.habitrpg.android.habitica.extensions.withImmutableFlag
|
||||
import com.habitrpg.android.habitica.models.tasks.RemindersItem
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.android.habitica.receivers.NotificationPublisher
|
||||
|
|
@ -93,13 +94,13 @@ class TaskAlarmManager(private var context: Context, private var taskRepository:
|
|||
|
||||
val intentId = remindersItem.id?.hashCode() ?: 0 and 0xfffffff
|
||||
// Cancel alarm if already exists
|
||||
val previousSender = PendingIntent.getBroadcast(context, intentId, intent, PendingIntent.FLAG_NO_CREATE)
|
||||
val previousSender = PendingIntent.getBroadcast(context, intentId, intent, withImmutableFlag(PendingIntent.FLAG_NO_CREATE))
|
||||
if (previousSender != null) {
|
||||
previousSender.cancel()
|
||||
am?.cancel(previousSender)
|
||||
}
|
||||
|
||||
val sender = PendingIntent.getBroadcast(context, intentId, intent, PendingIntent.FLAG_CANCEL_CURRENT)
|
||||
val sender = PendingIntent.getBroadcast(context, intentId, intent, withImmutableFlag(PendingIntent.FLAG_CANCEL_CURRENT))
|
||||
|
||||
setAlarm(context, cal.timeInMillis, sender)
|
||||
}
|
||||
|
|
@ -108,7 +109,7 @@ class TaskAlarmManager(private var context: Context, private var taskRepository:
|
|||
val intent = Intent(context, TaskReceiver::class.java)
|
||||
intent.action = remindersItem.id
|
||||
val intentId = remindersItem.id?.hashCode() ?: 0 and 0xfffffff
|
||||
val sender = PendingIntent.getBroadcast(context, intentId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val sender = PendingIntent.getBroadcast(context, intentId, intent, withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
val am = context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
|
||||
sender.cancel()
|
||||
am?.cancel(sender)
|
||||
|
|
@ -140,13 +141,13 @@ class TaskAlarmManager(private var context: Context, private var taskRepository:
|
|||
notificationIntent.putExtra(NotificationPublisher.CHECK_DAILIES, false)
|
||||
|
||||
val alarmManager = context?.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
|
||||
val previousSender = PendingIntent.getBroadcast(context, 0, notificationIntent, PendingIntent.FLAG_NO_CREATE)
|
||||
val previousSender = PendingIntent.getBroadcast(context, 0, notificationIntent, withImmutableFlag(PendingIntent.FLAG_NO_CREATE))
|
||||
if (previousSender != null) {
|
||||
previousSender.cancel()
|
||||
alarmManager?.cancel(previousSender)
|
||||
}
|
||||
|
||||
val pendingIntent = PendingIntent.getBroadcast(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val pendingIntent = PendingIntent.getBroadcast(context, 0, notificationIntent, withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
|
||||
if (context != null) {
|
||||
setAlarm(context, triggerTime, pendingIntent)
|
||||
|
|
@ -157,7 +158,7 @@ class TaskAlarmManager(private var context: Context, private var taskRepository:
|
|||
fun removeDailyReminder(context: Context?) {
|
||||
val notificationIntent = Intent(context, NotificationPublisher::class.java)
|
||||
val alarmManager = context?.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
|
||||
val displayIntent = PendingIntent.getBroadcast(context, 0, notificationIntent, 0)
|
||||
val displayIntent = PendingIntent.getBroadcast(context, 0, notificationIntent, withImmutableFlag(0))
|
||||
alarmManager?.cancel(displayIntent)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import androidx.core.app.Person
|
|||
import androidx.core.app.RemoteInput
|
||||
import androidx.core.os.bundleOf
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.extensions.withImmutableFlag
|
||||
import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver
|
||||
import com.habitrpg.android.habitica.ui.helpers.EmojiParser
|
||||
import java.text.SimpleDateFormat
|
||||
|
|
@ -75,7 +76,7 @@ class GroupActivityNotification(context: Context, identifier: String?) : Habitic
|
|||
PendingIntent.getBroadcast(
|
||||
context, groupID.hashCode(),
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
|
||||
val action: NotificationCompat.Action =
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.app.PendingIntent
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.extensions.withImmutableFlag
|
||||
import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver
|
||||
|
||||
/**
|
||||
|
|
@ -29,7 +30,7 @@ class GuildInviteLocalNotification(context: Context, identifier: String?) : Habi
|
|||
context,
|
||||
groupID.hashCode(),
|
||||
acceptInviteIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
notificationBuilder.addAction(0, "Accept", pendingIntentAccept)
|
||||
|
||||
|
|
@ -41,7 +42,7 @@ class GuildInviteLocalNotification(context: Context, identifier: String?) : Habi
|
|||
context,
|
||||
groupID.hashCode() + 1,
|
||||
rejectInviteIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
notificationBuilder.addAction(0, "Reject", pendingIntentReject)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import androidx.annotation.CallSuper
|
|||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.extensions.withImmutableFlag
|
||||
import com.habitrpg.android.habitica.ui.activities.MainActivity
|
||||
import java.util.*
|
||||
|
||||
|
|
@ -63,7 +64,7 @@ abstract class HabiticaLocalNotification(protected var context: Context, protect
|
|||
context,
|
||||
3000,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
notificationBuilder.setContentIntent(pendingIntent)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.app.PendingIntent
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.extensions.withImmutableFlag
|
||||
import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver
|
||||
|
||||
/**
|
||||
|
|
@ -24,7 +25,7 @@ class PartyInviteLocalNotification(context: Context, identifier: String?) : Habi
|
|||
context,
|
||||
groupID.hashCode(),
|
||||
acceptInviteIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
notificationBuilder.addAction(0, context.getString(R.string.accept), pendingIntentAccept)
|
||||
|
||||
|
|
@ -36,7 +37,7 @@ class PartyInviteLocalNotification(context: Context, identifier: String?) : Habi
|
|||
context,
|
||||
groupID.hashCode() + 1,
|
||||
rejectInviteIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
notificationBuilder.addAction(0, context.getString(R.string.reject), pendingIntentReject)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.habitrpg.android.habitica.helpers.notifications
|
|||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver
|
||||
|
||||
|
|
@ -22,11 +23,16 @@ class QuestInviteLocalNotification(context: Context, identifier: String?) : Habi
|
|||
val acceptInviteIntent = Intent(context, LocalNotificationActionReceiver::class.java)
|
||||
acceptInviteIntent.action = res.getString(R.string.accept_quest_invite)
|
||||
acceptInviteIntent.putExtra("NOTIFICATION_ID", notificationId)
|
||||
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_IMMUTABLE
|
||||
} else {
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
}
|
||||
val pendingIntentAccept = PendingIntent.getBroadcast(
|
||||
context,
|
||||
3001,
|
||||
acceptInviteIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
flags
|
||||
)
|
||||
notificationBuilder.addAction(0, "Accept", pendingIntentAccept)
|
||||
|
||||
|
|
@ -37,7 +43,7 @@ class QuestInviteLocalNotification(context: Context, identifier: String?) : Habi
|
|||
context,
|
||||
2001,
|
||||
rejectInviteIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
flags
|
||||
)
|
||||
notificationBuilder.addAction(0, "Reject", pendingIntentReject)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import androidx.core.app.NotificationCompat
|
|||
import androidx.core.app.RemoteInput
|
||||
import androidx.core.os.bundleOf
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.extensions.withImmutableFlag
|
||||
import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver
|
||||
import com.habitrpg.android.habitica.ui.helpers.EmojiParser
|
||||
|
||||
|
|
@ -74,7 +75,7 @@ class ReceivedPrivateMessageLocalNotification(context: Context, identifier: Stri
|
|||
PendingIntent.getBroadcast(
|
||||
context, senderID.hashCode(),
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
|
||||
val action: NotificationCompat.Action =
|
||||
|
|
|
|||
|
|
@ -30,11 +30,11 @@ open class Authentication : RealmObject(), BaseObject {
|
|||
var facebookAuthentication: SocialAuthentication? = null
|
||||
|
||||
val hasGoogleAuth: Boolean
|
||||
get() = googleAuthentication?.emails?.isEmpty() == false
|
||||
get() = googleAuthentication?.emails?.isEmpty() != true
|
||||
val hasAppleAuth: Boolean
|
||||
get() = appleAuthentication?.emails?.isEmpty() == false
|
||||
get() = appleAuthentication?.emails?.isEmpty() != true
|
||||
val hasFacebookAuth: Boolean
|
||||
get() = facebookAuthentication?.emails?.isEmpty() == false
|
||||
get() = facebookAuthentication?.emails?.isEmpty() != true
|
||||
|
||||
var timestamps: AuthenticationTimestamps? = null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import com.habitrpg.android.habitica.HabiticaBaseApplication
|
|||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.data.TaskRepository
|
||||
import com.habitrpg.android.habitica.data.UserRepository
|
||||
import com.habitrpg.android.habitica.extensions.withImmutableFlag
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.helpers.TaskAlarmManager
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
|
|
@ -126,7 +127,7 @@ class NotificationPublisher : BroadcastReceiver() {
|
|||
|
||||
val intent = PendingIntent.getActivity(
|
||||
thisContext, 0,
|
||||
notificationIntent, 0
|
||||
notificationIntent, withImmutableFlag(0)
|
||||
)
|
||||
builder.setContentIntent(intent)
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.core.app.NotificationManagerCompat
|
|||
import com.habitrpg.android.habitica.HabiticaBaseApplication
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.data.TaskRepository
|
||||
import com.habitrpg.android.habitica.extensions.withImmutableFlag
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.helpers.TaskAlarmManager
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
|
|
@ -61,7 +62,7 @@ class TaskReceiver : BroadcastReceiver() {
|
|||
HLogger.log(LogLevel.INFO, this::javaClass.name, "Create Notification")
|
||||
|
||||
intent.putExtra("notificationIdentifier", "task_reminder")
|
||||
val pendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), intent, 0)
|
||||
val pendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), intent, withImmutableFlag(0))
|
||||
val soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
|
||||
|
||||
var notificationBuilder = NotificationCompat.Builder(context, "default")
|
||||
|
|
@ -88,7 +89,7 @@ class TaskReceiver : BroadcastReceiver() {
|
|||
context,
|
||||
task.id.hashCode(),
|
||||
completeIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
notificationBuilder.addAction(0, context.getString(R.string.complete), pendingIntentComplete)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import com.habitrpg.android.habitica.extensions.addOkButton
|
|||
import com.habitrpg.android.habitica.extensions.updateStatusBarColor
|
||||
import com.habitrpg.android.habitica.helpers.*
|
||||
import com.habitrpg.android.habitica.models.auth.UserAuthResponse
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.ui.helpers.dismissKeyboard
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.AuthenticationViewModel
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
|
||||
|
|
@ -229,9 +230,17 @@ class LoginActivity : BaseActivity() {
|
|||
}
|
||||
|
||||
private fun handleAuthResponse(response: UserAuthResponse) {
|
||||
viewModel.handleAuthResponse(response)
|
||||
compositeSubscription.add(userRepository.retrieveUser(true)
|
||||
.subscribe({
|
||||
handleAuthResponse(it, response.newUser)
|
||||
}, RxErrorHandler.handleEmptyError())
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleAuthResponse(user: User, isNew: Boolean) {
|
||||
hideProgress()
|
||||
dismissKeyboard()
|
||||
viewModel.handleAuthResponse(response)
|
||||
|
||||
if (isRegistering) {
|
||||
FirebaseAnalytics.getInstance(this).logEvent("user_registered", null)
|
||||
|
|
@ -240,7 +249,7 @@ class LoginActivity : BaseActivity() {
|
|||
userRepository.retrieveUser(withTasks = true, forced = true)
|
||||
.subscribe(
|
||||
{
|
||||
if (response.newUser) {
|
||||
if (isNew) {
|
||||
this.startSetupActivity()
|
||||
} else {
|
||||
this.startMainActivity()
|
||||
|
|
@ -288,8 +297,8 @@ class LoginActivity : BaseActivity() {
|
|||
private val pickAccountResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
viewModel.googleEmail = it?.data?.getStringExtra(AccountManager.KEY_ACCOUNT_NAME)
|
||||
viewModel.handleGoogleLoginResult(this, recoverFromPlayServicesErrorResult) {
|
||||
handleAuthResponse(it)
|
||||
viewModel.handleGoogleLoginResult(this, recoverFromPlayServicesErrorResult) { user, isNew ->
|
||||
handleAuthResponse(user, isNew)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -297,8 +306,8 @@ class LoginActivity : BaseActivity() {
|
|||
private val recoverFromPlayServicesErrorResult = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode != Activity.RESULT_CANCELED) {
|
||||
viewModel.handleGoogleLoginResult(this, null) {
|
||||
handleAuthResponse(it)
|
||||
viewModel.handleGoogleLoginResult(this, null) { user, isNew ->
|
||||
handleAuthResponse(user, isNew)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,28 +8,37 @@ import android.content.SharedPreferences
|
|||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.Toast
|
||||
import android.widget.TextView
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.util.PatternsCompat
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.preference.EditTextPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceCategory
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.habitrpg.android.habitica.HabiticaBaseApplication
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.api.HostConfig
|
||||
import com.habitrpg.android.habitica.data.ApiClient
|
||||
import com.habitrpg.android.habitica.events.ShowSnackbarEvent
|
||||
import com.habitrpg.android.habitica.extensions.addCancelButton
|
||||
import com.habitrpg.android.habitica.extensions.dpToPx
|
||||
import com.habitrpg.android.habitica.extensions.layoutInflater
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.ui.activities.FixCharacterValuesActivity
|
||||
import com.habitrpg.android.habitica.ui.helpers.KeyboardUtil
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.AuthenticationViewModel
|
||||
import com.habitrpg.android.habitica.ui.views.ExtraLabelPreference
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
|
||||
import com.habitrpg.android.habitica.ui.views.ValidatingEditText
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaProgressDialog
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import javax.inject.Inject
|
||||
|
||||
class AccountPreferenceFragment: BasePreferencesFragment(),
|
||||
|
|
@ -52,8 +61,6 @@ class AccountPreferenceFragment: BasePreferencesFragment(),
|
|||
super.onCreate(savedInstanceState)
|
||||
viewModel = AuthenticationViewModel()
|
||||
findPreference<Preference>("confirm_username")?.isVisible = user?.flags?.verifiedUsername == false
|
||||
|
||||
viewModel.setupFacebookLogin { viewModel.handleAuthResponse(it) }
|
||||
}
|
||||
|
||||
override fun setupPreferences() {
|
||||
|
|
@ -74,12 +81,12 @@ class AccountPreferenceFragment: BasePreferencesFragment(),
|
|||
private fun updateUserFields() {
|
||||
val user = user ?: return
|
||||
configurePreference(findPreference("username"), user.authentication?.localAuthentication?.username)
|
||||
configurePreference(findPreference("email"), user.authentication?.localAuthentication?.email)
|
||||
configurePreference(findPreference("email"), user.authentication?.localAuthentication?.email ?: getString(R.string.not_set))
|
||||
findPreference<Preference>("confirm_username")?.isVisible = user.flags?.verifiedUsername != true
|
||||
|
||||
val passwordPref = findPreference<ExtraLabelPreference>("password")
|
||||
if (user.authentication?.hasPassword == true) {
|
||||
passwordPref?.summary = "··········"
|
||||
passwordPref?.summary = "✴✴✴✴✴✴✴✴✴✴✴"
|
||||
passwordPref?.extraText = getString(R.string.change_password)
|
||||
} else {
|
||||
passwordPref?.summary = getString(R.string.not_set)
|
||||
|
|
@ -87,16 +94,17 @@ class AccountPreferenceFragment: BasePreferencesFragment(),
|
|||
}
|
||||
val googlePref = findPreference<ExtraLabelPreference>("google_auth")
|
||||
if (user.authentication?.hasGoogleAuth == true) {
|
||||
googlePref?.summary = user.authentication?.googleAuthentication?.emails?.first()
|
||||
googlePref?.summary = user.authentication?.googleAuthentication?.emails?.firstOrNull()
|
||||
googlePref?.extraText = getString(R.string.disconnect)
|
||||
googlePref?.extraTextColor = context?.let { ContextCompat.getColor(it, R.color.text_red) }
|
||||
} else {
|
||||
googlePref?.summary = getString(R.string.not_connected)
|
||||
googlePref?.extraText = getString(R.string.connect)
|
||||
googlePref?.extraTextColor = context?.let { ContextCompat.getColor(it, R.color.text_ternary) }
|
||||
}
|
||||
val applePref = findPreference<ExtraLabelPreference>("apple_auth")
|
||||
if (user.authentication?.hasGoogleAuth == true) {
|
||||
applePref?.summary = user.authentication?.appleAuthentication?.emails?.first()
|
||||
if (user.authentication?.hasAppleAuth == true) {
|
||||
applePref?.summary = user.authentication?.appleAuthentication?.emails?.firstOrNull()
|
||||
applePref?.extraText = getString(R.string.disconnect)
|
||||
applePref?.extraTextColor = context?.let { ContextCompat.getColor(it, R.color.text_red) }
|
||||
} else {
|
||||
|
|
@ -104,7 +112,7 @@ class AccountPreferenceFragment: BasePreferencesFragment(),
|
|||
}
|
||||
val facebookPref = findPreference<ExtraLabelPreference>("facebook_auth")
|
||||
if (user.authentication?.hasFacebookAuth == true) {
|
||||
facebookPref?.summary = user.authentication?.facebookAuthentication?.emails?.first()
|
||||
facebookPref?.summary = user.authentication?.facebookAuthentication?.emails?.firstOrNull()
|
||||
facebookPref?.extraText = getString(R.string.disconnect)
|
||||
facebookPref?.extraTextColor = context?.let { ContextCompat.getColor(it, R.color.text_red) }
|
||||
} else {
|
||||
|
|
@ -127,42 +135,38 @@ class AccountPreferenceFragment: BasePreferencesFragment(),
|
|||
preference?.summary = value
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String) {
|
||||
val profileCategory = findPreference("profile") as? PreferenceCategory
|
||||
configurePreference(profileCategory?.findPreference(key), sharedPreferences?.getString(key, ""))
|
||||
if (sharedPreferences != null) {
|
||||
val newValue = sharedPreferences.getString(key, "") ?: return
|
||||
when (key) {
|
||||
"display_name" -> updateUser("profile.name", newValue, user?.profile?.name)
|
||||
"photo_url" -> updateUser("profile.imageUrl", newValue, user?.profile?.imageUrl)
|
||||
"about" -> updateUser("profile.blurb", newValue, user?.profile?.blurb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
|
||||
when(preference?.key) {
|
||||
"username" -> showLoginNameDialog()
|
||||
"confirm_username" -> showConfirmUsernameDialog()
|
||||
"email" -> showEmailDialog()
|
||||
"email" -> {
|
||||
if (user?.authentication?.hasPassword == true) {
|
||||
showEmailDialog()
|
||||
} else {
|
||||
showAddPasswordDialog(true)
|
||||
}
|
||||
}
|
||||
"password" -> {
|
||||
if (user?.authentication?.hasPassword == true) {
|
||||
showChangePasswordDialog()
|
||||
} else {
|
||||
showAddPasswordDialog()
|
||||
showAddPasswordDialog(true)
|
||||
}
|
||||
}
|
||||
"UserID" -> {
|
||||
copyValue(getString(R.string.SP_userID), user?.id)
|
||||
return true
|
||||
}
|
||||
"ApiToken" -> {
|
||||
"APIToken" -> {
|
||||
copyValue(getString(R.string.SP_APIToken_title), hostConfig.apiKey)
|
||||
return true
|
||||
}
|
||||
"display_name" -> updateUser("profile.name", user?.profile?.name, getString(R.string.display_name))
|
||||
"photo_url" -> updateUser("profile.imageUrl", user?.profile?.imageUrl, getString(R.string.photo_url))
|
||||
"about" -> updateUser("profile.blurb", user?.profile?.blurb, getString(R.string.about))
|
||||
"google_auth" -> {
|
||||
if (user?.authentication?.hasGoogleAuth == true) {
|
||||
apiClient.disconnectSocial("google").subscribe({}, RxErrorHandler.handleEmptyError())
|
||||
disconnect("google", "Google")
|
||||
} else {
|
||||
activity?.let {
|
||||
viewModel.handleGoogleLogin(it, pickAccountResult)
|
||||
|
|
@ -171,7 +175,7 @@ class AccountPreferenceFragment: BasePreferencesFragment(),
|
|||
}
|
||||
"apple_auth" -> {
|
||||
if (user?.authentication?.hasAppleAuth == true) {
|
||||
apiClient.disconnectSocial("apple").subscribe({}, RxErrorHandler.handleEmptyError())
|
||||
disconnect("apple", "Apple")
|
||||
} else {
|
||||
viewModel.connectApple(parentFragmentManager) {
|
||||
viewModel.handleAuthResponse(it)
|
||||
|
|
@ -180,17 +184,33 @@ class AccountPreferenceFragment: BasePreferencesFragment(),
|
|||
}
|
||||
"facebook_auth" -> {
|
||||
if (user?.authentication?.hasFacebookAuth == true) {
|
||||
apiClient.disconnectSocial("facebook").subscribe({}, RxErrorHandler.handleEmptyError())
|
||||
} else {
|
||||
viewModel.handleFacebookLogin(this)
|
||||
disconnect("facebook", "Facebook")
|
||||
}
|
||||
}
|
||||
"reset_account" -> showAccountResetConfirmation()
|
||||
"delete_account" -> showAccountDeleteConfirmation()
|
||||
"fixCharacterValues" -> {
|
||||
val intent = Intent(activity, FixCharacterValuesActivity::class.java)
|
||||
activity?.startActivity(intent)
|
||||
}
|
||||
}
|
||||
return super.onPreferenceTreeClick(preference)
|
||||
}
|
||||
|
||||
private fun disconnect(network: String, networkName: String) {
|
||||
context?.let { context ->
|
||||
val dialog = HabiticaAlertDialog(context)
|
||||
dialog.setTitle(R.string.are_you_sure)
|
||||
dialog.addButton(R.string.disconnect, true) { _, _ ->
|
||||
apiClient.disconnectSocial(network)
|
||||
.flatMap { userRepository.retrieveUser(true, true) }
|
||||
.subscribe({ displayDisconnectSuccess(networkName) }, RxErrorHandler.handleEmptyError())
|
||||
}
|
||||
dialog.addCancelButton()
|
||||
dialog.show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
viewModel.onActivityResult(requestCode, resultCode, data) {
|
||||
|
|
@ -202,47 +222,71 @@ class AccountPreferenceFragment: BasePreferencesFragment(),
|
|||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
viewModel.googleEmail = it?.data?.getStringExtra(AccountManager.KEY_ACCOUNT_NAME)
|
||||
activity?.let { it1 ->
|
||||
viewModel.handleGoogleLoginResult(it1, recoverFromPlayServicesErrorResult) {
|
||||
viewModel.handleAuthResponse(it)
|
||||
viewModel.handleGoogleLoginResult(it1, recoverFromPlayServicesErrorResult) { _, _ ->
|
||||
displayAuthenticationSuccess(getString(R.string.google))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun displayAuthenticationSuccess(network: String) {
|
||||
ShowSnackbarEvent(null, context?.getString(R.string.added_social_auth, network), HabiticaSnackbar.SnackbarDisplayType.SUCCESS).post()
|
||||
}
|
||||
|
||||
private fun displayDisconnectSuccess(network: String) {
|
||||
val event = ShowSnackbarEvent()
|
||||
event.text = context?.getString(R.string.removed_social_auth, network)
|
||||
event.type = HabiticaSnackbar.SnackbarDisplayType.SUCCESS
|
||||
EventBus.getDefault().post(event)
|
||||
}
|
||||
|
||||
private val recoverFromPlayServicesErrorResult = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode != Activity.RESULT_CANCELED) {
|
||||
activity?.let { it1 ->
|
||||
viewModel.handleGoogleLoginResult(it1, null) {
|
||||
viewModel.handleAuthResponse(it)
|
||||
viewModel.handleGoogleLoginResult(it1, null) { _, _ ->
|
||||
displayAuthenticationSuccess(getString(R.string.google))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateUser(path: String, newValue: String, oldValue: String?) {
|
||||
if (newValue != oldValue) {
|
||||
compositeSubscription.add(userRepository.updateUser(path, newValue).subscribe({}, RxErrorHandler.handleEmptyError()))
|
||||
private fun updateUser(path: String, value: String?, title: String) {
|
||||
showSingleEntryDialog(value, title) {
|
||||
if (value != it) {
|
||||
userRepository.updateUser(path, it ?: "")
|
||||
.subscribe({}, RxErrorHandler.handleEmptyError())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showChangePasswordDialog() {
|
||||
val inflater = context?.layoutInflater
|
||||
val view = inflater?.inflate(R.layout.dialog_edittext_change_pw, null)
|
||||
val oldPasswordEditText = view?.findViewById<EditText>(R.id.editText)
|
||||
val passwordEditText = view?.findViewById<EditText>(R.id.passwordEditText)
|
||||
val passwordRepeatEditText = view?.findViewById<EditText>(R.id.passwordRepeatEditText)
|
||||
val oldPasswordEditText = view?.findViewById<ValidatingEditText>(R.id.old_password_edit_text)
|
||||
val passwordEditText = view?.findViewById<ValidatingEditText>(R.id.new_password_edit_text)
|
||||
passwordEditText?.validator = { (it?.length ?: 0) >= 8 }
|
||||
passwordEditText?.errorText = getString(R.string.password_too_short, 8)
|
||||
val passwordRepeatEditText = view?.findViewById<ValidatingEditText>(R.id.new_password_repeat_edit_text)
|
||||
passwordRepeatEditText?.validator = { it == passwordEditText?.text }
|
||||
passwordRepeatEditText?.errorText = getString(R.string.password_not_matching)
|
||||
context?.let { context ->
|
||||
val dialog = HabiticaAlertDialog(context)
|
||||
dialog.setTitle(R.string.change_password)
|
||||
dialog.addButton(R.string.change, true) { _, _ ->
|
||||
userRepository.updatePassword(oldPasswordEditText?.text.toString(), passwordEditText?.text.toString(), passwordRepeatEditText?.text.toString())
|
||||
dialog.addButton(R.string.change, true, false, false) { dialog, _ ->
|
||||
KeyboardUtil.dismissKeyboard(activity)
|
||||
if (passwordEditText?.isValid != true || passwordRepeatEditText?.isValid != true) return@addButton
|
||||
userRepository.updatePassword(oldPasswordEditText?.text ?: "",
|
||||
passwordEditText.text ?: "",
|
||||
passwordRepeatEditText.text ?: "")
|
||||
.flatMap { userRepository.retrieveUser(true, true) }
|
||||
.subscribe(
|
||||
{
|
||||
Toast.makeText(activity, R.string.password_changed, Toast.LENGTH_SHORT).show()
|
||||
ShowSnackbarEvent(null, context.getString(R.string.password_changed), HabiticaSnackbar.SnackbarDisplayType.SUCCESS).post()
|
||||
},
|
||||
RxErrorHandler.handleEmptyError()
|
||||
)
|
||||
dialog.dismiss()
|
||||
}
|
||||
dialog.addCancelButton()
|
||||
dialog.setAdditionalContentView(view)
|
||||
|
|
@ -251,25 +295,39 @@ class AccountPreferenceFragment: BasePreferencesFragment(),
|
|||
}
|
||||
}
|
||||
|
||||
private fun showAddPasswordDialog() {
|
||||
private fun showAddPasswordDialog(showEmail: Boolean) {
|
||||
val inflater = context?.layoutInflater
|
||||
val view = inflater?.inflate(R.layout.dialog_edittext_change_pw, null)
|
||||
val oldPasswordEditText = view?.findViewById<EditText>(R.id.editText)
|
||||
oldPasswordEditText?.visibility = View.GONE
|
||||
val passwordEditText = view?.findViewById<EditText>(R.id.passwordEditText)
|
||||
val passwordRepeatEditText = view?.findViewById<EditText>(R.id.passwordRepeatEditText)
|
||||
val view = inflater?.inflate(R.layout.dialog_edittext_add_local_auth, null)
|
||||
val emailEditText = view?.findViewById<ValidatingEditText>(R.id.email_edit_text)
|
||||
emailEditText?.visibility = if (showEmail) View.VISIBLE else View.GONE
|
||||
emailEditText?.validator = { PatternsCompat.EMAIL_ADDRESS.matcher(it ?: "").matches() }
|
||||
emailEditText?.errorText = getString(R.string.email_invalid)
|
||||
val passwordEditText = view?.findViewById<ValidatingEditText>(R.id.password_edit_text)
|
||||
passwordEditText?.validator = { (it?.length ?: 0) >= 8 }
|
||||
passwordEditText?.errorText = getString(R.string.password_too_short, 8)
|
||||
val passwordRepeatEditText = view?.findViewById<ValidatingEditText>(R.id.password_repeat_edit_text)
|
||||
passwordRepeatEditText?.validator = { it == passwordEditText?.text }
|
||||
passwordRepeatEditText?.errorText = getString(R.string.password_not_matching)
|
||||
context?.let { context ->
|
||||
val dialog = HabiticaAlertDialog(context)
|
||||
dialog.setTitle(R.string.add_password)
|
||||
dialog.addButton(R.string.add, true) { _, _ ->
|
||||
val email = user?.authentication?.findFirstSocialEmail()
|
||||
apiClient.registerUser(user?.username ?: "", email ?: "", passwordEditText?.text.toString(), passwordRepeatEditText?.text.toString())
|
||||
if (showEmail) {
|
||||
dialog.setTitle(R.string.add_email_and_password)
|
||||
} else {
|
||||
dialog.setTitle(R.string.add_password)
|
||||
}
|
||||
dialog.addButton(R.string.add, true, false, false) { dialog, _ ->
|
||||
KeyboardUtil.dismissKeyboard(activity)
|
||||
if (emailEditText?.isValid != true || passwordEditText?.isValid != true || passwordRepeatEditText?.isValid != true) return@addButton
|
||||
val email = if (showEmail) emailEditText.text else user?.authentication?.findFirstSocialEmail()
|
||||
apiClient.registerUser(user?.username ?: "", email ?: "", passwordEditText.text ?: "", passwordRepeatEditText?.text ?: "")
|
||||
.flatMap { userRepository.retrieveUser(true, true) }
|
||||
.subscribe(
|
||||
{
|
||||
Toast.makeText(activity, R.string.password_changed, Toast.LENGTH_SHORT).show()
|
||||
ShowSnackbarEvent(null, context.getString(R.string.password_added), HabiticaSnackbar.SnackbarDisplayType.SUCCESS).post()
|
||||
},
|
||||
RxErrorHandler.handleEmptyError()
|
||||
)
|
||||
dialog.dismiss()
|
||||
}
|
||||
dialog.addCancelButton()
|
||||
dialog.setAdditionalContentView(view)
|
||||
|
|
@ -281,21 +339,27 @@ class AccountPreferenceFragment: BasePreferencesFragment(),
|
|||
private fun showEmailDialog() {
|
||||
val inflater = context?.layoutInflater
|
||||
val view = inflater?.inflate(R.layout.dialog_edittext_confirm_pw, null)
|
||||
val emailEditText = view?.findViewById<EditText>(R.id.editText)
|
||||
emailEditText?.setText(user?.authentication?.localAuthentication?.email)
|
||||
val emailEditText = view?.findViewById<ValidatingEditText>(R.id.email_edit_text)
|
||||
emailEditText?.text = user?.authentication?.localAuthentication?.email
|
||||
emailEditText?.validator = { PatternsCompat.EMAIL_ADDRESS.matcher(it ?: "").matches() }
|
||||
emailEditText?.errorText = getString(R.string.email_invalid)
|
||||
view?.findViewById<TextInputLayout>(R.id.input_layout)?.hint = context?.getString(R.string.email)
|
||||
val passwordEditText = view?.findViewById<EditText>(R.id.passwordEditText)
|
||||
val passwordEditText = view?.findViewById<ValidatingEditText>(R.id.password_edit_text)
|
||||
context?.let { context ->
|
||||
val dialog = HabiticaAlertDialog(context)
|
||||
dialog.setTitle(R.string.change_email)
|
||||
dialog.addButton(R.string.change, true) { _, _ ->
|
||||
userRepository.updateEmail(emailEditText?.text.toString(), passwordEditText?.text.toString())
|
||||
dialog.addButton(R.string.change, true, false, false) { dialog, _ ->
|
||||
KeyboardUtil.dismissKeyboard(activity)
|
||||
if (passwordEditText?.isValid != true || emailEditText?.isValid != true) return@addButton
|
||||
userRepository.updateEmail(emailEditText.text.toString(), passwordEditText.text.toString())
|
||||
.flatMap { userRepository.retrieveUser(true, true) }
|
||||
.subscribe(
|
||||
{
|
||||
configurePreference(findPreference("email"), emailEditText?.text.toString())
|
||||
configurePreference(findPreference("email"), emailEditText.text.toString())
|
||||
},
|
||||
RxErrorHandler.handleEmptyError()
|
||||
)
|
||||
dialog.dismiss()
|
||||
}
|
||||
dialog.addCancelButton()
|
||||
dialog.setAdditionalContentView(view)
|
||||
|
|
@ -305,22 +369,23 @@ class AccountPreferenceFragment: BasePreferencesFragment(),
|
|||
}
|
||||
|
||||
private fun showLoginNameDialog() {
|
||||
showSingleEntryDialog(user?.username, getString(R.string.username)) {
|
||||
userRepository.updateLoginName(it ?: "")
|
||||
.subscribe({}, RxErrorHandler.handleEmptyError())
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSingleEntryDialog(value: String?, title: String, onChange: (String?) -> Unit) {
|
||||
val inflater = context?.layoutInflater
|
||||
val view = inflater?.inflate(R.layout.dialog_edittext, null)
|
||||
val loginNameEditText = view?.findViewById<EditText>(R.id.editText)
|
||||
loginNameEditText?.setText(user?.authentication?.localAuthentication?.username)
|
||||
view?.findViewById<TextInputLayout>(R.id.input_layout)?.hint = context?.getString(R.string.username)
|
||||
val editText = view?.findViewById<EditText>(R.id.editText)
|
||||
editText?.setText(value)
|
||||
view?.findViewById<TextInputLayout>(R.id.input_layout)?.hint = title
|
||||
context?.let { context ->
|
||||
val dialog = HabiticaAlertDialog(context)
|
||||
dialog.setTitle(R.string.change_username)
|
||||
dialog.setTitle(title)
|
||||
dialog.addButton(R.string.save, true) { _, _ ->
|
||||
userRepository.updateLoginName(loginNameEditText?.text.toString())
|
||||
.subscribe(
|
||||
{
|
||||
configurePreference(findPreference("username"), loginNameEditText?.text.toString())
|
||||
},
|
||||
RxErrorHandler.handleEmptyError()
|
||||
)
|
||||
onChange(editText?.text?.toString())
|
||||
}
|
||||
dialog.addCancelButton()
|
||||
dialog.setAdditionalContentView(view)
|
||||
|
|
@ -408,6 +473,12 @@ class AccountPreferenceFragment: BasePreferencesFragment(),
|
|||
|
||||
private fun copyValue(name: String, value: CharSequence?) {
|
||||
ClipData.newPlainText(name, value)
|
||||
Toast.makeText(activity, "Copied $name to clipboard.", Toast.LENGTH_SHORT).show()
|
||||
val event = ShowSnackbarEvent()
|
||||
event.text = context?.getString(R.string.copied_to_clipboard, name)
|
||||
event.type = HabiticaSnackbar.SnackbarDisplayType.SUCCESS
|
||||
EventBus.getDefault().post(event)
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(p0: SharedPreferences?, p1: String?) {
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ import com.habitrpg.android.habitica.R
|
|||
import com.habitrpg.android.habitica.data.ApiClient
|
||||
import com.habitrpg.android.habitica.data.ContentRepository
|
||||
import com.habitrpg.android.habitica.events.ShowSnackbarEvent
|
||||
import com.habitrpg.android.habitica.extensions.addCancelButton
|
||||
import com.habitrpg.android.habitica.helpers.*
|
||||
import com.habitrpg.android.habitica.helpers.notifications.PushNotificationManager
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
|
|
@ -26,6 +27,7 @@ import com.habitrpg.android.habitica.ui.activities.FixCharacterValuesActivity
|
|||
import com.habitrpg.android.habitica.ui.activities.MainActivity
|
||||
import com.habitrpg.android.habitica.ui.activities.PrefsActivity
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
|
@ -109,8 +111,7 @@ class PreferencesFragment : BasePreferencesFragment(), SharedPreferences.OnShare
|
|||
override fun onPreferenceTreeClick(preference: Preference): Boolean {
|
||||
when (preference.key) {
|
||||
"logout" -> {
|
||||
context?.let { HabiticaBaseApplication.logout(it) }
|
||||
activity?.finish()
|
||||
logout()
|
||||
}
|
||||
"choose_class" -> {
|
||||
val bundle = Bundle()
|
||||
|
|
@ -148,19 +149,28 @@ class PreferencesFragment : BasePreferencesFragment(), SharedPreferences.OnShare
|
|||
RxErrorHandler.handleEmptyError()
|
||||
)
|
||||
}
|
||||
"fixCharacterValues" -> {
|
||||
val intent = Intent(activity, FixCharacterValuesActivity::class.java)
|
||||
activity?.startActivity(intent)
|
||||
}
|
||||
}
|
||||
return super.onPreferenceTreeClick(preference)
|
||||
}
|
||||
|
||||
private fun logout() {
|
||||
context?.let { context ->
|
||||
val dialog = HabiticaAlertDialog(context)
|
||||
dialog.setTitle(R.string.are_you_sure)
|
||||
dialog.addButton(R.string.logout, true) { _, _ ->
|
||||
HabiticaBaseApplication.logout(context)
|
||||
activity?.finish()
|
||||
}
|
||||
dialog.addCancelButton()
|
||||
dialog.show()
|
||||
}
|
||||
}
|
||||
|
||||
private val classSelectionResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
userRepository.retrieveUser(true, forced = true)
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
|
||||
when (key) {
|
||||
"use_reminder" -> {
|
||||
val useReminder = sharedPreferences.getBoolean(key, false)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import android.os.Build
|
|||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.core.content.edit
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import com.facebook.AccessToken
|
||||
import com.facebook.CallbackManager
|
||||
|
|
@ -32,12 +31,14 @@ import com.habitrpg.android.habitica.HabiticaBaseApplication
|
|||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.api.HostConfig
|
||||
import com.habitrpg.android.habitica.data.ApiClient
|
||||
import com.habitrpg.android.habitica.data.UserRepository
|
||||
import com.habitrpg.android.habitica.extensions.addCloseButton
|
||||
import com.habitrpg.android.habitica.helpers.KeyHelper
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.helpers.SignInWithAppleResult
|
||||
import com.habitrpg.android.habitica.helpers.SignInWithAppleService
|
||||
import com.habitrpg.android.habitica.models.auth.UserAuthResponse
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.proxy.AnalyticsManager
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
|
||||
import com.willowtreeapps.signinwithapplebutton.SignInWithAppleConfiguration
|
||||
|
|
@ -52,6 +53,8 @@ class AuthenticationViewModel() {
|
|||
@Inject
|
||||
internal lateinit var apiClient: ApiClient
|
||||
@Inject
|
||||
internal lateinit var userRepository: UserRepository
|
||||
@Inject
|
||||
internal lateinit var sharedPrefs: SharedPreferences
|
||||
@Inject
|
||||
internal lateinit var hostConfig: HostConfig
|
||||
|
|
@ -168,10 +171,11 @@ class AuthenticationViewModel() {
|
|||
fun handleGoogleLoginResult(
|
||||
activity: Activity,
|
||||
recoverFromPlayServicesErrorResult: ActivityResultLauncher<Intent>?,
|
||||
onSuccess: (UserAuthResponse) -> Unit
|
||||
onSuccess: (User, Boolean) -> Unit
|
||||
) {
|
||||
val scopesString = Scopes.PROFILE + " " + Scopes.EMAIL
|
||||
val scopes = "oauth2:$scopesString"
|
||||
var newUser = false
|
||||
compositeSubscription.add(
|
||||
Flowable.defer {
|
||||
try {
|
||||
|
|
@ -187,9 +191,14 @@ class AuthenticationViewModel() {
|
|||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.flatMap { token -> apiClient.connectSocial("google", googleEmail ?: "", token) }
|
||||
.doOnNext {
|
||||
newUser = it.newUser
|
||||
handleAuthResponse(it)
|
||||
}
|
||||
.flatMap { userRepository.retrieveUser(true, true) }
|
||||
.subscribe(
|
||||
{
|
||||
onSuccess(it)
|
||||
onSuccess(it, newUser)
|
||||
},
|
||||
{ throwable ->
|
||||
if (recoverFromPlayServicesErrorResult == null) return@subscribe
|
||||
|
|
@ -211,26 +220,24 @@ class AuthenticationViewModel() {
|
|||
recoverFromPlayServicesErrorResult: ActivityResultLauncher<Intent>
|
||||
) {
|
||||
if (e is GooglePlayServicesAvailabilityException) {
|
||||
// The Google Play services APK is old, disabled, or not present.
|
||||
// Show a dialog created by Google Play services that allows
|
||||
// the user to update the APK
|
||||
val statusCode = e
|
||||
.connectionStatusCode
|
||||
GoogleApiAvailability.getInstance()
|
||||
@Suppress("DEPRECATION")
|
||||
GooglePlayServicesUtil.showErrorDialogFragment(
|
||||
statusCode,
|
||||
e.connectionStatusCode,
|
||||
activity,
|
||||
null,
|
||||
AuthenticationViewModel.REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR
|
||||
) {
|
||||
}
|
||||
return
|
||||
} else if (e is UserRecoverableAuthException) {
|
||||
// Unable to authenticate, such as when the user has not yet granted
|
||||
// the app access to the account, but the user can fix this.
|
||||
// Forward the user to an activity in Google Play services.
|
||||
val intent = e.intent
|
||||
recoverFromPlayServicesErrorResult.launch(intent)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -257,7 +264,6 @@ class AuthenticationViewModel() {
|
|||
}
|
||||
|
||||
HabiticaBaseApplication.reloadUserComponent()
|
||||
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
|
|
@ -276,7 +282,7 @@ class AuthenticationViewModel() {
|
|||
putString(user, encryptedKey)
|
||||
} else {
|
||||
// Something might have gone wrong with encryption, so fall back to this.
|
||||
putString("ApiToken", api)
|
||||
putString("APIToken", api)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
package com.habitrpg.android.habitica.ui.views
|
||||
|
||||
import android.content.Context
|
||||
import android.text.InputType
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.ValidatingEditTextBinding
|
||||
import com.habitrpg.android.habitica.extensions.layoutInflater
|
||||
|
||||
class ValidatingEditText @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null
|
||||
) : LinearLayout(context, attrs) {
|
||||
|
||||
private var binding: ValidatingEditTextBinding = ValidatingEditTextBinding.inflate(context.layoutInflater, this)
|
||||
|
||||
var text: String?
|
||||
get() = binding.editText.text?.toString()
|
||||
set(value) = binding.editText.setText(value)
|
||||
var errorText: String?
|
||||
get() = binding.errorText.text?.toString()
|
||||
set(value) {
|
||||
binding.errorText.text = value
|
||||
}
|
||||
|
||||
var validator: ((String?) -> Boolean)? = null
|
||||
|
||||
val isValid: Boolean
|
||||
get() = validator?.invoke(text) == true
|
||||
|
||||
init {
|
||||
orientation = VERTICAL
|
||||
context.theme?.obtainStyledAttributes(
|
||||
attrs,
|
||||
R.styleable.ValidatingEditText,
|
||||
0, 0
|
||||
)?.let { attributes ->
|
||||
binding.inputLayout.hint = attributes.getString(R.styleable.ValidatingEditText_hint)
|
||||
binding.editText.inputType = attributes.getInt(R.styleable.ValidatingEditText_android_inputType, InputType.TYPE_CLASS_TEXT)
|
||||
}
|
||||
|
||||
|
||||
binding.editText.setOnFocusChangeListener { _, isEditing ->
|
||||
if (isEditing) return@setOnFocusChangeListener
|
||||
if (validator?.invoke(text) == true) {
|
||||
binding.errorText.visibility = View.GONE
|
||||
} else {
|
||||
binding.errorText.visibility = View.VISIBLE
|
||||
}
|
||||
(this.parent as? ViewGroup)?.forceLayout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.habitrpg.android.habitica.utils
|
||||
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonParseException
|
||||
import com.habitrpg.android.habitica.extensions.getAsString
|
||||
import com.habitrpg.android.habitica.models.TutorialStep
|
||||
import com.habitrpg.android.habitica.models.user.auth.SocialAuthentication
|
||||
import java.lang.reflect.Type
|
||||
|
||||
class SocialAuthenticationDeserializer : JsonDeserializer<SocialAuthentication> {
|
||||
@Throws(JsonParseException::class)
|
||||
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): SocialAuthentication {
|
||||
val authentication = SocialAuthentication()
|
||||
val obj = json.asJsonObject
|
||||
if (obj.has("emails")) {
|
||||
val emailJson = obj.getAsJsonArray("emails")
|
||||
for (entry in emailJson) {
|
||||
if (entry.isJsonPrimitive) {
|
||||
authentication.emails.add(entry.asString)
|
||||
} else if (entry.isJsonObject) {
|
||||
authentication.emails.add(entry.asJsonObject.getAsString("value"))
|
||||
}
|
||||
}
|
||||
}
|
||||
return authentication
|
||||
}
|
||||
}
|
||||
|
|
@ -169,7 +169,6 @@ class AvatarStatsWidgetFactory(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
override fun getLoadingView() = RemoteViews(context.packageName, R.layout.widget_avatar_stats)
|
||||
|
||||
override fun getViewTypeCount(): Int {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import android.os.Bundle
|
|||
import android.widget.RemoteViews
|
||||
import com.habitrpg.android.habitica.HabiticaBaseApplication
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.extensions.withImmutableFlag
|
||||
import com.habitrpg.android.habitica.ui.activities.MainActivity
|
||||
|
||||
class AvatarStatsWidgetProvider : BaseWidgetProvider() {
|
||||
|
|
@ -37,7 +38,7 @@ class AvatarStatsWidgetProvider : BaseWidgetProvider() {
|
|||
intent.data = Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))
|
||||
|
||||
val openAppIntent = Intent(context.applicationContext, MainActivity::class.java)
|
||||
val openApp = PendingIntent.getActivity(context, 0, openAppIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val openApp = PendingIntent.getActivity(context, 0, openAppIntent, withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
|
||||
val remoteViews = RemoteViews(context.packageName, R.layout.widget_main_avatar_stats)
|
||||
remoteViews.setRemoteAdapter(R.id.widget_avatar_list, intent)
|
||||
|
|
|
|||
|
|
@ -39,8 +39,6 @@ abstract class BaseWidgetProvider : AppWidgetProvider() {
|
|||
|
||||
protected var context: Context? = null
|
||||
|
||||
|
||||
|
||||
override fun onAppWidgetOptionsChanged(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int, newOptions: Bundle) {
|
||||
this.context = context
|
||||
val options = appWidgetManager.getAppWidgetOptions(appWidgetId)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import androidx.core.content.ContextCompat
|
|||
import com.habitrpg.android.habitica.HabiticaBaseApplication
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.data.TaskRepository
|
||||
import com.habitrpg.android.habitica.extensions.withImmutableFlag
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.models.responses.TaskDirection
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
|
|
@ -107,7 +108,7 @@ class HabitButtonWidgetService : Service() {
|
|||
taskIntent.putExtra(HabitButtonWidgetProvider.TASK_DIRECTION, direction)
|
||||
return PendingIntent.getBroadcast(
|
||||
context, widgetId + direction.hashCode(), taskIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import android.widget.RemoteViews
|
|||
import com.habitrpg.android.habitica.HabiticaBaseApplication
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.data.TaskRepository
|
||||
import com.habitrpg.android.habitica.extensions.withImmutableFlag
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.ui.activities.MainActivity
|
||||
import javax.inject.Inject
|
||||
|
|
@ -80,7 +81,7 @@ abstract class TaskListWidgetProvider : BaseWidgetProvider() {
|
|||
|
||||
// if the user click on the title: open App
|
||||
val openAppIntent = Intent(context.applicationContext, MainActivity::class.java)
|
||||
val openApp = PendingIntent.getActivity(context, 0, openAppIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val openApp = PendingIntent.getActivity(context, 0, openAppIntent, withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
rv.setOnClickPendingIntent(R.id.widget_title, openApp)
|
||||
|
||||
val taskIntent = Intent(context, providerClass)
|
||||
|
|
@ -89,7 +90,7 @@ abstract class TaskListWidgetProvider : BaseWidgetProvider() {
|
|||
intent.data = Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))
|
||||
val toastPendingIntent = PendingIntent.getBroadcast(
|
||||
context, 0, taskIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
rv.setPendingIntentTemplate(R.id.list_view, toastPendingIntent)
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ buildscript {
|
|||
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
|
||||
classpath 'com.google.gms:google-services:4.3.10'
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.0'
|
||||
classpath "io.realm:realm-gradle-plugin:10.8.0"
|
||||
classpath "io.realm:realm-gradle-plugin:10.8.1"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.18.1"
|
||||
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5"
|
||||
|
|
|
|||
|
|
@ -3,12 +3,11 @@ apply plugin: 'kotlin-multiplatform'
|
|||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
compileSdkVersion 31
|
||||
|
||||
defaultConfig {
|
||||
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
targetSdkVersion 31
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
|
|
|
|||
Loading…
Reference in a new issue