Merge branch 'main' into 4.7.8-dev
|
|
@ -99,7 +99,7 @@
|
|||
tools:ignore="UnusedAttribute">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.activities.LoginActivity"
|
||||
android:name=".ui.activities.OnboardingActivity"
|
||||
android:label="@string/LoginActivityName"
|
||||
android:configChanges="screenSize | smallestScreenSize | screenLayout | orientation"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
|
|
@ -112,14 +112,6 @@
|
|||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="com.habitrpg.android.habitica.ui.activities.MainActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.activities.IntroActivity"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.activities.SetupActivity"
|
||||
android:windowSoftInputMode="stateHidden|adjustResize">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.activities.SkillTasksActivity">
|
||||
</activity>
|
||||
|
|
|
|||
|
|
@ -41,22 +41,12 @@
|
|||
tools:ignore="UnusedAttribute">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.activities.LoginActivity"
|
||||
android:name=".ui.activities.OnboardingActivity"
|
||||
android:label="@string/LoginActivityName"
|
||||
android:theme="@style/AppThemeWithActionBarBlackText"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:screenOrientation="portrait">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.activities.IntroActivity"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:screenOrientation="portrait">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.activities.SetupActivity"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:screenOrientation="portrait">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.activities.SkillTasksActivity"
|
||||
android:label="@string/app_name"
|
||||
|
|
|
|||
1
Habitica/proguard-rules.pro
vendored
|
|
@ -118,7 +118,6 @@
|
|||
-dontwarn com.android.volley.toolbox.**
|
||||
-dontwarn com.facebook.infer.**
|
||||
-dontwarn com.habitrpg.android.habitica.ui.views.bottombar.**
|
||||
-dontwarn com.viewpagerindicator.**
|
||||
#-ignorewarnings
|
||||
|
||||
-keep class com.google.firebase.provider.FirebaseInitProvider
|
||||
|
|
|
|||
BIN
Habitica/res/drawable-hdpi/border_pixelated.png
Normal file
|
After Width: | Height: | Size: 357 B |
BIN
Habitica/res/drawable-hdpi/header_verify_username.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
4
Habitica/res/drawable-hdpi/ic_close_white_18dp.xml
Normal 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="18dp" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="18dp">
|
||||
<path android:fillColor="@color/white" 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>
|
||||
BIN
Habitica/res/drawable-hdpi/ic_email_color.png
Normal file
|
After Width: | Height: | Size: 781 B |
BIN
Habitica/res/drawable-hdpi/login_background.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
Habitica/res/drawable-hdpi/login_email.png
Normal file
|
After Width: | Height: | Size: 516 B |
BIN
Habitica/res/drawable-hdpi/login_logo.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 3 KiB |
BIN
Habitica/res/drawable-hdpi/login_password.png
Normal file
|
After Width: | Height: | Size: 519 B |
BIN
Habitica/res/drawable-hdpi/login_username.png
Normal file
|
After Width: | Height: | Size: 567 B |
BIN
Habitica/res/drawable-hdpi/stable_background_spring.png
Normal file
|
After Width: | Height: | Size: 202 KiB |
BIN
Habitica/res/drawable-mdpi/border_pixelated.png
Normal file
|
After Width: | Height: | Size: 299 B |
BIN
Habitica/res/drawable-mdpi/header_verify_username.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
Habitica/res/drawable-mdpi/ic_email_color.png
Normal file
|
After Width: | Height: | Size: 535 B |
BIN
Habitica/res/drawable-mdpi/login_background.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 5.7 KiB |
BIN
Habitica/res/drawable-mdpi/login_email.png
Normal file
|
After Width: | Height: | Size: 373 B |
BIN
Habitica/res/drawable-mdpi/login_logo.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
BIN
Habitica/res/drawable-mdpi/login_password.png
Normal file
|
After Width: | Height: | Size: 351 B |
BIN
Habitica/res/drawable-mdpi/login_username.png
Normal file
|
After Width: | Height: | Size: 447 B |
BIN
Habitica/res/drawable-mdpi/stable_background_spring.png
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
Habitica/res/drawable-xhdpi/border_pixelated.png
Normal file
|
After Width: | Height: | Size: 427 B |
BIN
Habitica/res/drawable-xhdpi/header_verify_username.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
Habitica/res/drawable-xhdpi/ic_email_color.png
Normal file
|
After Width: | Height: | Size: 950 B |
BIN
Habitica/res/drawable-xhdpi/login_background.png
Normal file
|
After Width: | Height: | Size: 145 KiB |
BIN
Habitica/res/drawable-xhdpi/login_email.png
Normal file
|
After Width: | Height: | Size: 589 B |
BIN
Habitica/res/drawable-xhdpi/login_logo.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
BIN
Habitica/res/drawable-xhdpi/login_password.png
Normal file
|
After Width: | Height: | Size: 559 B |
BIN
Habitica/res/drawable-xhdpi/login_username.png
Normal file
|
After Width: | Height: | Size: 779 B |
BIN
Habitica/res/drawable-xhdpi/stable_background_spring.png
Normal file
|
After Width: | Height: | Size: 301 KiB |
BIN
Habitica/res/drawable-xxhdpi/border_pixelated.png
Normal file
|
After Width: | Height: | Size: 630 B |
BIN
Habitica/res/drawable-xxhdpi/header_verify_username.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
Habitica/res/drawable-xxhdpi/ic_email_color.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Habitica/res/drawable-xxhdpi/login_background.png
Normal file
|
After Width: | Height: | Size: 312 KiB |
BIN
Habitica/res/drawable-xxhdpi/login_email.png
Normal file
|
After Width: | Height: | Size: 832 B |
BIN
Habitica/res/drawable-xxhdpi/login_logo.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 6.1 KiB |
BIN
Habitica/res/drawable-xxhdpi/login_password.png
Normal file
|
After Width: | Height: | Size: 768 B |
BIN
Habitica/res/drawable-xxhdpi/login_username.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Habitica/res/drawable-xxhdpi/stable_background_spring.png
Normal file
|
After Width: | Height: | Size: 589 KiB |
|
|
@ -1,13 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:drawable="@color/brand_100"/>
|
||||
|
||||
<item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@drawable/login_logo"/>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
||||
</layer-list>
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
<gradient
|
||||
android:angle="90"
|
||||
android:endColor="#3E148A"
|
||||
android:startColor="#8962cc"
|
||||
android:endColor="#6133B4"
|
||||
android:startColor="#6133B4"
|
||||
android:type="linear" />
|
||||
|
||||
</shape>
|
||||
</shape>
|
||||
|
|
|
|||
|
|
@ -1,258 +1,11 @@
|
|||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".ui.activities.LoginActivity">
|
||||
|
||||
<com.habitrpg.android.habitica.ui.views.login.LockableScrollView
|
||||
tools:context=".ui.activities.OnboardingActivity">
|
||||
<androidx.compose.ui.platform.ComposeView
|
||||
android:id="@+id/compose_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/background_container"
|
||||
android:scrollIndicators="none"
|
||||
android:scrollbars="none">
|
||||
|
||||
<com.habitrpg.android.habitica.ui.views.login.LoginBackgroundView
|
||||
android:id="@+id/background_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/login_gradient"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:layout_marginEnd="-2dp"
|
||||
>
|
||||
<ImageView
|
||||
android:id="@+id/city_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
app:srcCompat="@drawable/login_background"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:scaleType="centerCrop" />
|
||||
<Space android:id="@+id/cloud_anchor"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:visibility="invisible"
|
||||
android:layout_above="@id/city_view" />
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/cloud_1"
|
||||
android:id="@+id/left_cloud_view"
|
||||
android:layout_toStartOf="@id/cloud_anchor"
|
||||
android:layout_marginEnd="150dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:paddingBottom="460dp"
|
||||
/>
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/cloud_2"
|
||||
android:id="@+id/right_cloud_view"
|
||||
android:layout_toEndOf="@id/cloud_anchor"
|
||||
android:layout_marginStart="170dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:paddingBottom="400dp"
|
||||
/>
|
||||
</com.habitrpg.android.habitica.ui.views.login.LoginBackgroundView>
|
||||
|
||||
</com.habitrpg.android.habitica.ui.views.login.LockableScrollView>
|
||||
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="@dimen/spacing_large"
|
||||
android:paddingStart="@dimen/spacing_large"
|
||||
android:paddingEnd="@dimen/spacing_large"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/transparent"
|
||||
>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/logo_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:src="@drawable/login_logo"
|
||||
android:layout_margin="24dp"
|
||||
android:contentDescription="@string/brand_name" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/new_game_button"
|
||||
android:layout_width="@dimen/login_intro_button_size"
|
||||
android:layout_height="@dimen/login_intro_button_size"
|
||||
android:text="@string/register_btn"
|
||||
android:layout_gravity="center_vertical|center_horizontal"
|
||||
android:textColor="@color/white"
|
||||
android:layout_marginTop="@dimen/spacing_large"
|
||||
android:background="@drawable/login_begin_button"
|
||||
android:backgroundTint="@color/brand_100"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/show_login_button"
|
||||
android:layout_width="@dimen/login_intro_button_size"
|
||||
android:layout_height="@dimen/login_intro_button_size"
|
||||
android:text="@string/login_btn"
|
||||
android:layout_gravity="center_vertical|center_horizontal"
|
||||
android:textColor="@color/white"
|
||||
android:layout_marginTop="@dimen/spacing_large"
|
||||
android:background="@drawable/login_begin_button"
|
||||
android:backgroundTint="@color/brand_100"/>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/transparent"
|
||||
android:visibility="invisible"
|
||||
android:id="@+id/login_scrollview"
|
||||
android:scrollIndicators="none"
|
||||
tools:visibility="visible"
|
||||
android:padding="0dp"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/spacing_large"
|
||||
android:id="@+id/form_wrapper"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/transparent">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/username"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableEnd="@drawable/ic_username"
|
||||
android:imeOptions="actionNext"
|
||||
android:inputType="textPersonName"
|
||||
android:nextFocusDown="@+id/email"
|
||||
android:hint="@string/username"
|
||||
android:theme="@style/LoginEditTextTheme"
|
||||
android:textColor="@color/white_75_alpha"
|
||||
android:textColorHighlight="@color/white"
|
||||
android:isCredential="true"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/email"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textEmailAddress"
|
||||
android:imeOptions="actionNext"
|
||||
android:nextFocusDown="@+id/password"
|
||||
android:drawableEnd="@drawable/ic_email"
|
||||
android:hint="@string/emailAddress"
|
||||
android:textColor="@color/white_75_alpha"
|
||||
android:theme="@style/LoginEditTextTheme"
|
||||
android:autofillHints="emailAddress"
|
||||
/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword"
|
||||
android:imeOptions="actionNext"
|
||||
android:nextFocusDown="@+id/confirm_password"
|
||||
android:drawableEnd="@drawable/ic_password"
|
||||
android:hint="@string/password"
|
||||
android:textColor="@color/white_75_alpha"
|
||||
android:theme="@style/LoginEditTextTheme"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/confirm_password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword"
|
||||
android:drawableEnd="@drawable/ic_password"
|
||||
android:imeOptions="actionDone"
|
||||
android:hint="@string/confirmpassword"
|
||||
android:textColor="@color/white_75_alpha"
|
||||
android:theme="@style/LoginEditTextTheme"
|
||||
android:autofillHints="newPassword"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_view"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/submit_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="42dp"
|
||||
android:text="@string/register_btn"
|
||||
android:layout_marginTop="@dimen/spacing_xlarge"
|
||||
style="@style/LoginButton"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/google_login_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="42dp"
|
||||
android:text="@string/login_btn_google"
|
||||
android:layout_marginTop="@dimen/spacing_large"
|
||||
android:drawableStart="@drawable/google_icon"
|
||||
style="@style/LoginButton"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/apple_login_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="@dimen/spacing_large"
|
||||
android:layout_height="@dimen/diamond_button_height"
|
||||
android:text="@string/login_btn_apple"
|
||||
android:drawableStart="@drawable/apple_icon"
|
||||
android:visibility="gone"
|
||||
style="@style/LoginButton"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/forgot_password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="@dimen/spacing_xlarge"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/forgot_pw_btn"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:textColor="@color/white_75_alpha"
|
||||
android:background="@color/transparent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/privacy_policy"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="@dimen/spacing_large"
|
||||
android:layout_marginBottom="@dimen/spacing_xlarge"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/login_disclaimer"
|
||||
android:linksClickable="true"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:textColor="@color/white_75_alpha"
|
||||
android:background="@color/transparent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/back_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_arrow_back_white_36dp"
|
||||
android:background="@color/transparent"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:layout_marginTop="@dimen/spacing_small"
|
||||
android:alpha="0" />
|
||||
</FrameLayout>
|
||||
|
||||
android:layout_height="match_parent">
|
||||
</androidx.compose.ui.platform.ComposeView>
|
||||
</FrameLayout>
|
||||
|
|
|
|||
|
|
@ -1,55 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/viewPager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:background="@color/window_background"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/bottomBar"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:background="@color/background_brand"
|
||||
android:gravity="center_vertical"
|
||||
android:elevation="8dp">
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/view_pager_indicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:layout_centerInParent="true"
|
||||
app:tabBackground="@drawable/indicator_diamond"
|
||||
android:background="@color/transparent"
|
||||
app:tabIndicator="@null" />
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/previousButton"
|
||||
android:drawableStart="@drawable/back_arrow_disabled"
|
||||
android:background="@color/transparent"
|
||||
android:drawablePadding="12dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/next_button"
|
||||
android:id="@+id/nextButton"
|
||||
android:drawablePadding="12dp"
|
||||
android:textColor="@color/white"
|
||||
android:drawableEnd="@drawable/forward_arrow_enabled"
|
||||
android:background="@color/transparent"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"/>
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/text_view"
|
||||
tools:text="@string/avatar_body"
|
||||
android:textAllCaps="true"
|
||||
android:paddingTop="8dp"
|
||||
android:textColor="@color/white_50_alpha"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:gravity="center"
|
||||
tools:background="?colorPrimaryOffset"
|
||||
tools:drawableTop="@drawable/icon_body" />
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
<?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"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/subcategory_layout"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/customization_drawer_subcategory_height"
|
||||
android:background="@drawable/avatar_customization_subcategory_bg"
|
||||
android:layout_alignParentTop="true">
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/subcategory_tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:tabIndicatorColor="?colorAccent"
|
||||
app:tabIndicatorHeight="2dp"
|
||||
app:tabTextColor="@color/white_50_alpha"
|
||||
app:tabSelectedTextColor="@color/white"
|
||||
android:background="@color/brand"
|
||||
app:tabGravity="center"
|
||||
app:tabPaddingEnd="24dp"
|
||||
app:tabPaddingStart="24dp"
|
||||
/>
|
||||
|
||||
<com.habitrpg.android.habitica.ui.helpers.RecyclerViewEmptySupport
|
||||
android:id="@+id/customization_list"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center_horizontal"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/customization_drawer_category_height"
|
||||
android:background="@drawable/avatar_customization_category_bg"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center"
|
||||
android:layout_below="@id/subcategory_layout"
|
||||
android:elevation="4dp"
|
||||
android:id="@+id/category_layout">
|
||||
|
||||
<com.habitrpg.android.habitica.ui.views.setup.AvatarCategoryView
|
||||
android:id="@+id/body_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
app:iconDrawable="@drawable/icon_body"
|
||||
app:categoryTitle="@string/avatar_body"
|
||||
android:gravity="center"/>
|
||||
|
||||
<com.habitrpg.android.habitica.ui.views.setup.AvatarCategoryView
|
||||
android:id="@+id/skin_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
app:iconDrawable="@drawable/icon_skin"
|
||||
app:categoryTitle="@string/avatar_skin"
|
||||
android:gravity="center"/>
|
||||
|
||||
<com.habitrpg.android.habitica.ui.views.setup.AvatarCategoryView
|
||||
android:id="@+id/hair_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
app:iconDrawable="@drawable/icon_hair"
|
||||
app:categoryTitle="@string/avatar_hair"
|
||||
android:gravity="center"/>
|
||||
|
||||
<com.habitrpg.android.habitica.ui.views.setup.AvatarCategoryView
|
||||
android:id="@+id/extras_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
app:iconDrawable="@drawable/icon_extras"
|
||||
app:categoryTitle="@string/avatar_extras"
|
||||
android:gravity="center"/>
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/customization_selection_caret"
|
||||
android:id="@+id/caret_view"
|
||||
android:layout_alignBottom="@id/category_layout"
|
||||
android:layout_marginBottom="@dimen/customization_drawer_caret_margin"
|
||||
android:elevation="6dp"/>
|
||||
</RelativeLayout>
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout 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:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:background="@color/brand_300"
|
||||
android:id="@+id/container_view">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/subtitleTextView"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal"
|
||||
style="@style/Body2"
|
||||
android:textSize="17sp"
|
||||
android:textColor="@color/white"
|
||||
tools:text="@string/intro_2_subtitle"
|
||||
android:layout_marginBottom="4dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/titleImageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:contentDescription="@string/brand_name" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/titleTextView"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center_horizontal"
|
||||
style="@style/Title1"
|
||||
android:textSize="30sp"
|
||||
android:textColor="@color/white"
|
||||
tools:text="@string/intro_2_title"
|
||||
android:layout_marginBottom="28dp" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/imageView"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:src="@drawable/intro_2"
|
||||
app:srcCompat="@drawable/intro_2" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/descriptionTextView"
|
||||
android:layout_gravity="center_horizontal"
|
||||
style="@style/Body2"
|
||||
android:textColor="@android:color/white"
|
||||
android:layout_margin="28dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:lineSpacingExtra="4dp"
|
||||
android:maxWidth="350dp"
|
||||
tools:text="@string/intro_2_description"/>
|
||||
</LinearLayout>
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
android:id="@+id/content_wrapper"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical" android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/setup_background"
|
||||
android:gravity="center_horizontal">
|
||||
<Space
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/randomize_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:text="@string/randomize"
|
||||
style="@style/DiamondButton"/>
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/setup_vertical_spacing">
|
||||
<ImageView
|
||||
android:id="@+id/avatar_background"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/creator_hills_bg"
|
||||
android:layout_centerInParent="true"
|
||||
android:importantForAccessibility="no" />
|
||||
<com.habitrpg.common.habitica.views.AvatarView
|
||||
android:id="@+id/avatarView"
|
||||
android:layout_width="@dimen/avatar_small_width"
|
||||
android:layout_height="@dimen/avatar_small_height"
|
||||
app:showBackground="false"
|
||||
app:showMount="false"
|
||||
app:showPet="false"
|
||||
app:showSleeping="false"
|
||||
android:layout_alignTop="@id/avatar_background"
|
||||
android:layout_marginStart="54dp"
|
||||
/>
|
||||
</RelativeLayout>
|
||||
<Space
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
<com.habitrpg.android.habitica.ui.SpeechBubbleView
|
||||
android:id="@+id/speech_bubble"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:namePlate="Justin"
|
||||
app:npcDrawable="@drawable/justin_textbox"
|
||||
android:layout_marginStart="@dimen/content_inset"
|
||||
android:layout_marginEnd="@dimen/content_inset"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
|
||||
<com.habitrpg.android.habitica.ui.views.setup.AvatarCustomizationDrawer
|
||||
android:id="@+id/customization_drawer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
android:id="@+id/content_wrapper"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical" android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/setup_background"
|
||||
android:gravity="center_horizontal">
|
||||
<Space
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
<ImageView
|
||||
android:id="@+id/heart_icon"
|
||||
android:layout_height="36dp"
|
||||
android:layout_width="36dp" />
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/setup_vertical_spacing">
|
||||
<ImageView
|
||||
android:id="@+id/avatar_background"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/creator_purple_bg"
|
||||
android:layout_centerInParent="true" />
|
||||
<com.habitrpg.common.habitica.views.AvatarView
|
||||
android:id="@+id/avatarView"
|
||||
android:layout_width="@dimen/avatar_small_width"
|
||||
android:layout_height="@dimen/avatar_small_height"
|
||||
app:showBackground="false"
|
||||
app:showMount="false"
|
||||
app:showPet="false"
|
||||
app:showSleeping="false"
|
||||
android:layout_alignTop="@id/avatar_background"
|
||||
android:layout_marginStart="54dp"
|
||||
/>
|
||||
</RelativeLayout>
|
||||
<Space
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
<com.habitrpg.android.habitica.ui.SpeechBubbleView
|
||||
android:id="@+id/speech_bubble"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:namePlate="Justin"
|
||||
app:npcDrawable="@drawable/justin_textbox"
|
||||
android:layout_marginStart="@dimen/content_inset"
|
||||
android:layout_marginEnd="@dimen/content_inset"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="230dp"
|
||||
android:background="@drawable/avatar_customization_category_bg"
|
||||
android:id="@+id/recyclerView"
|
||||
android:padding="17dp"/>
|
||||
</LinearLayout>
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView 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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
android:background="@color/setup_background">
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/spacing_large"
|
||||
android:paddingEnd="@dimen/spacing_large"
|
||||
android:paddingTop="32dp"
|
||||
android:paddingBottom="16dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="294dp"
|
||||
android:layout_height="121dp"
|
||||
android:src="@drawable/hello_onboarding"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginBottom="@dimen/spacing_large"
|
||||
/>
|
||||
|
||||
<com.habitrpg.android.habitica.ui.views.DayNightTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:dayTextColor="@color/brand_200"
|
||||
app:nightTextColor="@color/brand_500"
|
||||
android:text="@string/welcomeNameTitle"
|
||||
android:gravity="center"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="@dimen/spacing_large"/>
|
||||
|
||||
<TableLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:stretchColumns="2"
|
||||
android:background="@drawable/rounded_border_content">
|
||||
<TableRow>
|
||||
<TextView
|
||||
android:layout_height="42dp"
|
||||
android:text="@string/display_name"
|
||||
android:paddingStart="@dimen/spacing_medium"
|
||||
android:paddingEnd="@dimen/spacing_medium"
|
||||
android:layout_marginStart="1dp"
|
||||
android:background="@color/setup_label_background"
|
||||
android:gravity="center_vertical"
|
||||
android:textSize="14sp"
|
||||
/>
|
||||
<View android:background="@color/offset_background"
|
||||
android:layout_height="42dp"
|
||||
android:layout_width="1dp" />
|
||||
<EditText android:id="@+id/display_name_edit_text"
|
||||
android:background="@color/transparent"
|
||||
android:layout_height="42dp"
|
||||
android:layout_marginStart="@dimen/spacing_large"
|
||||
android:layout_marginEnd="@dimen/spacing_large"
|
||||
android:textSize="14sp"
|
||||
android:lines="1"/>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<View android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/offset_background" android:layout_span="3" />
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TextView android:text="@string/username"
|
||||
android:background="@color/setup_label_background"
|
||||
android:layout_height="41dp"
|
||||
android:paddingStart="@dimen/spacing_medium"
|
||||
android:paddingEnd="@dimen/spacing_medium"
|
||||
android:layout_marginStart="1dp"
|
||||
android:gravity="center_vertical"
|
||||
android:textSize="14sp"/>
|
||||
<View android:background="@color/offset_background"
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="42dp"/>
|
||||
<LinearLayout
|
||||
android:layout_marginStart="@dimen/spacing_large"
|
||||
android:layout_marginEnd="@dimen/spacing_large">
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:text="\@"
|
||||
android:gravity="center_vertical"
|
||||
android:textColor="@color/text_dimmed"
|
||||
android:textSize="14sp"/>
|
||||
<EditText android:id="@+id/username_edit_text"
|
||||
android:background="@color/transparent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="42dp"
|
||||
android:layout_marginStart="@dimen/spacing_small"
|
||||
android:lines="1"
|
||||
android:textSize="14sp"/>
|
||||
</LinearLayout>
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/issues_text_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/spacing_medium"
|
||||
android:textColor="@color/red_50"
|
||||
android:textSize="12sp"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
tools:text="Issues go here"/>
|
||||
|
||||
<Space
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginTop="@dimen/spacing_large"/>
|
||||
|
||||
<com.habitrpg.android.habitica.ui.SpeechBubbleView
|
||||
android:id="@+id/speech_bubble"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:namePlate="Justin"
|
||||
app:npcDrawable="@drawable/justin_textbox"
|
||||
android:layout_marginTop="@dimen/spacing_large"/>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
|
@ -1,8 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="profile_pets_and_mounts">Mascotas y Monturas</string>
|
||||
<string name="profile_pets_and_mounts">Mascotas y monturas</string>
|
||||
<string name="profile_pets_found">Mascotas encontradas</string>
|
||||
<string name="profile_mounts_tamed">Monturas Domesticadas</string>
|
||||
<string name="profile_mounts_tamed">Monturas domesticadas</string>
|
||||
<string name="profile_loading_data">Cargando datos del miembro…</string>
|
||||
<string name="profile_send_message_to">Enviar mensaje a %s</string>
|
||||
<string name="profile_message_sent_to">Mensaje enviado a %s</string>
|
||||
<string name="profile_level">Nivel:</string>
|
||||
<string name="profile_class_bonus">Bonus de clase:</string>
|
||||
<string name="profile_allocated">Asignado:</string>
|
||||
<string name="profile_achievements">Logros</string>
|
||||
</resources>
|
||||
|
|
@ -4,5 +4,17 @@
|
|||
<string name="sidebar_skills">Habilidades</string>
|
||||
<string name="sidebar_section_social">Social</string>
|
||||
<string name="sidebar_inbox">Mensajes</string>
|
||||
<string name="sidebar_party">Equipo</string>
|
||||
<string name="sidebar_party">Grupo</string>
|
||||
<string name="sidebar_gems">Comprar gemas</string>
|
||||
<string name="sidebar_subscription">Suscripción</string>
|
||||
<string name="sidebar_challenges">Retos</string>
|
||||
<string name="sidebar_section_inventory">Inventario</string>
|
||||
<string name="sidebar_avatar">Personalizar personaje</string>
|
||||
<string name="sidebar_avatar_equipment">Personaje y equipamiento</string>
|
||||
<string name="sidebar_equipment">Equipamiento</string>
|
||||
<string name="sidebar_stable">Mascotas y Monturas</string>
|
||||
<string name="sidebar_news">Noticias</string>
|
||||
<string name="sidebar_about">Información</string>
|
||||
<string name="sidebar_shops">Tiendas</string>
|
||||
<string name="sidebar_stats">Estadísticas</string>
|
||||
</resources>
|
||||
|
|
@ -5,4 +5,18 @@
|
|||
<string name="tutorial_overview">¡Aquí estamos! He hecho algunas tareas para ti según tus gustos. Intenta agregar algunos propios. Puedes editar cualquier tarea tocando el título.</string>
|
||||
<string name="tutorial_habits_2">¡Cada vez que hagas un hábito positivo, toca el + para recibir experiencia y oro!</string>
|
||||
<string name="tutorial_habits_4">¡Dale un tiro! Puede explorar los otros tipos de tareas mediante de la navegación inferior.</string>
|
||||
<string name="tutorial_dailies_1">Usa Tareas Diarias para aquellas tareas que necesitan ser completadas en un horario regular.</string>
|
||||
<string name="tutorial_dailies_2">Ten cuidado — si no completas una, tu avatar recibirá daño por la noche. ¡Completarlas constantemente trae grandes recompensas!</string>
|
||||
<string name="tutorial_todos_1">Usa las Tareas Pendientes para gestionar las tareas que solo tienes que realizar una vez.</string>
|
||||
<string name="tutorial_rewards_1">¡Compra equipamiento para tu avatar con el oro que ganas!</string>
|
||||
<string name="tutorial_rewards_2">También puedes crear recompensas personalizadas del mundo real en función de lo que te motiva.</string>
|
||||
<string name="tutorial_tasks_complete">Eso es todo por ahora. Si necesitas un recordatorio, consulta la sección de Preguntas Frecuentes (FAQ).</string>
|
||||
<string name="tutorial_party">¡Aquí es donde tú y tus amigos pueden estar pendientes del uno al otro para cumplir sus objetivos y luchar contra monstruos junto a sus tareas!</string>
|
||||
<string name="tutorial_stats">Toca el botón gris para asignar muchas de tus estadísticas a la vez, o toca las flechas para agregarlas un punto a la vez.</string>
|
||||
<string name="tutorial_todos_2">Si tu Tarea Pendiente necesita ser completada en cierta hora, ánade una fecha límite. Parece que puedes tachar una — ¡Adelante!</string>
|
||||
<string name="tutorial_skills">¡Las Habiliades son habilidades especiales con efectos poderosos! Toca una habilidad para usarla. Costará Maná (la barra azul), que puedes conseguir entrando todos los días y completar tareas en la vida real. ¡Consulta la sección de Preguntas Frecuentes (FAQ) para más información!</string>
|
||||
<string name="tutorial_party_created">¡Bienvenido a tu Grupo!
|
||||
\nPuedes encontrar más miembros desde una lista de jugadores que estén buscando un grupo o invitar a tus amigos directamente para conseguir el pergamino de Tareas de la Basi-Lista.
|
||||
\n
|
||||
\nVe a Soporte para aprender más sobre los Grupos.</string>
|
||||
</resources>
|
||||
|
|
@ -16,4 +16,15 @@
|
|||
<string name="pref_reminder_header">Recordatorio diario</string>
|
||||
<string name="pref_reminder_checkbox">Activar Recordatorio</string>
|
||||
<string name="pref_first_day_of_the_week_title">Primer día del mes</string>
|
||||
<string name="gem_purchase_toolbartitle">Comprar</string>
|
||||
<string name="special_items">Objetos especiales</string>
|
||||
<string name="million_abbrev">M</string>
|
||||
<string name="billion_abbrev">B</string>
|
||||
<string name="gem_purchase_listitem2">Fondos increíbles</string>
|
||||
<string name="gem_purchase_listitem4">La habilidad para cambiar tu clase antes del nivel 100</string>
|
||||
<string name="thousand_abbrev">k</string>
|
||||
<string name="gem_purchase_confirmation">Ganaste %s gemas.</string>
|
||||
<string name="trillion_abbrev">T</string>
|
||||
<string name="garden">Jardín</string>
|
||||
<string name="x_and_y">%1$s y %2$s</string>
|
||||
</resources>
|
||||
|
|
@ -246,7 +246,7 @@
|
|||
<string name="share_using">Teilen via</string>
|
||||
<string name="share_levelup">Ich habe durch das Verbessern meiner realen Lebensgewohnheiten Level %d in #Habitica erreicht!</string>
|
||||
<string name="share_hatched">Durch das Erfüllen meiner Real-Life-Aufgaben habe ich ein %1$s %2$s Haustier in #Habitica ausgebrütet!</string>
|
||||
<string name="share_raised">Durch das Erfüllen meiner Real-Life-Aufgaben habe ich ein %s Reittier in Habitica erhalten!</string>
|
||||
<string name="share_raised">Durch das Erfüllen meiner Real-Life-Aufgaben habe ich ein %1$s Reittier in Habitica erhalten!</string>
|
||||
<string name="open_in_store">Im Play Store öffnen</string>
|
||||
<string name="change_class_confirmation">Möchtest du deine Klasse für 3 Edelsteine ändern\?</string>
|
||||
<string name="change_class_equipment_warning">Dies wird deine Statuswerte erstatten, wechselt verfügbare Ausrüstung im Marktplatz und ändert die möglichen Fähigkeiten.</string>
|
||||
|
|
@ -271,12 +271,12 @@
|
|||
<string name="add_task">Neue Aufgabe hinzufügen</string>
|
||||
<string name="add_habit">Neue Gewohnheit hinzufügen</string>
|
||||
<string name="add_daily">Neue Tagesaufgabe hinzufügen</string>
|
||||
<string name="add_todo">Neues To Do hinzufügen</string>
|
||||
<string name="add_todo">Neues To-Do hinzufügen</string>
|
||||
<string name="add_reward">Neue Belohnung hinzufügen</string>
|
||||
<string name="all_dailies_completed">Du hast alle Deine Tagesaufgaben erledigt. Gut gemacht!</string>
|
||||
<string name="widget_habit_button">Habitica: Gewohnheit</string>
|
||||
<string name="widget_dailies">Habitica: Tagesaufgaben</string>
|
||||
<string name="widget_todo_list">Habitica To Do-Liste</string>
|
||||
<string name="widget_todo_list">Habitica To-Do-Liste</string>
|
||||
<string name="google_services_missing">Google Play Dienste konnten nicht gefunden werden.</string>
|
||||
<string name="gem_purchase_toolbartitle">Kaufen</string>
|
||||
<string name="gem_purchase_title">Das Erwerben von Edelsteinen unterstützt die Entwickler und hilft Habitica am Laufen zu halten</string>
|
||||
|
|
@ -462,7 +462,7 @@ Die Quest-Schriftrolle wird an den Quest-Besitzer zurückgegeben.</string>
|
|||
<string name="insufficientHourglasses">Du wirst mehr mystische Sanduhren benötigen um diesen Gegenstand zu kaufen!</string>
|
||||
<string name="get_hourglasses">Kaufe Sanduhren</string>
|
||||
<string name="subscribe_for_hourglasses">Abonniere für Sanduhren</string>
|
||||
<string name="timetravelers_closed_description">Erhalte eine mystische Sanduhr alle drei Monate für ein aufeinanderfolgendes Abonnement, nutze diese dann, um Limitiertes freizuschalten: Gegenstände, Haustiere oder Reittiere aus der Vergangenheit… und aus der Zukunft!</string>
|
||||
<string name="timetravelers_closed_description">Erhalte jeden Monat deines Abonnements eine mystische Sanduhr, nutze diese dann, um Limitiertes freizuschalten: Gegenstände, Haustiere oder Reittiere aus der Vergangenheit… und aus der Zukunft!</string>
|
||||
<string name="want_to_subscribe">Ich möchte ein Abo abschließen</string>
|
||||
<string name="seasonal_closed_description">Die Großen Galas werden an Sonnenwenden und Tag-Nacht-Gleichen veranstaltet, also komme zu dieser Zeit wieder, um eine lustige Auswahl von speziellen saisonalen Gegenständen zu finden!</string>
|
||||
<string name="come_back_soon">Komm bald zurück!</string>
|
||||
|
|
@ -841,7 +841,7 @@ Die Quest-Schriftrolle wird an den Quest-Besitzer zurückgegeben.</string>
|
|||
<string name="x_months">%d Monate</string>
|
||||
<string name="subscribe_listitem5_description">Erhalte einen Purpur Wolpertinger plus die doppelte Menge Einer, Schlüpfelixiere und Futter jeden Tag um deine Haustiersammlung zu vergrößern!</string>
|
||||
<string name="subscribe_listitem3_description_new">Abonniere jetzt, um dieses %s gleich zu bekommen und jeden Monat neue Gegenstände zu erhalten!</string>
|
||||
<string name="subscribe_listitem5">Besonderes Haustier & mehr Beute</string>
|
||||
<string name="subscribe_listitem5">Besondere Haustiere & Mehr Gegenstände</string>
|
||||
<string name="create_account_short">Konto erstellen</string>
|
||||
<string name="login_incentive_short_count">%d Anmeldungen</string>
|
||||
<string name="login_incentive_short">regelmäßig bei Habitica einloggen</string>
|
||||
|
|
@ -939,9 +939,9 @@ Die Quest-Schriftrolle wird an den Quest-Besitzer zurückgegeben.</string>
|
|||
\n
|
||||
\nWenn Du Habitica abonnierst, kannst Du mit Gold sogar eine gewisse, von der Dauer Deines Abonnements abhängige, Anzahl an Edelsteinen kaufen.</string>
|
||||
<string name="update_app_description">Wir lassen immer wieder neue Bugfixes raus. Deshalb solltest Du regelmäßig im Play-Store nach Aktualisierungen ausschau halten.</string>
|
||||
<string name="hourglasses_description">Mystische Sanduhren sind eine extrem seltene Währung, die Du nur durch ein Habitica-Abonnement von mindestens drei aufeinanderfolgenden Monaten erhalten kannst. Du brauchst die Sanduhren im Shop der Mystischen Zeitreisenden, um Ausrüstungssets aus vergangenen Zeiten zu kaufen, oder auch Haustiere, Reittiere, animierte Hintergründe oder spezielle Quests.
|
||||
<string name="hourglasses_description">Mystische Sanduhren sind eine extrem seltene Währung, die du nur durch ein Habitica-Abonnement erhalten kannst. Du brauchst die Sanduhren im Shop der Mystischen Zeitreisenden, um Ausrüstungssets aus vergangenen Zeiten zu kaufen, oder auch Haustiere, Reittiere, animierte Hintergründe oder spezielle Quests! Diese extra Vorteile sind toll um dich durch das ganze Jahr motiviert zu halten und dich selbst zu belohnen.
|
||||
\n
|
||||
\nDu kannst bis zu vier Mystische Sanduhren pro Jahr erhalten. Der Zeitpunkt, wann das passiert, hängt von Deiner Abonnements-Erneuerung ab. Die Sanduhren trudeln am ersten Tag des neuen Monats nach der Abonnementszahlung ein, für die Dir eine Sanduhr zusteht. Sie auf der [Abonnement]-Seite nach, wenn Du mehr wissen willst.</string>
|
||||
\nAbonnenten erhalten eine Mystische Sanduhr, zusammen mit vielen anderen Vorteilen, zu beginn jeden Monats an dem sie Abonnement-Vorteile haben. Schau dir unsere Abo-Optionen an, falls dich interessiert was die Zeitreisenden so anbieten!</string>
|
||||
<string name="gems_description">Edelsteine sind eine Währung, die Du mit echtem Geld kaufst. Sie ermöglichen Dir, zusätzliche Inhalte in Habitica zu kaufen und sind neben Abonnements eine der Haupteinnahmequellen für das Habitica-Team.
|
||||
\n
|
||||
\nAlle Inhalte, die mit Edelsteinen gekauft werden, sind rein kosmetischer Natur oder können mit der Zeit auch kostenlos erhalten werden.
|
||||
|
|
@ -1095,7 +1095,7 @@ Die Quest-Schriftrolle wird an den Quest-Besitzer zurückgegeben.</string>
|
|||
<string name="fortify_shop_description">Setze alle Aufgaben auf einen neutralen Wert zurück (gelbe Farbe) und stelle alle verlorene Gesundheit wieder her</string>
|
||||
<string name="won_achievement_description"><b>%s</b> hat dich als Gewinner gewählt! Dein Sieg wurde in deinen Erfolgen vermerkt.</string>
|
||||
<string name="quest_leave_message">Bist Du sicher, dass Du den Quest verlassen möchtest\? All Dein Fortschritt wird verloren gehen.</string>
|
||||
<string name="user_is_blocked_explanation">Ein blockierte Spieler*in kann dir keine privaten Nachrichten schicken, aber du wirst seine Nachrichten in der Taverne oder in Gilden weiterhin sehen.</string>
|
||||
<string name="user_is_blocked_explanation">Ein blockierte Spieler*in kann dir keine privaten Nachrichten schicken, aber du wirst seine Nachrichten in der Taverne oder in Gilden weiterhin sehen</string>
|
||||
<string name="launch_screen">Startbildschirm</string>
|
||||
<string name="something_new">Neuer</string>
|
||||
<string name="limited_potions_available">Saisonale Artikel verfügbar</string>
|
||||
|
|
@ -1133,7 +1133,7 @@ Die Quest-Schriftrolle wird an den Quest-Besitzer zurückgegeben.</string>
|
|||
<string name="connect">Verbinden</string>
|
||||
<string name="add">Hinzufügen</string>
|
||||
<string name="add_email_and_password">E-Mail und Passwort hinzufügen</string>
|
||||
<string name="password_not_matching">Die eingegebenen Passwörter stimmen nicht überein.</string>
|
||||
<string name="password_not_matching">Die eingegebenen Passwörter stimmen nicht überein</string>
|
||||
<string name="email_invalid">Ungültige E-Mail-Adresse</string>
|
||||
<string name="discard">Verwerfen</string>
|
||||
<string name="class_confirmation_price">Möchtest Du Deine Klasse in %1$s ändern für %2$d Edelsteine\?</string>
|
||||
|
|
@ -1203,7 +1203,7 @@ Die Quest-Schriftrolle wird an den Quest-Besitzer zurückgegeben.</string>
|
|||
<string name="death_description">Aber Du kannst sie alle mit harter Arbeit zurückerlangen! Viel Erfolg - Du wirst das schaffen.</string>
|
||||
<string name="faint_broken_equipment">Zerstörte Ausrüstungsgegenstände können
|
||||
\nim Belohnungen-Bereich zurückgekauft werden</string>
|
||||
<string name="faint_loss_description">Du fällst zurück auf Level <b>%1$d</b>, verlierst <b>%2$d</b> Gold und <b>ein Ausrüstungsgegenstand</b> wird zerstört…</string>
|
||||
<string name="faint_loss_description">Du fällst zurück auf Level <b>%1$d</b>, verlierst <b>%2$d</b> Gold und <b>ein Ausrüstungsgegenstand</b> wird zerstört… Du kannst sie alle mit harter Arbeit wieder verdienen!</string>
|
||||
<string name="account_reset">Account zurücksetzen</string>
|
||||
<string name="avatar_skin_customization">Avatar Haut Anpassung</string>
|
||||
<string name="avatar_beard_customization">Avatar Bart Anpassung</string>
|
||||
|
|
@ -1263,4 +1263,281 @@ Die Quest-Schriftrolle wird an den Quest-Besitzer zurückgegeben.</string>
|
|||
<string name="positive_sentence">positiv</string>
|
||||
<string name="title">Titel</string>
|
||||
<string name="negative_sentence">negativ</string>
|
||||
</resources>
|
||||
<string name="change_class_message">Dadurch wird deine aktuelle Klasse entfernt, du bekommst alle Attributspunkte zurück und kannst eine neue Klasse wählen</string>
|
||||
<string name="subscribe_listitemFaint_description">Behalte deinen Fortschritt mit einer sofortigen Heilung pro Tag, wenn deine Gesundheit aufgebraucht ist!</string>
|
||||
<string name="enable_class_description">Wähle eine Klasse, um Fähigkeiten, Werte und Mana zu aktivieren</string>
|
||||
<string name="choosing_class_progress">Klasse wählen</string>
|
||||
<string name="change_class_selected_confirmation">Willst du deine Klasse zu %1$s ändern\?</string>
|
||||
<string name="your_tags">Deine Tags</string>
|
||||
<string name="challenge_tags">Herausforderungs-Tags</string>
|
||||
<string name="group_tags">Gruppen-Tags</string>
|
||||
<string name="exact_alarm_system_settings_title">Wecker und Erinnerungen deaktiviert</string>
|
||||
<string name="opt_out_description">In diesem Fall wird dir keine Klasse zugewiesen. Du kannst jederzeit wieder eine Klasse auswählen, indem du das Klassensystem in den Einstellungen aktivierst.</string>
|
||||
<string name="subscribe_listitemFaint">Bekomme eine zweite Chance</string>
|
||||
<string name="reset_account_description_no_pw">Du wirst alle deine Level, Gold und Erfahrung verlieren. Alle deine Aufgaben und deren historischen Daten werden gelöscht (Herausforderungsaufgaben bleiben erhalten). Du wirst alle Ausrüstungsgegenstände verlieren, mit Ausnahme von Abonnenten-Gegenständen und kostenlosen Erinnerungsgegenständen, aber du wirst sie zurückkaufen können. Du wirst der richtigen Klasse angehören müssen, um klassenspezifische Ausrüstung erneut zu kaufen. Du behältst deine aktuelle Klasse, deine Errungenschaften sowie deine Haus- und Reittiere. Um das Zurücksetzen zu bestätigen, gib RESET in das Textfeld unten ein.</string>
|
||||
<string name="report_challenge_explanation">Du solltest eine Herausforderung nur melden, wenn sie gegen die [Community Richtlinien](https://habitica.com/static/community-guidelines) und/oder [AGB](https://habitica.com/static/terms) verstößt. Eine Falschmeldung ist ein Verstoß gegen Habiticas Community Richtlinien.</string>
|
||||
<string name="challenges">Herausforderungen</string>
|
||||
<string name="x_checkins">%d Anmeldungen</string>
|
||||
<string name="need_help_description">Du kannst uns kontaktieren und ein Mitglied unseres Teams wird sein Bestes tun, um dir zu helfen!</string>
|
||||
<plurals name="x_days">
|
||||
<item quantity="one">%d Tag</item>
|
||||
<item quantity="other">%d Tage</item>
|
||||
</plurals>
|
||||
<plurals name="x_hours">
|
||||
<item quantity="one">%d Stunde</item>
|
||||
<item quantity="other">%d Stunden</item>
|
||||
</plurals>
|
||||
<plurals name="x_minutes">
|
||||
<item quantity="one">%d Minute</item>
|
||||
<item quantity="other">%d Minuten</item>
|
||||
</plurals>
|
||||
<string name="subscribe_listitem3_description_alt">Rüste dich mit der neuesten exklusiven Ausrüstung aus. Abonniere jetzt um %1$ss %2$s zu erhalten!</string>
|
||||
<string name="subscription_hourglasses_3month_timeframe">Sanduhr in 3 Monaten</string>
|
||||
<string name="report_user_description">Dies wird auch @%s blockieren</string>
|
||||
<string name="report_formatted_name">%1$s
|
||||
\n@%2$s</string>
|
||||
<string name="report_successful">%s gemeldet</string>
|
||||
<string name="class_equipment_shop_dialog_new">Dieser Gegenstand ist nur für %s verfügbar. Öffne die Einstellungen, um deine Klasse zu ändern.</string>
|
||||
<string name="insufficient_level_equipment_dialog_new">Dieser Gegenstand ist nur für %s verfügbar. Nach Level 10 kannst du eine Klasse wählen.</string>
|
||||
<string name="register_tos_confirm">Du stimmst unseren <a href="https://habitica.com/static/terms">Nutzungsbedingungen</a> zu und hast unsere <a href="https://habitica.com/static/privacy">Datenschutzbestimmungen</a> gelesen.</string>
|
||||
<string name="report_hint">Begründung der Meldung</string>
|
||||
<string name="reset_account_description_new">Du wirst alle deine Level, Gold und Erfahrung verlieren. Alle deine Aufgaben und deren historischen Daten werden gelöscht (Herausforderungsaufgaben bleiben erhalten). Du wirst alle Ausrüstungsgegenstände verlieren, mit Ausnahme von Abonnenten-Gegenständen und kostenlosen Erinnerungsgegenständen, aber du wirst sie zurückkaufen können. Du wirst der richtigen Klasse angehören müssen, um klassenspezifische Ausrüstung erneut zu kaufen. Du behältst deine aktuelle Klasse, deine Errungenschaften sowie deine Haus- und Reittiere. Um das Zurücksetzen zu bestätigen, gib dein Passwort unten ein.</string>
|
||||
<string name="report_message_explanation">Du solltest nur Beiträge melden, die die [Community Richtlinien](https://habitica.com/static/community-guidelines) and/or [Terms of Service](https://habitica.com/static/terms) verletzen. Eine Falschmeldung ist ein Verstoß gegen Habiticas Community Richtlinien.</string>
|
||||
<string name="invited_to_guild_notification"><font color=#925CF3>%2$s</font> hat dich eingeladen der Gruppe <b>%1$s</b> beizutreten</string>
|
||||
<string name="report_player_title">%s melden\?</string>
|
||||
<string name="report_player">Spieler*in melden</string>
|
||||
<string name="report_user_explanation">Du solltest einen Spieler nur melden, wenn er gegen die [Community Richtlinien](https://habitica.com/static/community-guidelines) und/oder [AGB](https://habitica.com/static/terms) verstößt. Eine Falschmeldung ist ein Verstoß gegen Habiticas Community Richtlinien.</string>
|
||||
<string name="report_message">Nachricht melden</string>
|
||||
<string name="report_failure">Melden fehlgeschlagen, bitte versuche es später erneut</string>
|
||||
<string name="report_reason_title_challenge">Warum meldest du diese Herausforderung\?</string>
|
||||
<string name="cancel_subscription_apple_description">Du möchtest kein Abonnement mehr\? Aufgrund deines gewählten Zahlungsverfahrens musst du das Abonnement in den Einstellungen deines iOS Gerätes mit deiner Apple ID kündigen.</string>
|
||||
<string name="report_reason_title_message">Warum meldest du diese Nachricht\?</string>
|
||||
<string name="report_reason_title_player">Warum meldest du diese/n Spieler*in\?</string>
|
||||
<string name="exact_alarm_system_settings_reminders">Erinnerungen können verspätet erscheinen, da die Berechtigung nicht gegeben wurde. Klicke um Berechtigungen anzuzeigen und zu ändern.</string>
|
||||
<string name="exact_alarm_system_settings_description">Erlaube \"Wecker und Erinnerungen\" in der Einstellungen-App, damit Erinnerungen exakt zur richtigen Zeit erscheinen</string>
|
||||
<string name="sell_confirmation_title">Bist du dir sicher, dass du %s verkaufen möchtest\?</string>
|
||||
<string name="leave_party_confirmation_quest">Bist du sicher, dass du diese Party verlassen willst\?</string>
|
||||
<string name="rejoin_party_quest">Du kannst dieser Party ohne Einladung nicht erneut beitreten.
|
||||
\n
|
||||
\nWenn du diese Party verlässt, nimmst du auch nicht mehr an deiner aktuellen Quest teil.</string>
|
||||
<string name="level_abbreviated">Level %d</string>
|
||||
<string name="insufficient_level_equipment_dialog">Dieser Gegenstand ist nur für eine bestimmte Klasse verfügbar. Nach Level 10 kannst du eine Klasse wählen.</string>
|
||||
<string name="change_class_confirmation_message">Dadurch wird die Ausrüstung in den Geschäften freigeschaltet und die verfügbaren Fertigkeiten ändern sich</string>
|
||||
<string name="rejoin_party_quest_leader">Du kannst dich dieser Party nur wieder anschließen, wenn du eingeladen wirst.
|
||||
\n
|
||||
\nDurch das Verlassen der Party, wird auch die aktuelle Quest abgebrochen.</string>
|
||||
<string name="see_other_sub_options">Siehe weitere Optionen fürs Abonnement</string>
|
||||
<string name="send_invite">Einladung verschicken</string>
|
||||
<string name="report_player_toolbar_title">Melde %s</string>
|
||||
<string name="resubscribe_description_gift">Möchten Sie weiterhin die Vorteile eines Abonnements nutzen\? Sie können ein wiederkehrendes Abonnement abschließen, um Ihre Vorteile beizubehalten.</string>
|
||||
<string name="subscribers_mythic_hourglasses"><i>Abonnenten erhalten mystische Sanduhren innerhalb der ersten drei Tage des Monats.</i></string>
|
||||
<string name="assigned_to">Zugewiesen zu</string>
|
||||
<string name="completed_at">Erledigt am %s</string>
|
||||
<string name="benefits_end">Ende der Vorteile %s</string>
|
||||
<string name="username_or_email">Benutzername oder E-Mail-Adresse</string>
|
||||
<string name="sub_perk">ABO-VORTEIL</string>
|
||||
<string name="assigned_to_you_by">Dir zugeordnet von @%1$s in %2$s</string>
|
||||
<string name="pause_damage_1_description">Bosse fügen dir weiterhin Schaden zu, wenn andere Gruppenmitglieder ihre Tagesaufgaben verpapssen</string>
|
||||
<string name="you_re_looking_for_party">Du suchst eine Party!</string>
|
||||
<string name="subscribe_listitemArmoire_description">Erhalte jedes Mal zwei Chancen, neue Ausrüstung zu bekommen, wenn du den Verzauberten Schrank öffnest!</string>
|
||||
<string name="subscribe_incentive_button_armoire">Abonniere, um kostenlos erneut zu öffnen!</string>
|
||||
<string name="not_set" tools:override="true">Nicht gesetzt</string>
|
||||
<string name="mute_user_confirm">Möchtest du den Zugriff zum Chat für diesen Spieler widerrufen\?</string>
|
||||
<string name="shadow_muted_hidden">Unsichtbar stumm, ausgeblendet</string>
|
||||
<string name="daily_summary_description">Dies ist eine **%1$s** Aufgabe die sich **%2$s%3$s** wiederholt.</string>
|
||||
<string name="on_the_x_of_month">am %1$s %2$s des Monats</string>
|
||||
<string name="gryphatrice_description">Der seltene Jubilierende Greifatrix kommt zur Geburtstagsfeier! Verpasse nicht deine Chance auf dieses exklusive animierte Haustier.</string>
|
||||
<string name="for_for_free">Vier für umsonst</string>
|
||||
<string name="resume_damage_3_title">Dein Quest-Fortschritt wird wieder angewandt</string>
|
||||
<string name="pause_damage_2_description">Zähler für tägliche Strähnen und Gewohnheiten steigen nur, wenn dies eingeschaltet ist</string>
|
||||
<string name="pause_damage_3_title">Der Fortschritt deines Quests verbleibt bei Ausstehend</string>
|
||||
<string name="by_invite">Per Einladung</string>
|
||||
<string name="invited">Eingeladen</string>
|
||||
<string name="habiticans_send_invite">Sende eine Einladung direkt an Spieler*innen die du kennst</string>
|
||||
<string name="invite_with_username_email">Versende Einladungen per @Benutzername oder E-Mail</string>
|
||||
<string name="empty_food_description">Beende Aufgaben, kaufe einen Schrank oder gehe zum [Marktplatz](/shops/market) um mehr zu erhalten!</string>
|
||||
<string name="subscribe_incentive_text_armoire">Erhalte jede Mal eine zusätzliche Chance auf den Schrank, wenn du ihn mit einem Abonnement kaufst</string>
|
||||
<string name="quest_mechanics_pausing_title">Schaden pausieren</string>
|
||||
<string name="subscribe_listitemArmoire_description_2">Jedes Mal, wenn Du den Schrank öffnest, kannst Du ihn erneut kostenlos öffnen!</string>
|
||||
<string name="customization_shop_check_out">Besuche den Individualisierungsmarkt, um die vielen Möglichkeiten zur Anpassung Deines Avatar zu durchstöbern!</string>
|
||||
<string name="set">Set</string>
|
||||
<string name="get_12_mystic_hourglasses">Erhalte nach Abschluss deines ersten 12-monatigen Abonnements sofort 12 Mystische Sanduhren!</string>
|
||||
<string name="rescrubscribe_to_pick_up">Abonniere erneut, um dort weiterzumachen, wo Du aufgehört hast!</string>
|
||||
<string name="gem_cap_extra">%1$d/%2$d Edelsteinobergrenze</string>
|
||||
<string name="open_habitica_website">Habitica Webseite öffnen</string>
|
||||
<string name="limited_edition">Limitierte Ausgabe</string>
|
||||
<string name="todo_summary_description_duedate">Dies ist eine **%1$s** Aufgabe, fällig **%2$s**.</string>
|
||||
<string name="assign">Zuweisen</string>
|
||||
<string name="edit_assignees">Zuweisungen bearbeiten</string>
|
||||
<string name="assign_to">Zuweisen zu…</string>
|
||||
<string name="ban_user">Spieler*in bannen</string>
|
||||
<string name="promote_to_manager">Zum Manager befördern</string>
|
||||
<string name="member_list">Mitgliederliste</string>
|
||||
<string name="shadowmute_user_confirm">Möchtest du diese Spieler*in unsichtbar stummschalten\?</string>
|
||||
<string name="stripe_payment">Stripe</string>
|
||||
<string name="on_x">Am %s</string>
|
||||
<string name="on_weekdays">An Wochentagen</string>
|
||||
<string name="on_weekends">An Wochenenden</string>
|
||||
<string name="on_every_day_of_week">An jedem Tag der Woche</string>
|
||||
<string name="second">zweiten</string>
|
||||
<string name="third">dritten</string>
|
||||
<string name="fourth">vierten</string>
|
||||
<string name="fifth">fünften</string>
|
||||
<string name="animated_gryphatrice_pet">Jubilierender Greifatrix Haustier</string>
|
||||
<string name="plenty_of_potions">Jede menge Tränke</string>
|
||||
<string name="thanks_for_support">Danke für deine Unterstützung!</string>
|
||||
<string name="buy_for_x">Für %s kaufen</string>
|
||||
<string name="buy_for">Kaufen für</string>
|
||||
<string name="plenty_of_potions_description">Wir bringen 10 eurer liebsten magischen Schlüpfelixiere zurück. Gehe zum Marktplatz um dein Inventar zu füllen!</string>
|
||||
<string name="background">Hintergrund</string>
|
||||
<string name="resume_damage_1_description">Bosse fügen weiterhin Schaden zu wenn
|
||||
\nandere Mitglieder ihre Tagesaufgaben verpassen</string>
|
||||
<string name="pause_damage_2_title">Deine Strähnen und Zähler werden nicht zurückgesetzt</string>
|
||||
<string name="food_footer_description">Schau im [Marktplatz](/shops/market) nach um Futter oder einen Sattel zu kaufen, der dein Haustier sofort großzieht!</string>
|
||||
<string name="quests_footer_description">Schau im [Quest-Shop](/shops/quests) vorbei, um zu sehen, was verfügbar ist!</string>
|
||||
<string name="eggs_footer_description">Schau im [Markt](/shops/market) vorbei, um genau die Eier zu kaufen, die du brauchst!</string>
|
||||
<string name="hatchingPotions_footer_description">Schau im [Markt](/shops/market) vorbei, um Standard- und saisonale Bruttränke zu kaufen!</string>
|
||||
<string name="subscriber_benefit_used_faint">Du hast deine zweite Chance heute bereits genutzt. Sie ist wieder verfügbar in %s</string>
|
||||
<string name="quest_mechanics_collecting_title">Quest-Gegenstände sammeln</string>
|
||||
<string name="quest_mechanics_damage_title">Bosse schlagen zurück, wenn tägliche Aufgaben verpasst werden</string>
|
||||
<string name="buy_gems_with_gold">Kaufe Edelsteine mit Gold</string>
|
||||
<string name="preference_push_joined_group_plan_mention">\@Erwähnungen in Gruppenplänen</string>
|
||||
<string name="positive_and_negative">Positiv und Negativ</string>
|
||||
<string name="find_new_member">Finde Mitglieder</string>
|
||||
<string name="change_class_to_x">Wechsel Klasse zu %s</string>
|
||||
<string name="quests_footer_title">Möchtest du mehr Quests\?</string>
|
||||
<string name="open_profile">Profil öffnen</string>
|
||||
<string name="subscriber_button_armoire">Erneut kostenlos öffnen!</string>
|
||||
<string name="twelve_mystic_hourglasses">12 Mystische Sanduhren</string>
|
||||
<string name="thanks_for_subscribing">Danke für dein Abonnement</string>
|
||||
<string name="shadow_mute_user">Heimlich Stummschalten</string>
|
||||
<string name="mute_user">Spieler*in stumm schalten</string>
|
||||
<string name="ban_user_confirm">Willst du diese Spieler*in bannen\?</string>
|
||||
<string name="unban_user_confirm">Willst du diese Spieler*in entbannen\?</string>
|
||||
<string name="equipment_class_locked">Diese Ausrüstung ist aufgrund seiner Klasse gesperrt</string>
|
||||
<string name="manager">Manager*in</string>
|
||||
<string name="x_and_y">%1$s und %2$s</string>
|
||||
<string name="on_the_x">am %s</string>
|
||||
<string name="first">Zuerst</string>
|
||||
<string name="seeking_party_title">Suchst du nach einer Party\?</string>
|
||||
<string name="look_for_party">Suche eine Party</string>
|
||||
<string name="shop_armoire_title">Du besitzt bereits alle %s-Ausrüstung</string>
|
||||
<string name="empty_quests_description">Bekomme Quests vom Leveln, Anmeldebonus oder dem [Quest Shop](/shops/quests)!</string>
|
||||
<string name="empty_special_description_subscribed">Erhalte Verwandlungsgegenstände während der saisonalen Galas, und Mystery-Boxen mit Abonnenten-Ausrüstung werden zu Beginn jedes Monats geliefert!</string>
|
||||
<string name="item_footer_description">Schau im [Markt](/shops/market) vorbei, um genau das zu kaufen, was du brauchst!</string>
|
||||
<string name="subscribe_second_chance_incentive_text">Abonnenten erhalten jeden Tag eine zweite Chance im Leben und diese weiteren Vorteile!</string>
|
||||
<string name="watch_ad_to_hang_on">Schau dir eine Werbung an, um mit 1HP durchzuhalten!</string>
|
||||
<string name="rage">Wut</string>
|
||||
<string name="preference_push_invited_to_group_plan">Zu Gruppenplan eingeladen</string>
|
||||
<string name="nye">Silvester</string>
|
||||
<string name="valentines">Valentinstag</string>
|
||||
<string name="search_equipment">Ausrüstung suchen</string>
|
||||
<string name="day_x">Tag %d</string>
|
||||
<string name="visit_market">Markt besuchen</string>
|
||||
<string name="pause_damage_summary">Schaden ist aktiv. Mehr erfahren.</string>
|
||||
<string name="pause_damage_3_description">Schaden an einem Boss oder gefundene Sammelgegenstände wird gespeichert bis du Schaden wieder einschaltest</string>
|
||||
<string name="list">Liste</string>
|
||||
<string name="seeking_party_description">Möchtest du einer Party mit anderen beitreten, kennst aber niemanden\? Lasse Party-Leiter*innen wissen, dass du auf der Suche bist!</string>
|
||||
<string name="subscribe_second_armoire_open_text">Abonnenten erhalten zusätzliche Chancen auf den Schrank und diese weiteren Vorteile!</string>
|
||||
<string name="todo_summary_description">Dies ist eine **%1$s** Aufgabe die keine Fälligkeit hat.</string>
|
||||
<string name="jubilant_gryphatrice_confirmation_gift">Du hast den Jubilierenden Greifatrix verschenkt!</string>
|
||||
<string name="unmute_user_confirm">Möchtest du diese Spieler*in wieder Zugriff zum Chat geben\?</string>
|
||||
<string name="unshadowmute_user_confirm">Möchtest du die unsichtbare Stummschaltung aufheben\?</string>
|
||||
<string name="resume_damage_3_description">Ausstehender Schaden oder Sammelgegenstände werden beim nächsten Tages-Reset angewandt</string>
|
||||
<string name="open_settings">Öffne Einstellungen</string>
|
||||
<string name="a_party_robe">Eine Party-Robe</string>
|
||||
<string name="empty_potions_description">Schließe Aufgaben oder Elixier-Quests ab, oder gehe zum [Marktplatz](/shops/market) um mehr zu erhalten!</string>
|
||||
<string name="empty_eggs_description">Schließe Aufgaben und Ei-Quests ab oder besuche den [Markt](/shops/market), um dich einzudecken!</string>
|
||||
<string name="item_footer_title">Bekommst du nicht das richtige %s aus Aufgaben\?</string>
|
||||
<string name="subriber_benefit_notification_faint">Du hast eine zweite Chance mit 1HP erhalten!</string>
|
||||
<string name="quest_mechanics_collecting_description">Wenn du auf einer Sammel-Quest bist, schließe deine Aufgaben ab, um eine zufällige Chance zu haben, einen Quest-Gegenstand zu finden. Ausstehende Gegenstände werden beim nächsten Tages-Reset angewendet. Wahrnehmung erhöht die Wahrscheinlichkeit, Gegenstände zu finden.</string>
|
||||
<string name="auth_unknown_error">Unbekannter Fehler bei der Authentifizierung.</string>
|
||||
<string name="twenty_gems">20 Edelsteine</string>
|
||||
<string name="birthday_title_description">Feiere Habiticas 10. Geburtstag mit Geschenken und exklusiven Gegenständen!</string>
|
||||
<string name="pause_damage">Pausiere Schaden</string>
|
||||
<string name="pause_damage_1_title">"Deine verpassten Tagesaufgaben fügen dir keinen Schaden zu "</string>
|
||||
<string name="quest_mechanics">Quest-Mechaniken</string>
|
||||
<string name="quest_mechanics_boss_title">Einem Boss Schaden zufügen</string>
|
||||
<string name="quest_mechanics_boss_description">Schließe jede Art von Aufgabe ab oder nutze Fähigkeiten, um ausstehende Schäden zu sammeln! Der Schaden wird beim nächsten Tages-Reset angewendet. Stärke beeinflusst, wie viel Schaden du anrichtest.</string>
|
||||
<string name="resume_damage_summary">Schaden ist pausiert.</string>
|
||||
<string name="habiticans_looking_party">Hier ist die Liste der Spieler*innen, welche einer Party beitreten möchten</string>
|
||||
<string name="habiticans_looking_party_empty">Gerade sucht niemand nach einer Party. Du kannst später noch mal nachschauen!</string>
|
||||
<string name="seeking_hint">Halte Ausschau nach Einladungen oder starte jederzeit eine eigene Party</string>
|
||||
<string name="no_x">Kein %s</string>
|
||||
<string name="potions">Tränke</string>
|
||||
<string name="customize_avatar">Avatar anpassen</string>
|
||||
<string name="share_avatar">Avatar teilen</string>
|
||||
<string name="subscribe_listitemArmoire">Doppelte Schrank-Belohnungen</string>
|
||||
<string name="subscriber_button_faint">Zweite Chance: Halte durch mit 1HP!</string>
|
||||
<string name="subscribe_incentive_text_faint">Mit einem Abonnement erhälst du jeden Tag eine zweite Chance, um zu verhindern, dass dir die HP ausgehen</string>
|
||||
<string name="customization_shop_more">Im Anpassungsshop findest du noch mehr Möglichkeiten, deinen Avatar individuell zu gestalten!</string>
|
||||
<string name="turkey_day">Truthahntag</string>
|
||||
<string name="auth_get_credentials_error">Fehler beim Abrufen der Anmeldedaten zur Authentifizierung.</string>
|
||||
<string name="resume_damage_2_description">Erfüllst du eine Tagesaufgabe nicht, wird deine Strähne und der Zähler normal zurückgesetzt</string>
|
||||
<string name="gem_cap">Edelsteinobergrenze</string>
|
||||
<string name="auth_invalid_credentials">Ungültige Anmeldedaten erhalten.</string>
|
||||
<plurals name="items_pending">
|
||||
<item quantity="one">%d Gegenstand ausstehend</item>
|
||||
<item quantity="other">%d Gegenstände ausstehend</item>
|
||||
</plurals>
|
||||
<string name="g1g1_promo_info_limitations_noutc">Dies ist ein zeitlich begrenztes Event das am %1$s startet und am %2$s endet. Die Aktion gilt nur für Geschenke an andere Benutzer. Wenn du oder der Empfänger des Geschenks bereits ein Abonnement haben, werden die Monate als Guthaben an das Abonnement angehängt und benutzt, sobald das aktuelle Abonnement endet oder gekündigt wird.</string>
|
||||
<string name="never">Niemals</string>
|
||||
<string name="find_more_members">Finde mehr Mitglieder</string>
|
||||
<string name="go_to_settings">Gehe zu Einstellungen</string>
|
||||
<string name="resume_damage">Schaden fortsetzen</string>
|
||||
<string name="resume_damage_1_title">Deine verpassten Tagesaufgaben werden dir keinen Schaden zufügen</string>
|
||||
<string name="resume_damage_2_title">Deine Strähnen und Zähler werden zurückgesetzt</string>
|
||||
<string name="shop_armoire_description">Neue Ausrüstung wird zu saisonalen Galas veröffentlicht. Bis dahin gibt es %d Ausrüstungsgegenstände im Verzauberten Schrank zu finden!</string>
|
||||
<string name="shop_armoire_empty_description">Neue Ausrüstung wird zu saisonalen Galas veröffentlicht. Auch der Verzauberte Schrank bekommt monatlich neue Gegenstände!</string>
|
||||
<string name="unlock_gear_and_skills">Schalte %s Ausrüstung und Fähigkeiten frei</string>
|
||||
<string name="rescind_invite">Beende Einladung</string>
|
||||
<string name="rescinded">Aufgehoben</string>
|
||||
<string name="pending_invite">Einladung ausstehend</string>
|
||||
<string name="invalid_input">Eingabe ungültig</string>
|
||||
<string name="username_requirements">Darf nur die Buchstaben a-z, Nummern, Bindestrich und Unterstrich enthalten</string>
|
||||
<string name="contributor_tiers">Mitwirkenden-Level</string>
|
||||
<string name="contrib_tier_description">Wenn du einen Account mit farbigem Benutzernamen und einem Symbol siehst, dann handelt es sich um einen Mitwirkenden-Rang der Person. Ränge werden an Personen vergeben, die im Zusammenhang mit Habitica mitgeholfen haben, z.B. durch Übersetzungen, Code oder hilfreiches Verhalten. Je höher der Rang, desto mehr hat die Person zu Habitica beigetragen.</string>
|
||||
<string name="visit_faq">Besuche FAQ</string>
|
||||
<string name="unban_user">Spieler*in-Bann aufheben</string>
|
||||
<string name="status">Status</string>
|
||||
<string name="regular_access">Regulärer Zugriff</string>
|
||||
<string name="message_flagged">Nachricht %d mal gemeldet.</string>
|
||||
<string name="amazon">Amazon</string>
|
||||
<string name="apple_pay">Apple Pay</string>
|
||||
<string name="google_pay">Google Pay</string>
|
||||
<string name="paypal">PayPal</string>
|
||||
<string name="unshadowmute_user">Entferne unsichtbare Stummschaltung</string>
|
||||
<string name="unmute_user">Stummschaltung aufheben</string>
|
||||
<string name="for_for_free_description">Um die Party in Schwung zu halten, verschenken wir Party-Roben, 20 Edelsteine sowie ein limitiertes Umhang-Set und Hintergrund!</string>
|
||||
<string name="birthday_limitations">Dies ist eine zeitlich begrenzte Veranstaltung, die am %1$s beginnt und am %2$s endet. Die limitierte Ausgabe des Jubilierenden Greifatrix und der zehn Magischen Schlüpfelixiere können in dieser Zeit gekauft werden. Die anderen Geschenke im Gratis-Bereich werden automatisch an alle Accounts ausgegeben, die in den 30 Tagen vor dem Datum der Geschenkübergabe aktiv waren. Accounts, die nach der Geschenkübergabe erstellt wurden, haben keinen Anspruch darauf.</string>
|
||||
<string name="visit_the_market">Markt besuchen</string>
|
||||
<string name="exclusive_items_await">Exklusive Gegenstände und Geschenke warten</string>
|
||||
<string name="ends_in_x">Endet in %s</string>
|
||||
<string name="see_more">Mehr anzeigen</string>
|
||||
<string name="jubilant_gryphatrice_confirmation">Du hast den Jubilierenden Greifatrix gekauft!</string>
|
||||
<string name="undo">Rückgängig</string>
|
||||
<string name="birthday_set">Geburtstags-Set</string>
|
||||
<string name="you_equipped_x">Du hast dich mit %s ausgerüstet</string>
|
||||
<string name="purchase_gryphatrice_confirmation">Kaufe den Jubilierenden Greifatrix für %d Edelsteine\?</string>
|
||||
<plurals name="repeat_daily">
|
||||
<item quantity="one">jeden Tag</item>
|
||||
<item quantity="other">alle %d Tage</item>
|
||||
</plurals>
|
||||
<plurals name="repeat_weekly">
|
||||
<item quantity="one">jede Woche</item>
|
||||
<item quantity="other">alle %d Wochen</item>
|
||||
</plurals>
|
||||
<plurals name="repeat_monthly">
|
||||
<item quantity="one">jeden Monat</item>
|
||||
<item quantity="other">alle %d Monate</item>
|
||||
</plurals>
|
||||
<plurals name="repeat_yearly">
|
||||
<item quantity="one">jedes Jahr</item>
|
||||
<item quantity="other">alle %d Jahre</item>
|
||||
</plurals>
|
||||
<string name="faint_loss_description_alt">Du fällst auf Level <b>%1$d</b> zurück, verlierst <b>%2$d</b> Gold und deine Erfahrung ... Du kannst mit harter Arbeit alles wieder zurückerlangen!</string>
|
||||
<string name="gems_promo_info_limitations_fixed">Diese Aktion gilt nur während des zeitlich begrenzten Events. Das Event beginnt am %1$s (%2$s) und endet am %3$s (%4$s). Die Aktion gilt nur für Edelsteine welche du für dich selbst kaufst.</string>
|
||||
<string name="task_summary">Zusammenfassung der Aufgaben</string>
|
||||
<string name="habit_summary_description">Dies ist eine **%1$s** Gewohnheit, die **%2$s** ist.</string>
|
||||
<string name="use_skill">Verwende Fähigkeit</string>
|
||||
</resources>
|
||||
|
|
@ -1015,9 +1015,9 @@
|
|||
<string name="rejoin_guild">Puedes realistarte al gremio desde el submenú \"Gremios > Descubre Gremios\"</string>
|
||||
<string name="rejoin_party">No podrás volver a unirte al equipo a no ser que te vuelvan a invitar.</string>
|
||||
<string name="available_for">Disponible durante %s</string>
|
||||
<string name="hourglasses_description">Los relojes de arena Místicos son una forma de moneda extremadamente rara que solo puedes recibir por suscribirte a Habitica durante tres meses consecutivos o más. Se utilizan en la tienda del Viajero del tiempo para comprar conjuntos de equipamiento, mascotas, monturas, fondos animados o incluso misiones especiales.
|
||||
<string name="hourglasses_description">Los Relojes de arena Místicos son un tipo de divisa extremadamente raro que solo puedes recibir por suscribirte a Habitica. Se utilizan en la Tienda de los Viajeros del Tiempo para comprar Conjuntos de Equipamiento por Suscripción de meses pasados, Macostas exclusivas, Monturas, Fondos animados o incluso ¡Misiones especiales! Estos beneficios adicionales son una excelente manera de mantenerte motivado durante el año y gratificarte por un trabajo bien hecho.
|
||||
\n
|
||||
\nPuedes recibir hasta cuatro elojes de arena Místicos al año. El tiempo que reciben las recompensas se basa en el calendario de renovación de su suscripción. Se envían el primer día de un nuevo mes después de su último pago de suscripción que lo calificó para un reloj de arena. Consulte la página [Suscripción] para obtener más detalles.</string>
|
||||
\nLos suscriptores reciben un Reloj de arena Místico al principio de cada mes en el cual se disfruta de los beneficios de suscriptor, además de muchos otros beneficios. ¡Comprueba nuestras opciones de suscripción si estás interesado en lo que los Viajeros del Tiempo tienen para ofrecerte!</string>
|
||||
<string name="leave_delete_tasks">Abandonar y borrar tareas</string>
|
||||
<string name="premium_currency">Divisas premium</string>
|
||||
<string name="month_reminder_text">A veces un nuevo comienzo es la mejor opción, ¡Habitica puede ayudarte!</string>
|
||||
|
|
@ -1618,4 +1618,5 @@
|
|||
</plurals>
|
||||
<string name="next_switch_in_x">Próximo cambio en %s</string>
|
||||
<string name="register_tos_confirm">Estás de acuerdo con nuestros <a href="https://habitica.com/static/terms">Terminos de Servicio</a> y has leído nuestra <a href="https://habitica.com/static/privacy">Política de Privacidad</a>.</string>
|
||||
<string name="use_skill">Usar Habilidad</string>
|
||||
</resources>
|
||||
|
|
@ -1620,4 +1620,10 @@
|
|||
</plurals>
|
||||
<string name="next_switch_in_x">Prochaine sélection dans %s</string>
|
||||
<string name="register_tos_confirm">Vous acceptez nos <a href="https://habitica.com/static/terms">Conditions d\'Utilisation</a> et avez lu notre <a href="https://habitica.com/static/privacy">Politique de Confidentialité</a>.</string>
|
||||
<string name="use_skill">Utiliser une Compétence</string>
|
||||
<string name="photo_url_description">Vous pouvez ajouter une image sur votre profil Habitica qui pourra être vue par les autres en ajoutant un lien vers l\'image ici.</string>
|
||||
<string name="save_photo_url">Enregistrer le lien vers la Photo</string>
|
||||
<string name="change_email_description">C\'est l\'adresse mail que vous utilisez pour vous connecter à Habitica, ainsi que pour recevoir des notifications.</string>
|
||||
<string name="change_username_description">Les noms d\'utilisat·eur·rices doivent comprendre entre 1 et 20 caractères, composés de lettres de A à Z, de numéros de 0 à 9, de trait d\'union et/ou de tiret bas.</string>
|
||||
<string name="change_display_name">Changer le Pseudo</string>
|
||||
</resources>
|
||||
|
|
@ -259,8 +259,8 @@
|
|||
<string name="setup_task_teams_2">A csapat tájékoztatása az állapotról</string>
|
||||
<string name="setup_task_teams_3">Csapatprojekt befejezése</string>
|
||||
<string name="quest_items_found">%d küldetési tárgyat találtál</string>
|
||||
<string name="armoireFood_new">A Megbűvölt komódban kutakodva élelmet találsz. Hogy került ez ide\?</string>
|
||||
<string name="armoireExp">Megharcolsz a Megbűvölt komóddal és tapasztalatot szerzel. Ez az!</string>
|
||||
<string name="armoireFood_new">A Megbűvölt komódban kutakodva élelmet találtál. Hogy került ez ide\?</string>
|
||||
<string name="armoireExp">Megharcoltál a Megbűvölt komóddal és tapasztalatot szereztél. Ez az!</string>
|
||||
<string name="sell_confirmation_title">Biztosan el akarod adni ezt: %s\?</string>
|
||||
<string name="sell_no_price">Eladás</string>
|
||||
<string name="invite_party">Csapat meghívása</string>
|
||||
|
|
@ -294,7 +294,7 @@
|
|||
<item quantity="one">%d óra</item>
|
||||
<item quantity="other">%d óra</item>
|
||||
</plurals>
|
||||
<string name="armoireEquipment_new">Találsz egy ritka felszerelést a Megbűvölt komódban!</string>
|
||||
<string name="armoireEquipment_new">Találtál egy ritka felszerelést a Megbűvölt komódban!</string>
|
||||
<plurals name="x_days">
|
||||
<item quantity="one">%d nap</item>
|
||||
<item quantity="other">%d nap</item>
|
||||
|
|
@ -308,8 +308,8 @@
|
|||
<string name="due">Esedékes</string>
|
||||
<string name="cancel_subscription">Előfizetés lemondása</string>
|
||||
<string name="visit_habitica_website">Habitica weboldal meglátogatása</string>
|
||||
<string name="months_subscribed">Előfizetett hónapok</string>
|
||||
<string name="monthly_gem_cap">Havi gyémánt keret</string>
|
||||
<string name="months_subscribed">hónapja előfizetve</string>
|
||||
<string name="monthly_gem_cap">havi gyémánt keret</string>
|
||||
<string name="cancel_subscription_apple_description">Nem szeretnél tovább előfizetni\? A fizetési módod miatt az előfizetésedet az Apple ID előfizetési beállításain keresztül kell lemondanod az iOS eszközödön.</string>
|
||||
<string name="setup_task_chores_3">Szekrény rendszerezése</string>
|
||||
<string name="level_abbreviated">%d. szint</string>
|
||||
|
|
@ -531,7 +531,7 @@
|
|||
<string name="insufficient_level_equipment_dialog">Ez a tárgy csak egy adott kaszt számára érhető el. Kasztot a 10. szint után választhatsz.</string>
|
||||
<string name="insufficient_level_equipment_dialog_new">Ez a tárgy csak %s számára érhető el. Kasztot a 10. szint után választhatsz.</string>
|
||||
<string name="clear">Törlés</string>
|
||||
<string name="no_billing_gems">A készülékeden nem található egyetlen támogatott fizetési mód sem. Kérlek, használd a Habitica weboldalát, ha gyémántokat szeretnél vásárolni.</string>
|
||||
<string name="no_billing_gems">Az eszközödön nincs elérhető támogatott fizetési mód. Ha gyémántot szeretnél vásárolni, használd a Habitica weboldalát.</string>
|
||||
<string name="unsaved_changes">Nem mentett változások</string>
|
||||
<string name="gold_reward">%d arany</string>
|
||||
<string name="insufficientGold">Több feladatot kell teljesítened, mielőtt megengedhetnéd magadnak ezt a tárgyat!</string>
|
||||
|
|
@ -604,10 +604,10 @@
|
|||
<string name="welcome_back">Üdv újra itt</string>
|
||||
<string name="check_into_inn">Bejelentkezés a fogadóba</string>
|
||||
<string name="insufficientGems">Több gyémántra lesz szükséged a vásárláshoz!</string>
|
||||
<string name="guidelines_description">A Habitica célja, hogy barátságos környezetet biztosítson minden korkaszt és háttér számára. Ha kérdésed van, kérlek, olvasd el az irányelveinket.</string>
|
||||
<string name="guidelines_description">A Habitica arra törekszik, hogy minden korosztály és háttér előtt nyitott, barátságos környezetet teremtsen. Ha kérdésed van, olvasd el az irányelveinket.</string>
|
||||
<string name="get_hourglasses">Homokórák beszerzése</string>
|
||||
<string name="navigation_drawer_close">Zárd be a navigációs menüt</string>
|
||||
<string name="no_billing_subscriptions">A készülékeden nem található egyetlen támogatott fizetési mód sem. Kérlek, használd a Habitica weboldalát, ha előfizetést szeretnél vásárolni.</string>
|
||||
<string name="no_billing_subscriptions">Az eszközödön nincs elérhető támogatott fizetési mód. Ha előfizetést szeretnél vásárolni, használd a Habitica weboldalát.</string>
|
||||
<string name="tiers_descriptions">A csevegésben megjelenő színes felhasználónevek a közreműködők rangját jelzik. Minél magasabb a rang, annál többet járult hozzá a játékhoz művészettel, kóddal, a közösség támogatásával vagy más módon!</string>
|
||||
<string name="quest_owner_rewards">Küldetésindító jutalmai</string>
|
||||
<string name="world_boss">Főellenség</string>
|
||||
|
|
@ -644,7 +644,7 @@
|
|||
<string name="quest_leader_header">Indította: %s</string>
|
||||
<string name="reloaded_content">Tartalom újratöltve</string>
|
||||
<string name="available_until">Elérhető eddig: %s</string>
|
||||
<string name="available_for">Még elérhető: %s</string>
|
||||
<string name="available_for">Elérhető eddig: %s</string>
|
||||
<string name="gems_left_max">Havi gyémántok: %1$d/%2$d maradt</string>
|
||||
<string name="rejoin_party">Nem fogsz tudni újra csatlakozni ehhez a csapathoz, hacsak meg nem hívnak újra.</string>
|
||||
<string name="gems_left_nomax">Havi gyémántok: %d maradt</string>
|
||||
|
|
@ -1203,7 +1203,7 @@
|
|||
<string name="levelup_detail_10">Válassz egy kasztot, amely illik a játékstílusodhoz, és oldj fel speciális képességeket és páncélokat, hogy segítsenek az utadon</string>
|
||||
<string name="sent_card">Elküldtél egy %s kártyát</string>
|
||||
<string name="unpin">Rögzítés feloldása</string>
|
||||
<string name="take_me_back">Vissza akarok menni</string>
|
||||
<string name="take_me_back">Vigyél vissza</string>
|
||||
<string name="sidebar_achievements">Kitüntetések</string>
|
||||
<string name="inbox_messages_title_single">Új üzenet érkezett %1$s felhasználótól</string>
|
||||
<string name="inbox_messages_title">%1$d új üzenet érkezett %2$s felhasználótól</string>
|
||||
|
|
@ -1233,7 +1233,7 @@
|
|||
<string name="resubscribe_description">Szeretnéd folytatni az előnyeid élvezetét\? Indíthatsz új előfizetést, mielőtt a jelenlegi lejár, így folyamatosan aktív marad.</string>
|
||||
<string name="gifted">Ajándékozva</string>
|
||||
<string name="unlock_level">A(z) %d. szint elérésével oldható fel</string>
|
||||
<string name="unlock_previous_short">Fejezd be a(z) %d. küldetést</string>
|
||||
<string name="unlock_previous_short">%d. küldetés után</string>
|
||||
<string name="level_unabbreviated">%d. szint</string>
|
||||
<string name="not_participating">Nem veszel részt</string>
|
||||
<string name="quest_completed">Küldetés teljesítve!</string>
|
||||
|
|
|
|||
|
|
@ -429,12 +429,12 @@
|
|||
<string name="resetting_account">Me-reset Akun</string>
|
||||
<string name="deleting_account">Menghapus Akun</string>
|
||||
<string name="insufficientGold">Kamu perlu menyelesaikan lebih banyak tugas sebelum bisa membeli item ini!</string>
|
||||
<string name="insufficientGems">Kamu perlu lebih banyak Permata untuk membeli item ini!</string>
|
||||
<string name="insufficientGems">Anda memerlukan lebih banyak Permata untuk membeli ini!</string>
|
||||
<string name="purchase_gems">Beli permata</string>
|
||||
<string name="insufficientHourglasses">Kamu perlu lebih banyak Jam Pasir Mistis untuk membeli item ini!</string>
|
||||
<string name="get_hourglasses">Dapatkan jam pasir</string>
|
||||
<string name="subscribe_for_hourglasses">Berlangganan untuk Jam Pasir</string>
|
||||
<string name="timetravelers_closed_description">Dapatkan satu Jam Pasir Mistis setiap tiga bulan berturut-turut kamu berlangganan, lalu gunakan untuk membuka item-item edisi terbatas, peliharaan, dan tunggangan dari masa lalu… dan masa depan!</string>
|
||||
<string name="timetravelers_closed_description">Dapatkan satu Mystic Hourglass untuk setiap bulan berlangganan, lalu gunakan untuk membuka item edisi terbatas, hewan peliharaan, dan tunggangan dari masa lalu… dan masa depan!</string>
|
||||
<string name="want_to_subscribe">Aku mau Berlangganan</string>
|
||||
<string name="seasonal_closed_description">Grand Gala diadakan dekat dengan titik balik matahari dan ekuinoks, jadi cek kembali nanti untuk menemukan sekumpulan seru item musiman yang spesial!</string>
|
||||
<string name="come_back_soon">Datang kembali nanti!</string>
|
||||
|
|
@ -486,12 +486,12 @@
|
|||
<string name="class_equipment">Perlengkapan</string>
|
||||
<string name="equipment_empty">Kamu sudah punya semua perlengkapan! Lebih banyak lagi tersedia sewaktu Grand Gala, dekat titik balik matahari dan ekuinoks.</string>
|
||||
<string name="classless">Tanpa Pekerjaan</string>
|
||||
<string name="class_equipment_shop_dialog">Item ini hanya tersedia untuk pekerjaan tetentu. \nKamu bisa menggantu pekerjaanmu dari Pengaturan.</string>
|
||||
<string name="class_equipment_shop_dialog">Item ini hanya tersedia untuk kelas tertentu. Anda dapat mengubah kelas Anda dari Pengaturan.</string>
|
||||
<string name="class_gear_disclaimer">Kamu hanya bisa membeli perlengkapan untuk pekerjaanmu yang sekarang</string>
|
||||
<string name="check_into_inn">Masuk ke Penginapan</string>
|
||||
<string name="community_guidelines">Pedoman Komunitas</string>
|
||||
<string name="view_community_guidelines">Lihat Pedoman Komunitas</string>
|
||||
<string name="guidelines_description">Habitica mengusahakan untuk membuat sebuah lingkungan yang ramah untuk pengguna segala usia dan latar belakang, khususnya di tempat umum seperti Kedai Minuman. Jika kamu ada pertanyaan, silahkan periksa pedoman kami.</string>
|
||||
<string name="guidelines_description">Habitica berupaya menciptakan lingkungan yang ramah bagi pemain dari segala usia dan latar belakang. Jika Anda memiliki pertanyaan, silakan baca panduan kami.</string>
|
||||
<string name="helpful_links">Tautan Berguna</string>
|
||||
<string name="view_faq">Lihat FAQ</string>
|
||||
<string name="report_bug">Laporkan Gangguan</string>
|
||||
|
|
@ -507,7 +507,7 @@
|
|||
<string name="strike_description_title">Apakah itu Tingkatan Kemarahan?</string>
|
||||
<string name="strike_active_title">Sang %s telah Diserang!</string>
|
||||
<string name="strike_active_description">%1$s kita yang tercinta hancur sewaktu sang %2$s meremukkan %3$s. Cepat, kalahkan tugas-tugasmu untuk mengalahkan sang monster dan bantu membangun kembali!!</string>
|
||||
<string name="questShop">Toko Misi</string>
|
||||
<string name="questShop">Pencarian</string>
|
||||
<string name="market_owner_long">Alex sang Pedagang</string>
|
||||
<string name="tavern_owner_long">Daniel sang penjaga penginapan</string>
|
||||
<string name="stable_owner_long">Matt sang penguasa hewan buas</string>
|
||||
|
|
@ -651,13 +651,13 @@
|
|||
<string name="usernamePromptDisclaimer">Nama pengguna harus sesuai dengan <a href="https://habitica.com/static/terms">Syarat Layanan</a> dan <a href="https://habitica.com/static/community-guidelines">Pedoman Komunitas</a>. Jika kamu sebelumnya belum membuat nama masuk, nama penggunamu akan dibuat otomatis.</string>
|
||||
<string name="stat_description">Semua karakter di Habitica memiliki 4 status (stat) yang berpengaruh pada permainan di Habitica.
|
||||
\n
|
||||
\n**Kekuatan (KUAT)** berpengaruh pada pukulan kritikal dan menaikkan dampak kerusakan pada Misi Boss. Prajurit dan Pencuri mendapat KUAT dari perlengkapan kelas mereka.
|
||||
\n**Kekuatan (STR)** berpengaruh pada pukulan kritikal dan menaikkan dampak kerusakan pada Boss misi. Profesi Prajurit dan Pencuri mendapat STR dari perlengkapan profesi mereka.
|
||||
\n
|
||||
\n**Ketahanan (THN)** berpengaruh pada kenaikan HP dan membuatmu lebih tahan serangan. Penyembuh dan Prajurit mendapat THN dari perlengkapan kelas mereka.
|
||||
\n**Ketahanan (CON)** berpengaruh pada kenaikan HP dan membuatmu lebih tahan serangan. Profesi Penyembuh dan Prajurit mendapat CON dari perlengkapan profesi mereka.
|
||||
\n
|
||||
\n**Kecerdasan (CRDS)** berpengaruh pada kenaikan EXP yang didapat dan memberimu lebih banyak Mana. Penyihir dan Penyembuh mendapat CRDS dari perlengkapan kelas mereka.
|
||||
\n**Kecerdasan (INT)** berpengaruh pada kenaikan EXP yang didapat dan memberimu lebih banyak Mana. Profesi Penyihir dan Penyembuh mendapat INT dari perlengkapan profesi mereka.
|
||||
\n
|
||||
\n**Persepsi (PERS)** berpengaruh pada banyaknya emas yang didapat dan seberapa sering mendapat item yang dijatuhkan. Pencuri dan Penyihir mendapat PERS dari perlengkapan kelas mereka.
|
||||
\n**Persepsi (PER)** berpengaruh pada banyaknya emas yang didapat dan seberapa sering mendapat item yang dijatuhkan. Profesi Pencuri dan Penyihir mendapat PER dari perlengkapan profesi mereka.
|
||||
\n
|
||||
\nSetelah mencapai level 10, kamu mendapat 1 Poin Stat pada setiap kenaikan level yang dapat kamu alokasikan pada stat yang kamu mau. Kamu juga bisa memakai perlengkapan dengan kombinasi kenaikan stat yang berbeda-beda.</string>
|
||||
<string name="empty_discover_description">Pergi ke tab Temukan untuk mencari yang lain untuk bergabung!</string>
|
||||
|
|
@ -692,13 +692,13 @@
|
|||
<string name="daily_tip_4">Sesekali evaluasi kembalilah tugasmu agar kamu tetap di jalur yang benar.</string>
|
||||
<string name="support_bug_title">Kami secara konsisten berusaha agar aplikasi kami lebih baik berdasarkan feedback dari pemain namun terkadang beberapa bug muncul…</string>
|
||||
<string name="contact_us">Hubungi Kami</string>
|
||||
<string name="resubscribe_description">Masih ingin melanjutkan keuntunganmu\? Kamu dapat memulai berlangganan baru sebelum masa berlangganan ini selesai agar keuntunganmu tetap aktif.</string>
|
||||
<string name="resubscribe_description">Masih ingin melanjutkan keuntunganmu\? Kamu dapat memulai langganan baru sebelum masa berlangganan ini selesai agar keuntunganmu tetap aktif.</string>
|
||||
<string name="remove_member_confirm">Apakah kamu yakin ingin mengeluarkan %s dari grup\?</string>
|
||||
<string name="transfer_ownership_confirm_message">Ini akan membuat %s sebagai pemimpin baru Party</string>
|
||||
<string name="transfer_ownership_confirm">Memindahkan Kepimpinan\?</string>
|
||||
<string name="transfer_ownership_confirm">Memindahkan Kepemimpinan\?</string>
|
||||
<string name="confirm_deletion">Konfirmasi penghapusan</string>
|
||||
<string name="leave_guild_challenges_confirmation">Apakah kamu ingin tetap berpartisipasi dalam Tantangan saat meninggalkan Perkumpulan\?</string>
|
||||
<string name="leave_guild_confirmation">Apakah kamu yakin ingin keluar dari perkumpulan ini\?</string>
|
||||
<string name="leave_guild_confirmation">Apakah kamu yakin ingin keluar dari guild ini\?</string>
|
||||
<string name="gift_confirmation_text_sub">Kamu mengirimkan %1$s berlangganan %2$s-bulan Habitica.</string>
|
||||
<string name="gift_confirmation_text_sub_g1g1">Kamu mengirimkan %1$s sebuah berlangganan %2$s-bulan dan berlangganan yang sama telah diterapkan pada akunmu melalui promo kami Beri Satu Dapat Satu!</string>
|
||||
<string name="update_available">Update tersedia: %1$s (%2$d)</string>
|
||||
|
|
@ -736,9 +736,9 @@
|
|||
<string name="damage_paused">Kerusakan ditunda</string>
|
||||
<string name="discover">Telusuri</string>
|
||||
<string name="gift_gems_subtitle">Pilihlah paket permata yang ingin kamu hadiahkan!</string>
|
||||
<string name="gift_subscription_subtitle">Pilihlah berlangganan yang ingin kamu berikan dibawah! Pembelian ini tidak akan diperbarui secara otomatis.</string>
|
||||
<string name="gift_subscription_subtitle">Pilih langganan yang ingin Anda berikan sebagai hadiah! Pembelian ini tidak akan diperpanjang.</string>
|
||||
<string name="gift_one_get_one_detailed_description">Selama promosi ini aktif, kamu akan mendapatkan berlangganan yang sama secara otomatis setelah kamu memberikannya.</string>
|
||||
<string name="gift_one_get_one">Berikan satu ,Dapatkan satu!</string>
|
||||
<string name="gift_one_get_one">Hadiah satu, Dapatkan satu</string>
|
||||
<string name="gift_title">Kepada siapa ingin kamu hadiahkan\?</string>
|
||||
<string name="gift_one_get_one_description">Even beri satu berlangganan gratis satu berlangganan sedang berlangsung!</string>
|
||||
<string name="gift_subscription">Hadiahkan Berlangganan</string>
|
||||
|
|
@ -746,10 +746,10 @@
|
|||
<string name="guild_members">Anggota Guild</string>
|
||||
<string name="guild_summary">Ringkasan Guild</string>
|
||||
<string name="setup_task_habit_2_notes">Atau hapus ini dari layar edit</string>
|
||||
<string name="setup_task_habit_2">Sentuh disini untuk mengedit ini menjadi kebiasaan buruk yang ingin kamu hentikan</string>
|
||||
<string name="setup_task_habit_2">Sentuh disini untuk mengubah ini menjadi kebiasaan buruk yang ingin kamu tinggalkan</string>
|
||||
<string name="setup_task_habit_1_notes">Apakah itu Kebiasaan, Harian, atau To Do</string>
|
||||
<string name="setup_task_habit_1">Tambahkan tugas ke Habitica</string>
|
||||
<string name="cost">Menggunakan</string>
|
||||
<string name="cost">biaya</string>
|
||||
<string name="joined_guild">Kamu bergabung dengan guild</string>
|
||||
<string name="left_guild">Kamu meninggalkan guild</string>
|
||||
<string name="rejoin_guild">Kamu dapat bergabung kembali dengan guild ini melalui halaman Telusuri Guild</string>
|
||||
|
|
@ -765,30 +765,30 @@
|
|||
<string name="need_more_help">Butuh bantuan lebih lanjut\?</string>
|
||||
<string name="need_help_header_description">Post sebuah pesan di %s agar pertanyaanmu dapat dijawab oleh pemain lain.</string>
|
||||
<string name="from_balance">Dari Saldo</string>
|
||||
<string name="purchase">Membeli</string>
|
||||
<string name="purchase">Beli</string>
|
||||
<string name="preference_push_party_mention">\@Mentions di Party</string>
|
||||
<string name="more_out_of_habitica">Dapatkan yang lebih banyak lagi di Habitica</string>
|
||||
<string name="gift_gems_disclaimer">Habitica tidak akan pernah meminta kamu untuk memberikan permata kepada pemain lain. Mengemis orang lain agar diberi permata adalah tindakan pelanggaran pada Pedoman Komunitas dan akan dilaporkan pada admin@habitica.com.</string>
|
||||
<string name="gems_gift_description">Ingin menganugerahi Permata berkilauan kepada orang lain\?</string>
|
||||
<string name="profile_gift_subscription">Memberikan
|
||||
\nBerlangganan</string>
|
||||
<string name="profile_gift_gems">Memberikan
|
||||
<string name="gift_gems_disclaimer">Habitica tidak akan pernah mengharuskan kamu untuk memberi permata kepada pemain lain. Mengemis permata ke orang ain adalah tindakan pelanggaran pada Pedoman Komunitas dan akan dilaporkan pada admin@habitica.com.</string>
|
||||
<string name="gems_gift_description">Ingin memberikan Permata berkilauan kepada orang lain\?</string>
|
||||
<string name="profile_gift_subscription">Hadiahkan
|
||||
\npaket langganan</string>
|
||||
<string name="profile_gift_gems">Hadiahkan
|
||||
\nPermata</string>
|
||||
<string name="profile_send_message">Mengirimkan
|
||||
<string name="profile_send_message">Kirim
|
||||
\nPesan</string>
|
||||
<string name="gift_gems">Memberikan Permata</string>
|
||||
<string name="see_other_options">Lihat opsi lainnya</string>
|
||||
<string name="promo_subscription_buy_gems_description">Jadilah Berlangganan untuk membeli Permata dengan emas, mendapatkan item misteri bulanan, meningkatkan drop caps dan masih banyak lagi!</string>
|
||||
<string name="gift_gems">Hadiahkan Permata</string>
|
||||
<string name="see_other_options">Lihat pilihan lainnya</string>
|
||||
<string name="promo_subscription_buy_gems_description">Jadilah pelanggan untuk membeli Permata dengan emas, mendapatkan item misteri bulanan, tingkatkan drop caps dan masih banyak lagi!</string>
|
||||
<string name="promo_subscription_buy_gems_prompt">Butuh Permata\?</string>
|
||||
<string name="preference_push_party_activity">Aktifitas Party</string>
|
||||
<string name="search_hint">Mencari tugas</string>
|
||||
<string name="search_hint">cari tugas</string>
|
||||
<string name="search">Cari</string>
|
||||
<string name="complete">Selesai</string>
|
||||
<string name="reject">Menolak</string>
|
||||
<string name="accept">Menerima</string>
|
||||
<string name="equip_automatically">Menggunakan otomatis perlengkapan baru</string>
|
||||
<string name="leave_challenges_delete_tasks">Meninggalkan Tantangan dan Menghapus Tugas</string>
|
||||
<string name="leave_challenges">Meninggalkan Tantangan</string>
|
||||
<string name="reject">Tolak</string>
|
||||
<string name="accept">Terima</string>
|
||||
<string name="equip_automatically">Otomatis gunakan perlengkapan baru</string>
|
||||
<string name="leave_challenges_delete_tasks">Tinggalkan Tantangan dan hapus Tugas</string>
|
||||
<string name="leave_challenges">Tinggalkan Tantangan</string>
|
||||
<string name="keep_challenges">Menyimpan Tantangan</string>
|
||||
<string name="inviteFriendDescription">Kamu mengundang seorang teman (atau banyak teman) untuk bertualang bersamamu!</string>
|
||||
<string name="inviteFriendTitle">Mengundang Teman</string>
|
||||
|
|
@ -806,26 +806,26 @@
|
|||
<string name="partyOnTitle">Party On</string>
|
||||
<string name="partyUpDescription">Kamu bergabung dengan anggota party!</string>
|
||||
<string name="partyUpTitle">Party Up</string>
|
||||
<string name="achievement_title">Kamu mendapatkan Pencapaian!</string>
|
||||
<string name="view_achievements">Melihat Pencapaian</string>
|
||||
<string name="achievement_title">Kamu mendapatkan sebuah Pencapaian!</string>
|
||||
<string name="view_achievements">lihat Pencapaian</string>
|
||||
<string name="see_what_s_new">Lihat apa yang baru</string>
|
||||
<string name="mystery_item_received">Kamumembuka kotak dan menerima %s</string>
|
||||
<string name="removed_member">%s telah dibuang dari grup</string>
|
||||
<string name="mystery_item_received">Kamu membuka kotak dan menerima %s</string>
|
||||
<string name="removed_member">%s telah dikeluarkan dari grup</string>
|
||||
<string name="transfer">Memindahkan</string>
|
||||
<string name="transferred_ownership">Kepimpinan dipindahkan ke %s</string>
|
||||
<string name="transferred_ownership">Kepemimpinan dipindahkan ke %s</string>
|
||||
<string name="send_message">Mengirim Pesan</string>
|
||||
<string name="remove_member">Keluarkan Anggota</string>
|
||||
<string name="transfer_ownership">Memindahkan Kepimpinan</string>
|
||||
<string name="remove">Membuang</string>
|
||||
<string name="transfer_ownership">serahkan Kepemimpinan</string>
|
||||
<string name="remove">hapus</string>
|
||||
<string name="inbox_messages_title_nosender">%d pesan baru</string>
|
||||
<string name="inbox_messages_title">%1$d Pesan Baru dari %2$s</string>
|
||||
<string name="inbox_messages_title_single">Pesan Baru dari %1$s</string>
|
||||
<string name="sent_card">Kamu mengirimkan sebuah %s</string>
|
||||
<string name="app_theme">Tema App</string>
|
||||
<string name="share_challenge_with">Membagikan Tantangan kepada</string>
|
||||
<string name="share_challenge_with">bagikan Tantangan dengan</string>
|
||||
<string name="details">Detail</string>
|
||||
<string name="switch_to_grid_view">Berpindah ke grid view</string>
|
||||
<string name="switch_to_list_view">Berpindah ke list view</string>
|
||||
<string name="switch_to_grid_view">Berganti ke grid view</string>
|
||||
<string name="switch_to_list_view">berganti ke list view</string>
|
||||
<string name="special_achievements">Pencapaian Spesial</string>
|
||||
<string name="seasonal_achievements">Pencapaian Musiman</string>
|
||||
<string name="basic_achievements">Pencapaian Dasar</string>
|
||||
|
|
@ -833,37 +833,37 @@
|
|||
<string name="take_me_back">Bawa aku Kembali</string>
|
||||
<string name="unpin">Hapus pin</string>
|
||||
<string name="pin">Pin</string>
|
||||
<string name="levelup_title_10">Sistem Pekerjaan Terbuka!</string>
|
||||
<string name="levelup_detail_10">Pilihlah kelas yang sesuai dengan gaya permainanmu dan buka kemampuan khusus dan baju pelindung untuk membantumu di perjalanan</string>
|
||||
<string name="levelup_title_10">Sistem kelas Terbuka!</string>
|
||||
<string name="levelup_detail_10">Pilihlah kelas yang sesuai dengan gaya permainanmu dan buka skill spesial dan baju pelindung untuk membantu di perjalananmu</string>
|
||||
<string name="not_now">Tidak sekarang</string>
|
||||
<string name="delete_reminder">Menghapus Pengingat</string>
|
||||
<string name="delete_task">Menghapus Tugas</string>
|
||||
<string name="invite_to_party">Mengundang ke Party</string>
|
||||
<string name="delete_reminder">hapus Pengingat</string>
|
||||
<string name="delete_task">hapus Tugas</string>
|
||||
<string name="invite_to_party">undang ke Party</string>
|
||||
<string name="guild_bank">Bank Guild</string>
|
||||
<string name="i_agree_to_follow_the_guidelines">Saya setuju untuk mengikuti pedoman</string>
|
||||
<string name="garden">Taman</string>
|
||||
<string name="join_guild">Bergabung dengan Guild</string>
|
||||
<string name="invite_to_guild">Mengundang ke Guild</string>
|
||||
<string name="guild_description">Deskripsi Guild</string>
|
||||
<string name="g1g1_info_2_text">Sentuh \"Memberikan Berlangganan\" dan ketik akun nama pengguna yang kamu berikan. Kemudian, pilih jangka waktu berlangganan yang ingin kamu berikan dan bayar. Akunmu secara otomatis akan diberikan hadiah berlangganan yang sama.</string>
|
||||
<string name="g1g1_info_2_text">Sentuh \"Hadiahkan langganan\" dan ketik nama pengguna akun yang ingin kamu hadiahkan. Kemudian, pilih jangka waktu langganan yang ingin kamu hadiahkan dan bayar. Akunmu secara otomatis akan diberikan hadiah langganan yang sama.</string>
|
||||
<string name="g1g1_info_2">Bagaimana cara kerjanya</string>
|
||||
<string name="gift_a_subscription">Berikan Berlangganan</string>
|
||||
<string name="g1g1_info_1">Sebagai penghormatan musim pemberian ini kami menghadirkan kembali sebuah promosi yang sangat spesial. Sekarang ketika kamu memberikan seseorang sebuah berlangganan, kamu akan mendapatkan berlangganan yang sama secara cuma-cuma!</string>
|
||||
<string name="gift_a_subscription">Hadiahkan langganan</string>
|
||||
<string name="g1g1_info_1">Sebagai penghormatan musim pemberian ini kami menghadirkan kembali sebuah promosi yang sangat spesial. Sekarang ketika kamu memberikan seseorang sebuah langganan, kamu akan mendapatkan langganan yang sama secara cuma-cuma!</string>
|
||||
<string name="sale">Diskon</string>
|
||||
<string name="promo_g1g1_prompt">Event Beri Satu, Dapat Satu</string>
|
||||
<string name="promo_g1g1_description">Berikan berlangganan sekarang dan dapatkan berlangganan yang sama gratis!</string>
|
||||
<string name="promo_g1g1_description">Berikan langganan sekarang dan dapatkan langganan yang sama dengan gratis!</string>
|
||||
<string name="event">Event</string>
|
||||
<string name="member_group_plan">Anggota Rencana Grup</string>
|
||||
<string name="member">Anggota</string>
|
||||
<string name="owner">Pemilik</string>
|
||||
<string name="cancel_subscription_group_plan_owner">Kamu memiliki berlangganan gratis karena kamu adalah pemilik Party atau Perkumpulan yang memiliki Rencana Grup. Ini dapat berakhir jika kamu membatalkan rencana tersebut. Bulan berlangganan ekstra yang tersisa yang kamu punya akan dimasukkan ke dalam Rencana Grup. Kamu dapat membatalkannya melalui situs Habitica.</string>
|
||||
<string name="cancel_subscription_group_plan">Kamu memiliki berlangganan gratis karena kamu adalah anggota dari Party atau Perkumpulan yang mempunyai Rencana Grup. Ini dapat dibatalkan jika kamu meninggalkan grup atau Rencana Grup dibatalkan oleh sang pemilik. Bulan ekstra berlangganan tersisa yang kamu punya akan dimasukkan pada akhir Rencana Grup.</string>
|
||||
<string name="cancel_subscription_group_plan_owner">Kamu memiliki langganan gratis karena kamu adalah pemilik Party atau Guild yang memiliki Rencana Grup. Langganan dapat berakhir jika kamu membatalkan rencana tersebut. Bulan-bulan langganan milikmu yang tersisa akan dimasukkan ke dalam Rencana Grup. Kamu dapat membatalkannya melalui situs Habitica.</string>
|
||||
<string name="cancel_subscription_group_plan">Kamu memiliki langganan gratis karena kamu adalah anggota dari Party atau Guild yang mempunyai Rencana Grup. Langganan dapat dibatalkan jika kamu meninggalkan atau Rencana Grup dibatalkan oleh pemilik. Bulan-bulan ekstra dari langganan milikmu yang tersisa akan dimasukkan pada akhir Rencana Grup.</string>
|
||||
<string name="group_plan">Rencana Grup</string>
|
||||
<string name="subscription_credit">Kredit Berlangganan</string>
|
||||
<string name="subscription_credit">Kredit langganan</string>
|
||||
<string name="save_20">Hemat 20%</string>
|
||||
<string name="gifted">Telah diberikan</string>
|
||||
<string name="gifted">Telah dihadiahkan</string>
|
||||
<string name="resubscribe">Berlangganan Kembali</string>
|
||||
<string name="renew_subscription">Memperbarui Berlangganan</string>
|
||||
<string name="renew_subscription">perbarui langganan</string>
|
||||
<string name="thanks_for_supporting">Terima kasih telah berlangganan</string>
|
||||
<string name="subscribe_options_title">Pilih masa Berlangganan yang kamu inginkan</string>
|
||||
<string name="ending_on">Berakhir pada %s</string>
|
||||
|
|
@ -879,7 +879,7 @@
|
|||
<string name="invitation_title">%1$s mengundangmu untuk bergabung dengan grup %2$s</string>
|
||||
<string name="password_too_short">Kata sandimu harus setidaknya %d karakter</string>
|
||||
<string name="copy_username">Salin Nama Pengguna</string>
|
||||
<string name="id_copied">Salin ID Pengguna ke clipboard</string>
|
||||
<string name="id_copied">ID Pengguna di salin ke clipboard</string>
|
||||
<string name="copy_userid">Salin ID Pengguna</string>
|
||||
<string name="preference_push_unjoined_guild_mention">\@Mentions di Guild yang tidak diikuti</string>
|
||||
<string name="preference_push_joined_guild_mention">\@Mentions di Guild yang diikuti</string>
|
||||
|
|
@ -914,7 +914,7 @@
|
|||
<string name="learn_more">Pelajari lebih lanjut</string>
|
||||
<string name="g1g1_duration">17 Des sampai 6 Jan</string>
|
||||
<string name="limited_event">Event terbatas</string>
|
||||
<string name="g1g1_info_3_text">Promo ini hanya berlaku jika kamu telah menghadiahkan pada Habitican lain. Jika kamu atau penerima hadiah telah berlangganan, maka hadiah berlangganan akan ditambahkan pada kredit yang hanya akan digunakan setelah masa berlangganan saat ini selesai atau dibatalkan.</string>
|
||||
<string name="g1g1_info_3_text">Promo ini hanya berlaku jika kamu telah menghadiahkan pada Habitican lain. Jika kamu atau penerima hadiah telah berlangganan, maka hadiah langganan akan menambahkan kredit bulan yang hanya akan digunakan setelah masa berlangganan saat ini selesai atau dibatalkan.</string>
|
||||
<string name="g1g1_info_3">Batasan</string>
|
||||
<string name="quest_party_required_title">Kamu harus berada di dalam suatu Party sebelum memulai Misi</string>
|
||||
<string name="usernames_party">Party-nya %s</string>
|
||||
|
|
@ -928,18 +928,18 @@
|
|||
\n**Pulihkan Nyawa** dengan naik level, menggunakan Ramuan Penyembuh, atau dengan kemampuan penyembuh.
|
||||
\n
|
||||
\nJika **Nyawa kamu sampai 0** kamu akan kehilangan satu level, semua Koin Emas, dan sebuah perlengkapan. Perlengkapan yang hilang bisa dibeli lagi nanti.</string>
|
||||
<string name="experience_description">Poin Pengalaman / EXP merepresentasikan progressmu dan mempebolehkanmu untuk naik level. Kamu pada intinya akan **mendapatkan EXP** dengan menyelesaikan tugas atau misi, namun ada beberapa kelas yang dapat memberikan EXP.
|
||||
<string name="experience_description">Poin Pengalaman / EXP merepresentasikan progressmu dan mempebolehkanmu untuk naik level. Kamu pada intinya akan **mendapatkan EXP** dengan menyelesaikan tugas atau misi, namun ada beberapa profesi yang dapat memberikan EXP.
|
||||
\n
|
||||
\nTugas dengan kesulitan yang lebih tinggi, atau yang diwarnai merah akan memberikanmu **EXP lebih banyak**. **Stat Kecerdasan** yang kamu miliki juga berpengaruh pada pemerolehan EXP.</string>
|
||||
<string name="gold_description">Koin Emas adalah **mata uang utama** dalam Habitica dan dapat kamu gunakan untuk membeli perlengkapan, misi, items, atau bahkan hadiah custom yang kamu buat sendiri.
|
||||
\n
|
||||
\n**Koin Emas diperoleh** dengan cara menyelesaikan tugas atau misi, atau melalui beberapa skill Kelas. **Stat Persepsi (Perception)** yang lebih tinggi membuatmu memperoleh Emas lebih banyak.
|
||||
\n**Peroleh koin emas** dengan cara menyelesaikan tugas atau misi, atau melalui beberapa skill profesi. **Stat Persepsi (Perception)** yang lebih tinggi membuatmu memperoleh lebih banyak emas.
|
||||
\n
|
||||
\nJika kamu berlangganan Habitica, maka kamu bahkan dapat menggunakan Koin Emas untuk membeli Permata yang jumlahnya bergantung dengan seberapa lama kamu telah berlangganan.</string>
|
||||
\nJika kamu berlangganan ke Habitica, maka kamu bahkan dapat menggunakan Koin Emas untuk membeli Permata yang jumlahnya bergantung dengan seberapa lama kamu telah berlangganan.</string>
|
||||
<string name="mana_points">Poin Mana</string>
|
||||
<string name="mana_description">Poin Mana (MP) akan terbuka dengan sistem kelas pada level 10 dan membuatmu dapat **menggunakan Kemampuan** setelah kamu mempelajarinya pada level 11.
|
||||
<string name="mana_description">Poin Mana (MP) akan terbuka dengan sistem profesi pada level 10 dan membuatmu dapat **menggunakan skill** setelah kamu mempelajarinya pada level 11.
|
||||
\n
|
||||
\nBeberapa **MP terisi** setiap pergantian hari, tetapi kamu dapat memperoleh lebih banyak lagi dengan menyelesaikan tugas atau menggunakan Kemampuan Penyihir.</string>
|
||||
\nBeberapa **MP terisi** setiap pergantian hari, tetapi kamu dapat memperoleh lebih banyak dengan menyelesaikan tugas atau menggunakan skill profesi Penyihir.</string>
|
||||
<string name="stat_allocation">Alokasi Stat</string>
|
||||
<string name="standard">Standar</string>
|
||||
<string name="premium_currency">Mata Uang Premium</string>
|
||||
|
|
@ -960,11 +960,11 @@
|
|||
</plurals>
|
||||
<string name="choosing_class_progress">Memilih Profesi</string>
|
||||
<string name="level_abbreviated">Lv. %d</string>
|
||||
<string name="thousand_abbrev">rb</string>
|
||||
<string name="million_abbrev">jt</string>
|
||||
<string name="thousand_abbrev">K</string>
|
||||
<string name="million_abbrev">m</string>
|
||||
<string name="class_confirmation_price">Apakah kamu mau mengganti profesimu ke %1$s dengan %2$d permata\?</string>
|
||||
<string name="buy_all">Beli Semua</string>
|
||||
<string name="billion_abbrev">mil</string>
|
||||
<string name="billion_abbrev">b</string>
|
||||
<string name="push_notification_system_settings_description">Perbolehkan notifikasi Habitica di aplikasi Pengaturan untuk menerima notifikasi</string>
|
||||
<string name="push_notification_system_settings_reminders">Perbolehkan notifikasi Habitica di aplikasi Pengaturan untuk menerima pengingat</string>
|
||||
<string name="push_notification_system_settings_title">Notifikasi Dinonaktifkan</string>
|
||||
|
|
@ -996,4 +996,224 @@
|
|||
<string name="cancel_subscription_apple_description">Tidak ingin berlangganan lagi\? Karena metode pembayaran Anda, Anda harus berhenti berlangganan melalui pengaturan langganan ID Apple di perangkat iOS Anda.</string>
|
||||
<string name="unsaved_changes">Perubahan yang belum disimpan</string>
|
||||
<string name="use_item">Gunakan Item pada anggota party</string>>
|
||||
</resources>
|
||||
<string name="leave_party_confirmation_quest">Apakah Anda yakin ingin meninggalkan pesta ini\?</string>
|
||||
<string name="rejoin_party_quest">Anda tidak akan dapat bergabung kembali dengan kelompok ini kecuali diundang.
|
||||
\n
|
||||
\nMeninggalkan kelompok juga akan membuat Anda meninggalkan misi yang sedang Anda jalani.</string>
|
||||
<string name="rejoin_party_quest_leader">Anda tidak akan dapat bergabung kembali dengan kelompok ini kecuali diundang.
|
||||
\n
|
||||
\nMeninggalkan kelompok juga akan membatalkan misi saat ini.</string>
|
||||
<string name="forgot_password_hint_example">misalnya, habitrabbit atau gryphon@example.com</string>
|
||||
<string name="reset_account_description_new">Anda akan kehilangan semua level, Emas, dan Pengalaman. Semua tugas dan data historisnya akan dihapus (tugas Tantangan akan tetap ada). Anda akan kehilangan semua peralatan, kecuali item Pelanggan dan item peringatan gratis, tetapi Anda akan dapat membelinya kembali. Anda harus memiliki kelas yang tepat untuk membeli kembali perlengkapan khusus kelas. Anda akan tetap memiliki kelas, Prestasi, dan Hewan Peliharaan serta Tunggangan Anda saat ini. Untuk mengonfirmasi pengaturan ulang, ketik kata sandi Anda di bawah ini.</string>
|
||||
<string name="reset_account_description_no_pw">Anda akan kehilangan semua level, Emas, dan Pengalaman. Semua tugas dan data historisnya akan dihapus (tugas Tantangan akan tetap ada). Anda akan kehilangan semua peralatan, kecuali item Pelanggan dan item peringatan gratis, tetapi Anda akan dapat membelinya kembali. Anda harus memiliki kelas yang tepat untuk membeli kembali perlengkapan khusus kelas. Anda akan tetap memiliki kelas, Prestasi, dan Hewan Peliharaan serta Tunggangan Anda saat ini. Untuk mengonfirmasi pengaturan ulang, ketik RESET di bawah.</string>
|
||||
<string name="class_equipment_shop_dialog_new">Item ini hanya tersedia untuk %s. Buka Pengaturan untuk mengubah kelas.</string>
|
||||
<string name="reset_account_title">Apakah Anda yakin ingin mengatur ulang\?</string>
|
||||
<string name="discard_changes_to_task_message">Apakah Anda yakin ingin membuang perubahan pada tugas ini\?</string>
|
||||
<string name="next_hourglass">Pengiriman Jam Pasir Berikutnya</string>
|
||||
<string name="confirm_reset">Konfirmasi pengaturan ulang</string>
|
||||
<string name="report_reason_title_player">Mengapa Anda melaporkan pemain ini\?</string>
|
||||
<string name="report_successful">%s Dilaporkan</string>
|
||||
<string name="report_failure">Laporan gagal, silakan coba lagi nanti</string>
|
||||
<string name="invited_to_guild_notification"><font color=#925CF3>%2$s</font> telah mengundang Anda untuk bergabung dengan grup <b>%1$s</b></string>
|
||||
<string name="report_reason_title_challenge">Mengapa Anda melaporkan tantangan ini\?</string>
|
||||
<string name="report_player">Laporan Pemutar</string>
|
||||
<string name="report_player_title">Laporkan %s\?</string>
|
||||
<string name="report_formatted_name">%1$s
|
||||
\n@%2$s</string>
|
||||
<string name="need_help_description">Anda dapat menghubungi kami dan anggota tim kami akan melakukan yang terbaik untuk membantu!</string>
|
||||
<string name="report_user_explanation">Anda hanya boleh melaporkan pemain yang melanggar [Pedoman Komunitas](https://habitica.com/static/community-guidelines) dan/atau [Persyaratan Layanan](https://habitica.com/static/terms). Mengirimkan laporan palsu merupakan pelanggaran terhadap Pedoman Komunitas Habitica.</string>
|
||||
<string name="insufficient_level_equipment_dialog_new">Item ini hanya tersedia untuk %s. Anda dapat memilih kelas setelah level 10.</string>
|
||||
<string name="report_message_explanation">Anda hanya boleh melaporkan kiriman yang melanggar [Pedoman Komunitas](https://habitica.com/static/community-guidelines) dan/atau [Persyaratan Layanan](https://habitica.com/static/terms). Mengirimkan laporan palsu merupakan pelanggaran terhadap Pedoman Komunitas Habitica.</string>
|
||||
<string name="report_challenge_explanation">Anda hanya boleh melaporkan tantangan yang melanggar [Pedoman Komunitas](https://habitica.com/static/community-guidelines) dan/atau [Persyaratan Layanan](https://habitica.com/static/terms). Mengirimkan laporan palsu merupakan pelanggaran terhadap Pedoman Komunitas Habitica.</string>
|
||||
<string name="report_message">Laporkan Pesan</string>
|
||||
<string name="register_tos_confirm">Anda menyetujui <a href="https://habitica.com/static/terms">Persyaratan Layanan</a> kami dan telah membaca <a href="https://habitica.com/static/privacy">Kebijakan Privasi</a> kami.</string>
|
||||
<string name="report_hint">Alasan laporan</string>
|
||||
<string name="report_reason_title_message">Mengapa Anda melaporkan pesan ini\?</string>
|
||||
<string name="report_user_description">Ini juga akan memblokir @%s</string>
|
||||
<string name="x_checkins">%d Checkin</string>
|
||||
<string name="insufficient_level_equipment_dialog">Item ini hanya tersedia untuk kelas tertentu. Anda dapat memilih kelas setelah level 10.</string>
|
||||
<string name="adjust_counter">Sesuaikan Penghitung</string>
|
||||
<string name="reset_counter">Setel Ulang Penghitung</string>
|
||||
<string name="gem_purchase_confirmation">Anda memperoleh %s permata.</string>
|
||||
<string name="gift_confirmation_text_gems_new">Anda mengirim @%1$s %2$s permata.</string>
|
||||
<string name="subscription_confirmation">Anda sekarang berlangganan selama 1 bulan</string>
|
||||
<string name="subscription_confirmation_multiple">Anda sekarang berlangganan selama %s bulan</string>
|
||||
<string name="hourglasses_description">Mystic Hourglass adalah mata uang yang amat sangat langka yang hanya dapat kamu peroleh melalui berlangganan Habitica selama 3 bulan beruntun atau lebih. Kamu dapat menggunakannya di toko Penjelajah Waktu untuk membeli perlengkapan langganan yang telah lampau, peliharaan dan tunggangan eksklusif, background animasi, atau bahkan misi spesial! Keuntungan ekstra ini adalah cara yang bagus untuk tetap termotivasi sepanjang tahun dan menghadiahi dirimu untuk menyelesaikan tugas dengan baik.
|
||||
\n
|
||||
\nPelanggan akan menerima Mystic Hourglass pada awal setiap bulan, dengan banyak keuntungan lainya. Pastikan untuk melihat pilihan langganan kami jika kamu tertarik dengan apa yang penjelajah waktu tawarkan!</string>
|
||||
<string name="manual_sync_restart_description">Terkadang aplikasi tidak memperbarui konten secara otomatis. Coba tarik untuk menyegarkan atau menutup paksa aplikasi dan membukanya kembali.</string>
|
||||
<string name="delete_challenge_task_description">Tugas ini adalah satu dari %1$d tugas dari Tantangan \"%2$s\". Kamu harus meninggalkan Tantangan untuk menghapus tugas ini.</string>
|
||||
<string name="pms_disabled_description">Kamu masih dapat mengirimkan pesan, namun orang lain tidak mengirimkan pesan padamu. Kamu bisa mengaktifkan pesan kembali dari Pengaturan.</string>
|
||||
<string name="fall_promo_info_prompt">Gala Musim Gugur sedang berlangsung jadi kami pikir ini adalah waktu yang tepat untuk Diskon Permata! Sekarang kamu dapat memperoleh lebih banyak Permata daripada sebelumnya dalam setiap pembelian.</string>
|
||||
<plurals name="repeat_monthly">
|
||||
<item quantity="other">setiap %d bulan</item>
|
||||
</plurals>
|
||||
<string name="adventure_guide_description_new">Selesaikan target-target ini dan kamu akan mendapatkan <b>5 Pencapaian</b> dan <font color=#EE9109><b>100 Emas</b></font> setelah kamu selesai!</string>
|
||||
<string name="lets_get_started">Ayo mulai</string>
|
||||
<string name="hatchedPetTitle">Menetaskan peliharaan</string>
|
||||
<string name="fedPetTitle">Memberi makan peliharaan</string>
|
||||
<string name="hatchedPetDescription">Ada banyak sekali Peliharaan untuk dikoleksi, kamu pasti punya satu yang favorit. Jika kamu beri makan, mereka bisa saja bertumbuh…</string>
|
||||
<string name="first_drop_explanation2">Pergi ke Item milikmu dan cobalah mengombinasikan telur barumu dengan Ramuan Penetas!</string>
|
||||
<string name="report_player_toolbar_title">laporkan%s</string>
|
||||
<string name="subscribers_mythic_hourglasses"><i> Pelanggan menerima jam pasir mistik pada tiga hari pertama setiap bulan. </i></string>
|
||||
<string name="resubscribe_description_gift">Ingin terus menerima keuntungan langganan\? Kamu dapat memulai langganan berulang untuk menjaga keuntunganmu aktif.</string>
|
||||
<string name="habitica_questions">Pertanyaan Habitica</string>
|
||||
<string name="could_not_find_user">Tidak dapat menemukan pemain</string>
|
||||
<string name="avatar_headband">Ikat kepala</string>
|
||||
<string name="spooky_promo_info_prompt">Diskon Permata kembali menghantui hingga akhir Gala Musim Gugur tahun ini! Ini adalah kesempatan terakhir untuk mendapat Permata lebih banyak dari biasanya, jadi timbun sebanyak-banyaknya selagi bisa!</string>
|
||||
<string name="create_guild">Buat Guild</string>
|
||||
<string name="purchase_immediately_description">Efeknya langsung berlaku setelah pembelian!</string>
|
||||
<string name="complete_task_description">Centang tugas manapun untuk memperoleh hadiah</string>
|
||||
<string name="createdTaskDescription">Lanjutkan! Jika kamu perlu bantuan untuk merencanakan tugas, coba pikirkan hal apa yang ingin kamu lakukan pada waktu tertentu dalam sehari</string>
|
||||
<string name="view_onboarding_tasks">Lihat Tugas Pengenalan</string>
|
||||
<string name="block">Blokir</string>
|
||||
<string name="action_end_challenge">Akhiri Tantangan</string>
|
||||
<string name="end_challenge_description">Untuk mengakhiri Tantangan, masuk ke situs Habitica kemudian ketuk tombol \"Akhiri Tantangan\" pada layar Tantangan di sebelah kanan.</string>
|
||||
<string name="you_won_challenge">Kamu memenangkan Tantangan</string>
|
||||
<string name="open_website">Buka Situs</string>
|
||||
<string name="fedPetDescription">Setiap Peliharaan punya satu jenis makanan yang mereka sukai! Bereksperimenlah untuk mencari tahu mana yang tercepat menumbuhkan Peliharaanmu</string>
|
||||
<string name="first_drop_explanation1">Menyelesaikan tugas memberimu kesempatan untuk memperoleh telur, ramuan penetas, dan makanan peliharaan.</string>
|
||||
<string name="x_to_y">%1$s ke %2$s</string>
|
||||
<string name="fall_promo_info_instructions">Antara %1$s dan %2$s, cukup beli paket Permata mana saja dan akumu akan dikirimkan Permata dengan jumlah permata promosi. Semakin banyak Permata untuk digunakan, dibagikan, atau disimpan untuk kebutuhan mendatang!</string>
|
||||
<string name="manual_sync_restart">Sinkronisasi manual atau mulai ulang</string>
|
||||
<string name="update_app">Perbarui Aplikasi</string>
|
||||
<string name="hatch_your_pet">Tetaskan Peliharaanmu</string>
|
||||
<string name="how_it_works">Bagaimana cara kerjanya</string>
|
||||
<string name="finish">Selesai</string>
|
||||
<string name="you">Kamu</string>
|
||||
<string name="task_display">Tampilan daftar Tugas</string>
|
||||
<string name="use_skill">Gunakan kemampuan</string>
|
||||
<string name="update_app_description">Kami terus merilis perbaikan baru, jadi pastikan untuk memeriksa Play Store untuk melihat apakah ada pembaruan yang tersedia.</string>
|
||||
<string name="use_saddle">Gunakan pelana</string>
|
||||
<string name="delete_challenge_task_title">Hapus Tugas Tantangan\?</string>
|
||||
<string name="leave_delete_task">Tinggalkan & Hapus Tugas</string>
|
||||
<string name="purchase_amount_error">Kamu tidak dapat membeli sebanyak itu.</string>
|
||||
<string name="still_questions">Masih ada pertanyaan\?</string>
|
||||
<string name="feedPet_title">Beri makan peliharaan</string>
|
||||
<string name="completedTaskDescription">Sebuah tugas bisa berupa Kebiasaan, harian, atau To do. Lanjutkan menyelesaikan mereka untuk mendapatkan berbagai hadiah!</string>
|
||||
<string name="see_other_sub_options">Lihat pilihan berlangganan lainnya</string>
|
||||
<string name="benefits_end">Keuntungan berakhir pada %s</string>
|
||||
<string name="support">Dukungan</string>
|
||||
<string name="suggest_pet_hatch_again_missing_both">Kamu membutuhkan sebuah %1$s dan ramuan %2$s untuk menetaskan hewan peliharaan ini lagi</string>
|
||||
<string name="hatch_pet">Tetaskan Peliharaan</string>
|
||||
<string name="magic_potions">Ramuan Ajaib</string>
|
||||
<string name="magic_potion">Ramuan Ajaib</string>
|
||||
<string name="broken_challenge_description">Tugas ini adalah satu dari %d Tugas yang merupakan bagian dari Tantangan yang sudah tidak lagi aktif. Apa yang ingin kamu lakukan pada Tugas ini\?</string>
|
||||
<string name="pms_disabled">Pesan Pribadi dinonaktifkan</string>
|
||||
<string name="gems_promo_info_limitations_fixed">Promosi ini hanya berlaku pada waktu event terbatas. event ini mulai pada %1$s (%2$s) dan akan berakhir pada %3$s (%4$s). tawaran promosi hanya tersedia ketika membeli permata untukmu sendiri.</string>
|
||||
<string name="login_disclaimer">Dengan mendaftar, kamu menyatakan bahwa kamu telah membaca dan menyetujui <a href="https://habitica.com/static/terms">Persyaratan Layanan</a> dan <a href="https://habitica.com/static/privacy">Kebijakan Privasi.</a></string>
|
||||
<string name="invitation_title_no_leader">Kamu diundang untuk bergabung dengan Party</string>
|
||||
<string name="fortify_shop_description">Mengembalikan semua tugas ke nilai netral (warna kuning), dan mengembalikan semua Nyawa yang hilang</string>
|
||||
<string name="congratulations">Selamat!</string>
|
||||
<string name="won_achievement_description"><b>%s</b> memilihmu sebagai pemenang! Kemenanganmu telah tersimpan di Pencapaian.</string>
|
||||
<plurals name="items_pending">
|
||||
<item quantity="other">%d barang tertunda</item>
|
||||
</plurals>
|
||||
<string name="g1g1_promo_info_limitations_noutc">Ini dalah event waktu terbatas yang mulai pada %1$s dan akan berakhir pada %2$s. promosi ini hanya berlaku ketika kamu menghadiahkan ke pemain Habitica lain. Jika kamu atau penerima hadiahmu sudah mempunyai langganan,hadiah langganan akanditambahkan dlam bentuk kredit bulanan yang hanya akan digunakan setelah langganan saat ini dibatalkan atau kadaluarsa.</string>
|
||||
<string name="clear_cache">bersihkan Cache</string>
|
||||
<string name="clear_cache_description">Untuk membersihkan cache, buka Setelan di ponsel. Buka Penyimpanan > App > Habitica, lalu ketuk Hapus Cache.</string>
|
||||
<string name="animal_tail">Ekor Hewan</string>
|
||||
<string name="delete_checklist_entry">Hapus…</string>
|
||||
<string name="getting_started">Mulai</string>
|
||||
<string name="leave_delete_x_tasks">Tinggalkan & Hapus %d Tugas</string>
|
||||
<string name="broken_challenge">Tantangan gagal</string>
|
||||
<string name="common_questions">Pertanyaan umum</string>
|
||||
<string name="pet_ownership_fraction">%1$d / %2$d</string>
|
||||
<string name="suggest_pet_hatch_missing_potion">Kamu masih membutuhkansebuah ramuan %s untuk menetaskan hewan peliharaan ini</string>
|
||||
<string name="subscriber_currency">Mata Uang Pelanggan</string>
|
||||
<string name="onboarding_tasks">Tugas Pengenalan</string>
|
||||
<string name="g1g1_promo_info_instructions">Ketuk \"Hadiahkan fitur Langganan\" dan ketik nama pengguna akun yang ingin kamu berikan. Kemudian, pilih jangka masa langganan yang ingin kamu berikan dan bayar. Akunmu secara otomatis akan diberikan hadiah langganan yang serupa.</string>
|
||||
<plurals name="repeat_daily">
|
||||
<item quantity="other">setiap %d hari</item>
|
||||
</plurals>
|
||||
<string name="avatar_accent">Aksesoris</string>
|
||||
<string name="complete_for_gold">Selesaikan untuk memperoleh <font color=#EE9109><b>100 Koin Emas</b></font>!</string>
|
||||
<string name="percent_completed">%d%% Terselesaikan</string>
|
||||
<string name="hatch_pet_title">Tetaskan peliharaan baru</string>
|
||||
<string name="purchase_equipment_description">Perlengkapan adalah sebuah cara untuk mengkustomisasi avatarmu dan meningkatkan stat</string>
|
||||
<string name="createdTaskTitle">Tugas pertamamu dibuat</string>
|
||||
<string name="completedTaskTitle">menyelesaikan tugas</string>
|
||||
<string name="purchasedEquipmentDescription">Perlengkapan dapat berguna atau hanya sebagai penampilan semata. Tingkatkan stat milikmu untuk mendapatkan berbagai macam keuntungan pada avatarmu</string>
|
||||
<string name="onboardingCompleteDescription">Jika kamu menginginkan lebih banyak, lihat bagian Pencapaian dan mulailah mengumpulkannya!</string>
|
||||
<string name="onboardingComplete_achievement_title">Kamu menyelesaikan Tugas Pengenalanmu!</string>
|
||||
<string name="excess_items">Item Berlebih</string>
|
||||
<string name="purchaseX">Beli %d</string>
|
||||
<string name="equip">Pakai</string>
|
||||
<string name="unequip">Lepaskan</string>
|
||||
<string name="suggest_pet_hatch_missing_egg">Kamu masih membutuhkan sebuah telur %s untuk menetaskan hewan peliharaan ini</string>
|
||||
<string name="suggest_pet_hatch_missing_both">Kamu membutuhkan sebuah %1$s dan ramuan %2$s untuk menetaskan hewan peliharaan ini</string>
|
||||
<string name="suggest_pet_hatch_again_missing_egg">Kamu masih membutuhkan sebuah telur %s untuk menetaskan hewan peliharaan ini lagi</string>
|
||||
<string name="can_hatch_pet">Kombinasikan Telur %1$s-mu dan Ramuan %2$s untuk menetaskan peliharaan ini!</string>
|
||||
<string name="unhatched_pet">Peliharaan yang Belum Ditetaskan</string>
|
||||
<string name="hatch_pet_again">Tetaskan Peliharaan lagi</string>
|
||||
<string name="spooky_promo_info_instructions">Antara %1$s dan %2$s, cukup beli paket Permata mana saja dan akumu akan dikirimkan Permata dengan jumlah promosi permata! Semakin banyak Permata untuk digunakan, dibagikan, atau disimpan untuk kebutuhan mendatang!</string>
|
||||
<string name="user_is_blocked_explanation">Pemain yang terblokir tidak dapat mengirimkan Pesan Pribadi kepadamu tapi kamu masih tetap bisa melihat postingan mereka.</string>
|
||||
<string name="hatch">Tetaskan</string>
|
||||
<string name="keep_x_tasks">Simpan %d Tugas</string>
|
||||
<string name="challenge_task_name">Tugas Tantangan %s</string>
|
||||
<string name="pet_category">Peliharaan %s</string>
|
||||
<string name="mount_category">Tunggangan %s</string>
|
||||
<string name="block_user">Blokir Pemain</string>
|
||||
<string name="unblock_user">Buka Blokir Pemain</string>
|
||||
<string name="block_user_title">Blokir %s\?</string>
|
||||
<string name="block_user_description">Pemain yang terblokir tidak dapat mengirimkan Pesan Pribadi kepadamu tapi kamu masih tetap bisa melihat postingan mereka.</string>
|
||||
<string name="gems_description">Permata adalah mata uang yang dibeli dengan uang asli yang membolehkan kamu untuk membeli konten ekstra di Habitica dan adalah salah satu sumber dukungan finansial untuk tim Habitica disamping paket langganan.
|
||||
\n
|
||||
\nSemua Konten yang dibeli dengan Permata adalah murni fitur kosmetik atau bisa di peroleh secara gratis seiring waktu.
|
||||
\n
|
||||
\nkamu juga bisa menerima Permata dari hadiah yang diberikan pemain lain, hadiah tantangan, berkontribusi ke Habitica, atau berlangganan.</string>
|
||||
<string name="animal_ears">Telinga Hewan</string>
|
||||
<plurals name="repeat_weekly">
|
||||
<item quantity="other">setiap %d minggu</item>
|
||||
</plurals>
|
||||
<string name="purchase_equipment_title">Beli Perlengkapan</string>
|
||||
<string name="onboardingCompleteTitle">Kamu mendapatkan <b>5 Pencapaian</b> dan <font color=#EE9109><b>100 Koin Emas</b></font> atas usahamu.</string>
|
||||
<string name="create_guild_description">Untuk membuat Guild, masuk ke situs Habitica dan ketuk tombol \"Buat\" pada layar \"Guild Saya\".</string>
|
||||
<string name="hurray">Hore!</string>
|
||||
<string name="feedPet_description">Selesaikan tugas untuk mendapatkan makanan! Kamu dapat memberinya ke peliharaanmu dari Peliharaan & Tunggangan</string>
|
||||
<string name="hatch_pet_description">Selesaikan tugas untuk mendapatkan Ramuan Penetas dan Telur kemudian tetaskan peliharaanmu!</string>
|
||||
<string name="fortify_shop">Ramuan Penguat</string>
|
||||
<string name="delete_x_tasks">Hapus %d Tugas</string>
|
||||
<string name="game_mechanics">Mekanika Game</string>
|
||||
<string name="create_task_description">Tambahkan tugas untuk hal yang ingin kamu tuntaskan pekan ini</string>
|
||||
<string name="excessItemsXLeft">Kamu hanya perlu %1$d %2$s untuk menetaskan semua hewan peliharaan yang mungkin. Apakah kamu yakin ingin membeli %3$d\?</string>
|
||||
<string name="claim_x_gems">Klaim %d Permata</string>
|
||||
<string name="read_more">Baca Lebih Lanjut</string>
|
||||
<string name="disablePrivateMessages">Nonaktifkan Pesan Pribadi</string>
|
||||
<string name="usually_x_gems">Biasanya %d Permata</string>
|
||||
<string name="excessItemsNoneLeft">Kamu sudah memiliki semua yang kamu butuhkan untuk semua hewan peliharaan %1$s. Apakah kamu yakin ingin membeli %2$d %3$ss\?</string>
|
||||
<string name="purchasedEquipmentTitle">Perlengkapan dibeli</string>
|
||||
<string name="wacky">Aneh</string>
|
||||
<plurals name="people">
|
||||
<item quantity="other">%d orang</item>
|
||||
</plurals>
|
||||
<string name="your_progress">Kemajuanmu</string>
|
||||
<string name="go_to_items">Buka Item</string>
|
||||
<string name="first_drop_title">Kamu menemukan item baru!</string>
|
||||
<string name="suggest_pet_hatch_again_missing_potion">Kamu masih membutuhkan sebuah ramuan %s untuk menetaskan hewan peliharaan ini lagi</string>
|
||||
<string name="limitations">Batasan</string>
|
||||
<string name="view_gem_bundles">Lihat Paket Permata</string>
|
||||
<string name="dark_mode">Mode Gelap</string>
|
||||
<string name="theme_mode">Mode Tema</string>
|
||||
<string name="theme_color">Warna Tema</string>
|
||||
<string name="submit_feedback">Kirimkan Umpan Balik</string>
|
||||
<string name="user_is_blocked">Kamu memblokir pemain ini</string>
|
||||
<string name="won_achievement_description_noname">Kamu terpilih sebagai pemenang! Kemenanganmu telah tersimpan di Pencapaian.</string>
|
||||
<string name="g1g1_promo_info_prompt">Untuk menghormati musim pemberian kami menghadirkan kembali sebuah promosi yang sangat spesial. Sekarang kapanpun kamu memberikan seseorang langganan, kamu akan dapat langganan yang serupa secara cuma-cuma!</string>
|
||||
<plurals name="repeat_yearly">
|
||||
<item quantity="other">setiap %d tahun</item>
|
||||
</plurals>
|
||||
<string name="twelve_months_one_time">12 bulan</string>
|
||||
<string name="quest_leave_message_nostart">Apakah kamu yakin kamu mau meninggalkan Misi ini\? Kamu tidak akan bisa berpartisipasi lagi.</string>
|
||||
<string name="something_new">Baru</string>
|
||||
<string name="launch_screen">Layar Utama</string>
|
||||
<string name="cant_like_own_message">Tidak bisa menyukai pesanmu sendiri</string>
|
||||
<string name="quest_leave_message">Apakah kamu yakin kamu mau meninggalkan misi\? Semua kemajuan misimu akan hilang.</string>
|
||||
<string name="sidebar_teams">Tim</string>
|
||||
<string name="team_information">Informasi Tim</string>
|
||||
<string name="mystery_item_title">Kamu membuka Kotak Misteri dan menemukan…</string>
|
||||
<string name="three_months_one_time">3 bulan</string>
|
||||
<string name="one_month_one_time">1 bulan</string>
|
||||
<string name="gift_one_get_one_purchase_banner">hadiahkan langganan dan dapatkan langganan secara gratis sampai %s</string>
|
||||
<string name="six_months_one_time">6 bulan</string>
|
||||
</resources>
|
||||
|
|
@ -192,16 +192,16 @@
|
|||
<string name="changing_class_progress">שנו מקצוע</string>
|
||||
<string name="by_email">באמצעות אימייל</string>
|
||||
<string name="invite_existing_users">הזמינו משתמשים קיימים</string>
|
||||
<string name="send">שילחו</string>
|
||||
<string name="invite">חפשו משתתפים</string>
|
||||
<string name="invite_email_description">אם חברים מצטרפים להביטיקה באמצעות הזמנת אימייל מכם, הם יקבלו הזמנה לחבורה שלכם באופן אוטומטי!</string>
|
||||
<string name="add_invites">הוסיפו מוזמנים</string>
|
||||
<string name="email">אימייל</string>
|
||||
<string name="share_using">שתפו באמצעות</string>
|
||||
<string name="share_hatched">בדיוק הבקעתי ביצת חיית מחמד של %1$s %2$s בהביטיקה על ידי השלמת משימות מהחיים האמיתיים!</string>
|
||||
<string name="share_raised">בדיוק זכיתי ב%1$s רכיבה ב#הביטיקה על ידי השלמת המשימות שלי מהחיים האמיתיים!</string>
|
||||
<string name="send">שליחה</string>
|
||||
<string name="invite">איתור משתתפים</string>
|
||||
<string name="invite_email_description">אם חברים מצטרפים להביטיקה דרך כתובת הדואר האלקטרוני שלך, הם יוזמנו אוטומטית לחבורה שלך!</string>
|
||||
<string name="add_invites">הזמנת חברים נוספים</string>
|
||||
<string name="email">דואר אלקטרוני</string>
|
||||
<string name="share_using">שיתוף באמצעות</string>
|
||||
<string name="share_hatched">הרגע הבקעתי ביצת חיית מחמד של %1$s %2$s במשחק #הביטיקה בכך שהשלמתי משימות בחיים האמיתיים!</string>
|
||||
<string name="share_raised">הרגע זכיתי בחיית הרכיבה %1$s במשחק #הביטיקה בכך שהשלמתי משימות בחיים האמיתיים!</string>
|
||||
<string name="open_in_store">פתיחה בחנות Play</string>
|
||||
<string name="change_class_confirmation">ברצונכם לשנות מקצוע בעבור 3 אבני חן\?</string>
|
||||
<string name="change_class_confirmation">לשנות מקצוע בעבור 3 אבני חן\?</string>
|
||||
<string name="confirm">אישור</string>
|
||||
<string name="market">שוק</string>
|
||||
<string name="timeTravelers">נוסעים בזמן</string>
|
||||
|
|
@ -285,7 +285,7 @@
|
|||
<string name="level_abbreviated">דרגה %d</string>
|
||||
<string name="user_level_with_class_unabbreviated">%2$s בדרגה %1$d</string>
|
||||
<string name="choosing_class_progress">בחרו מקצוע</string>
|
||||
<string name="change_class_selected_confirmation">ברצונכם לשנות מקצוע ל%1$s\?</string>
|
||||
<string name="change_class_selected_confirmation">לשנות מקצוע ל%1$s\?</string>
|
||||
<string name="empty_inbox">אין לכם הודעות. תוכלו לשלוח הודעה חדשה למשתמש אחר דרך הודעות הצ\'ט הציבוריות שלהם!</string>
|
||||
<string name="login_incentive">שחררו על ידי כניסה להביטיקה באופן קבוע</string>
|
||||
<string name="create_account">שחררו על ידי יצירת משתמש</string>
|
||||
|
|
@ -304,15 +304,15 @@
|
|||
<string name="enable_class_description">בחרו מצוע על מנת להפעיל שימוש ביכולות, מדדים, ומאנה</string>
|
||||
<string name="usernames_party">החבורה של %s</string>
|
||||
<string name="intro_3_title">תכיר אנשים</string>
|
||||
<string name="change_class_confirmation_message">זה ישנה את הציוד הזמין בחנויות וישנה את היכולות שלכם</string>
|
||||
<string name="change_class_confirmation_message">פעולה זו תחליף את הציוד הזמין בחנויות וישנה את היכולות שלך</string>
|
||||
<string name="opt_out_description">אתם תישארו ללא מקצוע. תוכלו לבחור מקצוע בכל עת על ידי הפעלת מערכת המקצועות בהגדרות.</string>
|
||||
<string name="change_class_message">זה יבטל את המקצוע הנוכחי שלכם ויזכה אתכם בכל נקודות המדדים. לאחר מכן תוכלו לבחור מקצוע חדש</string>
|
||||
<string name="change_class_equipment_warning">זה יזכה את נקודות המדדים שלכם, ישנה את הציוד הזמין בחנויות, וישנה את היכולות שלכם.</string>
|
||||
<string name="change_class_message">פעולה זו תסיר ממך את המקצוע הנוכחי ותזכה אותך בכל נקודות המדדים, ואז יהיה אפשר לבחור מקצוע חדש</string>
|
||||
<string name="change_class_equipment_warning">פעולה זו תזכה את נקודות המדדים שלך, תחליף את הציוד הזמין בחנויות, ותשנה את היכולות שלך.</string>
|
||||
<string name="all_dailies_completed">השלמתם את כל המטלות היומיות שלכם. כל הכבוד!</string>
|
||||
<string name="class_changed">הינכם %s!</string>
|
||||
<string name="class_changed_description">כעת תוכלו להשתמש ב%s יכולות ולרכוש ציוד מחנויות. עלו בדרגות על מנת לזכות בנקודות מדדים נוספות, על מנת לחזק את היכולות שלכם.</string>
|
||||
<string name="invite_username_description">אם יש לכם חברים שמשתמשים בהביטיקה, תוכלו להזמין אותם באמצעות שם משתמש כאן.</string>
|
||||
<string name="share_levelup">הגעתי לדרגה %d ב#הביטיקה בכך ששיפרתי את ההרגלים שלי מהחיים האמיתיים!</string>
|
||||
<string name="invite_username_description">אם יש לך חברים שמשתמשים בהביטיקה, אפשר להזמין אותם באמצעות שם משתמש כאן.</string>
|
||||
<string name="share_levelup">הגעתי לרמה %d במשחק #הביטיקה בכך ששיפרתי את ההרגלים בחיים האמיתיים!</string>
|
||||
<string name="add_habit">הוספת הרגל</string>
|
||||
<string name="add_daily">הוספת יומית</string>
|
||||
<string name="open_market">פתיחת השוק</string>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,21 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="tutorial_overview">Daar zijn we dan! Ik heb enkele taken voor je ingevuld op basis van je interesses. Probeer er zelf enkele aan toe te voegen. Je kan elke taak bewerken door op de titel te tikken.</string>
|
||||
<string name="tutorial_habits_1">Als eerste de gewoontes. Dit kunnen goede gewoontes zijn die je wilt verbeteren of slechte gewoontes die je wilt afleren.</string>
|
||||
<string name="tutorial_habits_2">Iedere keer dat je een goede gewoonte doet, tik je op de + om ervaring en goud te krijgen!</string>
|
||||
<string name="tutorial_habits_3">Als je toegeeft en een slechte gewoonte doet, tik je op de - en verliest je avatar gezondheid, dus neem verantwoordelijkheid en voorkom slechte gewoontes.</string>
|
||||
<string name="tutorial_habits_4">Probeer het maar! Je kan de andere soorten taken doornemen met de onderstaande navigatie.</string>
|
||||
<string name="tutorial_dailies_1">Maak dagelijkse taken voor tijdgevoelige taken die volgens een vast schema gedaan moeten worden.</string>
|
||||
<string name="tutorial_dailies_2">Wees voorzichtig - als je er een mist, verliest je avatar gezondheid de volgende dag. Vink ze regelmatig af voor grootse beloningen!</string>
|
||||
<string name="tutorial_todos_1">Gebruik To Do\'s om taken bij te houden die je slechts één keer moet doen.</string>
|
||||
<string name="tutorial_todos_2">Als je To Do tegen een bepaalde dag afgewerkt moet zijn, voer dan een einddatum in. Je kan er een afvinken - doe maar!</string>
|
||||
<string name="tutorial_rewards_1">Koop een uitrusting voor je avatar met het goud dat je verdient!</string>
|
||||
<string name="tutorial_rewards_2">Je kan ook zelf beloningen maken voor in de echte wereld, gebaseerd op wat je motiveert.</string>
|
||||
<string name="tutorial_tasks_complete">Dat was het voorlopig. Als je iets vergeet, kijk dan in de FAQ sectie.</string>
|
||||
<string name="tutorial_overview">Daar zijn we dan! Ik heb enkele taken voor je ingevuld op basis van je interesses. Probeer er zelf enkele aan toe te voegen. Je kan elke taak bewerken door op de titel te tikken.</string>
|
||||
<string name="tutorial_habits_1">Als eerste de gewoontes. Dit kunnen goede gewoontes zijn die je wilt verbeteren of slechte gewoontes die je wilt afleren.</string>
|
||||
<string name="tutorial_habits_2">Iedere keer dat je een goede gewoonte doet, tik je op de + om ervaring en goud te krijgen!</string>
|
||||
<string name="tutorial_habits_3">Als je toegeeft en een slechte gewoonte doet, tik je op de - en verliest je avatar gezondheid, dus neem verantwoordelijkheid en voorkom slechte gewoontes.</string>
|
||||
<string name="tutorial_habits_4">Probeer het maar! Je kan de andere soorten taken doornemen met de onderstaande navigatie.</string>
|
||||
<string name="tutorial_dailies_1">Maak dagelijkse taken voor tijdgevoelige taken die volgens een vast schema gedaan moeten worden.</string>
|
||||
<string name="tutorial_dailies_2">Wees voorzichtig - als je er een mist, verliest je avatar gezondheid de volgende dag. Vink ze regelmatig af voor grootse beloningen!</string>
|
||||
<string name="tutorial_todos_1">Gebruik To Do\'s om taken bij te houden die je slechts één keer moet doen.</string>
|
||||
<string name="tutorial_todos_2">Als je To Do tegen een bepaalde dag afgewerkt moet zijn, voer dan een einddatum in. Je kan er een afvinken - doe maar!</string>
|
||||
<string name="tutorial_rewards_1">Koop een uitrusting voor je avatar met het goud dat je verdient!</string>
|
||||
<string name="tutorial_rewards_2">Je kan ook zelf beloningen maken voor in de echte wereld, gebaseerd op wat je motiveert.</string>
|
||||
<string name="tutorial_tasks_complete">Dat was het voorlopig. Als je iets vergeet, kijk dan in de FAQ sectie.</string>
|
||||
<string name="tutorial_skills">Vaardigheiden zijn speciale talenten die krachtige effecten hebben! Tik op een vaardigheid om ze te gebruiken. Het kost je Mana (de blauwe balk), dat je kan verdienen door elke dag in te checken en je taken in de echte wereld te voltooien. Kijk in de FAQ in het menu voor meer informatie!</string>
|
||||
<string name="tutorial_party">Dit is waar jij en je vrienden elkaar verantwoordelijk kunnen houden voor het bereiken van je doelen en je samen monsters kan bevechten met je taken!</string>
|
||||
<string name="tutorial_party">Dit is waar jij en je vrienden elkaar verantwoordelijk kunnen houden voor het bereiken van je doelen en je samen monsters kan bevechten met je taken!</string>
|
||||
<string name="tutorial_stats">Tik de grijze knop aan om meerdere punten ineens aan te wijzen, of tik op de pijlen om ze één punt per keer toe te voegen.</string>
|
||||
</resources>
|
||||
<string name="tutorial_party_created">Welkom bij je Groep! Je kunt meer leden vinden via een lijst met spelers die opzoek zijn naar een Groep, of nodig vrienden direct uit on de Basi-List Queeste Scroll te verdienen.
|
||||
\n
|
||||
\nGa naar Support om meer te leren over Groepen.</string>
|
||||
</resources>
|
||||
|
|
@ -995,4 +995,10 @@
|
|||
<string name="usernames_party">%s\'s Groep</string>
|
||||
<string name="your_tags">Jouw tags</string>
|
||||
<string name="challenge_tags">Uitdagingen tags</string>
|
||||
</resources>
|
||||
<string name="exact_alarm_system_settings_reminders">Herinneringen kunnen vertraagd zijn omdat de machtigingen niet zijn ingeschakeld. Tik om de machtigingen te bekijken en bij te werken.</string>
|
||||
<string name="exact_alarm_system_settings_title">`Alarm & Herinneringen` uitgeschakeld`</string>
|
||||
<string name="x_and_y">%1$s en %2$s</string>
|
||||
<string name="exact_alarm_system_settings_description">Sta`Alarmen & Herinneringen` toe in de app Instellingen om ervoor te zorgen dat de herinneringen op precies de juiste tijd verschijnen</string>
|
||||
<string name="quest_party_required_title">Je moet in een Groep zitten voordat je een Queeste kunt beginnen</string>
|
||||
<string name="quest_party_required_description">Als je eenmaal in een Groep zit, kan je zelf Queestes doen of vrienden uitnodigen om ook met hen Queestes te doen!</string>
|
||||
</resources>
|
||||
|
|
@ -17,4 +17,4 @@
|
|||
<string name="sidebar_subscription">Subskrypcja</string>
|
||||
<string name="sidebar_gems">Zakup Klejnotów</string>
|
||||
<string name="sidebar_avatar_equipment">Awatar i Wyposażenie</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
@ -3,11 +3,11 @@
|
|||
<string name="action_refresh">Odśwież</string>
|
||||
<string name="XP_default">Doświadczenie</string>
|
||||
<string name="HP_default">Zdrowie</string>
|
||||
<string name="MP_default">Mana</string>
|
||||
<string name="MP_default">Moc</string>
|
||||
<!--Prefs-->
|
||||
<string name="PS_settings_title">Ustawienia</string>
|
||||
<string name="SP_userID_title">Identyfikator użytkownika</string>
|
||||
<string name="SP_userID_summary">Twój identyfikator</string>
|
||||
<string name="SP_userID_summary">Twój identyfikator użytkownika</string>
|
||||
<string name="SP_APIToken_title">Token API</string>
|
||||
<string name="SP_APIToken_summary">Twój Token API</string>
|
||||
<string name="Language_title">Język</string>
|
||||
|
|
@ -16,21 +16,21 @@
|
|||
<string name="pref_account_header">Konto</string>
|
||||
<string name="pref_first_day_of_the_week_title">Pierwszy Dzień Tygodnia</string>
|
||||
<string name="pref_first_day_of_the_week_summary">Pierwszy dzień tygodnia we wszystkich kalendarzach</string>
|
||||
<string name="pref_reminder_header">Przypominienie</string>
|
||||
<string name="pref_reminder_checkbox">Aktywuj przypomnienie</string>
|
||||
<string name="pref_reminder_picker">Ustaw czas przypomnienia</string>
|
||||
<string name="pref_cds_header">Własny początek dnia</string>
|
||||
<string name="pref_reminder_header">Codzienne Przypomnienie</string>
|
||||
<string name="pref_reminder_checkbox">Aktywuj Przypomnienie</string>
|
||||
<string name="pref_reminder_picker">Ustaw czas Przypomnienia</string>
|
||||
<string name="pref_cds_header">Własny Początek Dnia</string>
|
||||
<string name="pref_push_notifications_checkbox">Użyj Powiadomień</string>
|
||||
<string name="push_notifications">Powiadomienia</string>
|
||||
<string name="push_notifications_sum">Ustaw swoje ustawienia powiadomień push</string>
|
||||
<string name="preference_push_you_won_challenge">Ukończono wyzwanie!</string>
|
||||
<string name="preference_push_received_a_private_message">Otrzymano prywatną wiadomość</string>
|
||||
<string name="preference_push_gifted_gems">Otrzymane diamenty</string>
|
||||
<string name="preference_push_gifted_subscription">Otrzymałeś subskrypcję</string>
|
||||
<string name="preference_push_invited_to_party">Otrzymano zaproszenie do drużyny</string>
|
||||
<string name="preference_push_invited_to_guild">Otrzymano zaproszenie do gildii</string>
|
||||
<string name="preference_push_your_quest_has_begun">Twoja misja się rozpoczęła</string>
|
||||
<string name="preference_push_invited_to_quest">Zaproszono Cię na misję</string>
|
||||
<string name="preference_push_you_won_challenge">Wygrano Wyzwanie!</string>
|
||||
<string name="preference_push_received_a_private_message">Otrzymano Prywatną Wiadomość</string>
|
||||
<string name="preference_push_gifted_gems">Otrzymane Klejnoty</string>
|
||||
<string name="preference_push_gifted_subscription">Otrzymano Subskrypcję</string>
|
||||
<string name="preference_push_invited_to_party">Otrzymano zaproszenie do Drużyny</string>
|
||||
<string name="preference_push_invited_to_guild">Otrzymano zaproszenie do Gildii</string>
|
||||
<string name="preference_push_your_quest_has_begun">Twoja Misja się Rozpoczęła</string>
|
||||
<string name="preference_push_invited_to_quest">Zaproszono Cię na Misję</string>
|
||||
<!--Adding tasks-->
|
||||
<string name="about_libraries">Biblioteki</string>
|
||||
<string name="about_habitica_open_source">Habitica jest dostępna jako otwarte oprogramowanie na platformie Github</string>
|
||||
|
|
@ -69,13 +69,13 @@
|
|||
<string name="levelup_detail">Wypełniając swoje cele życiowe, udało Ci się zyskać kolejny poziom i pełne uzdrowienie postaci!</string>
|
||||
<string name="onwards">Hurra!</string>
|
||||
<string name="faint_subtitle">Nie rozpaczaj!</string>
|
||||
<string name="faint_header">Możesz stracić całe Zdrowie!</string>
|
||||
<string name="faint_button">Uzupełnij Zdrowie & Spróbuj jeszcze raz</string>
|
||||
<string name="faint_header">Straciłeś całe Zdrowie!</string>
|
||||
<string name="faint_button">Uzupełnij Zdrowie i Spróbuj Jeszcze Raz</string>
|
||||
<string name="filter">Filtr</string>
|
||||
<string name="used_skill_without_mana">Użyto %1$s.</string>
|
||||
<string name="new_checklist_item">nowa część zadania</string>
|
||||
<string name="unlock_lvl_11">Odblokuj na poziomie 11</string>
|
||||
<string name="forgot_pw_btn">Zapomniane hasło</string>
|
||||
<string name="forgot_pw_btn">Zapomniałem hasła</string>
|
||||
<string name="tavern_inn_checkOut">Reaktywuj swoje codzienne</string>
|
||||
<string name="tavern_inn_rest">Zapauzuj swoje codzienne</string>
|
||||
<string name="reward_dialog_buy">Kup</string>
|
||||
|
|
@ -111,7 +111,7 @@
|
|||
<string name="quest_cancel">Anuluj Zaproszenie</string>
|
||||
<string name="quest_abort">Przerwij Quest</string>
|
||||
<string name="version_info">Wersja %1$s (%2$d)</string>
|
||||
<string name="sidebar_help">Pomoc i FAQ</string>
|
||||
<string name="sidebar_help">Wsparcie</string>
|
||||
<string name="complete_tutorial">Jasne!</string>
|
||||
<string name="dismiss_tutorial">Przypomnij mi później</string>
|
||||
<string name="intro_1_subtitle">Witaj w</string>
|
||||
|
|
@ -137,7 +137,7 @@
|
|||
<string name="setup_task_work_1">Przeglądnąć e-maile</string>
|
||||
<string name="setup_task_work_2">Najważniejsze zadanie</string>
|
||||
<string name="setup_task_work_3">Projekt zawodowy</string>
|
||||
<string name="setup_task_exercise_1">10 min cardio</string>
|
||||
<string name="setup_task_exercise_1">10 min kardio</string>
|
||||
<string name="setup_task_exercise_2">Rozciągnąć się</string>
|
||||
<string name="setup_task_exercise_3">Ustalić plan ćwiczeń</string>
|
||||
<string name="setup_task_healthWellness_1">Jeść zdrowe / śmieciowe jedzenie</string>
|
||||
|
|
@ -222,7 +222,7 @@
|
|||
<string name="class_changed">Jesteś %s!</string>
|
||||
<string name="choose_class">Wybierz klasę postaci</string>
|
||||
<string name="dialog_go_back">Wróć</string>
|
||||
<string name="opt_out_confirmation">Czy na pewno chcesz wyjść?</string>
|
||||
<string name="opt_out_confirmation">Czy chcesz wyjść z systemu klas\?</string>
|
||||
<string name="change_class">Zmień swoją klasę</string>
|
||||
<string name="change_class_description">Zmień klasę i odzyskaj punkty statystyk za 3 klejnoty.</string>
|
||||
<string name="enable_class">Odblokuj System Klas</string>
|
||||
|
|
@ -289,23 +289,23 @@
|
|||
<string name="belongs_to_challenge">Należy do wyzwania</string>
|
||||
<string name="has_reminder">Ma powiadomienie</string>
|
||||
<string name="has_tag">Ma tag</string>
|
||||
<string name="subscribe_title">Subskrypcja wspiera nasz skromny zespół i pozwala utrzymać Habitica. Dziękujemy!</string>
|
||||
<string name="subscribe_prompt">Subskrybuj aby uzyskać te wyjątkowe korzyści!</string>
|
||||
<string name="subscribe_listitem1">Złoto na Klejnoty</string>
|
||||
<string name="subscribe_listitem2">Mistyczne Klepsydry</string>
|
||||
<string name="subscribe_listitem3">Comiesięczne Przedmioty Mistyczne</string>
|
||||
<string name="subscribe_title">Subskrypcja wspiera deweloperów i pozwala utrzymać Habitikę</string>
|
||||
<string name="subscribe_prompt">Zmotywuj się jeszcze bardziej z nagrodami subskrybenta</string>
|
||||
<string name="subscribe_listitem1">Darmowe Miesięczne Klejnoty</string>
|
||||
<string name="subscribe_listitem2">Miesięczne Mistyczne Klepsydry</string>
|
||||
<string name="subscribe_listitem3">Miesięczne Mistyczne Przedmioty</string>
|
||||
<string name="subscribe_listitem4">Specjalny Chowaniec Subskrybenta</string>
|
||||
<string name="subscriptions">Subskrypcje</string>
|
||||
<string name="subscription_duration">Powtarzaj co %s</string>
|
||||
<string name="subscribe">Subskrybuj</string>
|
||||
<string name="subscribe_listitem1_description">Będziesz mieć możliwość zakupu Klejnotów na Targu za 20 sztuk złota każdy!</string>
|
||||
<string name="subscribe_listitem1_description">Zdobądź do 50 Klejnotów, które można kupić za Złoto na Rynku i kup za nie Przygody, Personalizacje, Zwierzęta i wiele więcej!</string>
|
||||
<string name="subscribe_listitem3_description">Zostań Subskrybentem aby uzyskać teraz elitarny zestaw oraz otrzymywać nowe przedmioty w każdym miesiącu!</string>
|
||||
<string name="subscription_hourglasses">+%d Mistycznej Klepsydry</string>
|
||||
<string name="subscription_hourglasses">%d Klepsydry</string>
|
||||
<string name="payment_method">Metoda płatności</string>
|
||||
<string name="subscription">Subskrypcja</string>
|
||||
<string name="active">Aktywne</string>
|
||||
<string name="cancel_subscription">Anuluj Subskrypcję</string>
|
||||
<string name="cancel_subscription_google_description">Z subskrybcji możesz zrezygnować w sklepie Google Play. Jakiekolwiek uznania z miesięcznych subskrypcji zostaną zastosowane po zakończeniu obecnej subskrypcji.</string>
|
||||
<string name="cancel_subscription_google_description">Nie chcesz już subskrybować\? Możesz znaleźć opcję anulowania subskrypcji, przechodząc do sekcji Płatności i subskrypcje w aplikacji Google Play Store. Wszelkie pozostałe miesiące kredytu subskrypcji zostaną dodane do daty zakończenia po anulowaniu.</string>
|
||||
<string name="cancel_subscription_notgoogle_description">Nie chcesz dłużej subskrybować\? Z powodu ograniczeń w płatnościach mobilnych, musisz anulować subskrypcję poprzez stronę webową. Aby to zrobić, tapnij poniższy przycisk, zaloguj się na swoje konto, tapnij ikonę Użytkownika w prawym, górnym rogu i przejdź do Subskrypcji. Wszelkie uznania z miesięcznych subskrypcji zostaną zastosowane po zakończeniu Twojej subskrypcji. Będzie nam Ciebie brakowało!</string>
|
||||
<string name="visit_habitica_website">Odwiedź Stronę Habitiki</string>
|
||||
<string name="current_bonuses">Obecne Dodatki</string>
|
||||
|
|
@ -473,7 +473,7 @@
|
|||
<string name="allocating_points">Przydzielanie Punktów</string>
|
||||
<string name="class_equipment">Ekwipunek</string>
|
||||
<string name="classless">Bez klasy</string>
|
||||
<string name="class_equipment_shop_dialog">Ten przedmiot jest dostępny tylko dla konkretnej klasy. \nMożesz zmienić swoją klasę w Ustawieniach.</string>
|
||||
<string name="class_equipment_shop_dialog">Ten przedmiot jest dostępny tylko dla konkretnej klasy. Możesz zmienić swoją klasę w Ustawieniach.</string>
|
||||
<string name="class_gear_disclaimer">Możesz kupić wyposażenie wyłącznie dla swojej obecnej klasy</string>
|
||||
<string name="check_into_inn">Zamelduj się w Karczmie</string>
|
||||
<string name="community_guidelines">Zasady Społeczności</string>
|
||||
|
|
@ -533,18 +533,18 @@
|
|||
<string name="gift_confirmation_title">Twój prezent został wysłany!</string>
|
||||
<string name="discover">Odkryj</string>
|
||||
<string name="preference_push_important_announcements">Ważne Ogłoszenia</string>
|
||||
<string name="reminder_title">Pamiętaj aby odznaczyć swoje zadania</string>
|
||||
<string name="reminder_title">Pamiętaj, aby odznaczyć swoje zadania</string>
|
||||
<string name="ago_minutes">%d minut temu</string>
|
||||
<string name="ago_hours">%d godzin temu</string>
|
||||
<string name="evolved_pet_title">Oswoiłeś %s, idź na przejażdżkę!</string>
|
||||
<string name="opting_out_progress">Wycofywanie się</string>
|
||||
<string name="subscription_duration_norenew">Na %s</string>
|
||||
<string name="subscribe_listitem2_description">Zdobądź Mistyczną Klepsydrę na zakupy w Sklepie Podróżników w Czasie!</string>
|
||||
<string name="subscribe_listitem2_description">Nigdy więcej nie przegap żadnego przedmiotu dzięki 1 Mistycznej Klepsydrze miesięcznie do wykorzystania w Sklepie Podróżników w Czasie!</string>
|
||||
<string name="subscribe_listitem4_description">Otrzymaj ekskluzywnego chowańca: Purpurowego, Królewskiego Jackalope, kiedy zostaniesz nowym Subskrybentem.</string>
|
||||
<string name="subscribe1month_gemcap">Limit 25 klejnotów</string>
|
||||
<string name="subscribe3month_gemcap">Limit 3 klejnotów</string>
|
||||
<string name="subscribe6month_gemcap">Limit 35 klejnotów</string>
|
||||
<string name="subscribe12month_gemcap">Limit 45 klejnotów</string>
|
||||
<string name="subscribe1month_gemcap">25 Klejnotów miesięcznie</string>
|
||||
<string name="subscribe3month_gemcap">30 Klejnotów miesięcznie</string>
|
||||
<string name="subscribe6month_gemcap">35 Klejnotów miesięcznie</string>
|
||||
<string name="subscribe12month_gemcap">45 Klejnotów miesięcznie</string>
|
||||
<string name="due">Do</string>
|
||||
<string name="join_a_challenge">Dołącz do wyzwania aby dodać wybrany zestaw zadań do twojej listy i rywalizuj z innymi Habitikanami aby zdobyć osiągnięcia a nawet klejnoty!</string>
|
||||
<string name="welcome_text">Oh, musisz byś to nowy. Jestem Justin, będę twoim przewodnikiem po Habitice.
|
||||
|
|
@ -574,7 +574,7 @@
|
|||
<string name="no_notifications_title">Teraz jesteś na bieżąco!</string>
|
||||
<string name="no_notifications_text">Wróżki powiadomień składają ci gromkie brawa. Dobra robota!</string>
|
||||
<string name="dismiss_all">Odrzuć wszystko</string>
|
||||
<string name="new_bailey_update">Nowości od Bailey</string>
|
||||
<string name="new_bailey_update">Nowości od Bailey!</string>
|
||||
<string name="new_msg_guild"><![CDATA[<b>%1$s</b> ma nowe posty]]></string>
|
||||
<string name="new_msg_party"><![CDATA[Twoja drużyna <b>%1$s</b> ma nowe posty]]></string>
|
||||
<string name="unallocated_stats_points"><![CDATA[Masz <b>nieprzydzielone Punkty Atrybutów: %1$s</b>]]></string>
|
||||
|
|
@ -626,7 +626,7 @@
|
|||
<string name="login_incentive_short">Odwiedzaj Habitikę regularnie</string>
|
||||
<string name="create_account_short">stwórz konto</string>
|
||||
<string name="login_incentive_short_count">%d Wizyt</string>
|
||||
<string name="subscribe_listitem5">Podwój Łup</string>
|
||||
<string name="subscribe_listitem5">Wyjątkowe Zwierzaki & Więcej Nagród</string>
|
||||
<string name="subscribe_listitem3_description_new">Subskrybuj teraz, aby zdobyć %s i otrzymywać nowe przedmioty każdego miesiąca!</string>
|
||||
<string name="world_boss_description_1">Wykonuj zadania aby zadawać obrażenia Bossowi</string>
|
||||
<string name="world_boss_description_intro">Boss Świata jest specjalnym wydarzeniem, kiedy cała społeczność, wypełniając swoje zadania, dąży wspólnie do pokonania bardzo silnego potwora!</string>
|
||||
|
|
@ -665,7 +665,7 @@
|
|||
<string name="join_party_description">Przekaż członkowi grupy poniższą nazwę użytkownika, aby mógł Ci wysłać zaproszenie</string>
|
||||
<string name="no_party_description">Razem z przyjaciółmi lub samodzielnie staw czoło misjom. Walcz z potworami, twórz wyzwania i pomóż sobie być odpowiedzialnym z pomocą Drużyn.</string>
|
||||
<string name="tiers_descriptions">Kolorowe nazwy użytkowników, które możesz zobaczyć w czacie, reprezentują poziom osób wnoszących wkład. Im wyższy poziom, tym większy wkład wniósł dany użytkownik do Habitica poprzez grafikę, kod, społeczność i inne!</string>
|
||||
<string name="subscribe_listitem5_description">Odkryj więcej przedmiotów w Habitica z podwojoną szansą na dzienne łupy.</string>
|
||||
<string name="subscribe_listitem5_description">Zdobądź Królewsko Fioletowego Szakala, a także dwa razy więcej Jaj, Mikstur Wylęgowych i Pożywienia każdego dnia, aby powiększyć swoją kolekcję Zwierząt!</string>
|
||||
<string name="need_help_header_description">Wyślij wiadomość do %s, aby inny gracz odpowiedział na twoje pytania.</string>
|
||||
<string name="update_available">Dostępna aktualizacja: %1$s (%2$d)</string>
|
||||
<string name="streak">Seria</string>
|
||||
|
|
@ -771,9 +771,9 @@
|
|||
<string name="discard_changes_to_task_message">Jesteś pewien, że chcesz porzucić zmiany w tym zadaniu\?</string>
|
||||
<string name="level_abbreviated">Poziom %d</string>
|
||||
<string name="change_class_selected_confirmation">Czy chcesz zmienić klasę na: %1$s\?</string>
|
||||
<string name="class_changed_description">Możesz teraz używać umiejętności i kupować wyposażenie klasy: %s!</string>
|
||||
<string name="enable_notifications">Włącz powiadomienia</string>
|
||||
<string name="push_notification_system_settings_title">Powiadomienia wyłączone</string>
|
||||
<string name="class_changed_description">Możesz teraz używać %s umiejętności i kupować wyposażenie ze sklepów. Podnoś poziomy aby zbierać punkty statystyk które możesz wykorzystać do podnoszenia swoich umiejętnosci.</string>
|
||||
<string name="enable_notifications">Włącz Powiadomienia</string>
|
||||
<string name="push_notification_system_settings_title">Powiadomienia Wyłączone</string>
|
||||
<string name="title">Tytuł</string>
|
||||
<string name="positive_sentence">pozytywne</string>
|
||||
<string name="negative_sentence">negatywne</string>
|
||||
|
|
@ -781,8 +781,7 @@
|
|||
<string name="partyUpDescription">Dołączyłeś(-aś) do drużyny z inną osobą!</string>
|
||||
<string name="reset_account_title">Czy na pewno chcesz zresetować konto\?</string>
|
||||
<string name="confirm_reset">Potwierdź zresetowanie</string>
|
||||
<string name="insufficient_level_equipment_dialog">Ten przedmiot jest dostępny tylko dla konkretnej klasy.
|
||||
\nWybór klasy zostanie odblokowany na Poziomie 10.</string>
|
||||
<string name="insufficient_level_equipment_dialog">Ten przedmiot jest dostępny wyłącznie dla konkretnej klasy. Wybór klasy zostanie odblokowany na Poziomie 10.</string>
|
||||
<string name="reset_counter">Zresetuj Licznik</string>
|
||||
<string name="month_reminder_title">Rutyna Cię dobija\?</string>
|
||||
<string name="month_reminder_text">Czasem łatwiej jest zacząć od zera. Habitica może Ci w tym pomóc!</string>
|
||||
|
|
@ -943,8 +942,8 @@
|
|||
<string name="hatchedPetDescription">Jest tak wiele Chowańców do zebrania, że z pewnością niedługo natrafisz na swojego ulubieńca. A jeśli je nakarmisz, być może urosną duże i silne…</string>
|
||||
<string name="someone">Ktoś</string>
|
||||
<string name="local">lokalne</string>
|
||||
<string name="push_notification_system_settings_description">Zezwól Habitica na wysyłanie powiadomień w ustawieniach telefonu, aby otrzymywać powiadomienia push</string>
|
||||
<string name="push_notification_system_settings_reminders">Zezwól Habitica na wysyłanie powiadomień w ustawieniach telefonu, aby otrzymywać przypomnienia</string>
|
||||
<string name="push_notification_system_settings_description">Zezwól Habitica na wysyłanie powiadomień w Ustawieniach telefonu, aby otrzymywać powiadomienia push</string>
|
||||
<string name="push_notification_system_settings_reminders">Pozwól Habitica na wysyłanie przypomnień</string>
|
||||
<string name="usernames_party">Drużyna %s</string>
|
||||
<string name="feedPet_title">Nakarm Chowańca</string>
|
||||
<string name="purchase_equipment_title">Kup ekwipunek</string>
|
||||
|
|
@ -1114,7 +1113,7 @@
|
|||
<string name="end_challenge_description">Aby zakończyć Wyzwanie, zaloguj się na stronie Habitica, a następnie naciśnij przycisk “Zakończ Wyzwanie” znajdujący się po prawej stronie ekranu Wyzwania.</string>
|
||||
<string name="you_won_challenge">Wygrałeś(-aś) Wyzwanie</string>
|
||||
<string name="congratulations">Gratulacje!</string>
|
||||
<string name="won_achievement_description_noname">Zostałeś(-aś) zwycięzcą Wyzwania! Twoja wygrana została zapisana w Twoich Osiągnięciach</string>
|
||||
<string name="won_achievement_description_noname">Zostałeś zwycięzcą Wyzwania! Twoja wygrana została zapisana w Twoich Osiągnięciach.</string>
|
||||
<string name="claim_x_gems">Odbierz %d Klejnotów</string>
|
||||
<string name="gift_one_get_one_purchase_banner">Podaruj Subskrypcję i otrzymaj darmową Subskrybcję do: %s</string>
|
||||
<string name="one_month_one_time">1 miesiąc jednorazowej Subskrypcji</string>
|
||||
|
|
@ -1128,7 +1127,7 @@
|
|||
<string name="challenge_tags">Tagi Wyzwań</string>
|
||||
<string name="group_tags">Tagi Grup</string>
|
||||
<string name="x_and_y">%1$s i %2$s</string>
|
||||
<string name="exact_alarm_system_settings_title">Alarmy i Przypomnienia wyłączone</string>
|
||||
<string name="exact_alarm_system_settings_title">Alarmy i Przypomnienia Wyłączone</string>
|
||||
<string name="sell_confirmation_title">Czy na pewno chcesz sprzedać %s\?</string>
|
||||
<string name="exact_alarm_system_settings_reminders">Przypomnienia mogą być opóźnione, ponieważ nie mają wymaganych uprawnień. Dotknij aby zobaczyć i zmienić uprawnienia.</string>
|
||||
<string name="exact_alarm_system_settings_description">Zezwól na Alarmy i Przypomnienia w Ustawieniach, aby mieć pewność, że przypomnienia pojawią się dokładnie o zaplanowanej porze</string>
|
||||
|
|
@ -1144,4 +1143,20 @@
|
|||
<item quantity="many">Co %d dni</item>
|
||||
<item quantity="other">Co %d dni</item>
|
||||
</plurals>
|
||||
<plurals name="x_hours">
|
||||
<item quantity="one">%d Godziny</item>
|
||||
<item quantity="few">%d Godzin</item>
|
||||
<item quantity="many">%d Godzin</item>
|
||||
<item quantity="other">%d Godzin</item>
|
||||
</plurals>
|
||||
<string name="subscribe_listitem3_description_alt">Wystrój się w najnowszy ekskluzywny sprzęt. Subskrybuj teraz, aby otrzymać %2$s od %1$s!</string>
|
||||
<string name="subscribe_listitemFaint_description">Uniknij utraty postępów dzięki natychmiastowemu uleczeniu, gdy raz dziennie zabraknie Ci punktów zdrowia!</string>
|
||||
<string name="subscribe_listitemFaint">Zdobąć Drugą Szansę</string>
|
||||
<string name="subscription_hourglasses_3month_timeframe">Klepsydra za 3 miesiące</string>
|
||||
<plurals name="x_minutes">
|
||||
<item quantity="one">%d Minuta</item>
|
||||
<item quantity="few">%d Minut</item>
|
||||
<item quantity="many">%d Minuty</item>
|
||||
<item quantity="other">%d Minuty</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
|
|
@ -710,7 +710,7 @@
|
|||
<string name="equip_automatically">Equipar novos equipamentos automaticamente</string>
|
||||
<string name="leave_challenges">Abandonar Desafios</string>
|
||||
<string name="keep_challenges">Manter Desafios</string>
|
||||
<string name="inviteFriendDescription">Você convidou um(a) amigo(a) (ou amigos) que se juntaram à você em sua aventura!</string>
|
||||
<string name="inviteFriendDescription">Você convidou um amigo ou mais que se juntaram a você em sua aventura!</string>
|
||||
<string name="inviteFriendTitle">Convidou um(a) amigo(a)</string>
|
||||
<string name="joinedChallengeDescription">Você colocou-se à prova participando de um Desafio!</string>
|
||||
<string name="joinedChallengeTitle">Entrou em um Desafio</string>
|
||||
|
|
@ -1032,7 +1032,7 @@
|
|||
<string name="block_user_title">Bloquear %s\?</string>
|
||||
<string name="unblock_user">Desbloquear Usuário</string>
|
||||
<string name="block_user">Bloquear Usuário</string>
|
||||
<string name="spooky_promo_info_prompt">A Promoção de Gemas está de volta para assombrar o final do Festival de Outono deste ano! Esta é a última chance de conseguir mais Gemas do que nunca, então estoque enquanto dura!</string>
|
||||
<string name="spooky_promo_info_prompt">A Promoção de Gemas está de volta para assombrar o final do Festival de Outono deste ano! Esta é a última chance de conseguir mais Gemas do que nunca, então aproveite enquanto duram os estoques!</string>
|
||||
<string name="spooky_promo_info_instructions">Entre %1$s e %2$s, basta comprar qualquer pacote de Gemas como de costume e sua conta será creditada com a quantidade promocional de Gemas. Mais Gemas para gastar, compartilhar ou economizar para lançamentos futuros!</string>
|
||||
<string name="view_gem_bundles">Ver Pacotes de Gemas</string>
|
||||
<string name="fall_promo_info_prompt">O Festival de Outono está em pleno andamento, então pensamos que era o momento perfeito para apresentar nossa primeira Promoção de Gemas! Agora você obterá mais Gemas com cada compra do que nunca.</string>
|
||||
|
|
@ -1181,7 +1181,7 @@
|
|||
<string name="confirm_reset">Confirmar reinicio</string>
|
||||
<string name="forgot_password_hint_example">ex. habitrabbit ou gryphon@exemplo.com</string>
|
||||
<string name="quest_party_required_title">Você precisa estar em um Grupo antes de começar uma Missão</string>
|
||||
<string name="quest_party_required_description">Quando estiver em um Grupo, poderá fazer Missões por conta própria ou convidar amigos para fazerem com você!</string>
|
||||
<string name="quest_party_required_description">Quando estiver em um Grupo, você poderá fazer Missões por conta própria ou convidar amigos para completarem Missões juntos!</string>
|
||||
<string name="armoireEquipment_new">Você encontrou um Equipamento raro no Armário!</string>
|
||||
<string name="armoireFood_new">Você vasculhou o Armário e encontrou comida. O que isso faz aqui\?</string>
|
||||
<string name="sell_no_price">Vender</string>
|
||||
|
|
|
|||
|
|
@ -1245,4 +1245,6 @@
|
|||
<string name="positive_sentence">正面</string>
|
||||
<string name="negative_sentence">負面</string>
|
||||
<string name="x_and_y">%1$s 與 %2$s</string>
|
||||
<string name="exact_alarm_system_settings_reminders">如果未開啓權限,提醒將可能會延遲。點擊查看和設置權限。</string>
|
||||
<string name="exact_alarm_system_settings_description">在設置內允許`警告和提醒`權限以保證提醒能準時觸發</string>
|
||||
</resources>
|
||||
|
|
@ -298,7 +298,7 @@
|
|||
<string name="has_reminder">有提醒</string>
|
||||
<string name="has_tag">有标签</string>
|
||||
<string name="subscribe_title">购买订阅支持我们的小团队以及帮助维持Habitica运作</string>
|
||||
<string name="subscribe_prompt">成为订阅者即可享受这些专属福利!</string>
|
||||
<string name="subscribe_prompt">订阅即享更多奖励,助您时刻保持动力</string>
|
||||
<string name="subscribe_listitem1">月度免费宝石</string>
|
||||
<string name="subscribe_listitem2">月度神秘沙漏</string>
|
||||
<string name="subscribe_listitem3">月度神秘物品</string>
|
||||
|
|
@ -306,7 +306,7 @@
|
|||
<string name="subscriptions">订阅</string>
|
||||
<string name="subscription_duration">每%s订阅一次</string>
|
||||
<string name="subscribe">订阅</string>
|
||||
<string name="subscribe_listitem1_description">最多可获取50个商店宝石,用于购买任务,装扮,宠物等!</string>
|
||||
<string name="subscribe_listitem1_description">最多可获取50个可用于购买金币的商店宝石,用于购买任务,装扮,宠物等!</string>
|
||||
<string name="subscribe_listitem2_description">不要错过每月可在时空旅行者商店中购买一次价格为1个神秘沙漏的物品的机会!</string>
|
||||
<string name="subscribe_listitem4_description">首次订阅可获得皇家紫色鹿角兔宠物。</string>
|
||||
<string name="subscribe1month_gemcap">每月25颗宝石</string>
|
||||
|
|
@ -318,7 +318,7 @@
|
|||
<string name="subscription">订阅</string>
|
||||
<string name="active">进行中</string>
|
||||
<string name="cancel_subscription">取消订阅</string>
|
||||
<string name="cancel_subscription_google_description">你可以通过GooglePlay商店取消订阅。任何结存的订阅将会在你的定期订阅结束后生效。</string>
|
||||
<string name="cancel_subscription_google_description">不想继续订阅了?你可以在GooglePlay商店的付款和订阅栏目找到取消订阅的选项。任何结存的订阅将会被累加到你取消前的最后一天。</string>
|
||||
<string name="cancel_subscription_notgoogle_description">不想继续订阅了?由于手机支付的限制,您只能通过我们的网站取消订阅。请点击下方按钮登录您的账号,点击右上角的用户图标,然后转到订阅。任何结存的订阅都将在订阅结束后生效。我们会想念您的!</string>
|
||||
<string name="visit_habitica_website">访问Habitica网页版</string>
|
||||
<string name="current_bonuses">现有的奖励</string>
|
||||
|
|
@ -453,7 +453,7 @@
|
|||
<string name="insufficientHourglasses">你需要更多的神秘沙漏才能购买此物品!</string>
|
||||
<string name="get_hourglasses">获取沙漏</string>
|
||||
<string name="subscribe_for_hourglasses">订阅以获取沙漏</string>
|
||||
<string name="timetravelers_closed_description">每连续订阅三个月即可一个神秘沙漏,然后用它们解锁过去和未来的限定版物品、宠物和坐骑!</string>
|
||||
<string name="timetravelers_closed_description">订阅期间每月可获得一个神秘沙漏,以解锁过去和未来的限定版物品、宠物和坐骑!</string>
|
||||
<string name="want_to_subscribe">我想订阅</string>
|
||||
<string name="seasonal_closed_description">盛典会在春分、夏至、秋分、冬至前后举行,到时再来看看各种有趣的特殊季节商品吧!</string>
|
||||
<string name="come_back_soon">很快回来!</string>
|
||||
|
|
@ -619,9 +619,9 @@
|
|||
<string name="gift_subscription">赠送订阅</string>
|
||||
<string name="gift_one_get_one_description">订阅赠一得一活动正在进行中!</string>
|
||||
<string name="gift_title">你想将礼物送给谁?</string>
|
||||
<string name="gift_one_get_one">赠一得一!</string>
|
||||
<string name="gift_one_get_one">赠一得一</string>
|
||||
<string name="gift_one_get_one_detailed_description">在此促销活动期间,你将在送出礼物后自动收到相同的订阅。</string>
|
||||
<string name="gift_subscription_subtitle">在下方选择你想要赠送的订阅时长!此购买不会自动续订。</string>
|
||||
<string name="gift_subscription_subtitle">请选择你想要赠送的订阅时长!此购买不会自动续订。</string>
|
||||
<string name="send_gift">赠送礼物</string>
|
||||
<string name="server">服务器</string>
|
||||
<string name="gift_confirmation_title">你的礼物已送出!</string>
|
||||
|
|
@ -904,9 +904,9 @@
|
|||
\n难度较高的任务或红色任务会给你**更多经验值**。**智力属性**也会提高你的经验值获取速度。</string>
|
||||
<string name="experience_points">经验值</string>
|
||||
<string name="manual_sync_restart_description">有时,应用程序不会自动更新内容。请尝试滑动刷新或强制关闭应用再重新打开它。</string>
|
||||
<string name="hourglasses_description">神秘沙漏是一种极为稀有的货币,只有连续订阅Habitica三个月或更长时间才能获得。它们可用于在时空旅行者商店中购买过去的装备套装、宠物、坐骑、动画背景甚至特殊副本。
|
||||
<string name="hourglasses_description">神秘沙漏是一种极为稀有的货币,仅限订阅用户获取。你可在时空旅行者商店中购买往期订阅专属装备套装、专属宠物、坐骑、动画背景甚至特殊副本!这些超值特权将助您全年保持高效,为每个成就加冕!
|
||||
\n
|
||||
\n你每年最多可以收到四个神秘沙漏。收到神秘沙漏的时间取决于你的续订时间。它们将在你支付了使你有资格获得沙漏的订阅费用后的次月月初发送。详情请查看[订阅]页面。</string>
|
||||
\n订阅用户将在每个享有订阅福利的月初收到1枚神秘沙漏,并享有多项特权。若对时空旅者提供的服务感兴趣,请务必查看我们的订阅选项!</string>
|
||||
<string name="clear_cache_description">要清除缓存,请打开手机的应用设置。前往存储>应用>Habitica,然后点击清除缓存。</string>
|
||||
<string name="clear_cache">清除缓存</string>
|
||||
<string name="manual_sync_restart">手动同步或重启</string>
|
||||
|
|
@ -1107,10 +1107,10 @@
|
|||
<string name="mystery_item_title">你打开神秘礼盒,得到了……</string>
|
||||
<string name="team_information">团队信息</string>
|
||||
<string name="sidebar_teams">团队</string>
|
||||
<string name="twelve_months_one_time">十二个月</string>
|
||||
<string name="six_months_one_time">六个月</string>
|
||||
<string name="three_months_one_time">三个月</string>
|
||||
<string name="one_month_one_time">一个月</string>
|
||||
<string name="twelve_months_one_time">12个月</string>
|
||||
<string name="six_months_one_time">6个月</string>
|
||||
<string name="three_months_one_time">3个月</string>
|
||||
<string name="one_month_one_time">1个月</string>
|
||||
<string name="gift_one_get_one_purchase_banner">赠一得一活动持续至%s</string>
|
||||
<string name="terms_of_service">服务条款</string>
|
||||
<string name="privacy_policy">隐私政策</string>
|
||||
|
|
@ -1134,9 +1134,9 @@
|
|||
<string name="add_password">添加密码</string>
|
||||
<string name="connect">连接</string>
|
||||
<string name="add">添加</string>
|
||||
<string name="apitoken_summary">复制令牌。小心,这相当于密码!</string>
|
||||
<string name="apitoken_summary">开发者及第三方工具专用密码令牌。</string>
|
||||
<string name="added_social_auth">已添加%s身份验证</string>
|
||||
<string name="password_not_matching">必须正确输入两次密码</string>
|
||||
<string name="password_not_matching">输入的密码不匹配</string>
|
||||
<string name="email_invalid">邮箱地址无效</string>
|
||||
<string name="discard_changes_to_task_message">你确定要放弃对此任务的更改吗?</string>
|
||||
<string name="gift_confirmation_text_gems_new">你向@%1$s赠送了%2$s颗宝石。</string>
|
||||
|
|
@ -1281,7 +1281,7 @@
|
|||
<string name="change_class_confirmation_message">这将切换商店中解锁的装备,并改变你可使用的技能</string>
|
||||
<string name="your_tags">你的标签</string>
|
||||
<string name="challenge_tags">挑战标签</string>
|
||||
<string name="subscribe_listitemFaint">用1点生命值撑住!</string>
|
||||
<string name="subscribe_listitemFaint">获得第二次机会</string>
|
||||
<string name="reset_account_description_new">你将会失去所有的等级、金币和经验。你设置的所有任务及其任务历史数据将会被删除(挑战任务除外)。你将失去除了订阅者物品和免费纪念物品外的所有装备,但你之后可以重新购买被删除的物品。重新购买职业专属装备需要对应的职业。你当前的职业、成就、宠物和坐骑将会保留。如果你已确定要重置数据,请在下方输入你的密码。</string>
|
||||
<string name="need_help_description">你可以联系我们,我们的团队成员会尽力提供帮助!</string>
|
||||
<string name="report_player_title">举报%s?</string>
|
||||
|
|
@ -1334,7 +1334,7 @@
|
|||
<string name="for_for_free_description">为了让派对继续下去,我们将送出派对长袍、20 颗宝石、限定版披风套装和背景!</string>
|
||||
<string name="visit_the_market">访问市场</string>
|
||||
<string name="ends_in_x">于%s结束</string>
|
||||
<string name="subscribe_listitemFaint_description">当你的生命值耗尽时,你可以得到第二次机会。</string>
|
||||
<string name="subscribe_listitemFaint_description">当你的生命值耗尽时,你可以得到第二次机会!</string>
|
||||
<string name="visit_market">访问市场</string>
|
||||
<string name="exclusive_items_await">专属物品和礼物在等着你</string>
|
||||
<string name="see_more">查看更多</string>
|
||||
|
|
@ -1455,7 +1455,7 @@
|
|||
<string name="subscriber_button_armoire">免费再次开启!</string>
|
||||
<string name="subscriber_button_faint">续命机制:1HP极限锁血!</string>
|
||||
<string name="sub_perk">订阅特权</string>
|
||||
<string name="subscribe_incentive_text_faint">订阅即享每日1次免死特权,避免生命值归零!</string>
|
||||
<string name="subscribe_incentive_text_faint">订阅即享每日1次免死特权,避免生命值归零</string>
|
||||
<string name="subscriber_benefit">订阅会员额外奖励</string>
|
||||
<string name="subscribe_listitemArmoire">双倍宝箱奖励</string>
|
||||
<string name="seeking_hint">关注对您的邀请或创建自己的队伍</string>
|
||||
|
|
@ -1480,7 +1480,7 @@
|
|||
<string name="quest_mechanics">副本机制</string>
|
||||
<string name="quest_mechanics_collecting_description">收集副本中,完成任务将有随机几率获得副本物品。获得中的副本物品将在每日重置时发放。感知属性可提高物品掉率。</string>
|
||||
<string name="see_more_subscription_options">查看更多订阅方案</string>
|
||||
<string name="faint_subscriber_description">订阅会员权利:若您的生命值耗尽,每日可获得一次复活机会。</string>
|
||||
<string name="faint_subscriber_description">订阅会员专属特权:若您的生命值耗尽,每日可获得一次复活机会</string>
|
||||
<string name="preference_push_invited_to_group_plan">您被邀请加入团队计划</string>
|
||||
<string name="preference_push_joined_group_plan_mention">在团队计划中被@提及</string>
|
||||
<string name="subscribe_incentive_button_faint">命悬一线?订阅立即锁住最后1HP!</string>
|
||||
|
|
@ -1524,7 +1524,7 @@
|
|||
<string name="item_footer_description">前往[市场](/shops/market)选购所需物品!</string>
|
||||
<string name="g1g1_promo_info_limitations_noutc">本次限时活动期限为 %1$s 至,%2$s。此促销仅适用于向其他Habitica用户赠送订阅。如您或受赠人为订阅用户,则所获赠时长将转为预存权益,将于现订阅服务终止后启用。</string>
|
||||
<string name="by_invite">通过邀请</string>
|
||||
<string name="habiticans_looking_party">这些玩家正在寻找队伍:</string>
|
||||
<string name="habiticans_looking_party">这些玩家正在寻找队伍</string>
|
||||
<string name="habiticans_looking_party_empty">当前暂无玩家申请组队,稍后再来看看吧!</string>
|
||||
<string name="invited">已邀请</string>
|
||||
<string name="invite_with_username_email">通过@用户名或邮箱进行邀请</string>
|
||||
|
|
@ -1572,7 +1572,7 @@
|
|||
<string name="quest_mechanics_rage_title">怒气值</string>
|
||||
<string name="quest_mechanics_rage_description">部分高难度Boss拥有橙色怒气条(位于血条下方)。当参与者未完成日常任务时,怒气值会持续累积。当怒气条攒满后,首领将发动致命一击,造成额外伤害!</string>
|
||||
<string name="subscribe_listitemArmoire_description_2">每次开启宝箱后,可立即获得一次免费再开机会!</string>
|
||||
<string name="subscribe_listitem2_description_2">随时间累积获得时之沙漏,即可在时空旅人商店兑换物品。</string>
|
||||
<string name="subscribe_listitem2_description_2">随时间累积获得时之沙漏,即可在时空旅人商店兑换物品</string>
|
||||
<string name="max_gem_cap">宝石持有上限</string>
|
||||
<string name="max_gem_cap_text">立即以最大宝石持有上限开始游戏</string>
|
||||
<string name="open_habitica_website">访问Habitica网站</string>
|
||||
|
|
@ -1591,4 +1591,33 @@
|
|||
<string name="subscribe_incentive_text_armoire">订阅后每次购买宝箱可额外获得一次开箱机会</string>
|
||||
<string name="subscribe_second_armoire_open_text">订阅者可获得额外的宝箱机会和其他福利!</string>
|
||||
<string name="empty_potions_description">完成任务、孵化药水副本,或直接前往 [Market](/shops/market)去购买所需物品吧!</string>
|
||||
<string name="next_switch_in_x">下次切换在%s后</string>
|
||||
<string name="rescrubscribe_to_pick_up">重新订阅即可继续您的旅程!</string>
|
||||
<string name="register_tos_confirm">您同意我们的 <a href="https://habitica.com/static/terms">服务条款</a> 并且已经阅读了我们的 <a href="https://habitica.com/static/privacy">隐私政策</a>.</string>
|
||||
<string name="popular">热门</string>
|
||||
<string name="enjoy_benefits">享受你的订阅者专属奖励</string>
|
||||
<string name="customizations_owner">Felicitus</string>
|
||||
<string name="max_gem_cap_text_gift">他们将立即以最大宝石持有上限开始游戏</string>
|
||||
<string name="unlocks_x_gems_per_month">他们每月可在商店解锁%d颗宝石</string>
|
||||
<string name="two_gems_per_month_gift">他们在订阅期间每月可获取+2宝石</string>
|
||||
<string name="use_skill">使用技能</string>
|
||||
<string name="change_email_description">此邮箱用于登录 Habitica 及接收系统通知。</string>
|
||||
<string name="change_username_description">用户名长度应为 1-20 个字符,仅可使用:英文小写字母 a 至 z、数字 0 至 9、连字符 (-) 或下划线 (_)。</string>
|
||||
<string name="change_display_name">修改昵称</string>
|
||||
<string name="display_name_description">此名称将作为您在 Habitica 中的角色名称显示。与用户名不同,昵称无需唯一。</string>
|
||||
<string name="password_change_info">密码需要至少为8位字符。修改密码后,系统将自动登出您在其他设备及第三方工具中的所有会话。</string>
|
||||
<string name="confirm_new_password">确认新密码</string>
|
||||
<string name="use_on_party">在队伍中使用</string>
|
||||
<string name="save_about_me">保存个人简介</string>
|
||||
<string name="about_me_description">添加个人简介,它将显示于 Habitica 个人资料中供其他玩家查看。</string>
|
||||
<string name="api_token_title">API 令牌</string>
|
||||
<string name="api_token_is_password">你的API令牌功能相当于密码</string>
|
||||
<string name="api_token_reset_title">如您需要新的API令牌</string>
|
||||
<string name="api_token_is_password_info">切勿公开分享此令牌。 他人可能要求您提供用户ID,但绝对不可在 GitHub 等公开平台发布API令牌。</string>
|
||||
<string name="copy_token">复制令牌</string>
|
||||
<string name="save_photo_url">保存照片链接</string>
|
||||
<string name="photo_url_description">您可在 Habitica 个人资料中添加图片链接,向其他玩家展示您的专属形象。</string>
|
||||
<string name="chat_empty_state_title">开始畅聊吧!</string>
|
||||
<string name="api_token_reset_info">通过修改密码即可重置令牌。重置后需要:在所有设备重新登录Habitica、向您使用的第三方工具提供新令牌。</string>
|
||||
<string name="chat_empty_state_description">请务必保持友善并遵循社区准则。</string>
|
||||
</resources>
|
||||
|
|
@ -1438,7 +1438,7 @@
|
|||
<string name="invited">Invited</string>
|
||||
<string name="invite_with_username_email">Invite with @username or email</string>
|
||||
<string name="habiticans_send_invite">Send an invite directly to players you know</string>
|
||||
<string name="username_or_email">Username or email address</string>
|
||||
<string name="username_or_email">Username or email</string>
|
||||
<string name="seeking_party_description">Want to join a Party with others but don’t know any other players? Let Party leaders know you’re looking for an invite!</string>
|
||||
<string name="seeking_party_title">Looking for a Party?</string>
|
||||
<string name="look_for_party">Look for a Party</string>
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.LargeTest
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.ui.activities.ActivityTestCase
|
||||
import com.habitrpg.android.habitica.ui.activities.IntroActivity
|
||||
import io.github.kakaocup.kakao.screen.Screen
|
||||
import io.github.kakaocup.kakao.text.KButton
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
class IntroActivityScreen : Screen<IntroActivityScreen>() {
|
||||
val skipButton = KButton { withId(R.id.skipButton) }
|
||||
val finishButton = KButton { withId(R.id.finishButton) }
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class IntroActivityTest : ActivityTestCase() {
|
||||
@Rule
|
||||
@JvmField
|
||||
var mActivityTestRule = ActivityScenarioRule(IntroActivity::class.java)
|
||||
|
||||
val screen = IntroActivityScreen()
|
||||
|
||||
@Test
|
||||
fun introActivityTest() {
|
||||
screen {
|
||||
device.activities.isCurrent(IntroActivity::class.java)
|
||||
skipButton {
|
||||
isVisible()
|
||||
}
|
||||
finishButton {
|
||||
isNotDisplayed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -35,7 +35,7 @@ import com.habitrpg.android.habitica.helpers.notifications.PushNotificationManag
|
|||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.modules.AuthenticationHandler
|
||||
import com.habitrpg.android.habitica.ui.activities.BaseActivity
|
||||
import com.habitrpg.android.habitica.ui.activities.LoginActivity
|
||||
import com.habitrpg.android.habitica.ui.activities.OnboardingActivity
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
|
||||
import com.habitrpg.common.habitica.extensions.setupCoil
|
||||
import com.habitrpg.common.habitica.helpers.ExceptionHandler
|
||||
|
|
@ -349,7 +349,7 @@ abstract class HabiticaBaseApplication : Application(), Application.ActivityLife
|
|||
|
||||
instance?.lazyApiHelper?.updateAuthenticationCredentials(null, null)
|
||||
Wearable.getCapabilityClient(context).removeLocalCapability("provide_auth")
|
||||
startActivity(LoginActivity::class.java, context)
|
||||
startActivity(OnboardingActivity::class.java, context)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -178,7 +178,6 @@ class ApiClientImpl(
|
|||
|| errField.equals("invalidCredentials", ignoreCase = true)
|
||||
|| msgField.contains("invalidCredentials", ignoreCase = true)
|
||||
|| msgField.contains("Missing authentication headers", ignoreCase = true)
|
||||
|| msgField.contains("There is no account that uses those credentials", ignoreCase = true)
|
||||
|
||||
if (shouldLogout) {
|
||||
HabiticaBaseApplication.logout(context)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import android.content.Intent
|
|||
import com.google.android.gms.wearable.MessageEvent
|
||||
import com.google.android.gms.wearable.Wearable
|
||||
import com.google.android.gms.wearable.WearableListenerService
|
||||
import com.habitrpg.android.habitica.ui.activities.LoginActivity
|
||||
import com.habitrpg.android.habitica.ui.activities.OnboardingActivity
|
||||
import com.habitrpg.android.habitica.ui.activities.MainActivity
|
||||
import com.habitrpg.android.habitica.ui.activities.TaskFormActivity
|
||||
import com.habitrpg.common.habitica.api.HostConfig
|
||||
|
|
@ -23,8 +23,8 @@ class DeviceCommunicationService : WearableListenerService() {
|
|||
super.onMessageReceived(event)
|
||||
when (event.path) {
|
||||
DeviceCommunication.REQUEST_AUTH -> processAuthRequest(event)
|
||||
DeviceCommunication.SHOW_REGISTER -> openActivity(event, LoginActivity::class.java)
|
||||
DeviceCommunication.SHOW_LOGIN -> openActivity(event, LoginActivity::class.java)
|
||||
DeviceCommunication.SHOW_REGISTER -> openActivity(event, OnboardingActivity::class.java)
|
||||
DeviceCommunication.SHOW_LOGIN -> openActivity(event, OnboardingActivity::class.java)
|
||||
DeviceCommunication.SHOW_RYA -> openActivity(event, MainActivity::class.java)
|
||||
DeviceCommunication.SHOW_TASK_EDIT -> openTaskForm(event)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,161 +0,0 @@
|
|||
package com.habitrpg.android.habitica.ui.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.data.ContentRepository
|
||||
import com.habitrpg.android.habitica.databinding.ActivityIntroBinding
|
||||
import com.habitrpg.android.habitica.extensions.setNavigationBarDarkIcons
|
||||
import com.habitrpg.android.habitica.ui.fragments.setup.IntroFragment
|
||||
import com.habitrpg.common.habitica.helpers.ExceptionHandler
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class IntroActivity : BaseActivity() {
|
||||
private lateinit var binding: ActivityIntroBinding
|
||||
|
||||
@Inject
|
||||
lateinit var contentRepository: ContentRepository
|
||||
|
||||
override fun getLayoutResId(): Int {
|
||||
return R.layout.activity_intro
|
||||
}
|
||||
|
||||
override fun getContentView(layoutResId: Int?): View {
|
||||
binding = ActivityIntroBinding.inflate(layoutInflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setupIntro()
|
||||
|
||||
binding.skipButton.setOnClickListener { finishIntro() }
|
||||
binding.finishButton.setOnClickListener { finishIntro() }
|
||||
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine()) {
|
||||
contentRepository.retrieveContent()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
window.isNavigationBarContrastEnforced = false
|
||||
val controller = WindowCompat.getInsetsController(window, window.decorView)
|
||||
controller.isAppearanceLightNavigationBars = false
|
||||
controller.isAppearanceLightStatusBars = false
|
||||
window.setNavigationBarDarkIcons(false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupIntro() {
|
||||
setViewPagerAdapter()
|
||||
binding.viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
super.onPageSelected(position)
|
||||
if (position == 2) {
|
||||
binding.finishButton.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.finishButton.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun finishIntro() {
|
||||
val intent = Intent(this, LoginActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
|
||||
this.startActivity(intent)
|
||||
overridePendingTransition(0, R.anim.activity_fade_out)
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun setViewPagerAdapter() {
|
||||
val fragmentManager = supportFragmentManager
|
||||
val viewPagerAdapter = object : FragmentStateAdapter(fragmentManager, lifecycle) {
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
val fragment = IntroFragment()
|
||||
configureFragment(fragment, position)
|
||||
return fragment
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return 3
|
||||
}
|
||||
}
|
||||
binding.viewPager.adapter = viewPagerAdapter
|
||||
TabLayoutMediator(binding.viewPagerIndicator, binding.viewPager) { tab, position -> }.attach()
|
||||
}
|
||||
|
||||
private fun configureFragment(
|
||||
fragment: IntroFragment,
|
||||
position: Int
|
||||
) {
|
||||
when (position) {
|
||||
0 -> {
|
||||
fragment.setImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_1, null))
|
||||
fragment.setSubtitle(getString(R.string.intro_1_subtitle))
|
||||
fragment.setTitleImage(
|
||||
ResourcesCompat.getDrawable(
|
||||
resources,
|
||||
R.drawable.intro_1_title,
|
||||
null
|
||||
)
|
||||
)
|
||||
fragment.setDescription(
|
||||
getString(
|
||||
R.string.intro_1_description,
|
||||
getString(R.string.habitica_user_count)
|
||||
)
|
||||
)
|
||||
fragment.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
this@IntroActivity,
|
||||
R.color.brand_300
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
1 -> {
|
||||
fragment.setImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_2, null))
|
||||
fragment.setSubtitle(getString(R.string.intro_2_subtitle))
|
||||
fragment.setTitle(getString(R.string.intro_2_title))
|
||||
fragment.setDescription(getString(R.string.intro_2_description))
|
||||
fragment.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
this@IntroActivity,
|
||||
R.color.blue_10
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
2 -> {
|
||||
fragment.setImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_3, null))
|
||||
fragment.setSubtitle(getString(R.string.intro_3_subtitle))
|
||||
fragment.setTitle(getString(R.string.intro_3_title))
|
||||
fragment.setDescription(getString(R.string.intro_3_description))
|
||||
fragment.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
this@IntroActivity,
|
||||
R.color.red_100
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,457 +0,0 @@
|
|||
package com.habitrpg.android.habitica.ui.activities
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ObjectAnimator
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.text.SpannableString
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.text.style.UnderlineSpan
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.EditText
|
||||
import android.widget.LinearLayout
|
||||
import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.addCallback
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.gms.tasks.Tasks
|
||||
import com.google.android.gms.wearable.CapabilityClient
|
||||
import com.google.android.gms.wearable.MessageClient
|
||||
import com.google.android.gms.wearable.Wearable
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.ActivityLoginBinding
|
||||
import com.habitrpg.android.habitica.extensions.addCancelButton
|
||||
import com.habitrpg.android.habitica.extensions.addOkButton
|
||||
import com.habitrpg.android.habitica.extensions.lifecycleLaunchWhen
|
||||
import com.habitrpg.android.habitica.extensions.updateStatusBarColor
|
||||
import com.habitrpg.android.habitica.helpers.AppConfigManager
|
||||
import com.habitrpg.android.habitica.ui.helpers.dismissKeyboard
|
||||
import com.habitrpg.android.habitica.extensions.AuthenticationErrors
|
||||
import com.habitrpg.android.habitica.extensions.setNavigationBarDarkIcons
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.AuthenticationViewModel
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
|
||||
import com.habitrpg.common.habitica.helpers.launchCatching
|
||||
import com.habitrpg.common.habitica.models.auth.UserAuthResponse
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class LoginActivity : BaseActivity() {
|
||||
private lateinit var binding: ActivityLoginBinding
|
||||
val viewModel by viewModels<AuthenticationViewModel>()
|
||||
|
||||
@Inject
|
||||
lateinit var configManager: AppConfigManager
|
||||
|
||||
private var isShowingForm: Boolean = false
|
||||
|
||||
override fun getLayoutResId(): Int {
|
||||
return R.layout.activity_login
|
||||
}
|
||||
|
||||
override fun getContentView(layoutResId: Int?): View {
|
||||
binding = ActivityLoginBinding.inflate(layoutInflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
supportActionBar?.hide()
|
||||
// Set default values to avoid null-responses when requesting unedited settings
|
||||
PreferenceManager.setDefaultValues(this, R.xml.preferences_fragment, false)
|
||||
|
||||
configureSpecialUI()
|
||||
binding.backgroundContainer.isScrollable = false
|
||||
|
||||
setupOnClickListeners()
|
||||
setupViewmodelObserving()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
window.isNavigationBarContrastEnforced = false
|
||||
val controller = WindowCompat.getInsetsController(window, window.decorView)
|
||||
controller.isAppearanceLightNavigationBars = false
|
||||
controller.isAppearanceLightStatusBars = false
|
||||
window.setNavigationBarDarkIcons(false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupViewmodelObserving() {
|
||||
lifecycleLaunchWhen(Lifecycle.State.RESUMED) {
|
||||
viewModel.showAuthProgress.collect { showProgress ->
|
||||
binding.progressView.isVisible = showProgress
|
||||
}
|
||||
}
|
||||
lifecycleLaunchWhen(Lifecycle.State.RESUMED) {
|
||||
viewModel.isRegistering.collect { isRegistering ->
|
||||
if (isRegistering) {
|
||||
configureForRegistering()
|
||||
} else {
|
||||
configureForLogin()
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycleLaunchWhen(Lifecycle.State.RESUMED) {
|
||||
viewModel.authenticationError
|
||||
.filterNotNull()
|
||||
.collect { showError(it) }
|
||||
}
|
||||
lifecycleLaunchWhen(Lifecycle.State.RESUMED) {
|
||||
viewModel.authenticationSuccess
|
||||
.filterNotNull()
|
||||
.collect { didRegister ->
|
||||
if (didRegister) {
|
||||
startSetupActivity()
|
||||
} else {
|
||||
startMainActivity()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupOnClickListeners() {
|
||||
onBackPressedDispatcher.addCallback(this) {
|
||||
if (isShowingForm) {
|
||||
hideForm()
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
binding.newGameButton.setOnClickListener { newGameButtonClicked() }
|
||||
binding.showLoginButton.setOnClickListener { showLoginButtonClicked() }
|
||||
binding.backButton.setOnClickListener { backButtonClicked() }
|
||||
binding.forgotPassword.setOnClickListener { onForgotPasswordClicked() }
|
||||
binding.googleLoginButton.setOnClickListener {
|
||||
viewModel.startGoogleAuth(this)
|
||||
}
|
||||
binding.submitButton.setOnClickListener {
|
||||
if (viewModel.isRegistering.value) {
|
||||
registerWithPassword()
|
||||
} else {
|
||||
loginWithPassword()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun configureSpecialUI() {
|
||||
val content = SpannableString(binding.forgotPassword.text)
|
||||
content.setSpan(UnderlineSpan(), 0, content.length, 0)
|
||||
binding.forgotPassword.text = content
|
||||
binding.privacyPolicy.movementMethod = LinkMovementMethod.getInstance()
|
||||
|
||||
binding.backgroundContainer.post {
|
||||
binding.backgroundContainer.scrollTo(
|
||||
0,
|
||||
binding.backgroundContainer.bottom,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetLayout() {
|
||||
val isRegistering = viewModel.isRegistering.value
|
||||
binding.email.isVisible = isRegistering
|
||||
binding.confirmPassword.isVisible = isRegistering
|
||||
}
|
||||
|
||||
private fun startMainActivity() {
|
||||
val intent = Intent(this@LoginActivity, MainActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun startSetupActivity() {
|
||||
val intent = Intent(this@LoginActivity, SetupActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun configureForRegistering() {
|
||||
binding.submitButton.text = getString(R.string.register_btn)
|
||||
binding.username.setHint(R.string.username)
|
||||
binding.username.setAutofillHints("newUsername")
|
||||
binding.password.setAutofillHints("newPassword")
|
||||
binding.password.imeOptions = EditorInfo.IME_ACTION_NEXT
|
||||
binding.googleLoginButton.setText(R.string.register_btn_google)
|
||||
|
||||
this.resetLayout()
|
||||
}
|
||||
|
||||
private fun configureForLogin() {
|
||||
binding.submitButton.text = getString(R.string.login_btn)
|
||||
binding.username.setHint(R.string.email_username)
|
||||
binding.username.setAutofillHints("username")
|
||||
binding.password.setAutofillHints("password")
|
||||
binding.password.imeOptions = EditorInfo.IME_ACTION_DONE
|
||||
binding.googleLoginButton.setText(R.string.login_btn_google)
|
||||
this.resetLayout()
|
||||
}
|
||||
|
||||
private fun loginWithPassword() {
|
||||
val username: String = binding.username.text.toString()
|
||||
val password: String = binding.password.text.toString()
|
||||
viewModel.validateInputs(username, password)?.let {
|
||||
showError(it)
|
||||
return
|
||||
}
|
||||
viewModel.login(username, password)
|
||||
}
|
||||
|
||||
private fun registerWithPassword() {
|
||||
val username = binding.username.text.toString()
|
||||
val email = binding.email.text.toString()
|
||||
val password = binding.password.text.toString()
|
||||
val confirmPassword = binding.confirmPassword.text.toString()
|
||||
viewModel.validateInputs(username, password, email, confirmPassword)?.let {
|
||||
showError(it)
|
||||
return
|
||||
}
|
||||
viewModel.register(username, email, password, confirmPassword)
|
||||
}
|
||||
|
||||
private fun sendAuthToWearables(response: UserAuthResponse) {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val messageClient: MessageClient = Wearable.getMessageClient(this@LoginActivity)
|
||||
val capabilityClient: CapabilityClient = Wearable.getCapabilityClient(this@LoginActivity)
|
||||
try {
|
||||
val info =
|
||||
Tasks.await(
|
||||
capabilityClient.getCapability(
|
||||
"receive_message",
|
||||
CapabilityClient.FILTER_REACHABLE,
|
||||
),
|
||||
)
|
||||
info.nodes.forEach {
|
||||
Tasks.await(
|
||||
messageClient.sendMessage(
|
||||
it.id,
|
||||
"/auth",
|
||||
"${response.id}:${response.apiToken}".toByteArray(),
|
||||
),
|
||||
)
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
// Wearable API is not available on this device.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showError(error: AuthenticationErrors) {
|
||||
val alert = HabiticaAlertDialog(this)
|
||||
if (error.isValidationError) {
|
||||
alert.setTitle(R.string.login_validation_error_title)
|
||||
} else {
|
||||
alert.setTitle(R.string.authentication_error_title)
|
||||
}
|
||||
alert.setMessage(error.translatedMessage(this))
|
||||
alert.addOkButton()
|
||||
alert.show()
|
||||
}
|
||||
|
||||
private fun newGameButtonClicked() {
|
||||
viewModel.isRegistering.value = true
|
||||
showForm()
|
||||
}
|
||||
|
||||
private fun showLoginButtonClicked() {
|
||||
viewModel.isRegistering.value = false
|
||||
showForm()
|
||||
}
|
||||
|
||||
private fun backButtonClicked() {
|
||||
hideForm()
|
||||
}
|
||||
|
||||
private fun showForm() {
|
||||
isShowingForm = true
|
||||
val panAnimation =
|
||||
ObjectAnimator.ofInt(binding.backgroundContainer, "scrollY", 0).setDuration(1000)
|
||||
val newGameAlphaAnimation =
|
||||
ObjectAnimator.ofFloat(binding.newGameButton, View.ALPHA, 0.toFloat())
|
||||
val showLoginAlphaAnimation =
|
||||
ObjectAnimator.ofFloat(binding.showLoginButton, View.ALPHA, 0.toFloat())
|
||||
val scaleLogoAnimation =
|
||||
ValueAnimator.ofInt(
|
||||
binding.logoView.measuredHeight,
|
||||
(binding.logoView.measuredHeight * 0.75).toInt(),
|
||||
)
|
||||
scaleLogoAnimation.addUpdateListener { valueAnimator ->
|
||||
val value = valueAnimator.animatedValue as? Int ?: 0
|
||||
val layoutParams = binding.logoView.layoutParams
|
||||
layoutParams.height = value
|
||||
binding.logoView.layoutParams = layoutParams
|
||||
}
|
||||
if (viewModel.isRegistering.value) {
|
||||
newGameAlphaAnimation.startDelay = 600
|
||||
newGameAlphaAnimation.duration = 400
|
||||
showLoginAlphaAnimation.duration = 400
|
||||
newGameAlphaAnimation.addListener(
|
||||
object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
binding.newGameButton.visibility = View.GONE
|
||||
binding.showLoginButton.visibility = View.GONE
|
||||
binding.loginScrollview.visibility = View.VISIBLE
|
||||
binding.loginScrollview.alpha = 1f
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
showLoginAlphaAnimation.startDelay = 600
|
||||
showLoginAlphaAnimation.duration = 400
|
||||
newGameAlphaAnimation.duration = 400
|
||||
showLoginAlphaAnimation.addListener(
|
||||
object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
binding.newGameButton.visibility = View.GONE
|
||||
binding.showLoginButton.visibility = View.GONE
|
||||
binding.loginScrollview.visibility = View.VISIBLE
|
||||
binding.loginScrollview.alpha = 1f
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
val backAlphaAnimation =
|
||||
ObjectAnimator.ofFloat(binding.backButton, View.ALPHA, 1.toFloat()).setDuration(800)
|
||||
val showAnimation = AnimatorSet()
|
||||
showAnimation.playTogether(
|
||||
panAnimation,
|
||||
newGameAlphaAnimation,
|
||||
showLoginAlphaAnimation,
|
||||
scaleLogoAnimation,
|
||||
)
|
||||
showAnimation.play(backAlphaAnimation).after(panAnimation)
|
||||
for (i in 0 until binding.formWrapper.childCount) {
|
||||
val view = binding.formWrapper.getChildAt(i)
|
||||
view.alpha = 0f
|
||||
val animator = ObjectAnimator.ofFloat(view, View.ALPHA, 1.toFloat()).setDuration(400)
|
||||
animator.startDelay = (100 * i).toLong()
|
||||
showAnimation.play(animator).after(panAnimation)
|
||||
}
|
||||
|
||||
showAnimation.start()
|
||||
}
|
||||
|
||||
private fun hideForm() {
|
||||
if (!isShowingForm) {
|
||||
return
|
||||
}
|
||||
isShowingForm = false
|
||||
val panAnimation =
|
||||
ObjectAnimator.ofInt(
|
||||
binding.backgroundContainer,
|
||||
"scrollY",
|
||||
binding.backgroundContainer.bottom,
|
||||
).setDuration(1000)
|
||||
val newGameAlphaAnimation =
|
||||
ObjectAnimator.ofFloat(binding.newGameButton, View.ALPHA, 1.toFloat()).setDuration(700)
|
||||
val showLoginAlphaAnimation =
|
||||
ObjectAnimator.ofFloat(binding.showLoginButton, View.ALPHA, 1.toFloat())
|
||||
.setDuration(700)
|
||||
val scaleLogoAnimation =
|
||||
ValueAnimator.ofInt(
|
||||
binding.logoView.measuredHeight,
|
||||
(binding.logoView.measuredHeight * 1.333333).toInt(),
|
||||
)
|
||||
scaleLogoAnimation.addUpdateListener { valueAnimator ->
|
||||
val value = valueAnimator.animatedValue as? Int
|
||||
val layoutParams = binding.logoView.layoutParams
|
||||
layoutParams.height = value ?: 0
|
||||
binding.logoView.layoutParams = layoutParams
|
||||
}
|
||||
showLoginAlphaAnimation.startDelay = 300
|
||||
val scrollViewAlphaAnimation =
|
||||
ObjectAnimator.ofFloat(binding.loginScrollview, View.ALPHA, 0.toFloat())
|
||||
.setDuration(800)
|
||||
scrollViewAlphaAnimation.addListener(
|
||||
object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
binding.newGameButton.visibility = View.VISIBLE
|
||||
binding.showLoginButton.visibility = View.VISIBLE
|
||||
binding.loginScrollview.visibility = View.INVISIBLE
|
||||
}
|
||||
},
|
||||
)
|
||||
val backAlphaAnimation =
|
||||
ObjectAnimator.ofFloat(binding.backButton, View.ALPHA, 0.toFloat()).setDuration(800)
|
||||
val showAnimation = AnimatorSet()
|
||||
showAnimation.playTogether(
|
||||
panAnimation,
|
||||
scrollViewAlphaAnimation,
|
||||
backAlphaAnimation,
|
||||
scaleLogoAnimation,
|
||||
)
|
||||
showAnimation.play(newGameAlphaAnimation).after(scrollViewAlphaAnimation)
|
||||
showAnimation.play(showLoginAlphaAnimation).after(scrollViewAlphaAnimation)
|
||||
showAnimation.start()
|
||||
dismissKeyboard()
|
||||
}
|
||||
|
||||
private fun onForgotPasswordClicked() {
|
||||
val input = EditText(this)
|
||||
input.setAutofillHints(EditText.AUTOFILL_HINT_EMAIL_ADDRESS)
|
||||
input.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
|
||||
input.hint = getString(R.string.forgot_password_hint_example)
|
||||
input.textSize = 16f
|
||||
val lp =
|
||||
LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
)
|
||||
input.layoutParams = lp
|
||||
val alertDialog = HabiticaAlertDialog(this)
|
||||
alertDialog.setTitle(R.string.forgot_password_title)
|
||||
alertDialog.setMessage(R.string.forgot_password_description)
|
||||
alertDialog.setAdditionalContentView(input)
|
||||
alertDialog.addButton(R.string.send, true) { _, _ ->
|
||||
lifecycleScope.launchCatching {
|
||||
userRepository.sendPasswordResetEmail(input.text.toString())
|
||||
showPasswordEmailConfirmation()
|
||||
}
|
||||
}
|
||||
alertDialog.addCancelButton()
|
||||
alertDialog.show()
|
||||
}
|
||||
|
||||
private fun showPasswordEmailConfirmation() {
|
||||
val alert = HabiticaAlertDialog(this)
|
||||
alert.setMessage(R.string.forgot_password_confirmation)
|
||||
alert.addOkButton()
|
||||
alert.show()
|
||||
}
|
||||
|
||||
override fun finish() {
|
||||
dismissKeyboard()
|
||||
super.finish()
|
||||
}
|
||||
}
|
||||
|
||||
fun AuthenticationErrors.translatedMessage(context: Context): String {
|
||||
return when (this) {
|
||||
AuthenticationErrors.GET_CREDENTIALS_ERROR -> context.getString(R.string.auth_get_credentials_error)
|
||||
AuthenticationErrors.INVALID_CREDENTIALS -> context.getString(R.string.auth_invalid_credentials)
|
||||
|
||||
AuthenticationErrors.MISSING_FIELDS -> context.getString(R.string.login_validation_error_fieldsmissing)
|
||||
AuthenticationErrors.PASSWORD_MISMATCH -> context.getString(R.string.password_not_matching)
|
||||
AuthenticationErrors.PASSWORD_TOO_SHORT -> context.getString(R.string.password_too_short, minPasswordLength)
|
||||
}
|
||||
}
|
||||
|
|
@ -236,7 +236,7 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
|
|||
DataBindingUtils.configManager = appConfigManager
|
||||
|
||||
if (!viewModel.isAuthenticated) {
|
||||
val intent = Intent(this, IntroActivity::class.java)
|
||||
val intent = Intent(this, OnboardingActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivity(intent)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -0,0 +1,240 @@
|
|||
package com.habitrpg.android.habitica.ui.activities
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.LinearLayout
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.expandIn
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.scaleIn
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.animation.with
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.graphics.TransformOrigin
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.gms.tasks.Tasks
|
||||
import com.google.android.gms.wearable.CapabilityClient
|
||||
import com.google.android.gms.wearable.MessageClient
|
||||
import com.google.android.gms.wearable.Wearable
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.ActivityLoginBinding
|
||||
import com.habitrpg.android.habitica.extensions.AuthenticationErrors
|
||||
import com.habitrpg.android.habitica.extensions.addCancelButton
|
||||
import com.habitrpg.android.habitica.extensions.addOkButton
|
||||
import com.habitrpg.android.habitica.extensions.setNavigationBarDarkIcons
|
||||
import com.habitrpg.android.habitica.helpers.AppConfigManager
|
||||
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
|
||||
import com.habitrpg.android.habitica.ui.views.intro.IntroScreen
|
||||
import com.habitrpg.android.habitica.ui.views.login.LoginScreen
|
||||
import com.habitrpg.android.habitica.ui.views.setup.SetupScreen
|
||||
import com.habitrpg.android.habitica.ui.views.setup.UsernameSelectionScreen
|
||||
import com.habitrpg.common.habitica.helpers.launchCatching
|
||||
import com.habitrpg.common.habitica.models.auth.UserAuthResponse
|
||||
import com.habitrpg.common.habitica.theme.HabiticaTheme
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
enum class OnboardingSteps {
|
||||
INTRO,
|
||||
LOGIN,
|
||||
USERNAME,
|
||||
SETUP
|
||||
}
|
||||
|
||||
@AndroidEntryPoint
|
||||
class OnboardingActivity: BaseActivity() {
|
||||
private lateinit var binding: ActivityLoginBinding
|
||||
|
||||
val authenticationViewModel: AuthenticationViewModel by viewModels()
|
||||
|
||||
val currentStep = mutableStateOf(OnboardingSteps.SETUP)
|
||||
|
||||
@Inject
|
||||
lateinit var configManager: AppConfigManager
|
||||
|
||||
override fun getLayoutResId(): Int {
|
||||
return R.layout.activity_login
|
||||
}
|
||||
|
||||
override fun getContentView(layoutResId: Int?): View {
|
||||
binding = ActivityLoginBinding.inflate(layoutInflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
supportActionBar?.hide()
|
||||
// Set default values to avoid null-responses when requesting unedited settings
|
||||
PreferenceManager.setDefaultValues(this, R.xml.preferences_fragment, false)
|
||||
|
||||
binding.composeView.setContent {
|
||||
val step by currentStep
|
||||
HabiticaTheme {
|
||||
AnimatedContent(step,
|
||||
transitionSpec = {
|
||||
(expandVertically(
|
||||
initialHeight = { fullHeight -> (fullHeight * 0.3f).roundToInt() }
|
||||
)+fadeIn())
|
||||
.togetherWith(
|
||||
slideOutVertically(
|
||||
targetOffsetY = { fullHeight -> (-fullHeight * 0.1f).roundToInt() }
|
||||
)
|
||||
)
|
||||
},) {
|
||||
when (it) {
|
||||
OnboardingSteps.INTRO -> IntroScreen({
|
||||
currentStep.value = OnboardingSteps.LOGIN
|
||||
})
|
||||
OnboardingSteps.LOGIN -> LoginScreen(authenticationViewModel,{ newUser ->
|
||||
if (newUser) {
|
||||
currentStep.value = OnboardingSteps.USERNAME
|
||||
} else {
|
||||
startMainActivity()
|
||||
}
|
||||
})
|
||||
OnboardingSteps.USERNAME -> UsernameSelectionScreen(authenticationViewModel,
|
||||
{
|
||||
currentStep.value = OnboardingSteps.LOGIN
|
||||
}, {
|
||||
currentStep.value = OnboardingSteps.SETUP
|
||||
}
|
||||
)
|
||||
OnboardingSteps.SETUP -> SetupScreen({
|
||||
startMainActivity()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
window.isNavigationBarContrastEnforced = false
|
||||
val controller = WindowCompat.getInsetsController(window, window.decorView)
|
||||
controller.isAppearanceLightNavigationBars = false
|
||||
controller.isAppearanceLightStatusBars = false
|
||||
window.setNavigationBarDarkIcons(false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startMainActivity() {
|
||||
val intent = Intent(this@OnboardingActivity, MainActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun sendAuthToWearables(response: UserAuthResponse) {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val messageClient: MessageClient = Wearable.getMessageClient(this@OnboardingActivity)
|
||||
val capabilityClient: CapabilityClient = Wearable.getCapabilityClient(this@OnboardingActivity)
|
||||
try {
|
||||
val info =
|
||||
Tasks.await(
|
||||
capabilityClient.getCapability(
|
||||
"receive_message",
|
||||
CapabilityClient.FILTER_REACHABLE,
|
||||
),
|
||||
)
|
||||
info.nodes.forEach {
|
||||
Tasks.await(
|
||||
messageClient.sendMessage(
|
||||
it.id,
|
||||
"/auth",
|
||||
"${response.id}:${response.apiToken}".toByteArray(),
|
||||
),
|
||||
)
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
// Wearable API is not available on this device.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showError(error: AuthenticationErrors) {
|
||||
val alert = HabiticaAlertDialog(this)
|
||||
if (error.isValidationError) {
|
||||
alert.setTitle(R.string.login_validation_error_title)
|
||||
} else {
|
||||
alert.setTitle(R.string.authentication_error_title)
|
||||
}
|
||||
alert.setMessage(error.translatedMessage(this))
|
||||
alert.addOkButton()
|
||||
alert.show()
|
||||
}
|
||||
|
||||
private fun onForgotPasswordClicked() {
|
||||
val input = EditText(this)
|
||||
input.setAutofillHints(EditText.AUTOFILL_HINT_EMAIL_ADDRESS)
|
||||
input.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
|
||||
input.hint = getString(R.string.forgot_password_hint_example)
|
||||
input.textSize = 16f
|
||||
val lp =
|
||||
LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
)
|
||||
input.layoutParams = lp
|
||||
val alertDialog = HabiticaAlertDialog(this)
|
||||
alertDialog.setTitle(R.string.forgot_password_title)
|
||||
alertDialog.setMessage(R.string.forgot_password_description)
|
||||
alertDialog.setAdditionalContentView(input)
|
||||
alertDialog.addButton(R.string.send, true) { _, _ ->
|
||||
lifecycleScope.launchCatching {
|
||||
userRepository.sendPasswordResetEmail(input.text.toString())
|
||||
showPasswordEmailConfirmation()
|
||||
}
|
||||
}
|
||||
alertDialog.addCancelButton()
|
||||
alertDialog.show()
|
||||
}
|
||||
|
||||
private fun showPasswordEmailConfirmation() {
|
||||
val alert = HabiticaAlertDialog(this)
|
||||
alert.setMessage(R.string.forgot_password_confirmation)
|
||||
alert.addOkButton()
|
||||
alert.show()
|
||||
}
|
||||
|
||||
override fun finish() {
|
||||
dismissKeyboard()
|
||||
super.finish()
|
||||
}
|
||||
}
|
||||
|
||||
fun AuthenticationErrors.translatedMessage(context: Context): String {
|
||||
return when (this) {
|
||||
AuthenticationErrors.GET_CREDENTIALS_ERROR -> context.getString(R.string.auth_get_credentials_error)
|
||||
AuthenticationErrors.INVALID_CREDENTIALS -> context.getString(R.string.auth_invalid_credentials)
|
||||
|
||||
AuthenticationErrors.MISSING_FIELDS -> context.getString(R.string.login_validation_error_fieldsmissing)
|
||||
AuthenticationErrors.PASSWORD_MISMATCH -> context.getString(R.string.password_not_matching)
|
||||
AuthenticationErrors.PASSWORD_TOO_SHORT -> context.getString(R.string.password_too_short, minPasswordLength)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,299 +0,0 @@
|
|||
package com.habitrpg.android.habitica.ui.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.data.ApiClient
|
||||
import com.habitrpg.android.habitica.data.InventoryRepository
|
||||
import com.habitrpg.android.habitica.data.TaskRepository
|
||||
import com.habitrpg.android.habitica.databinding.ActivitySetupBinding
|
||||
import com.habitrpg.android.habitica.extensions.consumeWindowInsetsAbove30
|
||||
import com.habitrpg.android.habitica.helpers.Analytics
|
||||
import com.habitrpg.android.habitica.helpers.EventCategory
|
||||
import com.habitrpg.android.habitica.helpers.HitType
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.ui.fragments.setup.AvatarSetupFragment
|
||||
import com.habitrpg.android.habitica.ui.fragments.setup.TaskSetupFragment
|
||||
import com.habitrpg.android.habitica.ui.fragments.setup.WelcomeFragment
|
||||
import com.habitrpg.common.habitica.extensions.dpToPx
|
||||
import com.habitrpg.common.habitica.helpers.ExceptionHandler
|
||||
import com.habitrpg.common.habitica.helpers.launchCatching
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SetupActivity : BaseActivity() {
|
||||
private lateinit var binding: ActivitySetupBinding
|
||||
|
||||
@Inject
|
||||
lateinit var apiClient: ApiClient
|
||||
|
||||
@Inject
|
||||
lateinit var inventoryRepository: InventoryRepository
|
||||
|
||||
@Inject
|
||||
lateinit var taskRepository: TaskRepository
|
||||
|
||||
internal var welcomeFragment: WelcomeFragment? = null
|
||||
internal var avatarSetupFragment: AvatarSetupFragment? = null
|
||||
internal var taskSetupFragment: TaskSetupFragment? = null
|
||||
internal var user: User? = null
|
||||
private var completedSetup = false
|
||||
private var createdTasks = false
|
||||
|
||||
private val isLastPage: Boolean
|
||||
get() =
|
||||
binding.viewPager.adapter == null || binding.viewPager.currentItem == (
|
||||
binding.viewPager.adapter?.itemCount
|
||||
?: 0
|
||||
) - 1
|
||||
|
||||
override fun getLayoutResId(): Int {
|
||||
return R.layout.activity_setup
|
||||
}
|
||||
|
||||
override fun getContentView(layoutResId: Int?): View {
|
||||
binding = ActivitySetupBinding.inflate(layoutInflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine()) {
|
||||
userRepository.getUser()
|
||||
.debounce(500.milliseconds)
|
||||
.collect { onUserReceived(it) }
|
||||
}
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine()) {
|
||||
userRepository.retrieveUser(true, true)
|
||||
}
|
||||
val additionalData = HashMap<String, Any>()
|
||||
additionalData["status"] = "displayed"
|
||||
Analytics.sendEvent("setup", EventCategory.BEHAVIOUR, HitType.EVENT, additionalData)
|
||||
|
||||
val currentDeviceLanguage = Locale.getDefault().language
|
||||
for (language in resources.getStringArray(R.array.LanguageValues)) {
|
||||
if (language == currentDeviceLanguage) {
|
||||
lifecycleScope.launchCatching {
|
||||
apiClient.registrationLanguage(currentDeviceLanguage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.viewPager.isUserInputEnabled = false
|
||||
|
||||
binding.previousButton.setOnClickListener { previousClicked() }
|
||||
binding.nextButton.setOnClickListener { nextClicked() }
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
window.isNavigationBarContrastEnforced = false
|
||||
}
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, windowInsets ->
|
||||
val insets = windowInsets.getInsets(
|
||||
WindowInsetsCompat.Type.systemBars()
|
||||
+ WindowInsetsCompat.Type.displayCutout()
|
||||
)
|
||||
binding.viewPager.updatePadding(
|
||||
left = insets.left,
|
||||
right = insets.right,
|
||||
top = insets.top
|
||||
)
|
||||
binding.bottomBar.updatePadding(
|
||||
bottom = insets.bottom
|
||||
)
|
||||
binding.bottomBar.layoutParams.height = 56.dpToPx(this) + insets.bottom
|
||||
consumeWindowInsetsAbove30(windowInsets)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
userRepository.close()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun setupViewpager() {
|
||||
setViewPagerAdapter()
|
||||
binding.viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
super.onPageSelected(position)
|
||||
when {
|
||||
position == 0 -> {
|
||||
setPreviousButtonEnabled(false)
|
||||
binding.nextButton.text = getString(R.string.next_button)
|
||||
}
|
||||
isLastPage -> {
|
||||
setPreviousButtonEnabled(true)
|
||||
binding.nextButton.text = getString(R.string.finish)
|
||||
}
|
||||
else -> {
|
||||
setPreviousButtonEnabled(true)
|
||||
binding.nextButton.text = getString(R.string.next_button)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun nextClicked() {
|
||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
sharedPreferences.edit {
|
||||
putString("FirstDayOfTheWeek", Calendar.getInstance().firstDayOfWeek.toString())
|
||||
}
|
||||
if (isLastPage) {
|
||||
if (this.taskSetupFragment == null) {
|
||||
return
|
||||
}
|
||||
if (createdTasks) {
|
||||
onUserReceived(user)
|
||||
return
|
||||
}
|
||||
val newTasks = this.taskSetupFragment?.createSampleTasks()
|
||||
this.completedSetup = true
|
||||
createdTasks = true
|
||||
newTasks?.let {
|
||||
lifecycleScope.launchCatching {
|
||||
taskRepository.createTasks(it)
|
||||
}
|
||||
}
|
||||
} else if (binding.viewPager.currentItem == 0) {
|
||||
confirmNames(welcomeFragment?.displayName ?: "", welcomeFragment?.username ?: "")
|
||||
|
||||
val imm = getSystemService(INPUT_METHOD_SERVICE) as? InputMethodManager
|
||||
imm?.hideSoftInputFromWindow(currentFocus?.windowToken, 0)
|
||||
}
|
||||
binding.viewPager.currentItem = binding.viewPager.currentItem + 1
|
||||
}
|
||||
|
||||
private fun previousClicked() {
|
||||
binding.viewPager.currentItem = binding.viewPager.currentItem - 1
|
||||
}
|
||||
|
||||
private fun setPreviousButtonEnabled(enabled: Boolean) {
|
||||
val leftDrawable: Drawable?
|
||||
if (enabled) {
|
||||
binding.previousButton.setText(R.string.action_back)
|
||||
leftDrawable = AppCompatResources.getDrawable(this, R.drawable.back_arrow_enabled)
|
||||
} else {
|
||||
binding.previousButton.text = null
|
||||
leftDrawable = AppCompatResources.getDrawable(this, R.drawable.back_arrow_disabled)
|
||||
}
|
||||
binding.previousButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
leftDrawable,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
private fun setNextButtonEnabled(enabled: Boolean) {
|
||||
binding.nextButton.isEnabled = enabled
|
||||
val rightDrawable = AppCompatResources.getDrawable(this, R.drawable.forward_arrow_enabled)
|
||||
if (enabled) {
|
||||
binding.nextButton.setTextColor(ContextCompat.getColor(this, R.color.white))
|
||||
rightDrawable?.alpha = 255
|
||||
} else {
|
||||
binding.nextButton.setTextColor(ContextCompat.getColor(this, R.color.white_50_alpha))
|
||||
rightDrawable?.alpha = 127
|
||||
}
|
||||
binding.nextButton.setCompoundDrawablesWithIntrinsicBounds(null, null, rightDrawable, null)
|
||||
}
|
||||
|
||||
private var hasCompleted = false
|
||||
|
||||
private fun onUserReceived(user: User?) {
|
||||
if (completedSetup && !hasCompleted) {
|
||||
val additionalData = HashMap<String, Any>()
|
||||
additionalData["status"] = "completed"
|
||||
Analytics.sendEvent("setup", EventCategory.BEHAVIOUR, HitType.EVENT, additionalData)
|
||||
hasCompleted = true
|
||||
lifecycleScope.launchCatching {
|
||||
userRepository.updateUser("flags.welcomed", true)
|
||||
userRepository.retrieveUser(true, true)
|
||||
startMainActivity()
|
||||
}
|
||||
return
|
||||
}
|
||||
this.user = user
|
||||
if (binding.viewPager.adapter == null) {
|
||||
setupViewpager()
|
||||
}
|
||||
avatarSetupFragment?.setUser(user)
|
||||
taskSetupFragment?.setUser(user)
|
||||
}
|
||||
|
||||
private fun startMainActivity() {
|
||||
val intent = Intent(this@SetupActivity, MainActivity::class.java)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun confirmNames(
|
||||
displayName: String,
|
||||
username: String
|
||||
) {
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine()) {
|
||||
userRepository.updateUser("profile.name", displayName)
|
||||
userRepository.updateLoginName(username)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setViewPagerAdapter() {
|
||||
val fragmentManager = supportFragmentManager
|
||||
val viewPagerAdapter = object : FragmentStateAdapter(fragmentManager, lifecycle) {
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return when (position) {
|
||||
1 -> {
|
||||
val fragment = AvatarSetupFragment()
|
||||
fragment.activity = this@SetupActivity
|
||||
fragment.setUser(user)
|
||||
fragment.width = binding.viewPager.width
|
||||
avatarSetupFragment = fragment
|
||||
fragment
|
||||
}
|
||||
|
||||
2 -> {
|
||||
val fragment = TaskSetupFragment()
|
||||
fragment.setUser(user)
|
||||
taskSetupFragment = fragment
|
||||
fragment
|
||||
}
|
||||
|
||||
else -> {
|
||||
val fragment = WelcomeFragment()
|
||||
welcomeFragment = fragment
|
||||
welcomeFragment?.onNameValid = { setNextButtonEnabled(it == true) }
|
||||
fragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return 3
|
||||
}
|
||||
}
|
||||
binding.viewPager.adapter = viewPagerAdapter
|
||||
TabLayoutMediator(binding.viewPagerIndicator, binding.viewPager) { tab, position -> }.attach()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,349 +0,0 @@
|
|||
package com.habitrpg.android.habitica.ui.fragments.setup
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.data.InventoryRepository
|
||||
import com.habitrpg.android.habitica.data.SetupCustomizationRepository
|
||||
import com.habitrpg.android.habitica.data.UserRepository
|
||||
import com.habitrpg.android.habitica.databinding.FragmentSetupAvatarBinding
|
||||
import com.habitrpg.android.habitica.extensions.applyScrollContentWindowInsets
|
||||
import com.habitrpg.android.habitica.models.SetupCustomization
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.ui.activities.SetupActivity
|
||||
import com.habitrpg.android.habitica.ui.adapter.setup.CustomizationSetupAdapter
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
|
||||
import com.habitrpg.android.habitica.ui.views.setup.AvatarCategoryView
|
||||
import com.habitrpg.common.habitica.helpers.launchCatching
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.util.Random
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AvatarSetupFragment : BaseFragment<FragmentSetupAvatarBinding>() {
|
||||
@Inject
|
||||
lateinit var customizationRepository: SetupCustomizationRepository
|
||||
|
||||
@Inject
|
||||
lateinit var userRepository: UserRepository
|
||||
|
||||
@Inject
|
||||
lateinit var inventoryRepository: InventoryRepository
|
||||
|
||||
override var binding: FragmentSetupAvatarBinding? = null
|
||||
|
||||
override fun createBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?
|
||||
): FragmentSetupAvatarBinding {
|
||||
return FragmentSetupAvatarBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
var activity: SetupActivity? = null
|
||||
var width: Int = 0
|
||||
|
||||
internal var adapter: CustomizationSetupAdapter? = null
|
||||
|
||||
private var user: User? = null
|
||||
private var subcategories: List<String> = emptyList()
|
||||
private var activeButton: AvatarCategoryView? = null
|
||||
private var activeCategory: String? = null
|
||||
private var activeSubCategory: String? = null
|
||||
private var random = Random()
|
||||
|
||||
override fun onViewCreated(
|
||||
view: View,
|
||||
savedInstanceState: Bundle?
|
||||
) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
this.adapter = CustomizationSetupAdapter()
|
||||
this.adapter?.userSize = this.user?.preferences?.size ?: "slim"
|
||||
adapter?.onUpdateUser = {
|
||||
lifecycleScope.launchCatching {
|
||||
userRepository.updateUser(it)
|
||||
}
|
||||
}
|
||||
adapter?.onEquipGear = {
|
||||
lifecycleScope.launchCatching {
|
||||
inventoryRepository.equip("equipped", it)
|
||||
}
|
||||
}
|
||||
|
||||
this.adapter?.user = this.user
|
||||
val layoutManager = LinearLayoutManager(activity)
|
||||
layoutManager.orientation = LinearLayoutManager.HORIZONTAL
|
||||
binding?.customizationDrawer?.binding?.customizationList?.layoutManager = layoutManager
|
||||
|
||||
binding?.customizationDrawer?.binding?.customizationList?.adapter = this.adapter
|
||||
|
||||
binding?.customizationDrawer?.binding?.subcategoryTabs?.addOnTabSelectedListener(
|
||||
object :
|
||||
TabLayout.OnTabSelectedListener {
|
||||
override fun onTabSelected(tab: TabLayout.Tab) {
|
||||
val position = tab.position
|
||||
if (position < subcategories.size) {
|
||||
activeSubCategory = subcategories[position]
|
||||
}
|
||||
loadCustomizations()
|
||||
}
|
||||
|
||||
override fun onTabUnselected(tab: TabLayout.Tab) { // no-on
|
||||
}
|
||||
|
||||
override fun onTabReselected(tab: TabLayout.Tab) { // no-on
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
binding?.customizationDrawer?.binding?.bodyButton?.setOnClickListener { selectedBodyCategory() }
|
||||
binding?.customizationDrawer?.binding?.skinButton?.setOnClickListener { selectedSkinCategory() }
|
||||
binding?.customizationDrawer?.binding?.hairButton?.setOnClickListener { selectedHairCategory() }
|
||||
binding?.customizationDrawer?.binding?.extrasButton?.setOnClickListener { selectedExtrasCategory() }
|
||||
binding?.randomizeButton?.setOnClickListener { randomizeCharacter() }
|
||||
|
||||
this.selectedBodyCategory()
|
||||
|
||||
if (this.user != null) {
|
||||
this.updateAvatar()
|
||||
}
|
||||
binding?.contentWrapper?.let { applyScrollContentWindowInsets(it) }
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (this.user != null) {
|
||||
this.updateAvatar()
|
||||
}
|
||||
this.selectedBodyCategory()
|
||||
if (context != null) {
|
||||
binding?.speechBubble?.animateText(
|
||||
context?.getString(R.string.avatar_setup_description) ?: ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadCustomizations() {
|
||||
val user = this.user ?: return
|
||||
val activeCategory = this.activeCategory ?: return
|
||||
|
||||
this.adapter?.setCustomizationList(
|
||||
customizationRepository.getCustomizations(
|
||||
activeCategory,
|
||||
activeSubCategory,
|
||||
user
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun setUser(user: User?) {
|
||||
this.user = user
|
||||
if (binding?.avatarView != null) {
|
||||
updateAvatar()
|
||||
}
|
||||
if (this.adapter != null) {
|
||||
this.adapter?.user = user
|
||||
this.adapter?.notifyDataSetChanged()
|
||||
loadCustomizations()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateAvatar() {
|
||||
user?.let {
|
||||
binding?.avatarView?.setAvatar(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectedBodyCategory() {
|
||||
activateButton(binding?.customizationDrawer?.binding?.bodyButton)
|
||||
this.activeCategory = SetupCustomizationRepository.CATEGORY_BODY
|
||||
binding?.customizationDrawer?.binding?.subcategoryTabs?.removeAllTabs()
|
||||
this.subcategories =
|
||||
listOf(
|
||||
SetupCustomizationRepository.SUBCATEGORY_SIZE,
|
||||
SetupCustomizationRepository.SUBCATEGORY_SHIRT
|
||||
)
|
||||
binding?.customizationDrawer?.binding?.subcategoryTabs?.newTab()
|
||||
?.setText(R.string.avatar_size)
|
||||
?.let { binding?.customizationDrawer?.binding?.subcategoryTabs?.addTab(it) }
|
||||
binding?.customizationDrawer?.binding?.subcategoryTabs?.newTab()
|
||||
?.setText(R.string.avatar_shirt)
|
||||
?.let { binding?.customizationDrawer?.binding?.subcategoryTabs?.addTab(it) }
|
||||
loadCustomizations()
|
||||
}
|
||||
|
||||
private fun selectedSkinCategory() {
|
||||
activateButton(binding?.customizationDrawer?.binding?.skinButton)
|
||||
this.activeCategory = SetupCustomizationRepository.CATEGORY_SKIN
|
||||
binding?.customizationDrawer?.binding?.subcategoryTabs?.removeAllTabs()
|
||||
this.subcategories = listOf(SetupCustomizationRepository.SUBCATEGORY_COLOR)
|
||||
binding?.customizationDrawer?.binding?.subcategoryTabs?.newTab()
|
||||
?.setText(R.string.avatar_skin_color)
|
||||
?.let { binding?.customizationDrawer?.binding?.subcategoryTabs?.addTab(it) }
|
||||
loadCustomizations()
|
||||
}
|
||||
|
||||
private fun selectedHairCategory() {
|
||||
activateButton(binding?.customizationDrawer?.binding?.hairButton)
|
||||
this.activeCategory = SetupCustomizationRepository.CATEGORY_HAIR
|
||||
binding?.customizationDrawer?.binding?.subcategoryTabs?.removeAllTabs()
|
||||
this.subcategories =
|
||||
listOf(
|
||||
SetupCustomizationRepository.SUBCATEGORY_BANGS,
|
||||
SetupCustomizationRepository.SUBCATEGORY_COLOR,
|
||||
SetupCustomizationRepository.SUBCATEGORY_PONYTAIL
|
||||
)
|
||||
binding?.customizationDrawer?.binding?.subcategoryTabs?.newTab()
|
||||
?.setText(R.string.avatar_hair_bangs)
|
||||
?.let { binding?.customizationDrawer?.binding?.subcategoryTabs?.addTab(it) }
|
||||
binding?.customizationDrawer?.binding?.subcategoryTabs?.newTab()
|
||||
?.setText(R.string.avatar_hair_color)
|
||||
?.let { binding?.customizationDrawer?.binding?.subcategoryTabs?.addTab(it) }
|
||||
binding?.customizationDrawer?.binding?.subcategoryTabs?.newTab()
|
||||
?.setText(R.string.avatar_hair_ponytail)
|
||||
?.let { binding?.customizationDrawer?.binding?.subcategoryTabs?.addTab(it) }
|
||||
loadCustomizations()
|
||||
}
|
||||
|
||||
private fun selectedExtrasCategory() {
|
||||
activateButton(binding?.customizationDrawer?.binding?.extrasButton)
|
||||
this.activeCategory = SetupCustomizationRepository.CATEGORY_EXTRAS
|
||||
binding?.customizationDrawer?.binding?.subcategoryTabs?.removeAllTabs()
|
||||
this.subcategories =
|
||||
listOf(
|
||||
SetupCustomizationRepository.SUBCATEGORY_GLASSES,
|
||||
SetupCustomizationRepository.SUBCATEGORY_FLOWER,
|
||||
SetupCustomizationRepository.SUBCATEGORY_WHEELCHAIR
|
||||
)
|
||||
binding?.customizationDrawer?.binding?.subcategoryTabs?.newTab()
|
||||
?.setText(R.string.avatar_glasses)
|
||||
?.let { binding?.customizationDrawer?.binding?.subcategoryTabs?.addTab(it) }
|
||||
binding?.customizationDrawer?.binding?.subcategoryTabs?.newTab()
|
||||
?.setText(R.string.avatar_flower)
|
||||
?.let { binding?.customizationDrawer?.binding?.subcategoryTabs?.addTab(it) }
|
||||
binding?.customizationDrawer?.binding?.subcategoryTabs?.newTab()
|
||||
?.setText(R.string.avatar_wheelchair)
|
||||
?.let { binding?.customizationDrawer?.binding?.subcategoryTabs?.addTab(it) }
|
||||
loadCustomizations()
|
||||
}
|
||||
|
||||
private fun randomizeCharacter() {
|
||||
val user = this.user ?: return
|
||||
val updateData = HashMap<String, Any>()
|
||||
updateData["preferences.size"] =
|
||||
chooseRandomKey(
|
||||
customizationRepository.getCustomizations(
|
||||
SetupCustomizationRepository.CATEGORY_BODY,
|
||||
SetupCustomizationRepository.SUBCATEGORY_SIZE,
|
||||
user
|
||||
),
|
||||
false
|
||||
)
|
||||
updateData["preferences.shirt"] =
|
||||
chooseRandomKey(
|
||||
customizationRepository.getCustomizations(
|
||||
SetupCustomizationRepository.CATEGORY_BODY,
|
||||
SetupCustomizationRepository.SUBCATEGORY_SHIRT,
|
||||
user
|
||||
),
|
||||
false
|
||||
)
|
||||
updateData["preferences.skin"] =
|
||||
chooseRandomKey(
|
||||
customizationRepository.getCustomizations(
|
||||
SetupCustomizationRepository.CATEGORY_SKIN,
|
||||
SetupCustomizationRepository.SUBCATEGORY_COLOR,
|
||||
user
|
||||
),
|
||||
false
|
||||
)
|
||||
updateData["preferences.hair.color"] =
|
||||
chooseRandomKey(
|
||||
customizationRepository.getCustomizations(
|
||||
SetupCustomizationRepository.CATEGORY_HAIR,
|
||||
SetupCustomizationRepository.SUBCATEGORY_COLOR,
|
||||
user
|
||||
),
|
||||
false
|
||||
)
|
||||
updateData["preferences.hair.base"] =
|
||||
chooseRandomKey(
|
||||
customizationRepository.getCustomizations(
|
||||
SetupCustomizationRepository.CATEGORY_HAIR,
|
||||
SetupCustomizationRepository.SUBCATEGORY_PONYTAIL,
|
||||
user
|
||||
),
|
||||
false
|
||||
)
|
||||
updateData["preferences.hair.bangs"] =
|
||||
chooseRandomKey(
|
||||
customizationRepository.getCustomizations(
|
||||
SetupCustomizationRepository.CATEGORY_HAIR,
|
||||
SetupCustomizationRepository.SUBCATEGORY_BANGS,
|
||||
user
|
||||
),
|
||||
false
|
||||
)
|
||||
updateData["preferences.hair.flower"] =
|
||||
chooseRandomKey(
|
||||
customizationRepository.getCustomizations(
|
||||
SetupCustomizationRepository.CATEGORY_EXTRAS,
|
||||
SetupCustomizationRepository.SUBCATEGORY_FLOWER,
|
||||
user
|
||||
),
|
||||
true
|
||||
)
|
||||
updateData["preferences.chair"] =
|
||||
chooseRandomKey(
|
||||
customizationRepository.getCustomizations(
|
||||
SetupCustomizationRepository.CATEGORY_EXTRAS,
|
||||
SetupCustomizationRepository.SUBCATEGORY_WHEELCHAIR,
|
||||
user
|
||||
),
|
||||
true
|
||||
)
|
||||
lifecycleScope.launchCatching {
|
||||
userRepository.updateUser(updateData)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("ReturnCount")
|
||||
private fun chooseRandomKey(
|
||||
customizations: List<SetupCustomization>,
|
||||
weighFirstOption: Boolean
|
||||
): String {
|
||||
if (customizations.isEmpty()) {
|
||||
return ""
|
||||
}
|
||||
if (weighFirstOption) {
|
||||
if (random.nextInt(10) > 3) {
|
||||
return customizations[0].key
|
||||
}
|
||||
}
|
||||
return customizations[random.nextInt(customizations.size)].key
|
||||
}
|
||||
|
||||
private fun activateButton(button: AvatarCategoryView?) {
|
||||
if (this.activeButton != null) {
|
||||
this.activeButton?.setActive(false)
|
||||
}
|
||||
this.activeButton = button
|
||||
this.activeButton?.setActive(true)
|
||||
val location = IntArray(2)
|
||||
val params =
|
||||
binding?.customizationDrawer?.binding?.caretView?.layoutParams as? RelativeLayout.LayoutParams
|
||||
this.activeButton?.getLocationOnScreen(location)
|
||||
val r = resources
|
||||
val px =
|
||||
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40f, r.displayMetrics).toInt()
|
||||
params?.marginStart = location[0] + px
|
||||
binding?.customizationDrawer?.binding?.caretView?.layoutParams = params
|
||||
}
|
||||
}
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
package com.habitrpg.android.habitica.ui.fragments.setup
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.habitrpg.android.habitica.databinding.FragmentIntroBinding
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class IntroFragment : BaseFragment<FragmentIntroBinding>() {
|
||||
override var binding: FragmentIntroBinding? = null
|
||||
|
||||
override fun createBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?
|
||||
): FragmentIntroBinding {
|
||||
return FragmentIntroBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
private var image: Drawable? = null
|
||||
private var titleImage: Drawable? = null
|
||||
private var subtitle: String? = null
|
||||
private var title: String? = null
|
||||
private var description: String? = null
|
||||
private var backgroundColor: Int? = null
|
||||
|
||||
override fun onViewCreated(
|
||||
view: View,
|
||||
savedInstanceState: Bundle?
|
||||
) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
if (this.image != null) {
|
||||
binding?.imageView?.setImageDrawable(this.image)
|
||||
}
|
||||
|
||||
if (this.titleImage != null) {
|
||||
binding?.titleImageView?.setImageDrawable(this.titleImage)
|
||||
}
|
||||
|
||||
if (this.subtitle != null) {
|
||||
binding?.subtitleTextView?.text = this.subtitle
|
||||
}
|
||||
|
||||
if (this.title != null) {
|
||||
binding?.titleTextView?.text = this.title
|
||||
}
|
||||
|
||||
if (this.description != null) {
|
||||
binding?.descriptionTextView?.text = this.description
|
||||
}
|
||||
|
||||
backgroundColor?.let {
|
||||
binding?.containerView?.setBackgroundColor(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun setImage(image: Drawable?) {
|
||||
this.image = image
|
||||
if (image != null) {
|
||||
binding?.imageView?.setImageDrawable(image)
|
||||
}
|
||||
}
|
||||
|
||||
fun setTitleImage(image: Drawable?) {
|
||||
this.titleImage = image
|
||||
binding?.titleImageView?.setImageDrawable(image)
|
||||
}
|
||||
|
||||
fun setSubtitle(text: String?) {
|
||||
this.subtitle = text
|
||||
binding?.subtitleTextView?.text = text
|
||||
}
|
||||
|
||||
fun setTitle(text: String?) {
|
||||
this.title = text
|
||||
binding?.titleTextView?.text = text
|
||||
}
|
||||
|
||||
fun setDescription(text: String?) {
|
||||
this.description = text
|
||||
binding?.descriptionTextView?.text = text
|
||||
}
|
||||
|
||||
fun setBackgroundColor(color: Int) {
|
||||
this.backgroundColor = color
|
||||
binding?.containerView?.setBackgroundColor(color)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,274 +0,0 @@
|
|||
package com.habitrpg.android.habitica.ui.fragments.setup
|
||||
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.FragmentSetupTasksBinding
|
||||
import com.habitrpg.android.habitica.extensions.applyScrollContentWindowInsets
|
||||
import com.habitrpg.android.habitica.models.tasks.Days
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.ui.activities.SetupActivity
|
||||
import com.habitrpg.android.habitica.ui.adapter.setup.TaskSetupAdapter
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
|
||||
import com.habitrpg.shared.habitica.models.tasks.Frequency
|
||||
import com.habitrpg.shared.habitica.models.tasks.TaskType
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.util.Date
|
||||
import java.util.UUID
|
||||
|
||||
@AndroidEntryPoint
|
||||
class TaskSetupFragment : BaseFragment<FragmentSetupTasksBinding>() {
|
||||
var activity: SetupActivity? = null
|
||||
var width: Int = 0
|
||||
|
||||
override var binding: FragmentSetupTasksBinding? = null
|
||||
|
||||
override fun createBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?
|
||||
): FragmentSetupTasksBinding {
|
||||
return FragmentSetupTasksBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
internal var adapter: TaskSetupAdapter = TaskSetupAdapter()
|
||||
private var taskGroups: List<List<String>> = listOf()
|
||||
private var tasks: List<List<Any>> = listOf()
|
||||
private var user: User? = null
|
||||
|
||||
override fun onViewCreated(
|
||||
view: View,
|
||||
savedInstanceState: Bundle?
|
||||
) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
this.setTasks()
|
||||
|
||||
this.adapter = TaskSetupAdapter()
|
||||
this.adapter.setTaskList(this.taskGroups)
|
||||
binding?.recyclerView?.layoutManager = GridLayoutManager(activity, 2)
|
||||
binding?.recyclerView?.adapter = this.adapter
|
||||
|
||||
if (this.user != null) {
|
||||
this.updateAvatar()
|
||||
}
|
||||
|
||||
binding?.heartIcon?.setImageDrawable(
|
||||
BitmapDrawable(
|
||||
resources,
|
||||
HabiticaIconsHelper.imageOfHeartLightBg()
|
||||
)
|
||||
)
|
||||
|
||||
binding?.contentWrapper?.let { applyScrollContentWindowInsets(it) }
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (context != null) {
|
||||
binding?.speechBubble?.animateText(
|
||||
context?.getString(R.string.task_setup_description) ?: ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun setUser(user: User?) {
|
||||
this.user = user
|
||||
if (binding?.avatarView != null) {
|
||||
updateAvatar()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateAvatar() {
|
||||
user?.let {
|
||||
binding?.avatarView?.setAvatar(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setTasks() {
|
||||
this.taskGroups =
|
||||
listOf(
|
||||
listOf(getString(R.string.setup_group_work), TYPE_WORK),
|
||||
listOf(getString(R.string.setup_group_exercise), TYPE_EXERCISE),
|
||||
listOf(getString(R.string.setup_group_health), TYPE_HEALTH),
|
||||
listOf(getString(R.string.setup_group_school), TYPE_SCHOOL),
|
||||
listOf(getString(R.string.setup_group_teams), TYPE_TEAMS),
|
||||
listOf(getString(R.string.setup_group_chores), TYPE_CHORES),
|
||||
listOf(getString(R.string.setup_group_creativity), TYPE_CREATIVITY),
|
||||
listOf(getString(R.string.setuP_group_other), TYPE_OTHER)
|
||||
)
|
||||
|
||||
this.tasks =
|
||||
listOf(
|
||||
listOf(TYPE_WORK, TaskType.HABIT, getString(R.string.setup_task_work_1), true, false),
|
||||
listOf(TYPE_WORK, TaskType.DAILY, getString(R.string.setup_task_work_2)),
|
||||
listOf(TYPE_WORK, TaskType.TODO, getString(R.string.setup_task_work_3)),
|
||||
listOf(
|
||||
TYPE_EXERCISE,
|
||||
TaskType.HABIT,
|
||||
getString(R.string.setup_task_exercise_1),
|
||||
true,
|
||||
false
|
||||
),
|
||||
listOf(TYPE_EXERCISE, TaskType.DAILY, getString(R.string.setup_task_exercise_2)),
|
||||
listOf(TYPE_EXERCISE, TaskType.TODO, getString(R.string.setup_task_exercise_3)),
|
||||
listOf(
|
||||
TYPE_HEALTH,
|
||||
TaskType.HABIT,
|
||||
getString(R.string.setup_task_healthWellness_1),
|
||||
true,
|
||||
true
|
||||
),
|
||||
listOf(TYPE_HEALTH, TaskType.DAILY, getString(R.string.setup_task_healthWellness_2)),
|
||||
listOf(TYPE_HEALTH, TaskType.TODO, getString(R.string.setup_task_healthWellness_3)),
|
||||
listOf(
|
||||
TYPE_SCHOOL,
|
||||
TaskType.HABIT,
|
||||
getString(R.string.setup_task_school_1),
|
||||
true,
|
||||
true
|
||||
),
|
||||
listOf(TYPE_SCHOOL, TaskType.DAILY, getString(R.string.setup_task_school_2)),
|
||||
listOf(TYPE_SCHOOL, TaskType.TODO, getString(R.string.setup_task_school_3)),
|
||||
listOf(TYPE_TEAMS, TaskType.HABIT, getString(R.string.setup_task_teams_1), true, false),
|
||||
listOf(TYPE_TEAMS, TaskType.DAILY, getString(R.string.setup_task_teams_2)),
|
||||
listOf(TYPE_TEAMS, TaskType.TODO, getString(R.string.setup_task_teams_3)),
|
||||
listOf(
|
||||
TYPE_CHORES,
|
||||
TaskType.HABIT,
|
||||
getString(R.string.setup_task_chores_1),
|
||||
true,
|
||||
false
|
||||
),
|
||||
listOf(TYPE_CHORES, TaskType.DAILY, getString(R.string.setup_task_chores_2)),
|
||||
listOf(TYPE_CHORES, TaskType.TODO, getString(R.string.setup_task_chores_3)),
|
||||
listOf(
|
||||
TYPE_CREATIVITY,
|
||||
TaskType.HABIT,
|
||||
getString(R.string.setup_task_creativity_1),
|
||||
true,
|
||||
false
|
||||
),
|
||||
listOf(TYPE_CREATIVITY, TaskType.DAILY, getString(R.string.setup_task_creativity_2)),
|
||||
listOf(TYPE_CREATIVITY, TaskType.TODO, getString(R.string.setup_task_creativity_3))
|
||||
)
|
||||
}
|
||||
|
||||
fun createSampleTasks(): List<Task> {
|
||||
val groups = ArrayList<String>()
|
||||
for ((i, checked) in this.adapter.checkedList.withIndex()) {
|
||||
if (checked) {
|
||||
groups.add(this.taskGroups[i][1])
|
||||
}
|
||||
}
|
||||
val tasks = ArrayList<Task>()
|
||||
for (task in this.tasks) {
|
||||
val taskGroup = task[0] as? String
|
||||
if (groups.contains(taskGroup)) {
|
||||
val taskObject: Task =
|
||||
if (task.size == 5) {
|
||||
this.makeTaskObject(
|
||||
task[1] as? TaskType,
|
||||
task[2] as? String,
|
||||
task[3] as? Boolean,
|
||||
task[4] as? Boolean
|
||||
)
|
||||
} else {
|
||||
this.makeTaskObject(task[1] as? TaskType, task[2] as? String, null, null)
|
||||
}
|
||||
tasks.add(taskObject)
|
||||
}
|
||||
}
|
||||
tasks.add(
|
||||
makeTaskObject(
|
||||
TaskType.HABIT,
|
||||
getString(R.string.setup_task_habit_1),
|
||||
true,
|
||||
false,
|
||||
getString(R.string.setup_task_habit_1_notes)
|
||||
)
|
||||
)
|
||||
tasks.add(
|
||||
makeTaskObject(
|
||||
TaskType.HABIT,
|
||||
getString(R.string.setup_task_habit_2),
|
||||
false,
|
||||
true,
|
||||
getString(R.string.setup_task_habit_2_notes)
|
||||
)
|
||||
)
|
||||
tasks.add(
|
||||
makeTaskObject(
|
||||
TaskType.REWARD,
|
||||
getString(R.string.setup_task_reward),
|
||||
null,
|
||||
null,
|
||||
getString(R.string.setup_task_reward_notes)
|
||||
)
|
||||
)
|
||||
tasks.add(
|
||||
makeTaskObject(
|
||||
TaskType.TODO,
|
||||
getString(R.string.setup_task_join_habitica),
|
||||
null,
|
||||
null,
|
||||
getString(R.string.setup_task_join_habitica_notes)
|
||||
)
|
||||
)
|
||||
return tasks
|
||||
}
|
||||
|
||||
private fun makeTaskObject(
|
||||
type: TaskType?,
|
||||
text: String?,
|
||||
up: Boolean?,
|
||||
down: Boolean?,
|
||||
notes: String? = null
|
||||
): Task {
|
||||
val task = Task()
|
||||
task.id = UUID.randomUUID().toString()
|
||||
task.text = text ?: ""
|
||||
task.notes = notes
|
||||
task.priority = 1.0f
|
||||
task.type = type ?: TaskType.HABIT
|
||||
task.frequency = Frequency.DAILY
|
||||
|
||||
if (type == TaskType.HABIT) {
|
||||
task.up = up
|
||||
task.down = down
|
||||
}
|
||||
|
||||
if (type == TaskType.DAILY) {
|
||||
task.frequency = Frequency.WEEKLY
|
||||
task.startDate = Date()
|
||||
task.everyX = 1
|
||||
val days = Days()
|
||||
days.m = true
|
||||
days.t = true
|
||||
days.w = true
|
||||
days.th = true
|
||||
days.f = true
|
||||
days.s = true
|
||||
days.su = true
|
||||
task.repeat = days
|
||||
}
|
||||
|
||||
return task
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TYPE_EXERCISE = "exercise"
|
||||
const val TYPE_HEALTH = "healthWellness"
|
||||
const val TYPE_WORK = "work"
|
||||
const val TYPE_SCHOOL = "school"
|
||||
const val TYPE_TEAMS = "teams"
|
||||
const val TYPE_CHORES = "chores"
|
||||
const val TYPE_CREATIVITY = "creativity"
|
||||
const val TYPE_OTHER = "other"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
package com.habitrpg.android.habitica.ui.fragments.setup
|
||||
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.VectorDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.data.UserRepository
|
||||
import com.habitrpg.android.habitica.databinding.FragmentWelcomeBinding
|
||||
import com.habitrpg.android.habitica.extensions.OnChangeTextWatcher
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
|
||||
import com.habitrpg.common.habitica.helpers.ExceptionHandler
|
||||
import com.habitrpg.common.habitica.helpers.launchCatching
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
|
||||
@AndroidEntryPoint
|
||||
class WelcomeFragment : BaseFragment<FragmentWelcomeBinding>() {
|
||||
var onNameValid: ((Boolean?) -> Unit)? = null
|
||||
|
||||
@Inject
|
||||
lateinit var userRepository: UserRepository
|
||||
|
||||
override var binding: FragmentWelcomeBinding? = null
|
||||
|
||||
override fun createBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?
|
||||
): FragmentWelcomeBinding {
|
||||
return FragmentWelcomeBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
private val displayNameVerificationEvents = MutableStateFlow<String?>(null)
|
||||
private val usernameVerificationEvents = MutableStateFlow<String?>(null)
|
||||
|
||||
private val checkmarkIcon: Drawable by lazy {
|
||||
context?.let {
|
||||
HabiticaIconsHelper.imageOfCheckmark(
|
||||
ContextCompat.getColor(it, R.color.green_50),
|
||||
1f
|
||||
).toDrawable(resources)
|
||||
} ?: VectorDrawable()
|
||||
}
|
||||
private val alertIcon: Drawable by lazy {
|
||||
HabiticaIconsHelper.imageOfAlertIcon().toDrawable(resources)
|
||||
}
|
||||
val username: String
|
||||
get() = binding?.usernameEditText?.text?.toString() ?: ""
|
||||
val displayName: String
|
||||
get() = binding?.displayNameEditText?.text?.toString() ?: ""
|
||||
|
||||
override fun onViewCreated(
|
||||
view: View,
|
||||
savedInstanceState: Bundle?
|
||||
) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding?.speechBubble?.animateText(context?.getString(R.string.welcome_text) ?: "")
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding?.displayNameEditText?.addTextChangedListener(
|
||||
OnChangeTextWatcher { p0, _, _, _ ->
|
||||
displayNameVerificationEvents.value = p0.toString()
|
||||
}
|
||||
)
|
||||
binding?.usernameEditText?.addTextChangedListener(
|
||||
OnChangeTextWatcher { p0, _, _, _ ->
|
||||
usernameVerificationEvents.value = p0.toString()
|
||||
}
|
||||
)
|
||||
|
||||
lifecycleScope.launchCatching {
|
||||
displayNameVerificationEvents
|
||||
.map { it?.length in 1..30 }
|
||||
.collect {
|
||||
if (it) {
|
||||
binding?.displayNameEditText?.setCompoundDrawablesWithIntrinsicBounds(
|
||||
null,
|
||||
null,
|
||||
checkmarkIcon,
|
||||
null
|
||||
)
|
||||
binding?.issuesTextView?.visibility = View.GONE
|
||||
} else {
|
||||
binding?.displayNameEditText?.setCompoundDrawablesWithIntrinsicBounds(
|
||||
null,
|
||||
null,
|
||||
alertIcon,
|
||||
null
|
||||
)
|
||||
binding?.issuesTextView?.visibility = View.VISIBLE
|
||||
binding?.issuesTextView?.text =
|
||||
context?.getString(R.string.display_name_length_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycleScope.launchCatching {
|
||||
usernameVerificationEvents
|
||||
.filter { it?.length in 1..30 }
|
||||
.filterNotNull()
|
||||
.map { userRepository.verifyUsername(it) }
|
||||
.collect {
|
||||
if (it?.isUsable == true) {
|
||||
binding?.usernameEditText?.setCompoundDrawablesWithIntrinsicBounds(
|
||||
null,
|
||||
null,
|
||||
checkmarkIcon,
|
||||
null
|
||||
)
|
||||
binding?.issuesTextView?.visibility = View.GONE
|
||||
} else {
|
||||
binding?.usernameEditText?.setCompoundDrawablesWithIntrinsicBounds(
|
||||
null,
|
||||
null,
|
||||
alertIcon,
|
||||
null
|
||||
)
|
||||
binding?.issuesTextView?.visibility = View.VISIBLE
|
||||
binding?.issuesTextView?.text = it?.issues?.joinToString("\n")
|
||||
}
|
||||
onNameValid?.invoke(it?.isUsable)
|
||||
}
|
||||
}
|
||||
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine()) {
|
||||
val user = userRepository.getUser().firstOrNull()
|
||||
binding?.displayNameEditText?.setText(user?.profile?.name)
|
||||
displayNameVerificationEvents.value = user?.profile?.name ?: ""
|
||||
binding?.usernameEditText?.setText(user?.authentication?.localAuthentication?.username)
|
||||
usernameVerificationEvents.value = user?.username ?: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,8 @@ import android.content.Context
|
|||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.core.content.edit
|
||||
import androidx.credentials.CredentialManager
|
||||
import androidx.credentials.CustomCredential
|
||||
|
|
@ -41,6 +43,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.tasks.await
|
||||
|
|
@ -56,6 +59,10 @@ class AuthenticationViewModel @Inject constructor(
|
|||
val hostConfig: HostConfig,
|
||||
private val keyHelper: KeyHelper?,
|
||||
) : ViewModel() {
|
||||
val email = mutableStateOf("")
|
||||
val password = mutableStateOf("")
|
||||
val username = mutableStateOf("")
|
||||
|
||||
private val _showAuthProgress = MutableStateFlow(false)
|
||||
val showAuthProgress: Flow<Boolean> = _showAuthProgress
|
||||
val isRegistering = MutableStateFlow(false)
|
||||
|
|
@ -63,8 +70,13 @@ class AuthenticationViewModel @Inject constructor(
|
|||
val authenticationError: Flow<AuthenticationErrors?> = _authenticationError
|
||||
.onEach { _showAuthProgress.value = false }
|
||||
private val _authenticationSuccess = MutableStateFlow<Boolean?>(null)
|
||||
val authenticationSuccess: Flow<Boolean?> = _authenticationSuccess
|
||||
val authenticationSuccess: Flow<Boolean> = _authenticationSuccess
|
||||
.filterNotNull()
|
||||
.onEach { _showAuthProgress.value = false }
|
||||
private val _isUsernameValid = MutableStateFlow<Boolean?>(null)
|
||||
val isUsernameValid: Flow<Boolean?> = _isUsernameValid
|
||||
private var _usernameIssues = MutableStateFlow<String?>(null)
|
||||
val usernameIssues: Flow<String?> = _usernameIssues
|
||||
|
||||
fun validateInputs(
|
||||
username: String,
|
||||
|
|
@ -91,6 +103,24 @@ class AuthenticationViewModel @Inject constructor(
|
|||
return null
|
||||
}
|
||||
|
||||
fun checkUsername(username: String) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val response = apiClient.verifyUsername(username)
|
||||
_isUsernameValid.value = response?.isUsable == true
|
||||
_usernameIssues.value = response?.issues?.joinToString("\n") { it }
|
||||
} catch (e: Exception) {
|
||||
_isUsernameValid.value = null
|
||||
Analytics.logException(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun invalidateUsernameState() {
|
||||
_isUsernameValid.value = null
|
||||
_usernameIssues.value = null
|
||||
}
|
||||
|
||||
fun login(username: String, password: String) {
|
||||
_showAuthProgress.value = true
|
||||
viewModelScope.launch {
|
||||
|
|
@ -119,7 +149,7 @@ class AuthenticationViewModel @Inject constructor(
|
|||
|
||||
suspend fun removeSocialAuth(network: String) {
|
||||
apiClient.disconnectSocial(network)
|
||||
userRepository.retrieveUser(true, true)
|
||||
userRepository.retrieveUser(true, forced = true)
|
||||
}
|
||||
|
||||
private fun authenticationError(error: AuthenticationErrors? = null) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,149 @@
|
|||
package com.habitrpg.android.habitica.ui.views
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.selection.TextSelectionColors
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.Wallpapers
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.common.habitica.theme.HabiticaTheme
|
||||
import com.habitrpg.common.habitica.views.HabiticaCircularProgressView
|
||||
|
||||
enum class LoginFieldState {
|
||||
DEFAULT,
|
||||
VALID,
|
||||
ERROR,
|
||||
LOADING,
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LoginScreenField(
|
||||
label: String,
|
||||
value: String,
|
||||
onValueChange: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
icon: @Composable (() -> Unit)? = null,
|
||||
prefix: @Composable () -> Unit = {},
|
||||
state: LoginFieldState = LoginFieldState.DEFAULT,
|
||||
hideInput: Boolean = false,
|
||||
) {
|
||||
val containerColor = colorResource(R.color.brand_100)
|
||||
TextField(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
placeholder = { Text(label, fontSize = 18.sp, fontWeight = FontWeight.Normal) },
|
||||
isError = state == LoginFieldState.ERROR,
|
||||
suffix = {
|
||||
AnimatedContent(state) {
|
||||
if (it == LoginFieldState.ERROR) {
|
||||
Image(
|
||||
painterResource(R.drawable.ic_close_white_18dp),
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(colorResource(R.color.red_100))
|
||||
)
|
||||
} else if (it == LoginFieldState.VALID) {
|
||||
Image(
|
||||
painterResource(R.drawable.checkmark),
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(colorResource(R.color.green_50))
|
||||
)
|
||||
} else if (it == LoginFieldState.LOADING) {
|
||||
HabiticaCircularProgressView(indicatorSize = 20.dp, strokeWidth = 2.dp)
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
singleLine = true,
|
||||
trailingIcon = icon,
|
||||
prefix = prefix,
|
||||
textStyle = TextStyle(
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Normal
|
||||
),
|
||||
colors = TextFieldDefaults.colors(
|
||||
unfocusedContainerColor = containerColor,
|
||||
focusedContainerColor = containerColor,
|
||||
errorContainerColor = containerColor,
|
||||
unfocusedTextColor = Color.White,
|
||||
focusedTextColor = Color.White,
|
||||
errorTextColor = colorResource(R.color.red_100),
|
||||
unfocusedPlaceholderColor = colorResource(R.color.brand_600),
|
||||
focusedPlaceholderColor = colorResource(R.color.brand_600).copy(alpha = 0.5f),
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
errorIndicatorColor = Color.Transparent,
|
||||
unfocusedTrailingIconColor = colorResource(R.color.brand_100),
|
||||
cursorColor = Color.White,
|
||||
selectionColors = TextSelectionColors(
|
||||
handleColor = colorResource(R.color.brand_600),
|
||||
backgroundColor = colorResource(R.color.brand_600).copy(alpha = 0.3f)
|
||||
),
|
||||
),
|
||||
shape = HabiticaTheme.shapes.large,
|
||||
modifier = modifier.fillMaxWidth().heightIn(min = 60.dp),
|
||||
visualTransformation = if (hideInput) PasswordVisualTransformation() else VisualTransformation.None,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(wallpaper = Wallpapers.BLUE_DOMINATED_EXAMPLE)
|
||||
@Composable
|
||||
fun LoginScreenFieldPreview() {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(10.dp), modifier = Modifier.padding(8.dp)) {
|
||||
LoginScreenField(
|
||||
label = "Email",
|
||||
value = "",
|
||||
icon = {
|
||||
Image(painterResource(R.drawable.login_email), contentDescription = null)
|
||||
}, onValueChange = {}
|
||||
)
|
||||
LoginScreenField(
|
||||
label = "Email",
|
||||
value = "test@test.com",
|
||||
icon = {
|
||||
Image(painterResource(R.drawable.login_email), contentDescription = null)
|
||||
}, onValueChange = {}, state = LoginFieldState.VALID
|
||||
)
|
||||
LoginScreenField(
|
||||
label = "Email",
|
||||
value = "test@test.com",
|
||||
icon = {
|
||||
Image(painterResource(R.drawable.login_email), contentDescription = null)
|
||||
}, onValueChange = {}, state = LoginFieldState.ERROR
|
||||
)
|
||||
LoginScreenField(
|
||||
label = "Username",
|
||||
value = "Bla",
|
||||
icon = {
|
||||
Image(painterResource(R.drawable.login_username), contentDescription = null)
|
||||
}, onValueChange = {}, state = LoginFieldState.LOADING
|
||||
)
|
||||
LoginScreenField(
|
||||
label = "Password",
|
||||
value = "abcd",
|
||||
hideInput = true,
|
||||
icon = {
|
||||
Image(painterResource(R.drawable.login_password), contentDescription = null)
|
||||
}, onValueChange = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,29 @@
|
|||
package com.habitrpg.android.habitica.ui.views
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.util.AttributeSet
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.habitrpg.android.habitica.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
|
@ -12,6 +31,7 @@ import kotlinx.coroutines.Job
|
|||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.streams.toList
|
||||
|
||||
// http://stackoverflow.com/a/6700718/1315039
|
||||
class Typewriter : androidx.appcompat.widget.AppCompatTextView {
|
||||
|
|
@ -70,3 +90,74 @@ class Typewriter : androidx.appcompat.widget.AppCompatTextView {
|
|||
index = stringBuilder?.length ?: 0
|
||||
}
|
||||
}
|
||||
|
||||
// https://medium.com/make-apps-simple/typewriter-animation-in-jetpack-compose-2b0c7ee323c2
|
||||
@Composable
|
||||
fun TypewriterText(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = Color.Unspecified,
|
||||
fontSize: TextUnit = TextUnit.Unspecified,
|
||||
fontStyle: FontStyle? = null,
|
||||
fontWeight: FontWeight? = null,
|
||||
fontFamily: FontFamily? = null,
|
||||
letterSpacing: TextUnit = TextUnit.Unspecified,
|
||||
textDecoration: TextDecoration? = null,
|
||||
textAlign: TextAlign? = null,
|
||||
lineHeight: TextUnit = TextUnit.Unspecified,
|
||||
delay: Long = 30L,
|
||||
) {
|
||||
var textToDisplay by remember {
|
||||
mutableStateOf("")
|
||||
}
|
||||
val textCharsList = remember {
|
||||
text.splitToCodePoints()
|
||||
}
|
||||
|
||||
LaunchedEffect(text,) {
|
||||
textCharsList.forEachIndexed { charIndex, _ ->
|
||||
textToDisplay = textCharsList
|
||||
.take(
|
||||
n = charIndex + 1,
|
||||
).joinToString(
|
||||
separator = "",
|
||||
)
|
||||
delay(delay)
|
||||
}
|
||||
}
|
||||
|
||||
Box(modifier = modifier) {
|
||||
Text(
|
||||
text = text,
|
||||
color = Color.Transparent,
|
||||
fontSize = fontSize,
|
||||
fontStyle = fontStyle,
|
||||
fontWeight = fontWeight,
|
||||
fontFamily = fontFamily,
|
||||
letterSpacing = letterSpacing,
|
||||
textDecoration = textDecoration,
|
||||
textAlign = textAlign,
|
||||
lineHeight = lineHeight,
|
||||
)
|
||||
Text(
|
||||
text = textToDisplay,
|
||||
color = color,
|
||||
fontSize = fontSize,
|
||||
fontStyle = fontStyle,
|
||||
fontWeight = fontWeight,
|
||||
fontFamily = fontFamily,
|
||||
letterSpacing = letterSpacing,
|
||||
textDecoration = textDecoration,
|
||||
textAlign = textAlign,
|
||||
lineHeight = lineHeight,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun String.splitToCodePoints(): List<String> {
|
||||
return codePoints()
|
||||
.toList()
|
||||
.map {
|
||||
String(Character.toChars(it))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,239 @@
|
|||
package com.habitrpg.android.habitica.ui.views.intro
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ProvideTextStyle
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.compositeOver
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.common.habitica.helpers.launchCatching
|
||||
|
||||
@Composable
|
||||
fun IntroPage(
|
||||
page: Int,
|
||||
title: @Composable () -> Unit,
|
||||
subtitle: @Composable () -> Unit,
|
||||
description: @Composable () -> Unit,
|
||||
image: @Composable () -> Unit,
|
||||
background: Brush,
|
||||
) {
|
||||
Box(modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 40.dp), contentAlignment = Alignment.Center) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
ProvideTextStyle(
|
||||
TextStyle(
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
textAlign = TextAlign.Center,
|
||||
color = Color.White
|
||||
)
|
||||
) {
|
||||
subtitle()
|
||||
}
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
ProvideTextStyle(
|
||||
TextStyle(
|
||||
fontSize = 28.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
textAlign = TextAlign.Center,
|
||||
color = Color.White
|
||||
)
|
||||
) {
|
||||
title()
|
||||
}
|
||||
Spacer(modifier = Modifier.height(50.dp))
|
||||
image()
|
||||
Spacer(modifier = Modifier.height(50.dp))
|
||||
ProvideTextStyle(
|
||||
TextStyle(
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
textAlign = TextAlign.Center,
|
||||
color = Color.White,
|
||||
lineHeight = 20.sp
|
||||
)
|
||||
) {
|
||||
description()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Color.blend(topColor: Color, ratio: Float = 0.5f): Color {
|
||||
if (ratio == 0f) return this
|
||||
if (ratio == 1f) return topColor
|
||||
val intColor = ColorUtils.blendARGB(toArgb(), topColor.toArgb(), ratio)
|
||||
return Color(intColor)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun IntroScreen(onNextOnboardingStep: () -> Unit) {
|
||||
val pagerState = rememberPagerState(pageCount = { 3 })
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val pageOffset by remember { derivedStateOf { pagerState.currentPageOffsetFraction } }
|
||||
var topColor: Color
|
||||
var bottomColor: Color
|
||||
print(pageOffset)
|
||||
if (pagerState.currentPage < 1) {
|
||||
if (pageOffset > 0) {
|
||||
topColor = colorResource(R.color.brand_400).blend(colorResource(R.color.blue_100), pageOffset)
|
||||
bottomColor = colorResource(R.color.brand_100).blend(colorResource(R.color.blue_10), pageOffset)
|
||||
} else {
|
||||
topColor = colorResource(R.color.brand_400)
|
||||
bottomColor = colorResource(R.color.brand_100)
|
||||
}
|
||||
} else if (pagerState.currentPage < 2) {
|
||||
if (pageOffset > 0) {
|
||||
topColor = colorResource(R.color.blue_100).blend(colorResource(R.color.red_100), pageOffset)
|
||||
bottomColor = colorResource(R.color.blue_10).blend(colorResource(R.color.red_10), pageOffset)
|
||||
} else {
|
||||
topColor = colorResource(R.color.blue_100).blend(colorResource(R.color.brand_400), -pageOffset)
|
||||
bottomColor = colorResource(R.color.blue_10).blend(colorResource(R.color.brand_200), -pageOffset)
|
||||
}
|
||||
} else {
|
||||
if (pageOffset > 0) {
|
||||
topColor = colorResource(R.color.blue_100).blend(colorResource(R.color.red_100), pageOffset)
|
||||
bottomColor = colorResource(R.color.blue_10).blend(colorResource(R.color.red_10), pageOffset)
|
||||
} else {
|
||||
topColor = colorResource(R.color.red_100).blend(colorResource(R.color.blue_100), -pageOffset)
|
||||
bottomColor = colorResource(R.color.red_10).blend(colorResource(R.color.blue_10), -pageOffset)
|
||||
}
|
||||
}
|
||||
Box(Modifier.fillMaxSize().background(
|
||||
Brush.verticalGradient(
|
||||
listOf(topColor, bottomColor)
|
||||
)
|
||||
)) {
|
||||
HorizontalPager(pagerState) { page ->
|
||||
when (page) {
|
||||
0 -> IntroPage(
|
||||
page = page,
|
||||
title = { Image(painterResource(R.drawable.intro_1_title), contentDescription = null) },
|
||||
subtitle = { Text(stringResource(R.string.intro_1_subtitle)) },
|
||||
description = { Text(stringResource(R.string.intro_1_description)) },
|
||||
image = { Image(painterResource(R.drawable.intro_1), contentDescription = null) },
|
||||
background = Brush.verticalGradient(listOf(colorResource(R.color.brand_400), colorResource(R.color.brand_200)))
|
||||
)
|
||||
|
||||
1 -> IntroPage(
|
||||
page = page,
|
||||
title = { Text(stringResource(R.string.intro_2_title)) },
|
||||
subtitle = { Text(stringResource(R.string.intro_2_subtitle)) },
|
||||
description = { Text(stringResource(R.string.intro_2_description)) },
|
||||
image = { Image(painterResource(R.drawable.intro_2), contentDescription = null) },
|
||||
background = Brush.verticalGradient(listOf(colorResource(R.color.blue_100), colorResource(R.color.blue_50)))
|
||||
)
|
||||
|
||||
2 -> IntroPage(
|
||||
page = page,
|
||||
title = { Text(stringResource(R.string.intro_3_title)) },
|
||||
subtitle = { Text(stringResource(R.string.intro_3_subtitle)) },
|
||||
description = { Text(stringResource(R.string.intro_3_description)) },
|
||||
image = { Image(painterResource(R.drawable.intro_3), contentDescription = null) },
|
||||
background = Brush.verticalGradient(listOf(colorResource(R.color.red_100), colorResource(R.color.red_50)))
|
||||
)
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
onNextOnboardingStep()
|
||||
},
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(WindowInsets.systemBars.asPaddingValues()),
|
||||
colors = ButtonDefaults.textButtonColors(),
|
||||
) {
|
||||
Text(stringResource(R.string.skip_button), color = Color.White, fontSize = 18.sp)
|
||||
}
|
||||
|
||||
Column(Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(WindowInsets.systemBars.asPaddingValues())
|
||||
.padding(horizontal = 20.dp)) {
|
||||
Row(
|
||||
Modifier
|
||||
.wrapContentHeight()
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 8.dp),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
repeat(pagerState.pageCount) { iteration ->
|
||||
val color = (if (pagerState.currentPage == iteration) Color.Black else Color.White).copy(alpha = 0.6f)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(8.dp)
|
||||
.rotate(45f)
|
||||
.clip(RectangleShape)
|
||||
.background(color)
|
||||
.size(8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
Button(
|
||||
{
|
||||
if (pagerState.currentPage < pagerState.pageCount - 1) {
|
||||
coroutineScope.launchCatching {
|
||||
pagerState.animateScrollToPage(pagerState.currentPage + 1)
|
||||
}
|
||||
} else {
|
||||
onNextOnboardingStep()
|
||||
}
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = Color.Black.copy(alpha = 0.4f),
|
||||
contentColor = Color.White
|
||||
),
|
||||
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
if (pagerState.currentPage < pagerState.pageCount - 1) {
|
||||
Text(stringResource(R.string.next_button), fontSize = 18.sp, fontWeight = FontWeight.Normal)
|
||||
} else {
|
||||
Text(stringResource(R.string.lets_go), fontSize = 18.sp, fontWeight = FontWeight.Normal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,13 @@ import android.widget.RelativeLayout
|
|||
import com.habitrpg.android.habitica.R
|
||||
import java.util.Random
|
||||
|
||||
class LoginBackgroundView(context: Context, attrs: AttributeSet?) : RelativeLayout(context, attrs) {
|
||||
class LoginBackgroundView
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : RelativeLayout(context, attrs, defStyleAttr) {
|
||||
private val random: Random = Random()
|
||||
|
||||
private lateinit var leftCloudView: ImageView
|
||||
|
|
|
|||
|
|
@ -0,0 +1,237 @@
|
|||
package com.habitrpg.android.habitica.ui.views.login
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ProvideTextStyle
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.ui.views.LoginFieldState
|
||||
import com.habitrpg.android.habitica.ui.views.LoginScreenField
|
||||
import com.habitrpg.common.habitica.theme.HabiticaTheme
|
||||
import com.habitrpg.common.habitica.views.HabiticaCircularProgressView
|
||||
|
||||
@Composable
|
||||
fun LoginForm(
|
||||
onToggleFormType: () -> Unit,
|
||||
email: String,
|
||||
emailFieldState: LoginFieldState,
|
||||
onEmailChange: (String) -> Unit,
|
||||
password: String,
|
||||
passwordFieldState: LoginFieldState,
|
||||
onPasswordChange: (String) -> Unit,
|
||||
isRegistering: Boolean,
|
||||
onSubmit: () -> Unit,
|
||||
showLoading: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var confirmPassword by remember { mutableStateOf("") }
|
||||
Column(
|
||||
horizontalAlignment = Alignment.Companion.CenterHorizontally,
|
||||
modifier = modifier.widthIn(max = 480.dp)
|
||||
) {
|
||||
Button(
|
||||
{
|
||||
onToggleFormType()
|
||||
},
|
||||
colors = ButtonDefaults.textButtonColors(),
|
||||
contentPadding = PaddingValues(15.dp),
|
||||
modifier = Modifier.Companion.fillMaxWidth().padding(top = 24.dp, bottom = 8.dp)
|
||||
) {
|
||||
ProvideTextStyle(TextStyle(fontSize=18.sp)) {
|
||||
AnimatedContent(isRegistering) {
|
||||
if (it) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
Text(
|
||||
stringResource(R.string.already_have_an_account),
|
||||
color = colorResource(R.color.brand_600)
|
||||
)
|
||||
Text(
|
||||
stringResource(R.string.login_btn),
|
||||
color = colorResource(R.color.white)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
Text(
|
||||
stringResource(R.string.need_an_account),
|
||||
color = colorResource(R.color.brand_600)
|
||||
)
|
||||
Text(
|
||||
stringResource(R.string.register_btn),
|
||||
color = colorResource(R.color.white)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LoginScreenField(
|
||||
label = if (isRegistering) stringResource(R.string.email) else stringResource(R.string.username_or_email),
|
||||
value = email,
|
||||
state = emailFieldState,
|
||||
onValueChange = onEmailChange,
|
||||
icon = {
|
||||
AnimatedContent(isRegistering) {
|
||||
if (it) {
|
||||
Image(
|
||||
painterResource(R.drawable.login_email),
|
||||
contentDescription = stringResource(R.string.email)
|
||||
)
|
||||
} else {
|
||||
Image(
|
||||
painterResource(R.drawable.login_username),
|
||||
contentDescription = stringResource(R.string.email)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
modifier = Modifier.Companion.fillMaxWidth().padding(bottom = 10.dp),
|
||||
)
|
||||
LoginScreenField(
|
||||
label = stringResource(R.string.password),
|
||||
value = password,
|
||||
state = passwordFieldState,
|
||||
onValueChange = onPasswordChange,
|
||||
hideInput = true,
|
||||
icon = {
|
||||
Image(
|
||||
painterResource(R.drawable.login_password),
|
||||
contentDescription = stringResource(R.string.password)
|
||||
)
|
||||
},
|
||||
modifier = Modifier.Companion.fillMaxWidth(),
|
||||
)
|
||||
AnimatedVisibility(isRegistering) {
|
||||
LoginScreenField(
|
||||
label = stringResource(R.string.confirmpassword),
|
||||
value = confirmPassword,
|
||||
onValueChange = { confirmPassword = it },
|
||||
hideInput = true,
|
||||
state = when {
|
||||
confirmPassword.isEmpty() -> LoginFieldState.DEFAULT
|
||||
confirmPassword == password && passwordFieldState == LoginFieldState.VALID -> LoginFieldState.VALID
|
||||
else -> LoginFieldState.ERROR
|
||||
},
|
||||
icon = {
|
||||
Image(
|
||||
painterResource(R.drawable.login_password),
|
||||
contentDescription = stringResource(R.string.confirmpassword)
|
||||
)
|
||||
},
|
||||
modifier = Modifier.Companion.fillMaxWidth().padding(top = 10.dp),
|
||||
)
|
||||
}
|
||||
AnimatedContent(showLoading) { isLoading ->
|
||||
if (isLoading) {
|
||||
HabiticaCircularProgressView(indicatorSize = 64.dp, modifier = Modifier.padding(top = 30.dp))
|
||||
} else {
|
||||
Button(
|
||||
{
|
||||
onSubmit()
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = Color.Companion.White,
|
||||
contentColor = colorResource(R.color.gray_50),
|
||||
disabledContainerColor = Color.White.copy(alpha = 0.5f),
|
||||
),
|
||||
shape = HabiticaTheme.shapes.large,
|
||||
contentPadding = PaddingValues(15.dp),
|
||||
enabled = if (isRegistering) {
|
||||
(emailFieldState == LoginFieldState.VALID && passwordFieldState == LoginFieldState.VALID && password == confirmPassword)
|
||||
} else {
|
||||
(email.isNotBlank() && password.isNotBlank())
|
||||
},
|
||||
modifier = Modifier.Companion.fillMaxWidth().padding(top = 30.dp)
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||
verticalAlignment = Alignment.Companion.CenterVertically
|
||||
) {
|
||||
if (isRegistering) {
|
||||
Text(
|
||||
stringResource(R.string.action_continue),
|
||||
fontWeight = FontWeight.Companion.Bold,
|
||||
fontSize = 18.sp
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
stringResource(R.string.login_btn), fontWeight = FontWeight.Companion.Bold,
|
||||
fontSize = 18.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(!isRegistering) {
|
||||
Button(
|
||||
{
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = Color.Companion.White,
|
||||
contentColor = colorResource(R.color.gray_50)
|
||||
),
|
||||
shape = HabiticaTheme.shapes.large,
|
||||
contentPadding = PaddingValues(15.dp),
|
||||
modifier = Modifier.Companion.widthIn(max = 480.dp).fillMaxWidth().padding(top = 10.dp)
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||
verticalAlignment = Alignment.Companion.CenterVertically
|
||||
) {
|
||||
Image(
|
||||
painterResource(R.drawable.googleg_standard_color_18),
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
stringResource(R.string.continue_with_google),
|
||||
fontWeight = FontWeight.Companion.Bold,
|
||||
fontSize = 18.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(!isRegistering) {
|
||||
Button(
|
||||
{
|
||||
onToggleFormType()
|
||||
},
|
||||
colors = ButtonDefaults.textButtonColors(),
|
||||
contentPadding = PaddingValues(15.dp),
|
||||
modifier = Modifier.Companion.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.forgot_pw_btn),
|
||||
color = colorResource(R.color.white),
|
||||
fontSize = 18.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
package com.habitrpg.android.habitica.ui.views.login
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.common.habitica.theme.HabiticaTheme
|
||||
|
||||
@Composable
|
||||
fun LoginInitialButtons(onLoginClicked: () -> Unit,
|
||||
onRegisterClicked: () -> Unit,
|
||||
modifier: Modifier = Modifier.Companion
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.Companion.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
modifier = modifier.fillMaxWidth()
|
||||
) {
|
||||
Button(
|
||||
{
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = Color.Companion.White,
|
||||
contentColor = colorResource(R.color.gray_50)
|
||||
),
|
||||
shape = HabiticaTheme.shapes.large,
|
||||
contentPadding = PaddingValues(15.dp),
|
||||
modifier = Modifier.Companion.widthIn(max = 480.dp).fillMaxWidth()
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||
verticalAlignment = Alignment.Companion.CenterVertically
|
||||
) {
|
||||
Image(
|
||||
painterResource(R.drawable.googleg_standard_color_18),
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
stringResource(R.string.continue_with_google),
|
||||
fontWeight = FontWeight.Companion.Bold,
|
||||
fontSize = 18.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
Button(
|
||||
{
|
||||
onRegisterClicked()
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = Color.Companion.White,
|
||||
contentColor = colorResource(R.color.gray_50)
|
||||
),
|
||||
shape = HabiticaTheme.shapes.large,
|
||||
contentPadding = PaddingValues(15.dp),
|
||||
modifier = Modifier.Companion.widthIn(max = 480.dp).fillMaxWidth()
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||
verticalAlignment = Alignment.Companion.CenterVertically
|
||||
) {
|
||||
Image(painterResource(R.drawable.ic_email_color), contentDescription = null)
|
||||
Text(
|
||||
stringResource(R.string.continue_with_email),
|
||||
fontWeight = FontWeight.Companion.Bold,
|
||||
fontSize = 18.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
Button(
|
||||
{
|
||||
onLoginClicked()
|
||||
},
|
||||
colors = ButtonDefaults.textButtonColors(),
|
||||
contentPadding = PaddingValues(15.dp),
|
||||
modifier = Modifier.Companion.fillMaxWidth()
|
||||
) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
Text(
|
||||
stringResource(R.string.already_have_an_account),
|
||||
color = colorResource(R.color.brand_600),
|
||||
fontSize = 18.sp
|
||||
)
|
||||
Text(stringResource(R.string.login_btn), color = colorResource(R.color.white),
|
||||
fontSize = 18.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,234 @@
|
|||
package com.habitrpg.android.habitica.ui.views.login
|
||||
|
||||
import android.util.Patterns
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.EaseInOut
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ProvideTextStyle
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.AuthenticationViewModel
|
||||
import com.habitrpg.android.habitica.ui.views.LoginFieldState
|
||||
import com.habitrpg.common.habitica.extensions.layoutInflater
|
||||
|
||||
enum class LoginScreenState {
|
||||
INITIAL,
|
||||
LOGIN,
|
||||
REGISTER,
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LoginScreen(authenticationViewModel: AuthenticationViewModel, onNextOnboardingStep: (Boolean) -> Unit, modifier: Modifier = Modifier) {
|
||||
val showLoading by authenticationViewModel.showAuthProgress.collectAsState(false)
|
||||
val authenticationError by authenticationViewModel.authenticationError.collectAsState(null)
|
||||
|
||||
LaunchedEffect(authenticationViewModel) {
|
||||
authenticationViewModel.authenticationSuccess.collect { isRegistering ->
|
||||
onNextOnboardingStep(isRegistering)
|
||||
}
|
||||
}
|
||||
|
||||
var loginScreenState by remember { mutableStateOf(LoginScreenState.INITIAL) }
|
||||
var password by authenticationViewModel.password
|
||||
var passwordFieldState by remember { mutableStateOf(LoginFieldState.DEFAULT) }
|
||||
var email by authenticationViewModel.email
|
||||
var emailFieldState by remember { mutableStateOf(LoginFieldState.DEFAULT) }
|
||||
Box(modifier.fillMaxSize()) {
|
||||
AndroidView(
|
||||
factory = { context ->
|
||||
val layout = context.layoutInflater.inflate(R.layout.login_background, null)
|
||||
(layout as? LockableScrollView)?.isScrollable = false
|
||||
layout
|
||||
},
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize().align(Alignment.BottomCenter)
|
||||
) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Image(
|
||||
painterResource(R.drawable.login_background),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier.width(800.dp)
|
||||
)
|
||||
AnimatedVisibility(
|
||||
loginScreenState == LoginScreenState.INITIAL,
|
||||
enter = expandVertically(tween(300, 400), expandFrom = Alignment.Top),
|
||||
exit = shrinkVertically(shrinkTowards = Alignment.Top),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth().height(200.dp).background(
|
||||
Brush.verticalGradient(
|
||||
listOf(
|
||||
Color(0xFFA995EA),
|
||||
colorResource(R.color.brand_400)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(
|
||||
loginScreenState != LoginScreenState.INITIAL,
|
||||
enter = fadeIn(), exit = fadeOut(),
|
||||
modifier = Modifier.padding(WindowInsets.systemBars.asPaddingValues())
|
||||
) {
|
||||
Button(
|
||||
{
|
||||
loginScreenState = LoginScreenState.INITIAL
|
||||
},
|
||||
colors = ButtonDefaults.textButtonColors(contentColor = Color.White),
|
||||
modifier = Modifier.align(Alignment.TopStart)
|
||||
) {
|
||||
Image(painterResource(R.drawable.arrow_back), contentDescription = null)
|
||||
}
|
||||
}
|
||||
val logoPadding by animateDpAsState(
|
||||
if (loginScreenState == LoginScreenState.INITIAL) {
|
||||
120.dp
|
||||
} else {
|
||||
50.dp
|
||||
},
|
||||
animationSpec = tween(
|
||||
delayMillis = if (loginScreenState == LoginScreenState.INITIAL) 400 else 0,
|
||||
easing = EaseInOut
|
||||
),
|
||||
label = "padding"
|
||||
)
|
||||
ProvideTextStyle(TextStyle(fontSize = 18.sp)) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.fillMaxWidth().padding(WindowInsets.systemBars.asPaddingValues()).padding(horizontal = 20.dp)
|
||||
) {
|
||||
Image(
|
||||
painterResource(R.drawable.login_logo),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.padding(top = logoPadding)
|
||||
)
|
||||
AnimatedVisibility(
|
||||
loginScreenState == LoginScreenState.INITIAL,
|
||||
enter = fadeIn(tween(300, 500)) + expandVertically(tween(300, 500)),
|
||||
exit = fadeOut()
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.enjoy_getting_things_done),
|
||||
fontSize = 24.sp,
|
||||
lineHeight = 30.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = colorResource(R.color.brand_600),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 30.dp)
|
||||
.padding(horizontal = 30.dp)
|
||||
)
|
||||
}
|
||||
AnimatedVisibility(
|
||||
loginScreenState != LoginScreenState.INITIAL,
|
||||
enter = fadeIn(tween(300, 400)),
|
||||
exit = fadeOut()
|
||||
) {
|
||||
LoginForm(
|
||||
onToggleFormType = {
|
||||
loginScreenState =
|
||||
if (loginScreenState == LoginScreenState.LOGIN) LoginScreenState.REGISTER else LoginScreenState.LOGIN
|
||||
},
|
||||
email = email,
|
||||
emailFieldState = emailFieldState,
|
||||
onEmailChange = {
|
||||
email = it
|
||||
if (loginScreenState == LoginScreenState.REGISTER) {
|
||||
emailFieldState = if (it.isEmpty()) {
|
||||
LoginFieldState.DEFAULT
|
||||
} else if (Patterns.EMAIL_ADDRESS.matcher(it).matches()) {
|
||||
LoginFieldState.VALID
|
||||
} else {
|
||||
LoginFieldState.ERROR
|
||||
}
|
||||
} else {
|
||||
emailFieldState = LoginFieldState.DEFAULT
|
||||
}
|
||||
},
|
||||
password = password,
|
||||
passwordFieldState = passwordFieldState,
|
||||
onPasswordChange = {
|
||||
password = it
|
||||
if (loginScreenState == LoginScreenState.REGISTER) {
|
||||
passwordFieldState = if (it.isEmpty()) {
|
||||
LoginFieldState.DEFAULT
|
||||
} else if (it.length >= 8) {
|
||||
LoginFieldState.VALID
|
||||
} else {
|
||||
LoginFieldState.ERROR
|
||||
}
|
||||
} else {
|
||||
passwordFieldState = LoginFieldState.DEFAULT
|
||||
}
|
||||
},
|
||||
isRegistering = loginScreenState == LoginScreenState.REGISTER,
|
||||
onSubmit = {
|
||||
if (loginScreenState == LoginScreenState.REGISTER) {
|
||||
authenticationViewModel.register("", email, password, password)
|
||||
} else {
|
||||
authenticationViewModel.login(email, password)
|
||||
}
|
||||
},
|
||||
showLoading = showLoading
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
AnimatedVisibility(
|
||||
loginScreenState == LoginScreenState.INITIAL,
|
||||
enter = fadeIn(tween(300, 500)) + expandVertically(tween(300, 400)),
|
||||
exit = fadeOut() + shrinkVertically(tween(300, 200))
|
||||
) {
|
||||
LoginInitialButtons({
|
||||
loginScreenState = LoginScreenState.LOGIN
|
||||
}, {
|
||||
loginScreenState = LoginScreenState.REGISTER
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
package com.habitrpg.android.habitica.ui.views.setup
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.common.habitica.extensions.setTintWith
|
||||
|
||||
class AvatarCategoryView(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) {
|
||||
private val icon: Drawable?
|
||||
private val textView: TextView
|
||||
|
||||
init {
|
||||
View.inflate(context, R.layout.avatar_category, this)
|
||||
|
||||
textView = findViewById(R.id.text_view)
|
||||
val a =
|
||||
context.theme.obtainStyledAttributes(
|
||||
attrs,
|
||||
R.styleable.AvatarCategoryView,
|
||||
0,
|
||||
0
|
||||
)
|
||||
|
||||
textView.text = a.getText(R.styleable.AvatarCategoryView_categoryTitle)
|
||||
|
||||
icon = a.getDrawable(R.styleable.AvatarCategoryView_iconDrawable)
|
||||
if (icon != null) {
|
||||
textView.setCompoundDrawablesWithIntrinsicBounds(null, icon, null, null)
|
||||
}
|
||||
setActive(false)
|
||||
}
|
||||
|
||||
fun setActive(active: Boolean) {
|
||||
val color: Int =
|
||||
if (active) {
|
||||
ContextCompat.getColor(context, R.color.white)
|
||||
} else {
|
||||
ContextCompat.getColor(context, R.color.white_50_alpha)
|
||||
}
|
||||
textView.setTextColor(color)
|
||||
if (icon != null) {
|
||||
icon.setTintWith(color, PorterDuff.Mode.MULTIPLY)
|
||||
textView.setCompoundDrawablesWithIntrinsicBounds(null, icon, null, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package com.habitrpg.android.habitica.ui.views.setup
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.LinearLayout
|
||||
import com.habitrpg.android.habitica.databinding.AvatarSetupDrawerBinding
|
||||
import com.habitrpg.common.habitica.extensions.layoutInflater
|
||||
|
||||
class AvatarCustomizationDrawer(context: Context, attrs: AttributeSet?) :
|
||||
LinearLayout(context, attrs) {
|
||||
val binding = AvatarSetupDrawerBinding.inflate(context.layoutInflater, this, true)
|
||||
}
|
||||
|
|
@ -0,0 +1,607 @@
|
|||
package com.habitrpg.android.habitica.ui.views.setup
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedContentTransitionScope
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.animateColor
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.animation.core.LinearOutSlowInEasing
|
||||
import androidx.compose.animation.core.animateDp
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.scaleIn
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ProvideTextStyle
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.TabRow
|
||||
import androidx.compose.material3.TabRowDefaults
|
||||
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.ImageShader
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.ShaderBrush
|
||||
import androidx.compose.ui.graphics.TileMode
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.imageResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.ui.views.TypewriterText
|
||||
import com.habitrpg.common.habitica.theme.HabiticaTheme
|
||||
import com.habitrpg.common.habitica.views.ComposableAvatarView
|
||||
|
||||
@Composable
|
||||
fun SetupScreen(onNextOnboardingStep: () -> Unit) {
|
||||
var currentStep by remember { mutableIntStateOf(0) }
|
||||
var selectedCustomizationCategory by remember { mutableStateOf("skin") }
|
||||
|
||||
val density = LocalDensity.current
|
||||
val img = ImageBitmap.imageResource(R.drawable.border_pixelated)
|
||||
|
||||
val bgColorTop = colorResource(R.color.brand_100)
|
||||
val bgColorMiddle = colorResource(R.color.brand_200)
|
||||
val bgColorBottom = colorResource(R.color.brand_300)
|
||||
val pixelImage = remember {
|
||||
ShaderBrush(
|
||||
ImageShader(
|
||||
img,
|
||||
TileMode.Repeated,
|
||||
TileMode.Repeated
|
||||
)
|
||||
)
|
||||
}
|
||||
val text = if (currentStep == 0) {
|
||||
stringResource(R.string.onboarding_step_avatar)
|
||||
} else {
|
||||
stringResource(R.string.onboarding_step_tasks)
|
||||
}
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = Modifier
|
||||
.background(Color(0xFFC7E7FD))
|
||||
.padding(
|
||||
top = WindowInsets.systemBars.asPaddingValues().calculateTopPadding()
|
||||
)
|
||||
.padding(top = 48.dp)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(bgColorMiddle)
|
||||
.weight(1f)
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.height(186.dp)
|
||||
.background(
|
||||
ShaderBrush(
|
||||
ImageShader(
|
||||
ImageBitmap.imageResource(R.drawable.stable_background_spring),
|
||||
TileMode.Repeated,
|
||||
TileMode.Repeated
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.fillMaxWidth()
|
||||
.height(18.dp)
|
||||
.drawBehind {
|
||||
drawRect(
|
||||
pixelImage,
|
||||
colorFilter = ColorFilter.tint(if (currentStep == 0) bgColorTop else bgColorMiddle)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedVisibility(currentStep == 0) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.background(bgColorTop)
|
||||
.padding(top = 170.dp)
|
||||
.padding(bottom = 26.dp)
|
||||
) {
|
||||
CustomizationCategoryView(selectedCustomizationCategory)
|
||||
}
|
||||
}
|
||||
AnimatedContent(currentStep, modifier = Modifier.weight(1f)) {
|
||||
if (it == 0) {
|
||||
CustomizationCategorySelector(
|
||||
selectedCustomizationCategory,
|
||||
{
|
||||
selectedCustomizationCategory = it
|
||||
}
|
||||
)
|
||||
} else {
|
||||
OnboardingTaskSelector(modifier = Modifier.padding(top = 160.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.height(18.dp)
|
||||
.background(bgColorMiddle)
|
||||
.drawBehind {
|
||||
drawRect(
|
||||
pixelImage,
|
||||
colorFilter = ColorFilter.tint(bgColorBottom)
|
||||
)
|
||||
}
|
||||
)
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(bgColorBottom)
|
||||
.padding(
|
||||
bottom = WindowInsets.systemBars.asPaddingValues()
|
||||
.calculateBottomPadding()
|
||||
)
|
||||
.animateContentSize()
|
||||
) {
|
||||
Row(
|
||||
Modifier
|
||||
.wrapContentHeight()
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 16.dp),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
repeat(2) { iteration ->
|
||||
val color =
|
||||
(if (currentStep == iteration) Color.White else Color.Black).copy(
|
||||
alpha = 0.4f
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(8.dp)
|
||||
.rotate(45f)
|
||||
.clip(RectangleShape)
|
||||
.background(color)
|
||||
.size(8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
Button(
|
||||
{
|
||||
if (currentStep == 0) {
|
||||
currentStep = 1
|
||||
} else {
|
||||
currentStep = 2
|
||||
}
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = colorResource(R.color.white),
|
||||
contentColor = colorResource(R.color.gray_50)
|
||||
),
|
||||
shape = HabiticaTheme.shapes.large,
|
||||
contentPadding = PaddingValues(15.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 12.dp)
|
||||
.padding(bottom = 18.dp)
|
||||
) {
|
||||
Text(
|
||||
stringResource(if (currentStep == 0) R.string.next_button else R.string.finish),
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 8.dp)
|
||||
.padding(
|
||||
top = WindowInsets.systemBars.asPaddingValues().calculateTopPadding()
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
"Username",
|
||||
color = colorResource(R.color.brand_600),
|
||||
fontSize = 18.sp,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.border(
|
||||
6.dp,
|
||||
colorResource(R.color.brand_400),
|
||||
shape = RoundedCornerShape(14.dp)
|
||||
)
|
||||
.padding(6.dp)
|
||||
.background(
|
||||
colorResource(R.color.brand_50),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
)
|
||||
.padding(horizontal = 20.dp, vertical = 12.dp)
|
||||
.widthIn(min = 120.dp)
|
||||
)
|
||||
ComposableAvatarView(null, null)
|
||||
}
|
||||
SpeechBubble(
|
||||
text, { Text("Justin") }, modifier = Modifier
|
||||
.padding(horizontal = 36.dp)
|
||||
.padding(top = 210.dp)
|
||||
.padding(
|
||||
top = WindowInsets.systemBars.asPaddingValues().calculateTopPadding()
|
||||
)
|
||||
)
|
||||
AnimatedVisibility(
|
||||
currentStep != 0,
|
||||
enter = fadeIn(), exit = fadeOut(),
|
||||
modifier = Modifier
|
||||
.padding(top = 12.dp)
|
||||
.padding(
|
||||
top = WindowInsets.systemBars.asPaddingValues().calculateTopPadding()
|
||||
)
|
||||
) {
|
||||
Button(
|
||||
{
|
||||
currentStep = 0
|
||||
},
|
||||
colors = ButtonDefaults.textButtonColors(),
|
||||
modifier = Modifier.align(Alignment.TopStart)
|
||||
) {
|
||||
Image(
|
||||
painterResource(R.drawable.arrow_back),
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(colorResource(R.color.brand_50)),
|
||||
)
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(
|
||||
currentStep == 2,
|
||||
enter = expandVertically(expandFrom = Alignment.Bottom) + fadeIn(),
|
||||
modifier = Modifier.align(Alignment.BottomCenter)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(bgColorBottom)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun OnboardingTaskSelector(modifier: Modifier = Modifier) {
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Adaptive(minSize = 180.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp, Alignment.CenterHorizontally),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp, Alignment.CenterVertically),
|
||||
contentPadding = PaddingValues(horizontal = 19.dp),
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
) {
|
||||
items(7) { index ->
|
||||
Text(
|
||||
"Test ${index}",
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = colorResource(R.color.white),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(colorResource(R.color.brand_100), RoundedCornerShape(30.dp))
|
||||
.padding(20.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CustomizationCategoryView(selectedCategory: String, modifier: Modifier = Modifier) {
|
||||
var selectedTabIndex by remember { mutableIntStateOf(0) }
|
||||
val unselected = Color.White.copy(0.5f)
|
||||
Column(verticalArrangement = Arrangement.spacedBy(26.dp)) {
|
||||
AnimatedContent(selectedCategory) {
|
||||
ProvideTextStyle(
|
||||
TextStyle(
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Normal
|
||||
)
|
||||
) {
|
||||
TabRow(
|
||||
selectedTabIndex,
|
||||
containerColor = Color.Transparent,
|
||||
contentColor = Color.White,
|
||||
divider = {},
|
||||
indicator = { positions ->
|
||||
if (selectedTabIndex < positions.size) {
|
||||
TabRowDefaults.PrimaryIndicator(
|
||||
Modifier.tabIndicatorOffset(positions[selectedTabIndex]),
|
||||
width = 60.dp,
|
||||
height = 2.dp,
|
||||
color = colorResource(R.color.brand_400)
|
||||
)
|
||||
}
|
||||
}) {
|
||||
when (it) {
|
||||
"skin" -> {
|
||||
Tab(
|
||||
selectedTabIndex == 0,
|
||||
unselectedContentColor = unselected,
|
||||
onClick = { selectedTabIndex = 0 }) {
|
||||
Text(
|
||||
stringResource(R.string.avatar_skin_color).uppercase(),
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
"hair" -> {
|
||||
Tab(
|
||||
selectedTabIndex == 0,
|
||||
unselectedContentColor = unselected,
|
||||
onClick = { selectedTabIndex = 0 }) {
|
||||
Text(
|
||||
stringResource(R.string.avatar_hair_color).uppercase(),
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
}
|
||||
Tab(
|
||||
selectedTabIndex == 1,
|
||||
unselectedContentColor = unselected,
|
||||
onClick = { selectedTabIndex = 1 }) {
|
||||
Text(
|
||||
stringResource(R.string.avatar_hair_bangs).uppercase(),
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
}
|
||||
Tab(
|
||||
selectedTabIndex == 2,
|
||||
unselectedContentColor = unselected,
|
||||
onClick = { selectedTabIndex = 2 }) {
|
||||
Text(
|
||||
stringResource(R.string.avatar_hair_styles).uppercase(),
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
}
|
||||
Tab(
|
||||
selectedTabIndex == 3,
|
||||
unselectedContentColor = unselected,
|
||||
onClick = { selectedTabIndex = 3 }) {
|
||||
Text(
|
||||
stringResource(R.string.avatar_hair_ponytail).uppercase(),
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
"clothes" -> {
|
||||
Tab(
|
||||
selectedTabIndex == 0,
|
||||
unselectedContentColor = unselected,
|
||||
onClick = { selectedTabIndex = 0 }) {
|
||||
Text(
|
||||
stringResource(R.string.avatar_shirt).uppercase(),
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
"accessories" -> {
|
||||
Tab(
|
||||
selectedTabIndex == 0,
|
||||
unselectedContentColor = unselected,
|
||||
onClick = { selectedTabIndex = 0 }) {
|
||||
Text(
|
||||
stringResource(R.string.avatar_wheelchair).uppercase(),
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
}
|
||||
Tab(
|
||||
selectedTabIndex == 0,
|
||||
unselectedContentColor = unselected,
|
||||
onClick = { selectedTabIndex = 0 }) {
|
||||
Text(
|
||||
stringResource(R.string.avatar_flower).uppercase(),
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
AnimatedContent(selectedCategory,
|
||||
transitionSpec = {
|
||||
(fadeIn(animationSpec = tween(220, delayMillis = 400)) +
|
||||
scaleIn(initialScale = 0.92f, animationSpec = tween(220, delayMillis = 400)))
|
||||
.togetherWith(fadeOut(animationSpec = tween(90)))
|
||||
}) {
|
||||
val scrollState = rememberScrollState()
|
||||
var selectedItem by remember { mutableIntStateOf(0) }
|
||||
AnimatedContent(
|
||||
selectedTabIndex,
|
||||
transitionSpec = {
|
||||
slideIntoContainer(
|
||||
if (targetState > initialState) {
|
||||
AnimatedContentTransitionScope.SlideDirection.End
|
||||
} else {
|
||||
AnimatedContentTransitionScope.SlideDirection.Start
|
||||
},
|
||||
tween(400, 300)
|
||||
)
|
||||
.togetherWith(
|
||||
slideOutOfContainer(
|
||||
if (targetState > initialState) {
|
||||
AnimatedContentTransitionScope.SlideDirection.End
|
||||
} else {
|
||||
AnimatedContentTransitionScope.SlideDirection.Start
|
||||
}, tween(400, easing = LinearOutSlowInEasing)
|
||||
)
|
||||
)
|
||||
},
|
||||
) { it ->
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 12.dp)
|
||||
.horizontalScroll(scrollState)
|
||||
) {
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
repeat(8) { index ->
|
||||
val transition = updateTransition(index == selectedItem)
|
||||
val borderColor by transition.animateColor { if (it) colorResource(R.color.brand_400) else Color.Transparent }
|
||||
val borderWidth by transition.animateDp({
|
||||
tween(300)
|
||||
}) { if (it) 4.dp else 0.dp }
|
||||
Image(
|
||||
painterResource(R.drawable.creator_hair_bangs_1_black),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.None,
|
||||
modifier = Modifier
|
||||
.size(68.dp)
|
||||
.border(borderWidth, borderColor, CircleShape)
|
||||
.padding(4.dp)
|
||||
.background(Color.White, CircleShape)
|
||||
.clickable {
|
||||
selectedItem = index
|
||||
}
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CustomizationCategorySelector(
|
||||
selectedCategory: String,
|
||||
onCategorySelected: (String) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 31.dp)
|
||||
) {
|
||||
val categories = listOf(
|
||||
Pair("skin", Pair(stringResource(R.string.avatar_skin), R.drawable.icon_skin)),
|
||||
Pair("hair", Pair(stringResource(R.string.avatar_hair), R.drawable.icon_hair)),
|
||||
Pair("clothes", Pair(stringResource(R.string.avatar_body), R.drawable.icon_body)),
|
||||
Pair(
|
||||
"accessories",
|
||||
Pair(stringResource(R.string.avatar_extras), R.drawable.icon_extras)
|
||||
)
|
||||
)
|
||||
categories.forEach { category ->
|
||||
val isSelected = selectedCategory == category.first
|
||||
val color = if (isSelected) Color.White else Color.White.copy(0.5f)
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.clickable {
|
||||
onCategorySelected(category.first)
|
||||
}
|
||||
.padding(8.dp)) {
|
||||
Image(
|
||||
painterResource(category.second.second), contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(color)
|
||||
)
|
||||
Text(
|
||||
category.second.first.uppercase(),
|
||||
color = if (isSelected) Color.White else colorResource(R.color.brand_500),
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
textAlign = TextAlign.Center,
|
||||
letterSpacing = 1.3.sp,
|
||||
modifier = Modifier.padding(top = 18.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SpeechBubble(
|
||||
text: String,
|
||||
npcName: @Composable () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
npc: @Composable (() -> Unit)? = null
|
||||
) {
|
||||
TypewriterText(
|
||||
text, fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
lineHeight = 21.sp,
|
||||
color = colorResource(R.color.yellow_1),
|
||||
modifier = modifier
|
||||
.border(
|
||||
width = 4.dp,
|
||||
colorResource(R.color.yellow_10),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
)
|
||||
.padding(4.dp)
|
||||
.background(Color.White, shape = RoundedCornerShape(8.dp))
|
||||
.padding(24.dp)
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
package com.habitrpg.android.habitica.ui.views.setup
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.ime
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.layout.union
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.TextLinkStyles
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.fromHtml
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.AuthenticationViewModel
|
||||
import com.habitrpg.android.habitica.ui.views.LoginFieldState
|
||||
import com.habitrpg.android.habitica.ui.views.LoginScreenField
|
||||
import com.habitrpg.common.habitica.theme.HabiticaTheme
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
@Composable
|
||||
fun <T> T.useDebounce(
|
||||
delayMillis: Long = 300L,
|
||||
coroutineScope: CoroutineScope = rememberCoroutineScope(),
|
||||
onChange: (T) -> Unit
|
||||
): T{
|
||||
val state by rememberUpdatedState(this)
|
||||
|
||||
DisposableEffect(state){
|
||||
val job = coroutineScope.launch {
|
||||
delay(delayMillis)
|
||||
onChange(state)
|
||||
}
|
||||
onDispose {
|
||||
job.cancel()
|
||||
}
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UsernameSelectionScreen(
|
||||
authenticationViewModel: AuthenticationViewModel,
|
||||
onPreviousOnboardingStep: () -> Unit,
|
||||
onNextOnboardingStep: () -> Unit
|
||||
) {
|
||||
var username by authenticationViewModel.username
|
||||
val isUsernameValid by authenticationViewModel.isUsernameValid.collectAsState(null)
|
||||
val usernameIssues by authenticationViewModel.usernameIssues.collectAsState(null)
|
||||
|
||||
username.useDebounce {
|
||||
if (it.length > 2) {
|
||||
authenticationViewModel.checkUsername(it)
|
||||
}
|
||||
}
|
||||
|
||||
var acceptedTerms by remember { mutableStateOf(false) }
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(colorResource(R.color.brand_300))
|
||||
.padding(WindowInsets.ime.union(WindowInsets.systemBars).asPaddingValues())
|
||||
) {
|
||||
Button(
|
||||
{
|
||||
onPreviousOnboardingStep()
|
||||
},
|
||||
colors = ButtonDefaults.textButtonColors(contentColor = Color.White),
|
||||
modifier = Modifier.align(Alignment.TopStart)
|
||||
) {
|
||||
Image(painterResource(R.drawable.arrow_back), contentDescription = null)
|
||||
}
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp, Alignment.CenterVertically),
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(top = 50.dp)
|
||||
.padding(horizontal = 20.dp)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.header_verify_username),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(bottom = 16.dp)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.what_should_call_you),
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = Color.White,
|
||||
modifier = Modifier
|
||||
)
|
||||
LoginScreenField(
|
||||
stringResource(R.string.username),
|
||||
value = username,
|
||||
prefix = {
|
||||
Text("@", fontSize = 16.sp, color = colorResource(R.color.brand_600), modifier = Modifier.padding(end = 8.dp))
|
||||
},
|
||||
state = when (isUsernameValid) {
|
||||
true -> LoginFieldState.VALID
|
||||
false -> LoginFieldState.ERROR
|
||||
else -> LoginFieldState.DEFAULT
|
||||
},
|
||||
onValueChange = {
|
||||
username = it
|
||||
authenticationViewModel.invalidateUsernameState()
|
||||
},
|
||||
modifier = Modifier
|
||||
)
|
||||
AnimatedVisibility(usernameIssues?.isNotBlank() == true) {
|
||||
Text(
|
||||
text = usernameIssues ?: "",
|
||||
fontSize = 16.sp,
|
||||
color = colorResource(R.color.red_500),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(bottom = 12.dp)
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = stringResource(R.string.username_description),
|
||||
fontSize = 16.sp,
|
||||
color = colorResource(R.color.brand_600),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
)
|
||||
Spacer(Modifier.weight(1f))
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 4.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(30.dp)
|
||||
.clickable {
|
||||
acceptedTerms = !acceptedTerms
|
||||
}
|
||||
.background(colorResource(R.color.brand_100), shape = HabiticaTheme.shapes.small)) {
|
||||
if (acceptedTerms) {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.checkmark),
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(Color.White),
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
)
|
||||
}
|
||||
}
|
||||
Text(
|
||||
AnnotatedString.fromHtml(
|
||||
stringResource(R.string.register_tos_confirm),
|
||||
linkStyles = TextLinkStyles(style = SpanStyle(
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = colorResource(R.color.white)
|
||||
))
|
||||
),
|
||||
fontSize = 16.sp,
|
||||
color = colorResource(R.color.brand_600),
|
||||
fontWeight = FontWeight.Normal,
|
||||
)
|
||||
}
|
||||
Button(
|
||||
onClick = onNextOnboardingStep,
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = Color.White,
|
||||
contentColor = colorResource(R.color.gray_50),
|
||||
disabledContainerColor = Color.White.copy(alpha = 0.5f),
|
||||
disabledContentColor = colorResource(R.color.gray_50)
|
||||
),
|
||||
enabled = isUsernameValid == true && acceptedTerms,
|
||||
shape = HabiticaTheme.shapes.large,
|
||||
contentPadding = PaddingValues(15.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 16.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.get_started), fontSize = 18.sp, fontWeight = FontWeight.Bold)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Habitica/src/main/res/layout/login_background.xml
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.habitrpg.android.habitica.ui.views.login.LockableScrollView 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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/background_container"
|
||||
android:scrollIndicators="none"
|
||||
android:scrollbars="none"
|
||||
tools:showIn="@layout/activity_login">
|
||||
|
||||
<com.habitrpg.android.habitica.ui.views.login.LoginBackgroundView
|
||||
android:id="@+id/background_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/login_gradient"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:layout_marginEnd="-2dp">
|
||||
|
||||
<Space
|
||||
android:id="@+id/cloud_anchor"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:visibility="invisible"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginTop="50dp" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/cloud_1"
|
||||
android:id="@+id/left_cloud_view"
|
||||
android:layout_toStartOf="@id/cloud_anchor"
|
||||
android:layout_marginEnd="150dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginTop="50dp"
|
||||
android:scaleType="centerCrop" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/cloud_2"
|
||||
android:id="@+id/right_cloud_view"
|
||||
android:layout_toEndOf="@id/cloud_anchor"
|
||||
android:layout_marginStart="170dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_marginTop="130dp" />
|
||||
</com.habitrpg.android.habitica.ui.views.login.LoginBackgroundView>
|
||||
|
||||
</com.habitrpg.android.habitica.ui.views.login.LockableScrollView>
|
||||
13
Habitica/src/main/res/values/strings.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="continue_with_google">Continue with Google</string>
|
||||
<string name="continue_with_email">Continue with email</string>
|
||||
<string name="already_have_an_account">Already have an account?</string>
|
||||
<string name="need_an_account">Need an account?</string>
|
||||
<string name="enjoy_getting_things_done">Enjoy having fun getting things done</string>
|
||||
<string name="what_should_call_you">What should we call you?</string>
|
||||
<string name="get_started">Get Started</string>
|
||||
<string name="username_description">This is a unique name you can change later at any time!</string>
|
||||
<string name="onboarding_step_avatar">You must be new here. I’m Justin, I’ll be your guide in Habitica.\n\nSo how would you like to look? Don’t worry, you can change this later.</string>
|
||||
<string name="onboarding_step_tasks">Looking good! Now, choose any categories you’d like to work on and we’ll create a few tasks to get started.\n\nWe’ll go to your task board after this!</string>
|
||||
</resources>
|
||||