Merge branch 'develop' of https://github.com/HabitRPG/habitrpg-android into full-profile

Conflicts:
	Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java
This commit is contained in:
Negue 2016-08-06 20:47:01 +02:00
commit 127464537d
72 changed files with 2202 additions and 139 deletions

1
.gitignore vendored
View file

@ -54,3 +54,4 @@ fabric.properties
Habitica/res/values/secret_strings.xml
habitica.properties
habitica.resources
Habitica/google-services.json

View file

@ -41,5 +41,6 @@ android:
script:
- cp habitica.properties.travis habitica.properties
- cp habitica.resources.example habitica.resources
- cp Habitica/google-services.json.example Habitica/google-services.json
- ./gradlew assembleDebug
- ./gradlew testDebugUnitTest --info

View file

@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.habitrpg.android.habitica"
android:versionCode="108"
android:versionName="0.0.33"
android:versionCode="111"
android:versionName="0.0.33.1"
android:screenOrientation="portrait"
android:installLocation="auto" >
@ -132,12 +132,35 @@
<receiver android:process=":remote" android:name=".NotificationPublisher" />
<receiver android:process=":remote" android:name=".receivers.TaskReceiver"></receiver>
<receiver android:name=".receivers.LocalNotificationActionReceiver">
<intent-filter>
<action android:name="ACCEPT_PARTY_INVITE"/>
<action android:name="REJECT_PARTY_INVITE"/>
<action android:name="ACCEPT_QUEST_INVITE"/>
<action android:name="REJECT_QUEST_INVITE"/>
</intent-filter>
</receiver>
<receiver android:name=".receivers.TaskAlarmBootReceiver" android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action._BOOT_COMPLETED"/>
</intent-filter>
</receiver>
<service
android:name=".helpers.notifications.HabiticaFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<service
android:name=".helpers.notifications.HabiticaFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.habitrpg.android.habitica.fileprovider"

View file

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.habitrpg.android.habitica"
android:versionCode="104"
android:versionName="0.0.32"
android:screenOrientation="portrait"
android:installLocation="auto" >
<uses-sdk
android:minSdkVersion="15"
android:targetSdkVersion="24" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:name=".HabiticaApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name=".ui.activities.MainActivity"
android:label="@string/app_name"
android:windowSoftInputMode="stateHidden|adjustPan"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ui.activities.PrefsActivity"
android:parentActivityName=".ui.activities.MainActivity"
android:label="@string/PS_settings_title"
android:screenOrientation="portrait"
tools:ignore="UnusedAttribute">
</activity>
<activity
android:name=".ui.activities.LoginActivity"
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"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activities.AboutActivity"
android:theme="@style/AppThemeWithActionBarBlackText"
android:screenOrientation="portrait"/>
<activity
android:name=".ui.activities.TaskFormActivity"
android:theme="@style/AppThemeWithActionBarBlackText"
android:parentActivityName=".ui.activities.MainActivity"
android:screenOrientation="portrait"
tools:ignore="UnusedAttribute">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.activities.MainActivity" />
</activity>
<activity
android:name=".ui.activities.GroupFormActivity"
android:theme="@style/AppThemeWithActionBarBlackText"
android:parentActivityName=".ui.activities.MainActivity"
android:screenOrientation="portrait"
tools:ignore="UnusedAttribute">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.activities.MainActivity" />
</activity>
<activity
android:name=".ui.activities.ClassSelectionActivity"
android:theme="@style/AppThemeWithActionBarBlackText"
android:parentActivityName=".ui.activities.MainActivity"
android:screenOrientation="portrait"
tools:ignore="UnusedAttribute"
android:label="@string/select_class">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.activities.MainActivity" />
</activity>
<activity
android:name=".ui.activities.PartyInviteActivity"
android:theme="@style/AppThemeWithActionBarBlackText"
android:screenOrientation="portrait"
tools:ignore="UnusedAttribute"
android:label="@string/invite_users">
</activity>
<activity android:name="com.facebook.FacebookActivity"
android:configChanges=
"keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:label="@string/app_name" />
<activity android:name=".ui.activities.MaintenanceActivity"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
<receiver android:process=":remote" android:name=".NotificationPublisher" />
<receiver android:process=":remote" android:name=".receivers.TaskReceiver"></receiver>
<receiver android:name=".receivers.TaskAlarmBootReceiver" android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action._BOOT_COMPLETED"/>
</intent-filter>
</receiver>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.habitrpg.android.habitica.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</application>
</manifest>

View file

@ -71,11 +71,11 @@ dependencies {
compile('com.mikepenz:materialdrawer:5.3.6@aar') {
transitive = true
}
compile 'com.android.support:appcompat-v7:24.0.0'
compile 'com.android.support:design:24.0.0'
compile 'com.android.support:gridlayout-v7:24.0.0'
compile 'com.android.support:recyclerview-v7:24.0.0'
compile 'com.android.support:preference-v14:24.0.0'
compile 'com.android.support:appcompat-v7:24.1.1'
compile 'com.android.support:design:24.1.1'
compile 'com.android.support:gridlayout-v7:24.1.1'
compile 'com.android.support:recyclerview-v7:24.1.1'
compile 'com.android.support:preference-v14:24.1.1'
compile 'com.android.support:multidex:1.0.1'
// Image Loading/Caching
@ -128,24 +128,32 @@ dependencies {
exclude module: 'bolts-android'
}
//Tests
compile 'com.android.support:appcompat-v7:23.0'
compile 'com.android.support:appcompat-v7:24.1.1'
testCompile "junit:junit:4.10"
testCompile "org.assertj:assertj-core:1.7.0"
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'
testCompile "org.robolectric:robolectric:3.2-SNAPSHOT"
testCompile 'org.robolectric:shadows-multidex:3.0'
testCompile "org.robolectric:shadows-support-v4:3.0"
testCompile "org.robolectric:robolectric:3.1"
testCompile 'org.robolectric:shadows-multidex:3.1'
testCompile "org.robolectric:shadows-support-v4:3.1"
testCompile "org.mockito:mockito-core:1.10.19"
//Leak Detection
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'
//Push Notifications
compile 'com.google.firebase:firebase-core:9.0.2'
compile 'com.google.firebase:firebase-messaging:9.0.2'
}
android {
compileSdkVersion 24
buildToolsVersion "23.0.3"
defaultConfig {
applicationId "com.habitrpg.android.habitica"
}
lintOptions {
abortOnError false
}
@ -184,7 +192,7 @@ android {
assets.srcDirs = ['assets']
}
robolectric {
manifest.srcFile 'AndroidManifest.xml'
manifest.srcFile 'AndroidManifestTesting.xml'
java.srcDir file('src/test/java/')
res.srcDirs = ['res']
}
@ -276,3 +284,4 @@ apply plugin: 'com.getkeepsafe.dexcount'
apply plugin: 'com.android.application' //or apply plugin: 'java'
apply plugin: 'me.tatarka.retrolambda'
apply plugin: 'com.jakewharton.hugo'
apply plugin: 'com.google.gms.google-services'

View file

@ -0,0 +1,73 @@
{
"project_info": {
"project_number": "project-number",
"firebase_url": "https://example.firebaseio.com",
"project_id": "project-id",
"storage_bucket": "storage.bucket"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "app-id",
"android_client_info": {
"package_name": "com.habitrpg.android.habitica.debug"
}
},
"oauth_client": [
{
"client_id": "client-key",
"client_type": 3
}
],
"api_key": [
{
"current_key": "api-key"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
},
{
"client_info": {
"mobilesdk_app_id": "app-id",
"android_client_info": {
"package_name": "com.habitrpg.android.habitica.debug"
}
},
"oauth_client": [
{
"client_id": "client-id",
"client_type": 3
}
],
"api_key": [
{
"current_key": "api-key"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
}
],
"configuration_version": "1"
}

View file

@ -2,9 +2,9 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:fresco="http://schemas.android.com/apk/res-auto"
android:id="@+id/fullprofile_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/fullprofile_scrollview">
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
@ -187,10 +187,10 @@
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:id="@+id/costume_group"
style="@style/CardView.Default"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/costume_group">
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
@ -233,10 +233,10 @@
android:weightSum="1">
<TableLayout
android:id="@+id/attributes_table"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stretchColumns="0,1,2,3,4"
android:id="@+id/attributes_table">
android:stretchColumns="0,1,2,3,4">
<TableRow android:layout_height="wrap_content">
<!-- Headers -->
@ -279,5 +279,38 @@
android:indeterminate="true" />
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
style="@style/CardView.Default"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="5dp"
android:weightSum="1">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Pets &amp; Mounts"
android:textSize="@dimen/abc_text_size_title_material"
android:textStyle="bold" />
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stretchColumns="0,1,2,3,4">
<TableRow android:layout_height="wrap_content">
</TableRow>
</TableLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</ScrollView>

View file

@ -9,6 +9,11 @@
android:icon="@drawable/ic_action_chat"
android:title="@string/send_pm" />
<group>
<item
android:id="@+id/menu.chat.copy"
android:icon="@drawable/ic_action_warning"
app:showAsAction="always|withText"
android:title="@string/copy_chat_message" />
<item
android:id="@+id/menu.chat.flag"
android:icon="@drawable/ic_action_warning"

View file

@ -0,0 +1,20 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="sidebar.tasks">Задачи</string>
<string name="sidebar.skills">Умения</string>
<string name="sidebar.section.social">Общност</string>
<string name="sidebar.inbox">Входящи</string>
<string name="sidebar.tavern">Кръчма</string>
<string name="sidebar.party">Група</string>
<string name="sidebar.purchaseGems">Купуване на диаманти</string>
<string name="sidebar.guilds">Гилдии</string>
<string name="sidebar.challenges">Предизвикателства</string>
<string name="sidebar.section.inventory">Чанта</string>
<string name="sidebar.avatar">Герой</string>
<string name="sidebar.equipment">Екипировка</string>
<string name="sidebar.stable">Конюшня</string>
<string name="sidebar.news">Новини</string>
<string name="sidebar.settings">Настройки</string>
<string name="sidebar.about">Относно</string>
<string name="sidebar_shops">Магазини</string>
</resources>

View file

@ -0,0 +1,14 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="tutorial_habits">Отбелязвайте положителните си навици, за да получавате злато и опит! Отрицателните навици ще наранят героя Ви, ако ги докоснете, така че ги избягвайте в истинския живот!</string>
<string name="tutorial_dailies">Побеждавайте своите повтарящи се ежедневни задачи, за да печелите злато и опит. Внимание! Ежедневните задачи ще наранят героя Ви, ако не ги изпълните навреме.</string>
<string name="tutorial_todos">Завършвайте задачите си за изпълнение в истинския живот, а след това ги отбелязвайте тук за ЗЛАТО и ОПИТ; така ще можете да отключвате награди и нови функционалности!</string>
<string name="tutorial_rewards">Това са наградите Ви! Печелете злато като изпълнявате истинските си навици, ежедневни задачи и задачи за изпълнение. След това го харчете за награди в играта или персонализирани такива за истинския живот!</string>
<string name="tutorial_equipment">Когато купувате екипировка, тя се появява тук. Бойното снаряжение променя атрибутите Ви, а костюмът (ако е включен) влияе на това какво носи героят Ви.</string>
<string name="tutorial_items">Печелете предмети като изпълнявате задачи и качвате нива. Докоснете предмет, за да го използвате!</string>
<string name="tutorial_pets">Когато изпълнявате задачите си в истинския живот, имате шанс на случаен принцип да Ви се паднат яйца и отвари. Комбинирайте ги, за да си излюпите любимци.</string>
<string name="tutorial_skills">Уменията са специални способности, които имат мощни ефекти! Докоснете умение, за да го използвате. Това ще Ви струва малко мана (синята лента), която се възстановява при влизане в играта всеки ден, както и като изпълнявате задачите от истинския си живот. Прегледайте ЧЗВ в менюто за повече информация!</string>
<string name="tutorial_party">Тук е мястото, където заедно с приятелите си можете взаимно да се държите отговорни за изпълнението на целите си, както и да се биете срещу чудовища със задачите си!</string>
<string name="tutorial_tavern">Добре дошли в кръчмата — обществено място за разговори, достъпно за хора от всички възрасти! Тук можете да разговаряте за продуктивността си и да задавате въпроси. Приятно прекарване!</string>
<string name="tutorial_classes">Изберете дали да бъдете воин, магьосник, лечител или мошеник! Всеки клас има уникална екипировка и умения. Докоснете (?), за да научите повече!</string>
</resources>

View file

@ -0,0 +1,344 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="app_name">Хабитика</string>
<string name="action_refresh">Опресняване</string>
<string name="XP_default">Опит</string>
<string name="HP_default">Здраве</string>
<string name="MP_default">Мана</string>
<string name="ERR_pb_barcode">Настройките не могат да бъдат заредени от кода</string>
<!--Prefs-->
<string name="PS_settings_title">Настройки</string>
<string name="SP_address_hint">Вашият персонализиран сървър</string>
<string name="SP_userID_title">Потребителски идентификатор</string>
<string name="SP_userID_summary">Вашият потребителски идентификатор</string>
<string name="SP_APIToken_title">Жетон за ППИ</string>
<string name="SP_APIToken_summary">Вашият жетон за ППИ</string>
<string name="PS_contact_title">Свържете се с мен</string>
<string name="unknown_error">Възникна грешка…</string>
<string name="pref_account_header">Профил</string>
<string name="pref_reminder_header">Ежедневно напомняне</string>
<string name="pref_reminder_checkbox">Включване на напомнянето</string>
<string name="pref_reminder_picker">Задаване на времето за напомняне</string>
<!--Adding tasks-->
<string name="task_value">Стойност</string>
<string name="new_todo">Нова задача за изпълнение</string>
<string name="new_reward">Нова награда</string>
<string name="new_daily">Нова ежедневна задача</string>
<string name="new_habit">Нов навик</string>
<string name="action_edit">Редактиране</string>
<string name="action_delete">Изтриване</string>
<string name="action_cancel">Отказ</string>
<string name="login_btn">Вход</string>
<string name="register_btn">Регистрация</string>
<string name="username">Потребителско име</string>
<string name="email_username">Е-поща или потребителско име</string>
<string name="password">Парола</string>
<string name="emailAddress">Е-поща</string>
<string name="confirmpassword">Потвърдете паролата</string>
<string name="logout">Изход</string>
<string name="logout_description">Излизане от Вашия профил</string>
<string name="account_details">Подробности за профила</string>
<string name="LoginActivityName">Добре дошли</string>
<string name="string_revive">Съживяване</string>
<string name="please_connect">Моля, свържете се чрез приложението, преди да използвате джаджа</string>
<string name="about.title">Относно</string>
<string name="about.libraries">Библиотеки</string>
<string name="about.versionhistory">История на версиите</string>
<string name="about.habitica_open_source">Хабитика е софтуер с отворен код, който е наличен в Github</string>
<string name="about.rate_our_app">Оценете приложението</string>
<string name="about.give_us_feedback">Изпратете ни отзивите си!</string>
<string name="about.bugreport">Докладване на грешка</string>
<string name="about.source_code">Изходен код</string>
<!--Network Errors-->
<string name="network_error_title">Грешка при свързване</string>
<string name="network_error_no_network_body">Нямате връзка с Интернет.</string>
<string name="internal_error_api">Изглежда има проблем със сървъра. Опитайте отново по-късно.</string>
<string name="authentication_error_title">Грешка при удостоверяване</string>
<string name="authentication_error_body">Потребителското име и/или паролата е грешна.</string>
<string name="login_validation_error_title">Грешка при проверяването</string>
<string name="login_validation_error_fieldsmissing">Трябва да попълните всички полета.</string>
<string name="save_changes">Запазване</string>
<string name="notes">Бележки</string>
<string name="text">Текст</string>
<string name="difficulty">Трудност</string>
<string name="tags">Етикети</string>
<string name="trivial">Елементарно</string>
<string name="easy">Лесно</string>
<string name="medium">Средно</string>
<string name="hard">Трудно</string>
<string name="start_date">Начална дата</string>
<string name="positive_habit_form">Положителен ( + )</string>
<string name="negative_habit_form">Отрицателен ( - )</string>
<string name="checklist">Подзадачи</string>
<string name="reminders">Напомняния</string>
<string name="actions">Действия</string>
<string name="attributes">Атрибути</string>
<string name="physical">Физическа</string>
<string name="mental">Умствена</string>
<string name="social">Обществена</string>
<string name="other">Други</string>
<string name="frequency">Честота</string>
<string name="frequency_weekly">В определени дни от седмицата</string>
<string name="frequency_daily">На всеки Х дни</string>
<string name="monday">Понеделник</string>
<string name="tuesday">Вторник</string>
<string name="wednesday">Сряда</string>
<string name="thursday">Четвъртък</string>
<string name="friday">Петък</string>
<string name="saturday">Събота</string>
<string name="sunday">Неделя</string>
<string name="levelup_header">Вдигнахте ниво!</string>
<string name="levelup_detail">Изпълнявайки целите си в истинския живот, Вие достигнахте ниво %1$d!</string>
<string name="levelup_health">Вие бяхте излекуван(а) напълно!</string>
<string name="levelup_button">Ура!</string>
<string name="faint_subtitle">Не се отчайвайте!</string>
<string name="faint_penalty_body">Изгубихте ниво, всичкото си злато и един предмет от екипировката си, но можете да си ги върнете с усърдна работа! Късмет — ще се справите.</string>
<string name="faint_header">Здравето Ви свърши!</string>
<string name="faint_button">Възстановете здравето си и опитайте отново</string>
<string name="filter">Филтър</string>
<string name="profile_image">Профилно изображение</string>
<string name="mana_price_button" formatted="false">%d ТМ</string>
<string name="used_skill" formatted="false">Използвахте %1$s за %2$d точки мана.</string>
<string name="new_checklist_item">нова подзадача</string>
<string name="add_checklist_item">Добавяне</string>
<string name="reminder_title">Не забравяйте да изпълнявате ежедневните си задачи!</string>
<string name="skill_progress_title">Използване на умение</string>
<string name="coming_soon">Очаквайте скоро</string>
<string name="chat_flag_confirmation">Наистина ли искате да докладвате това съобщение за нарушение?</string>
<string name="flag_confirm">Докладване на съобщението</string>
<string name="unlock_lvl_11">Оключва се на ниво 11</string>
<string name="no_party_message">Вие не участвате в група. За да се присъедините към група, моля, посетете уеб сайта.</string>
<string name="forgot_pw_btn">Забравена парола</string>
<string name="forgot_pw_tv">Забравена парола? Моля, използвайте мобилния уеб сайт.</string>
<string name="tavern.inn.checkOut">Включете отново ежедневните си задачи</string>
<string name="tavern.inn.rest">Пауза на ежедн.</string>
<string name="reward.dialog.buy">Купуване</string>
<string name="reward.dialog.dismiss">Премахване</string>
<string name="party">Група</string>
<string name="chat">Чат</string>
<string name="members">Членове</string>
<string name="habits">Навици</string>
<string name="dailies">Ежедневни</string>
<string name="todos">Задачи</string>
<string name="rewards">Награди</string>
<string name="taskform.delete.title">Сигурен/на ли сте?</string>
<string name="taskform.delete.message">Наистина ли искате да изтриете това?</string>
<string name="yes">Да</string>
<string name="no">Не</string>
<string name="quest">Мисия</string>
<string name="avatar_body">Тяло</string>
<string name="avatar_hair">Коса</string>
<string name="avatar_shirt">Риза</string>
<string name="avatar_skin">Кожа</string>
<string name="avatar_ears">Животински уши</string>
<string name="avatar_base">Основа</string>
<string name="avatar_color">Цвят</string>
<string name="avatar_beard">Брада</string>
<string name="avatar_mustache">Мустаци</string>
<string name="avatar_flower">Цвете</string>
<string name="avatar_bangs">Бретон</string>
<string name="avatar_nothing">Няма нищо</string>
<string name="avatar_size">Размер</string>
<string name="avatar_size_slim">Слаб</string>
<string name="avatar_size_broad">Широк</string>
<string name="avatar_background">Фон</string>
<string name="purchase_customization">Купуване на персонализация</string>
<string name="purchase_set_button" formatted="false">Отключване на комплекта за %d диамант(а)</string>
<string name="purchase_button">Купуване</string>
<string name="purchase_set_title" formatted="false">Купуване на комплекта %s</string>
<string name="due_date">Крайна дата</string>
<string name="quest.accept">Приемане</string>
<string name="quest.reject">Отказване</string>
<string name="quest.begin">Започване на мисия</string>
<string name="quest.cancel">Отказване на поканата</string>
<string name="quest.abort">Прекратяване на мисията</string>
<string name="quest.leave">Напускане на мисията</string>
<string name="quest.pending">В очакване</string>
<string name="quest.accepted">Приета</string>
<string name="quest.rejected">Отказана</string>
<string name="quest.participants">Участници</string>
<string name="version_info" formatted="false">Версия %s (%d)</string>
<string name="sidebar_help">Помощ и ЧЗВ</string>
<string name="complete_tutorial">Разбрано!</string>
<string name="dismiss_tutorial">Напомнете ми отново</string>
<string name="intro_1_title">Добре дошли в Хабитика</string>
<string name="intro_1_description">Над 1 100 000 хора се забавляват, докато вършат нещата си. Присъединете се към тях! Създайте си герой и следете задачите от истинския си живот.</string>
<string name="intro_2_title">Напредък в играта = напредък в живота</string>
<string name="intro_2_description">Отключвайте функционалностите на играта като изпълнявате задачите в истинския си живот. Печелете екипировка, любимци и още, като награда за постигането на целите си!</string>
<string name="intro_3_title">Общност и битки с чудовища</string>
<string name="intro_3_description">Приятелите Ви могат да Ви помогнат да постигнете целите си. Подкрепяйте се взаимно в живота и битките, за да израснете заедно!</string>
<string name="intro_finish_button">Започване</string>
<string name="previous_button">Назад</string>
<string name="next_button">Напред</string>
<string name="skip_button">Пропускане</string>
<string name="setup_welcome_description">Добре дошли в Хабитика, където напредването в играта ще подобри истинския Ви живот! Докато изпълнявате реалните си цели, ще отключвате екипировка, любимци, мисии и още.</string>
<string name="setup_welcome_title">Добре дошли</string>
<string name="avatar_setup_description">Първо, ще Ви трябва герой, който да Ви представлява в играта! Нещата, които вършите в истинския си живот, ще се отразяват на здравето, нивото и златото на героя Ви.</string>
<string name="task_setup_description">Чудесно! А сега да създадем няколко задачи, за да започнете да печелите опит и злато.
Кои области от живота си искате да подобрите?</string>
<string name="setup_group_work">Работа</string>
<string name="setup_group_exercise">Упражнения</string>
<string name="setup_group_heathWellness">Здравословно състояние</string>
<string name="setup_group_school">Училище</string>
<string name="setup_group_teams">Екипи</string>
<string name="setup_group_chores">Домакинска работа</string>
<string name="setup_group_creativity">Изобретателност</string>
<string name="setup_task_work_1">Обработка на е-поща</string>
<string name="setup_task_work_2">Най-важната задача</string>
<string name="setup_task_work_3">Работен проект</string>
<string name="setup_task_exercise_1">10 мин аеробни упражнения</string>
<string name="setup_task_exercise_2">Разтягане</string>
<string name="setup_task_exercise_3">Да организирам тренировките си</string>
<string name="setup_task_healthWellness_1">Да ям полезна/вредна храна</string>
<string name="setup_task_healthWellness_2">Да изчистя зъбите си с конец</string>
<string name="setup_task_healthWellness_3">Да се запиша за преглед</string>
<string name="setup_task_school_1">Учене/отлагане</string>
<string name="setup_task_school_2">Да напиша домашното си</string>
<string name="setup_task_school_3">Да завърша проекта за училище</string>
<string name="setup_task_teams_1">Да проверя какво прави екипът</string>
<string name="setup_task_teams_2">Да уведомя екипа за напредъка си</string>
<string name="setup_task_teams_3">Да завърша екипния проект</string>
<string name="setup_task_chores_1">10 минути чистене</string>
<string name="setup_task_chores_2">Да измия чиниите</string>
<string name="setup_task_chores_3">Да подредя килера</string>
<string name="setup_task_creativity_1">Да се уча от майстор в занаята</string>
<string name="setup_task_creativity_2">Да работя по проекта си</string>
<string name="setup_task_creativity_3">Да завърша проекта си</string>
<string name="gem.purchase.support">Искате ли да помогнете на Хабитика да продължи да съществува? Можете да подкрепите разработчиците като купите диаманти!\n\nС диамантите можете да си купувате забавни неща за профила си, като: \n\n — Интересни костюми за героя си;\n — Страхотни фонове;\n — Забавни мисии, от които ще получите яйца за любимци;\n — Възможността да променяте класа си преди да достигнете ниво 100.\n\nБлагодарим Ви за това, че помагате на Хабитика да се развива. Подкрепата Ви означава много за нас!</string>
<string name="my_guilds">Моите гилдии</string>
<string name="public_guilds">Обществени гилдии</string>
<string name="guild">Гилдия</string>
<string name="leave">Напускане</string>
<string name="join">Присъединяване</string>
<string name="leader">Водач</string>
<string name="gems">Диаманти</string>
<string name="copy_as_todo">Копиране като задача</string>
<string name="send_pm">Изпращане на ЛС</string>
<string name="flag">Докладване</string>
<string name="delete">Изтриване</string>
<string name="name">Име</string>
<string name="description">Описание</string>
<string name="add_tag">Добавяне на етикет</string>
<string name="privacy">Поверителност</string>
<string name="write_message">Напишете съобщение</string>
<string name="post">Изпр.</string>
<string name="todo_due" formatted="false">Срок: %s</string>
<string name="daily_streak" formatted="false">текуща серия: %d</string>
<string name="todo_has_duedate">Задачата има краен срок</string>
<string name="battle_gear">Бойно снаряжение</string>
<string name="costume">Костюм</string>
<string name="outfit_head">Глава</string>
<string name="outfit_headAccessory">Аксесоар за глава</string>
<string name="outfit_eyewear">Предмет за очи</string>
<string name="outfit_armor">Броня</string>
<string name="outfit_back">Гръб</string>
<string name="outfit_body">Тяло</string>
<string name="outfit_shield">Щит</string>
<string name="outfit_weapon">Оръжие</string>
<string name="wear_costume">Костюм</string>
<string name="equipped">Екипирано</string>
<string name="quest_cancel_message">Наистина ли искате да прекратите мисията? Всички приети покани ще бъдат загубени. Притежателят на мисията ще си запази свитъка ѝ.</string>
<string name="quest.invitation">Покана за мисия</string>
<string name="quest_begin_message">Наистина ли искате да започнете мисията? След като мисията започне, никой повече няма да може да се присъедини към нея.</string>
<string name="quest.invitation.text">Получихте покана за участие в мисия!</string>
<string name="ago_1day">Преди 1 ден</string>
<string name="ago_days" formatted="false">Преди %d дни</string>
<string name="ago_1Minute">Преди 1 минута</string>
<string name="ago_minutes" formatted="false">Преди %d минути</string>
<string name="ago_hours" formatted="false">Преди %d часа</string>
<string name="ago_1hour">Преди 1 час</string>
<string name="today">Днес</string>
<string name="sidebar_items">Предмети</string>
<string name="eggs">Яйца</string>
<string name="hatching_potions">Излюпващи отвари</string>
<string name="food">Храна</string>
<string name="quests">Мисии</string>
<string name="pets">Любимци</string>
<string name="mounts">Превози</string>
<string name="armoireEquipment" formatted="false">Намерихте рядък предмет в гардероба: %s! Страхотно!</string>
<string name="armoireFood" formatted="false">Тършувате из гардероба и намирате %1$s %2$s. Какво ли прави това там?</string>
<string name="armoireExp">Сборвате се с гардероба и получавате опит. Така му се пада!</string>
<string name="armoireNotesFull" formatted="false">Отворете гардероба, за да получите случайно избрана специална екипировка, опит или храна! Оставащи предмети: %d</string>
<string name="armoireLastItem">Намерихте последния рядък предмет за екипиране в омагьосания гардероб.</string>
<string name="armoireNotesEmpty">В гардероба ще се появяват нови предмети през първата седмица на всеки месец. До тогава продължавайте да щракате за опит и храна!</string>
<string name="sell" formatted="false">Продаване (%s злато)</string>
<string name="hatch_with_potion">Излюпване с отвара</string>
<string name="feed_to_pet">Даване на любимец</string>
<string name="hatch_egg">Излюпване с яйце</string>
<string name="invite_party">Поканване на групата</string>
<string name="dialog_feeding" formatted="false">Хранене на %1$s %2$s с:</string>
<string name="use_animal">Използване</string>
<string name="feed">Хранене</string>
<string name="hatch_with" formatted="false">Излюпване на любимец с %s</string>
<string name="hatched_pet_title" formatted="false">Вие излюпихте %1$s %2$s!</string>
<string name="close">Затваряне</string>
<string name="share">Споделяне</string>
<string name="notification_pet_fed" formatted="false">Вие нахранихте своя(та/то) %1$s %2$s!</string>
<string name="notification_purchase" formatted="false">%s беше закупен(а/о)</string>
<string name="sectionpets">Обикновени любимци</string>
<string name="sectionpremiumPets">Любимци от магически отвари</string>
<string name="sectionquestPets">Любимци от мисии</string>
<string name="sectionspecialPets">Редки любимци</string>
<string name="sectionmounts">Обикновени превози</string>
<string name="sectionpremiumMounts">Превози от магически отвари</string>
<string name="sectionquestMounts">Превози от мисии</string>
<string name="sectionspecialMounts">Редки превози</string>
<string name="world_quest">Световна мисия</string>
<string name="inn_description">Починете в странноприемницата, за да не могат да Ви наранят неизпълнените Ви ежедневни задачи! (Забележка: това не Ви защитава от получаването на щети от главатари.)</string>
<string name="empty_items" formatted="false">Нямате %s</string>
<string name="user_level" formatted="false">Ниво %s</string>
<string name="user_level_with_class" formatted="false">Ниво %1$s - %2$s</string>
<string name="warrior">Воин</string>
<string name="rogue">Мошеник</string>
<string name="mage">Магьосник</string>
<string name="healer">Лечител</string>
<string name="warrior_description">Воините нанасят повече и по-силни „критични удари“, които на случаен принцип дават злато, опит и шанс за падане на предмет при изпълнение на задача. Те също така нанасят сериозни щети на чудовищата-главатари. Играйте като воин, ако Ви мотивират изненадващите награди, или ако искате да раздавате правосъдие в мисиите с главатари.</string>
<string name="mage_description">Магьосниците се учат лесно, тъй като придобиват опит и нива по-бързо от останалите класове. Те имат много мана за специалните си умения. Играйте като магьосник, ако обичате тактическите страни на навиците, или ако се мотивирате от вдигане на нива и отключване на специални функционалности!</string>
<string name="rogue_description">Мошениците обичат да трупат богатства, печелят повече злато повече от останалите и са майстори в намирането на случайни предмети. Отличителното им умение „Невидимост“ им позволява да избегнат последствията от пропуснати ежедневни задачи. Играйте като мошеник, ако Ви мотивират наградите и постиженията, и обичате плячката и значките!</string>
<string name="healer_description">Лечителите трудно могат да бъдат наранени, и разпростират защитата си върху останалите. Пропуснатите ежедневни задачи и лошите навици не ги смущават толкова много; те винаги могат да възстановят здравето си след провал. Играйте като лечител, ако обичате да помагате на останалите в групата си, или ако искате да изиграете смъртта чрез усърдна работа!</string>
<string name="select_class">Изберете клас</string>
<string name="opt_out_class">Отказване</string>
<string name="opt_out_description">Не Ви се занимава с класове? Искате да изберете по-късно? Откажете се от тях — ще бъдете воин без специални умения. Можете да прочетете относно класовата система по-късно в уикито, както и да включите класовете когато пожелаете.</string>
<string name="class_confirmation" formatted="false">Наистина ли искате да бъдете %s?</string>
<string name="choose_class">Избиране на класа</string>
<string name="dialog_go_back">Назад</string>
<string name="opt_out_confirmation">Наистина ли искате да се откажете?</string>
<string name="change_class">Промяна на класа</string>
<string name="change_class_description">Променете класа си и възстановете атрибутните си точки за 3 диаманта. Можете да разпределите атрибутните си точки през уеб сайта</string>
<string name="enable_class">Включване на класовата система</string>
<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_id_description">Ако имате приятели, които вече използват Хабитика, поканете ги чрез потребителски идентификатор тук.</string>
<string name="invite_email_description">Ако приятел се присъедини към Хабитика през Вашето е-писмо, той автоматично ще бъде поканен в групата Ви!</string>
<string name="add_invites">Добавяне на покани</string>
<string name="user_id">Потребителски ид.</string>
<string name="email">Е-поща</string>
<string name="invite_users">Поканване в групата</string>
<string name="share_using">Споделяне чрез</string>
<string name="share_levelup" formatted="false">Достигнах ниво %s в Хабитика, като подобрих навиците си в истинския живот!</string>
<string name="share_hatched" formatted="false">Току-що излюпих любимец — %1$s %2$s в Хабитика, като изпълнявах задачите си в истинския живот!</string>
<string name="share_raised" formatted="false">Току-що се сдобих с превоз — %1$s %2$s в Хабитика, като изпълнявах задачите си в истинския живот!</string>
<string name="open_in_store">Отваряне в магазина</string>
<string name="change_class_confirmation">Наистина ли искате да промените класа си? Това ще Ви струва 3 диаманта.</string>
<string name="leaderMessage" formatted="false">Съобщение от %1$s</string>
<string name="confirm">Потвърждаване</string>
<string name="market">Пазар</string>
<string name="timeTravelers">Пътешественици във времето</string>
<string name="seasonalShop">Сезонен магазин</string>
<string name="empty_inbox">Нямате входящи съобщения. Можете да изпратите ново съобщение на потребител от екрана с неговите публични съобщения в чата!</string>
<string name="party_invite">Отключете, като поканите приятели</string>
<string name="no_gold">Нямате достатъчно злато</string>
<string name="no_potion">Няма нужда да купувате лечебна отвара</string>
<string name="successful_purchase" formatted="false">Закупено успешно: %1$s</string>
<string name="purchase_confirmation_title">Потвърждаване на покупката</string>
<string name="confirm_purchase_text" formatted="false">Купуване на %1$s за %2$s %3$s</string>
<string name="gem">диамант</string>
<string name="hourglass">пясъчен часовник</string>
<string name="hourglasses">пясъчни часовници</string>
<string name="gold_singular">злато</string>
<string name="gems_plural">злато</string>
</resources>

View file

@ -16,4 +16,5 @@
<string name="sidebar.news">Neuigkeiten</string>
<string name="sidebar.settings">Einstellungen</string>
<string name="sidebar.about">Über</string>
<string name="sidebar_shops">Märkte</string>
</resources>

View file

@ -53,7 +53,7 @@
<string name="network_error_title">Verbindungsfehler</string>
<string name="network_error_no_network_body">Keine Internetverbindung.</string>
<string name="internal_error_api">Es scheint ein Problem mit dem Server zu geben. Versuch es später noch mal.</string>
<string name="authentication_error_title">Autentifikationsfehler</string>
<string name="authentication_error_title">Authentifikationsfehler</string>
<string name="authentication_error_body">Ungültiger Benutzername und/oder Passwort</string>
<string name="login_validation_error_title">Validierungsfehler</string>
<string name="login_validation_error_fieldsmissing">Bitte fülle alle Felder aus.</string>
@ -166,14 +166,14 @@
<string name="intro_2_title">Spielfortschritt = Lebensfortschritt</string>
<string name="intro_2_description">Schalte zusätzliche Spielinhalte frei, indem Du Aufgaben im echten Leben erledigst. Verdiene Dir Ausrüstung, Haustiere und mehr als Belohnung für die Erfüllung Deiner Ziele!</string>
<string name="intro_3_title">Werde sozial und kämpfe gegen Monster</string>
<string name="intro_3_description">Behalte Deine Ziele im Auge mit Hilfe Deiner Freunde. Unterstützt Euch gegenseitig im Leben und im Kampf während Ihr Euch gemeinsam verbessert!</string>
<string name="intro_3_description">Behalte Deine Ziele im Auge mit Hilfe Deiner Freunde. Unterstützt Euch gegenseitig im Leben und im Kampf, während Ihr Euch gemeinsam verbessert!</string>
<string name="intro_finish_button">Anfangen</string>
<string name="previous_button">Zurück</string>
<string name="next_button">Weiter</string>
<string name="skip_button">Überspringen</string>
<string name="setup_welcome_description">Willkommen zu Habitica! Hier bedeutet Fortschritt im Spiel gleichzeitig Fortschritt im echten Leben! Während Du Ziele im echten Leben erreichst, wirst Du Ausrüstung, Haustiere, Quests und vieles mehr freischalten.</string>
<string name="setup_welcome_title">Willkommen</string>
<string name="avatar_setup_description">Als erstes benötigst Du einen Avater, der Dich repräsentiert! Dinge die Du im echten Leben tust beeinflussen Deine Lebenspunkte, Dein Level und Dein Gold.</string>
<string name="avatar_setup_description">Als erstes benötigst Du einen Avatar, der Dich repräsentiert! Dinge die Du im echten Leben tust beeinflussen Deine Lebenspunkte, Dein Level und Dein Gold.</string>
<string name="task_setup_description">Großartig! Jetzt lass uns Deine Aufgaben anlegen, damit Du anfangen kannst Erfahrung zu sammeln und Gold zu bekommen.
Für den Anfang: Welche Bereiche Deines Lebens möchtest Du verbessern?</string>
@ -330,4 +330,15 @@ Für den Anfang: Welche Bereiche Deines Lebens möchtest Du verbessern?</string>
<string name="timeTravelers">Mysteriöse Zeitreisende</string>
<string name="seasonalShop">Jahreszeitenmarkt</string>
<string name="empty_inbox">Du hast keine Nachrichten in Deinem Posteingang. Du kannst einem anderen Benutzer eine neue Nachricht von deren öffentlichen Chatnachrichten aus senden!</string>
<string name="party_invite">Durch Einladen von Freunden freischalten</string>
<string name="no_gold">Nicht genügend Gold</string>
<string name="no_potion">Du musst keinen Heiltrank kaufen</string>
<string name="successful_purchase" formatted="false">%1$s gekauft</string>
<string name="purchase_confirmation_title">Kauf bestätigen</string>
<string name="confirm_purchase_text" formatted="false">%1$s für %2$s %3$s kaufen</string>
<string name="gem">Edelstein</string>
<string name="hourglass">Sanduhr</string>
<string name="hourglasses">Sanduhren</string>
<string name="gold_singular">Gold</string>
<string name="gems_plural">Gold</string>
</resources>

View file

@ -16,4 +16,5 @@
<string name="sidebar.news">Noticias</string>
<string name="sidebar.settings">Ajustes</string>
<string name="sidebar.about">Información</string>
<string name="sidebar_shops">Tiendas</string>
</resources>

View file

@ -325,4 +325,9 @@ Para empezar, ¿qué aspectos de tu vida te gustaría mejorar?</string>
<string name="open_in_store">Abrir en Play Store</string>
<string name="change_class_confirmation">¿Seguro que quieres cambiar de clase? Te costará 3 gemas.</string>
<string name="leaderMessage" formatted="false">Mensaje de %1$s</string>
<string name="confirm">Confirmar</string>
<string name="market">Mercado</string>
<string name="timeTravelers">Viajeros del tiempo</string>
<string name="seasonalShop">Tienda estacional</string>
<string name="empty_inbox">No tienes ningún mensaje en la bandeja de entrada. Puedes enviar un mensaje a un usuario desde los mensajes de chat públicos que hayan enviado.</string>
</resources>

View file

@ -329,5 +329,16 @@ Pour commencer, quels aspects de votre vie souhaitez-vous améliorer ?</string>
<string name="market">Marché</string>
<string name="timeTravelers">Voyageurs temporels</string>
<string name="seasonalShop">Boutique saisonnière</string>
<string name="empty_inbox">Vous n\'avez pas de message dans la boite de réception. Vous pouvez envoyer un nouveau message à partir des discussions publiques de chaque personne.</string>
<string name="empty_inbox">Vous n\'avez pas de message dans votre boîte de réception. Vous pouvez envoyer un nouveau message à partir des messages de discussion publics de chaque personne.</string>
<string name="party_invite">Débloquer en invitant des ami•e•s</string>
<string name="no_gold">Pas assez d\'or</string>
<string name="no_potion">Vous n\'avez pas besoin d\'acheter une potion de santé</string>
<string name="successful_purchase" formatted="false">%1$s acheté avec succès</string>
<string name="purchase_confirmation_title">Confirmer l\'achat</string>
<string name="confirm_purchase_text" formatted="false">Acheter %1$s for %2$s %3$s</string>
<string name="gem">gemme</string>
<string name="hourglass">sablier</string>
<string name="hourglasses">sabliers</string>
<string name="gold_singular">or</string>
<string name="gems_plural">or</string>
</resources>

View file

@ -16,4 +16,5 @@
<string name="sidebar.news">お知らせ</string>
<string name="sidebar.settings">設定</string>
<string name="sidebar.about">Habitica について</string>
<string name="sidebar_shops">ショップ</string>
</resources>

View file

@ -326,4 +326,20 @@
<string name="open_in_store">Play ストアで開く</string>
<string name="change_class_confirmation">クラスを変更します。いいですか? 3 ジェムかかります。</string>
<string name="leaderMessage" formatted="false">%1$s からのメッセージ</string>
<string name="confirm">確認</string>
<string name="market">市場</string>
<string name="timeTravelers">タイムトラベラー</string>
<string name="seasonalShop">期間限定ショップ</string>
<string name="empty_inbox">受信箱にメッセージがありません。Habitica ユーザーあてにメッセージを送ることができます。公開チャットに投稿しているユーザーにメッセージを送ってみてはどうですか?</string>
<string name="party_invite">友達を招待するとアンロック</string>
<string name="no_gold">ゴールドが足りません</string>
<string name="no_potion">体力回復の薬を買う必要がありません</string>
<string name="successful_purchase" formatted="false">%1$s を買いました</string>
<string name="purchase_confirmation_title">購入の確認</string>
<string name="confirm_purchase_text" formatted="false">%2$s %3$s で %1$s を買う</string>
<string name="gem">ジェム</string>
<string name="hourglass">砂時計</string>
<string name="hourglasses">砂時計</string>
<string name="gold_singular">ゴールド</string>
<string name="gems_plural">ゴールド</string>
</resources>

View file

@ -330,4 +330,15 @@ Om te beginnen, welke delen van je leven wil je verbeteren?</string>
<string name="timeTravelers">Tijd Reizigers</string>
<string name="seasonalShop">Seizoenswinkel</string>
<string name="empty_inbox">Je hebt geen Inbox berichten. Je kan een gebruiker een nieuw bericht sturen vanuit hun publieke chat berichten.</string>
<string name="party_invite">Ontgrendel door vrienden uit te nodigen</string>
<string name="no_gold">Niet genoeg goud</string>
<string name="no_potion">Je hoeft geen gezondheidsdrankje te kopen</string>
<string name="successful_purchase" formatted="false">%1$s succesvol gekocht</string>
<string name="purchase_confirmation_title">Bevestig aankoop</string>
<string name="confirm_purchase_text" formatted="false">Koop %1$s voor %2$s %3$s</string>
<string name="gem">edelsteen</string>
<string name="hourglass">zandloper</string>
<string name="hourglasses">zandlopers</string>
<string name="gold_singular">goud</string>
<string name="gems_plural">goud</string>
</resources>

View file

@ -326,4 +326,12 @@ Para começar, quais partes da sua vida você quer melhorar?</string>
<string name="open_in_store">Abra na Play Store</string>
<string name="change_class_confirmation">Você tem certeza que quer mudar sua classe? Isso irá custar 3 gemas.</string>
<string name="leaderMessage" formatted="false">Mensagem de %1$s</string>
<string name="confirm">Confirmar</string>
<string name="market">Mercado</string>
<string name="no_potion">Você não precisa comprar uma poção de vida</string>
<string name="successful_purchase" formatted="false">%1$s comprado com sucesso</string>
<string name="purchase_confirmation_title">Confirmar compra</string>
<string name="gem">gema</string>
<string name="gold_singular">ouro</string>
<string name="gems_plural">ouro</string>
</resources>

View file

@ -16,4 +16,5 @@
<string name="sidebar.news">新聞</string>
<string name="sidebar.settings">設定</string>
<string name="sidebar.about">關於</string>
<string name="sidebar_shops">商店</string>
</resources>

View file

@ -113,7 +113,7 @@
<string name="tavern.inn.checkOut">重啟每日任務</string>
<string name="tavern.inn.rest">暫停每日任務</string>
<string name="reward.dialog.buy">購買</string>
<string name="reward.dialog.dismiss">解散</string>
<string name="reward.dialog.dismiss">取消</string>
<string name="party">隊伍</string>
<string name="chat">聊天</string>
<string name="members">成員</string>
@ -322,5 +322,12 @@
<string name="share_levelup" formatted="false">我在Habitica中通過改善我現實生活中的習慣到達了%s級</string>
<string name="share_hatched" formatted="false">我剛剛在Habitica中通完成我現實生活中的任務孵化了一隻寵物%1$s %2$s</string>
<string name="share_raised" formatted="false">我剛剛在Habitica中通完成我現實生活中的任務得到了一匹坐騎%1$s %2$s</string>
<string name="open_in_store">開啟Play sotre</string>
<string name="change_class_confirmation">你確定要變更職業嗎這會花費3寶石。</string>
<string name="leaderMessage" formatted="false">訊息來自%1$s</string>
<string name="confirm">確認</string>
<string name="market">市集</string>
<string name="timeTravelers">時光旅人</string>
<string name="seasonalShop">季節限定商店</string>
<string name="empty_inbox">你的收件匣裡面沒有任何信息。你可以利用聊天去傳送訊息給大家</string>
</resources>

View file

@ -16,4 +16,5 @@
<string name="sidebar.news">新闻</string>
<string name="sidebar.settings">设置</string>
<string name="sidebar.about">关于</string>
<string name="sidebar_shops">商店</string>
</resources>

View file

@ -325,4 +325,20 @@
<string name="open_in_store">在应用商店开放</string>
<string name="change_class_confirmation">你确定想要转变职业吗这将花费3颗宝石。</string>
<string name="leaderMessage" formatted="false">从%1$s来的消息</string>
<string name="confirm">确认</string>
<string name="market">市场</string>
<string name="timeTravelers">时光旅行</string>
<string name="seasonalShop">四季商店</string>
<string name="empty_inbox">收件箱里没有消息。您可以在用户的公共聊天窗口中给他发送新消息!</string>
<string name="party_invite">通过邀请朋友来解锁</string>
<string name="no_gold">没有足够的金币</string>
<string name="no_potion">你不需要购买生命药水</string>
<string name="successful_purchase" formatted="false">已购买 %1$s</string>
<string name="purchase_confirmation_title">确认购买</string>
<string name="confirm_purchase_text" formatted="false">为 %2$s %3$s 购买 %1$s</string>
<string name="gem">宝石</string>
<string name="hourglass">沙漏</string>
<string name="hourglasses">沙漏</string>
<string name="gold_singular">金币</string>
<string name="gems_plural">金币</string>
</resources>

View file

@ -7,4 +7,12 @@
<string name="SP_username" translatable="false">Username</string>
<string name="SP_email" translatable="false">E-mail</string>
<string name="base_url" translatable="false">https://habitica.com</string>
<!-- Local notification actions -->
<string name="accept_party_invite" translatable="false">ACCEPT_PARTY_INVITE</string>
<string name="reject_party_invite" translatable="false">REJECT_PARTY_INVITE</string>
<string name="accept_guild_invite" translatable="false">ACCEPT_GUILD_INVITE</string>
<string name="reject_guild_invite" translatable="false">REJECT_GUILD_INVITE</string>
<string name="accept_quest_invite" translatable="false">ACCEPT_QUEST_INVITE</string>
<string name="reject_quest_invite" translatable="false">REJECT_QUEST_INVITE</string>
</resources>

View file

@ -22,6 +22,17 @@
<string name="pref_reminder_checkbox">Activate Reminder</string>
<string name="pref_reminder_picker">Set Reminder Time</string>
<string name="pref_push_notifications_checkbox">User Push Notifications</string>
<string name="push_notifications">Push Notifications</string>
<string name="preference_push_you_won_challenge">You won a Challenge!</string>
<string name="preference_push_received_a_private_message">Received a Private Message</string>
<string name="preference_push_gifted_gems">Gifted Gems</string>
<string name="preference_push_gifted_subscription">Gifted Subscription</string>
<string name="preference_push_invited_to_party">Invited to Party</string>
<string name="preference_push_invited_to_guild">Invited to Guiild</string>
<string name="preference_push_your_quest_has_begun">Your Quest has Begun</string>
<string name="preference_push_invited_to_quest">Invited to Quest</string>
<!-- Adding tasks -->
<string name="task_value">Value</string>
<string name="new_todo">New todo</string>
@ -363,4 +374,6 @@ To start, which parts of your life do you want to improve?</string>
<string name="hourglasses">hourglasses</string>
<string name="gold_singular">gold</string>
<string name="gems_plural">gold</string>
<string name="chat_message_copied">Message copied to Clipboard</string>
<string name="copy_chat_message">Copy to clipboard</string>
</resources>

View file

@ -75,4 +75,67 @@
</PreferenceCategory>
<PreferenceCategory
android:title="@string/push_notifications">
<CheckBoxPreference
android:key="usePushNotifications"
android:defaultValue="true"
android:title="@string/pref_push_notifications_checkbox"/>
<PreferenceScreen
android:key="pushNotifications"
android:title="@string/push_notifications"
android:summary="Set your push notifications settings"
android:order="1">
<PreferenceCategory android:title="Push Notifications">
<CheckBoxPreference
android:key="preference_push_you_won_challenge"
android:defaultValue="true"
android:title="@string/preference_push_you_won_challenge"/>
<CheckBoxPreference
android:key="preference_push_received_a_private_message"
android:defaultValue="true"
android:title="@string/preference_push_received_a_private_message"/>
<CheckBoxPreference
android:key="preference_push_gifted_gems"
android:defaultValue="true"
android:title="@string/preference_push_gifted_gems"/>
<CheckBoxPreference
android:key="preference_push_gifted_subscription"
android:defaultValue="true"
android:title="@string/preference_push_gifted_subscription"/>
<CheckBoxPreference
android:key="preference_push_invited_to_party"
android:defaultValue="true"
android:title="@string/preference_push_invited_to_party"/>
<CheckBoxPreference
android:key="preference_push_invited_to_guild"
android:defaultValue="true"
android:title="@string/preference_push_invited_to_guild"/>
<CheckBoxPreference
android:key="preference_push_your_quest_has_begun"
android:defaultValue="true"
android:title="@string/preference_push_your_quest_has_begun"/>
<CheckBoxPreference
android:key="preference_push_invited_to_quest"
android:defaultValue="true"
android:title="@string/preference_push_invited_to_quest"/>
</PreferenceCategory>
</PreferenceScreen>
</PreferenceCategory>
</PreferenceScreen>

View file

@ -163,17 +163,22 @@ public class APIHelper implements Action1<Throwable> {
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
}
String userAgent = System.getProperty("http.agent");
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(remove_data_interceptor)
.addInterceptor(logging)
.addNetworkInterceptor(chain -> {
Request original = chain.request();
if (this.hostConfig.getUser() != null) {
Request request = original.newBuilder()
Request.Builder builder = original.newBuilder()
.header("x-api-key", this.hostConfig.getApi())
.header("x-api-user", this.hostConfig.getUser())
.header("x-client", "habitica-android")
.method(original.method(), original.body())
.header("x-client", "habitica-android");
if (userAgent != null) {
builder = builder.header("user-agent", userAgent);
}
Request request = builder.method(original.method(), original.body())
.build();
return chain.proceed(request);
} else {

View file

@ -7,5 +7,5 @@ public class HabitDatabase {
public static final String NAME = "Habitica";
public static final int VERSION = 23;
public static final int VERSION = 24;
}

View file

@ -25,6 +25,7 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.DatabaseErrorHandler;
@ -130,7 +131,11 @@ public class HabiticaApplication extends MultiDexApplication {
setupCrashlytics();
createBillingAndCheckout();
registerActivityLifecycleCallbacks();
Amplitude.getInstance().initialize(this, getString(R.string.amplitude_app_id)).enableForegroundTracking(this);
if (!BuildConfig.DEBUG) {
Amplitude.getInstance().initialize(this, getString(R.string.amplitude_app_id)).enableForegroundTracking(this);
}
Fresco.initialize(this);
checkIfNewVersion();
}
@ -180,7 +185,19 @@ public class HabiticaApplication extends MultiDexApplication {
}
private void setupFacebookSdk() {
FacebookSdk.sdkInitialize(getApplicationContext());
String fbApiKey = null;
try {
ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
Bundle bundle = ai.metaData;
fbApiKey = bundle.getString(FacebookSdk.APPLICATION_ID_PROPERTY);
} catch (PackageManager.NameNotFoundException e) {
Log.e("FB Error", "Failed to load meta-data, NameNotFound: " + e.getMessage());
} catch (NullPointerException e) {
Log.e("FB Error", "Failed to load meta-data, NullPointer: " + e.getMessage());
}
if (fbApiKey != null) {
FacebookSdk.sdkInitialize(getApplicationContext());
}
}
private void setupCrashlytics() {

View file

@ -1,8 +1,10 @@
package com.habitrpg.android.habitica.components;
import com.habitrpg.android.habitica.HabiticaApplication;
import com.habitrpg.android.habitica.helpers.notifications.PushNotificationManager;
import com.habitrpg.android.habitica.modules.ApiModule;
import com.habitrpg.android.habitica.modules.AppModule;
import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver;
import com.habitrpg.android.habitica.ui.activities.AboutActivity;
import com.habitrpg.android.habitica.ui.activities.ClassSelectionActivity;
import com.habitrpg.android.habitica.ui.activities.FullProfileActivity;
@ -158,5 +160,9 @@ public interface AppComponent {
void inject(ShopFragment shopFragment);
void inject(PushNotificationManager pushNotificationManager);
void inject(LocalNotificationActionReceiver localNotificationActionReceiver);
void inject(FullProfileActivity fullProfileActivity);
}

View file

@ -0,0 +1,12 @@
package com.habitrpg.android.habitica.events.commands;
import com.magicmicky.habitrpgwrapper.lib.models.ChatMessage;
/**
* Created by jjbillings on 7/27/16.
*/
public class CopyChatMessageCommand extends ChatMessageCommandBase {
public CopyChatMessageCommand(String groupId, ChatMessage chatMessage) {
super(groupId, chatMessage);
}
}

View file

@ -0,0 +1,56 @@
package com.habitrpg.android.habitica.helpers.notifications;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver;
import java.util.Map;
/**
* Created by keithholliday on 7/1/16.
*/
public class GuildInviteLocalNotification extends HabiticaLocalNotification {
@Override
public void notifyLocally(Context context, String title, String message) {
super.notifyLocally(context, title, message);
this.setNotificationActions();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(10, notificationBuilder.build());
}
protected void setNotificationActions() {
Resources res = context.getResources();
Intent acceptInviteIntent = new Intent(context, LocalNotificationActionReceiver.class);
acceptInviteIntent.setAction(res.getString(R.string.accept_guild_invite));
acceptInviteIntent.putExtra("groupID", this.data.get("groupID"));
PendingIntent pendingIntentAccept = PendingIntent.getBroadcast(
context,
3000,
acceptInviteIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
notificationBuilder.addAction(0, "Accept", pendingIntentAccept);
Intent rejectInviteIntent = new Intent(context, LocalNotificationActionReceiver.class);
rejectInviteIntent.setAction(res.getString(R.string.reject_guild_invite));
rejectInviteIntent.putExtra("groupID", this.data.get("groupID"));
PendingIntent pendingIntentReject = PendingIntent.getBroadcast(
context,
2000,
rejectInviteIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
notificationBuilder.addAction(0, "Reject", pendingIntentReject);
}
}

View file

@ -0,0 +1,28 @@
package com.habitrpg.android.habitica.helpers.notifications;
import android.util.Log;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
import com.habitrpg.android.habitica.APIHelper;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
/**
* Created by keithholliday on 6/24/16.
*/
public class HabiticaFirebaseInstanceIDService extends FirebaseInstanceIdService {
public PushNotificationManager pushNotificationManager;
@Override
public void onTokenRefresh() {
pushNotificationManager = PushNotificationManager.getInstance(this);
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
pushNotificationManager.setRefreshedToken(refreshedToken);
}
}

View file

@ -0,0 +1,17 @@
package com.habitrpg.android.habitica.helpers.notifications;
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
/**
* Created by keithholliday on 6/24/16.
*/
public class HabiticaFirebaseMessagingService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
PushNotificationManager pushNotificationManager = PushNotificationManager.getInstance(this);
pushNotificationManager.displayNotification(remoteMessage);
}
}

View file

@ -0,0 +1,47 @@
package com.habitrpg.android.habitica.helpers.notifications;
import android.content.Context;
import android.media.RingtoneManager;
import android.net.Uri;
import android.support.annotation.CallSuper;
import android.support.v4.app.NotificationCompat;
import com.habitrpg.android.habitica.R;
import java.util.Map;
/**
* Created by keithholliday on 6/28/16.
*/
public abstract class HabiticaLocalNotification {
protected Map<String, String> data;
protected Context context;
protected String title;
protected String message;
protected NotificationCompat.Builder notificationBuilder;
@CallSuper
public void notifyLocally(Context context, String title, String message) {
this.context = context;
this.title = title;
this.message = message;
Uri path = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
this.notificationBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_gryphon)
.setContentTitle(title)
.setContentText(message)
.setAutoCancel(true)
.setSound(path);
}
public void setExtras(Map<String, String> data) {
this.data = data;
}
protected abstract void setNotificationActions();
}

View file

@ -0,0 +1,35 @@
package com.habitrpg.android.habitica.helpers.notifications;
/**
* Created by keithholliday on 6/28/16.
*/
public class HabiticaLocalNotificationFactory {
//use getShape method to get object of type shape
public HabiticaLocalNotification build(String notificationType){
if(notificationType == null){
return null;
}
if (notificationType.equalsIgnoreCase(PushNotificationManager.PARTY_INVITE_PUSH_NOTIFICATION_KEY)) {
return new PartyInviteLocalNotification();
} else if (notificationType.contains(PushNotificationManager.RECEIVED_PRIVATE_MESSAGE_PUSH_NOTIFICATION_KEY)) {
return new ReceivedPrivateMessageLocalNotification();
} else if (notificationType.contains(PushNotificationManager.RECEIVED_GEMS_PUSH_NOTIFICATION_KEY)) {
return new ReceivedGemsGiftLocalNotification();
} else if (notificationType.contains(PushNotificationManager.RECEIVED_SUBSCRIPTION_GIFT_PUSH_NOTIFICATION_KEY)) {
return new ReceivedSubscriptionGiftLocalNotification();
} else if (notificationType.contains(PushNotificationManager.GUILD_INVITE_PUSH_NOTIFICATION_KEY)) {
return new GuildInviteLocalNotification();
} else if (notificationType.contains(PushNotificationManager.QUEST_INVITE_PUSH_NOTIFICATION_KEY)) {
return new QuestInviteLocalNotification();
} else if (notificationType.contains(PushNotificationManager.QUEST_BEGUN_PUSH_NOTIFICATION_KEY)) {
return new QuestBegunLocalNotification();
} else if (notificationType.contains(PushNotificationManager.WON_CHALLENGE_PUSH_NOTIFICATION_KEY)) {
return new WonChallengeLocalNotification();
}
return null;
}
}

View file

@ -0,0 +1,56 @@
package com.habitrpg.android.habitica.helpers.notifications;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.media.RingtoneManager;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver;
import com.habitrpg.android.habitica.ui.activities.MainActivity;
import java.util.Map;
/**
* Created by keithholliday on 6/28/16.
*/
public class PartyInviteLocalNotification extends HabiticaLocalNotification {
public void notifyLocally(Context context, String title, String message) {
super.notifyLocally(context, title, message);
this.setNotificationActions();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(10, notificationBuilder.build());
}
protected void setNotificationActions() {
Resources res = context.getResources();
Intent acceptInviteIntent = new Intent(context, LocalNotificationActionReceiver.class);
acceptInviteIntent.setAction(res.getString(R.string.accept_party_invite));
PendingIntent pendingIntentAccept = PendingIntent.getBroadcast(
context,
3000,
acceptInviteIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
notificationBuilder.addAction(0, "Accept", pendingIntentAccept);
Intent rejectInviteIntent = new Intent(context, LocalNotificationActionReceiver.class);
rejectInviteIntent.setAction(res.getString(R.string.reject_party_invite));
PendingIntent pendingIntentReject = PendingIntent.getBroadcast(
context,
2000,
rejectInviteIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
notificationBuilder.addAction(0, "Reject", pendingIntentReject);
}
}

View file

@ -0,0 +1,159 @@
package com.habitrpg.android.habitica.helpers.notifications;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.messaging.RemoteMessage;
import com.habitrpg.android.habitica.APIHelper;
import com.habitrpg.android.habitica.HabiticaApplication;
import com.habitrpg.android.habitica.callbacks.HabitRPGUserCallback;
import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
import com.magicmicky.habitrpgwrapper.lib.models.PushDevice;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
/**
* Created by keithholliday on 6/27/16.
*/
public class PushNotificationManager {
private static PushNotificationManager instance = null;
public static String DEVICE_TOKEN_PREFERENCE_KEY = "device-token-preference";
public static String PARTY_INVITE_PUSH_NOTIFICATION_KEY = "invitedParty";
public static String RECEIVED_PRIVATE_MESSAGE_PUSH_NOTIFICATION_KEY = "newPM";
public static String RECEIVED_GEMS_PUSH_NOTIFICATION_KEY = "giftedGems";
public static String RECEIVED_SUBSCRIPTION_GIFT_PUSH_NOTIFICATION_KEY = "giftedSubscription";
public static String GUILD_INVITE_PUSH_NOTIFICATION_KEY = "invitedGuild";
public static String QUEST_INVITE_PUSH_NOTIFICATION_KEY = "questInvitation";
public static String QUEST_BEGUN_PUSH_NOTIFICATION_KEY = "questStarted";
public static String WON_CHALLENGE_PUSH_NOTIFICATION_KEY = "wonChallenge";
@Inject
public APIHelper apiHelper;
private String refreshedToken;
private SharedPreferences sharedPreferences;
private Context context;
private HabitRPGUser user;
protected PushNotificationManager(Context context) {
HabiticaApplication.getInstance(context).getComponent().inject(this);
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
}
public void setUser(HabitRPGUser user) {
this.user = user;
}
public static PushNotificationManager getInstance(Context context) {
if(instance == null) {
instance = new PushNotificationManager(context);
}
instance.refreshedToken = instance.sharedPreferences.getString(DEVICE_TOKEN_PREFERENCE_KEY, "");
instance.context = context;
return instance;
}
public void setRefreshedToken (String refreshedToken) {
if (this.refreshedToken == null) {
return;
}
this.refreshedToken = refreshedToken;
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(DEVICE_TOKEN_PREFERENCE_KEY, refreshedToken);
editor.commit();
}
//@TODO: Use preferences
public void addPushDeviceUsingStoredToken () {
if (this.refreshedToken == null || this.refreshedToken.isEmpty()) {
this.refreshedToken = FirebaseInstanceId.getInstance().getToken();
}
if (this.refreshedToken == null || this.refreshedToken.isEmpty()) {
return;
}
if (this.user == null || this.userHasPushDevice()) {
return;
}
if (!this.userIsSubscribedToNotifications()) {
return;
}
Map<String, String> pushDeviceData = new HashMap<String, String>();
pushDeviceData.put("regId", this.refreshedToken);
pushDeviceData.put("type", "android");
apiHelper.apiService.addPushDevice(pushDeviceData)
.compose(apiHelper.configureApiCallObserver())
.subscribe(aVoid -> {}, throwable -> {});
}
public void removePushDeviceUsingStoredToken () {
apiHelper.apiService.deletePushDevice(this.refreshedToken)
.compose(apiHelper.configureApiCallObserver())
.subscribe(aVoid -> {}, throwable -> {});
}
private Boolean userHasPushDevice() {
for(PushDevice pushDevice : this.user.getPushDevices()) {
if(pushDevice.getRegId().equals(this.refreshedToken)) {
return true;
}
}
return false;
}
public void displayNotification (RemoteMessage remoteMessage) {
String remoteMessageIdentifier = remoteMessage.getData().get("identifier");
HabiticaLocalNotificationFactory notificationFactory = new HabiticaLocalNotificationFactory();
HabiticaLocalNotification notification = notificationFactory.build(remoteMessageIdentifier);
if (userIsSubscribedToNotificationType(remoteMessageIdentifier) && notification != null) {
notification.setExtras(remoteMessage.getData());
notification.notifyLocally(this.context, remoteMessage.getData().get("title"), remoteMessage.getData().get("body"));
}
}
private boolean userIsSubscribedToNotifications() {
return sharedPreferences.getBoolean("pushNotifications", true);
}
private boolean userIsSubscribedToNotificationType(String type) {
String key = "";
//@TODO: If user has push turned off to send
if (type.equals(PARTY_INVITE_PUSH_NOTIFICATION_KEY)) {
key = "preference_push_invited_to_party";
} else if (type.contains(RECEIVED_PRIVATE_MESSAGE_PUSH_NOTIFICATION_KEY)) {
key = "preference_push_received_a_private_message";
} else if (type.contains(RECEIVED_GEMS_PUSH_NOTIFICATION_KEY)) {
key = "preference_push_gifted_gems";
} else if (type.contains(RECEIVED_SUBSCRIPTION_GIFT_PUSH_NOTIFICATION_KEY)) {
key = "preference_push_gifted_subscription";
} else if (type.contains(GUILD_INVITE_PUSH_NOTIFICATION_KEY)) {
key = "preference_push_invited_to_guild";
} else if (type.contains(QUEST_INVITE_PUSH_NOTIFICATION_KEY)) {
key = "preference_push_invited_to_quest";
} else if (type.contains(QUEST_BEGUN_PUSH_NOTIFICATION_KEY)) {
key = "preference_push_your_quest_has_begun";
} else if (type.contains(WON_CHALLENGE_PUSH_NOTIFICATION_KEY)) {
key = "preference_push_you_won_challenge";
}
return sharedPreferences.getBoolean(key, true);
}
}

View file

@ -0,0 +1,38 @@
package com.habitrpg.android.habitica.helpers.notifications;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.ui.activities.MainActivity;
import java.util.Map;
/**
* Created by keithholliday on 7/1/16.
*/
public class QuestBegunLocalNotification extends HabiticaLocalNotification {
@Override
public void notifyLocally(Context context, String title, String message) {
super.notifyLocally(context, title, message);
this.setNotificationActions();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(10000, notificationBuilder.build());
}
protected void setNotificationActions() {
Intent intent = new Intent(context, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
context,
3000,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
);
notificationBuilder.setContentIntent(pendingIntent);
}
}

View file

@ -0,0 +1,52 @@
package com.habitrpg.android.habitica.helpers.notifications;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.media.RingtoneManager;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver;
import java.util.Map;
/**
* Created by keithholliday on 7/1/16.
*/
public class QuestInviteLocalNotification extends HabiticaLocalNotification {
@Override
public void notifyLocally(Context context, String title, String message) {
super.notifyLocally(context, title, message);
this.setNotificationActions();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(10000, notificationBuilder.build());
}
protected void setNotificationActions() {
Resources res = context.getResources();
Intent acceptInviteIntent = new Intent(context, LocalNotificationActionReceiver.class);
acceptInviteIntent.setAction(res.getString(R.string.accept_quest_invite));
PendingIntent pendingIntentAccept = PendingIntent.getBroadcast(
context,
3000,
acceptInviteIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
notificationBuilder.addAction(0, "Accept", pendingIntentAccept);
Intent rejectInviteIntent = new Intent(context, LocalNotificationActionReceiver.class);
rejectInviteIntent.setAction(res.getString(R.string.reject_quest_invite));
PendingIntent pendingIntentReject = PendingIntent.getBroadcast(
context,
2000,
rejectInviteIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
notificationBuilder.addAction(0, "Reject", pendingIntentReject);
}
}

View file

@ -0,0 +1,40 @@
package com.habitrpg.android.habitica.helpers.notifications;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.media.RingtoneManager;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver;
import com.habitrpg.android.habitica.ui.activities.MainActivity;
import java.util.Map;
/**
* Created by keithholliday on 7/1/16.
*/
public class ReceivedGemsGiftLocalNotification extends HabiticaLocalNotification {
@Override
public void notifyLocally(Context context, String title, String message) {
super.notifyLocally(context, title, message);
this.setNotificationActions();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(10, notificationBuilder.build());
}
protected void setNotificationActions() {
Intent intent = new Intent(context, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
context,
3000,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
);
notificationBuilder.setContentIntent(pendingIntent);
}
}

View file

@ -0,0 +1,46 @@
package com.habitrpg.android.habitica.helpers.notifications;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.media.RingtoneManager;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.RemoteInput;
import android.util.Log;
import android.widget.RemoteViews;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver;
import com.habitrpg.android.habitica.ui.activities.MainActivity;
import java.util.Map;
/**
* Created by keithholliday on 7/1/16.
*/
public class ReceivedPrivateMessageLocalNotification extends HabiticaLocalNotification {
@Override
public void notifyLocally(Context context, String title, String message) {
super.notifyLocally(context, title, message);
this.setNotificationActions();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
notificationManager.notify(10, notificationBuilder.build());
}
protected void setNotificationActions() {
Intent intent = new Intent(context, LocalNotificationActionReceiver.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
context,
3000,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
);
notificationBuilder.setContentIntent(pendingIntent);
}
}

View file

@ -0,0 +1,38 @@
package com.habitrpg.android.habitica.helpers.notifications;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.ui.activities.MainActivity;
import java.util.Map;
/**
* Created by keithholliday on 7/1/16.
*/
public class ReceivedSubscriptionGiftLocalNotification extends HabiticaLocalNotification {
@Override
public void notifyLocally(Context context, String title, String message) {
super.notifyLocally(context, title, message);
this.setNotificationActions();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
notificationManager.notify(10, notificationBuilder.build());
}
protected void setNotificationActions() {
Intent intent = new Intent(context, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
context,
3000,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
);
notificationBuilder.setContentIntent(pendingIntent);
}
}

View file

@ -0,0 +1,38 @@
package com.habitrpg.android.habitica.helpers.notifications;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.ui.activities.MainActivity;
import java.util.Map;
/**
* Created by keithholliday on 7/2/16.
*/
public class WonChallengeLocalNotification extends HabiticaLocalNotification {
@Override
public void notifyLocally(Context context, String title, String message) {
super.notifyLocally(context, title, message);
this.setNotificationActions();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
notificationManager.notify(10, notificationBuilder.build());
}
protected void setNotificationActions() {
Intent intent = new Intent(context, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
context,
3000,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
);
notificationBuilder.setContentIntent(pendingIntent);
}
}

View file

@ -0,0 +1,97 @@
package com.habitrpg.android.habitica.receivers;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.Log;
import com.habitrpg.android.habitica.APIHelper;
import com.habitrpg.android.habitica.HabiticaApplication;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.callbacks.HabitRPGUserCallback;
import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
import javax.inject.Inject;
/**
* Created by keithholliday on 6/30/16.
*/
public class LocalNotificationActionReceiver extends BroadcastReceiver implements HabitRPGUserCallback.OnUserReceived {
@Inject
public APIHelper apiHelper;
private HabitRPGUser user;
private String action;
private Resources resources;
private Intent intent;
private Context context;
@Override
public void onReceive(Context context, Intent intent) {
HabiticaApplication.getInstance(context).getComponent().inject(this);
this.resources = context.getResources();
this.action = intent.getAction();
this.intent = intent;
this.context = context;
this.apiHelper.apiService.getUser()
.compose(this.apiHelper.configureApiCallObserver())
.subscribe(new HabitRPGUserCallback(this), throwable -> {});
}
@Override
public void onUserReceived(HabitRPGUser user) {
this.user = user;
this.handleLocalNotificationAction(action);
}
private void handleLocalNotificationAction(String action) {
NotificationManager notificationManager = (NotificationManager) this.context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancelAll();
//@TODO: This is a good place for a factory and event emitter pattern
if (action.equals(this.resources.getString(R.string.accept_party_invite))) {
if (this.user.getInvitations().getParty() == null) return;
String partyId = this.user.getInvitations().getParty().getId();
apiHelper.apiService.joinGroup(partyId)
.compose(apiHelper.configureApiCallObserver())
.subscribe(aVoid -> {}, throwable -> {});
} else if (action.equals(this.resources.getString(R.string.reject_party_invite))) {
if (this.user.getInvitations().getParty() == null) return;
String partyId = this.user.getInvitations().getParty().getId();
apiHelper.apiService.rejectGroupInvite(partyId)
.compose(apiHelper.configureApiCallObserver())
.subscribe(aVoid -> {}, throwable -> {});
} else if (action.equals(this.resources.getString(R.string.accept_quest_invite))) {
if (this.user.getParty() == null) return;
String partyId = this.user.getParty().getId();
apiHelper.apiService.acceptQuest(partyId)
.compose(apiHelper.configureApiCallObserver())
.subscribe(aVoid -> {}, throwable -> {});
} else if (action.equals(this.resources.getString(R.string.reject_quest_invite))) {
if (this.user.getParty() == null) return;
String partyId = this.user.getParty().getId();
apiHelper.apiService.rejectQuest(partyId)
.compose(apiHelper.configureApiCallObserver())
.subscribe(aVoid -> {}, throwable -> {});
} else if (action.equals(this.resources.getString(R.string.accept_guild_invite))) {
Bundle extras = this.intent.getExtras();
String guildId = extras.getString("groupID");
if (guildId == null) return;
apiHelper.apiService.joinGroup(guildId)
.compose(apiHelper.configureApiCallObserver())
.subscribe(aVoid -> {}, throwable -> {});
} else if (action.equals(this.resources.getString(R.string.reject_guild_invite))) {
Bundle extras = this.intent.getExtras();
String guildId = extras.getString("groupID");
if (guildId == null) return;
apiHelper.apiService.rejectGroupInvite(guildId)
.compose(apiHelper.configureApiCallObserver())
.subscribe(aVoid -> {}, throwable -> {});
}
}
}

View file

@ -84,55 +84,6 @@ public class LoginActivity extends BaseActivity
TextView mForgotPWTV;
private Menu menu;
private CallbackManager callbackManager;
private View.OnClickListener mLoginNormalClick = new View.OnClickListener() {
@Override
public void onClick(View v) {
mProgressBar.setVisibility(View.VISIBLE);
if (isRegistering) {
String username, email, password, cpassword;
username = String.valueOf(mUsernameET.getText()).trim();
email = String.valueOf(mEmail.getText()).trim();
password = String.valueOf(mPasswordET.getText());
cpassword = String.valueOf(mConfirmPassword.getText());
if (username.length() == 0 || password.length() == 0 || email.length() == 0 || cpassword.length() == 0) {
showValidationError(R.string.login_validation_error_fieldsmissing);
return;
}
apiHelper.registerUser(username, email, password, cpassword)
.compose(apiHelper.configureApiCallObserver())
.subscribe(LoginActivity.this, throwable -> {
hideProgress();
});
} else {
String username, password;
username = String.valueOf(mUsernameET.getText()).trim();
password = String.valueOf(mPasswordET.getText());
if (username.length() == 0 || password.length() == 0) {
showValidationError(R.string.login_validation_error_fieldsmissing);
return;
}
apiHelper.connectUser(username, password)
.compose(apiHelper.configureApiCallObserver())
.subscribe(LoginActivity.this, throwable -> {
hideProgress();
});
}
}
};
private View.OnClickListener mForgotPWClick = v -> {
String url = BuildConfig.BASE_URL;
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
startActivity(i);
};
public static void show(final View v) {
v.setVisibility(View.VISIBLE);
}
public static void hide(final View v) {
v.setVisibility(View.GONE);
}
@Override
protected int getLayoutResId() {
@ -214,6 +165,54 @@ public class LoginActivity extends BaseActivity
hide(this.mConfirmPasswordRow);
}
}
}
private View.OnClickListener mLoginNormalClick = new View.OnClickListener() {
@Override
public void onClick(View v) {
mProgressBar.setVisibility(View.VISIBLE);
if (isRegistering) {
String username, email,password,cpassword;
username = String.valueOf(mUsernameET.getText()).trim();
email = String.valueOf(mEmail.getText()).trim();
password = String.valueOf(mPasswordET.getText());
cpassword = String.valueOf(mConfirmPassword.getText());
if (username.length() == 0 || password.length() == 0 || email.length() == 0 || cpassword.length() == 0) {
showValidationError(R.string.login_validation_error_fieldsmissing);
return;
}
apiHelper.registerUser(username,email,password, cpassword)
.compose(apiHelper.configureApiCallObserver())
.subscribe(LoginActivity.this, throwable -> {hideProgress();});
} else {
String username,password;
username = String.valueOf(mUsernameET.getText()).trim();
password = String.valueOf(mPasswordET.getText());
if (username.length() == 0 || password.length() == 0) {
showValidationError(R.string.login_validation_error_fieldsmissing);
return;
}
apiHelper.connectUser(username,password)
.compose(apiHelper.configureApiCallObserver())
.subscribe(LoginActivity.this, throwable -> {hideProgress();});
}
}
};
private View.OnClickListener mForgotPWClick = v -> {
String url = BuildConfig.BASE_URL;
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
startActivity(i);
};
public static void show(final View v) {
v.setVisibility(View.VISIBLE);
}
public static void hide(final View v) {
v.setVisibility(View.GONE);
}
private void startMainActivity() {

View file

@ -32,6 +32,7 @@ import com.habitrpg.android.habitica.events.commands.OpenMenuItemCommand;
import com.habitrpg.android.habitica.events.commands.SellItemCommand;
import com.habitrpg.android.habitica.events.commands.UnlockPathCommand;
import com.habitrpg.android.habitica.events.commands.UpdateUserCommand;
import com.habitrpg.android.habitica.helpers.notifications.PushNotificationManager;
import com.habitrpg.android.habitica.ui.AvatarView;
import com.habitrpg.android.habitica.ui.AvatarWithBarsViewModel;
import com.habitrpg.android.habitica.ui.TutorialView;
@ -138,6 +139,7 @@ import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import butterknife.BindView;
import retrofit2.adapter.rxjava.HttpException;
import rx.Observable;
import rx.functions.Action1;
@ -205,6 +207,8 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
return (Math.round(value * Math.pow(10, n))) / (Math.pow(10, n));
}
PushNotificationManager pushNotificationManager;
@Override
protected int getLayoutResId() {
return R.layout.activity_main;
@ -221,6 +225,8 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
//Check if reminder alarm is set
scheduleReminder(this);
pushNotificationManager = PushNotificationManager.getInstance(this);
new Select().from(HabitRPGUser.class).where(Condition.column("id").eq(hostConfig.getUser())).async().querySingle(userTransactionListener);
setupToolbar(toolbar);
@ -348,8 +354,9 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
displayDeathDialogIfNeeded();
if (!fromLocalDb) {
displayNewInboxMessagesBadge();
pushNotificationManager.setUser(user);
pushNotificationManager.addPushDeviceUsingStoredToken();
// Update the oldEntries
new Thread(() -> {
@ -868,26 +875,38 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
@Subscribe
public void onEvent(final BuyGemItemCommand event) {
Observable<Void> observable;
if (event.shopIdentifier.equals(Shop.TIME_TRAVELERS_SHOP)) {
if (event.item.purchaseType.equals("gear")) {
observable = apiHelper.apiService.purchaseMysterySet(event.item.categoryIdentifier);
if (event.item.canBuy(user) || !event.item.getCurrency().equals("gems")) {
Observable<Void> observable;
if (event.shopIdentifier.equals(Shop.TIME_TRAVELERS_SHOP)) {
if (event.item.purchaseType.equals("gear")) {
observable = apiHelper.apiService.purchaseMysterySet(event.item.categoryIdentifier);
} else {
observable = apiHelper.apiService.purchaseHourglassItem(event.item.purchaseType, event.item.key);
}
} else if (event.item.purchaseType.equals("quests") && event.item.getCurrency().equals("gold")) {
observable = apiHelper.apiService.purchaseQuest(event.item.key);
} else {
observable = apiHelper.apiService.purchaseHourglassItem(event.item.purchaseType, event.item.key);
observable = apiHelper.apiService.purchaseItem(event.item.purchaseType, event.item.key);
}
observable
.compose(apiHelper.configureApiCallObserver())
.doOnNext(aVoid -> {
showSnackbar(this, floatingMenuWrapper, getString(R.string.successful_purchase, event.item.text), SnackbarDisplayType.NORMAL);
})
.subscribe(buyResponse -> {
apiHelper.retrieveUser(false)
.compose(apiHelper.configureApiCallObserver())
.subscribe(new HabitRPGUserCallback(this), throwable -> {
});
}, throwable -> {
HttpException error = (HttpException) throwable;
if (error.code() == 401 && event.item.getCurrency().equals("gems")) {
openGemPurchaseFragment(null);
}
});
} else {
observable = apiHelper.apiService.purchaseItem(event.item.purchaseType, event.item.key);
openGemPurchaseFragment(null);
}
observable
.compose(apiHelper.configureApiCallObserver())
.doOnNext(aVoid -> {
showSnackbar(this, floatingMenuWrapper, getString(R.string.successful_purchase, event.item.text), SnackbarDisplayType.NORMAL);
})
.subscribe(buyResponse -> {
apiHelper.retrieveUser(false)
.compose(apiHelper.configureApiCallObserver())
.subscribe(new HabitRPGUserCallback(this), throwable -> {});
}, throwable -> {});
}
@Subscribe
@ -978,7 +997,7 @@ public class MainActivity extends BaseActivity implements Action1<Throwable>, Ha
}
@Subscribe
public void onEvent(OpenGemPurchaseFragmentCommand cmd) {
public void openGemPurchaseFragment(OpenGemPurchaseFragmentCommand cmd) {
drawer.setSelection(MainDrawerBuilder.SIDEBAR_PURCHASE);
}

View file

@ -6,6 +6,7 @@ import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.ui.fragments.preferences.AccountDetailsFragment;
import com.habitrpg.android.habitica.ui.fragments.preferences.PreferencesFragment;
import com.habitrpg.android.habitica.ui.fragments.preferences.PushNotificationsPreferencesFragment;
import android.content.Context;
import android.content.SharedPreferences;
@ -88,6 +89,10 @@ public class PrefsActivity extends BaseActivity implements
if (preferenceScreen.getKey().equals("accountDetails")) {
fragment = new AccountDetailsFragment();
}
if (preferenceScreen.getKey().equals("pushNotifications")) {
fragment = new PushNotificationsPreferencesFragment();
}
return fragment;
}
}

View file

@ -162,6 +162,10 @@ public class ShopRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
});
}
private void canBuy() {
}
private void buyItem() {
BuyGemItemCommand command = new BuyGemItemCommand();
command.shopIdentifier = shopIdentifier;
@ -171,6 +175,7 @@ public class ShopRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
public void bind(ShopItem item) {
this.item = item;
buyButton.setVisibility(View.VISIBLE);
titleView.setText(item.getText());
descriptionView.setText(Html.fromHtml(item.getNotes()));
@ -178,15 +183,12 @@ public class ShopRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
if (item.getUnlockCondition() == null) {
buyButton.setText(item.getValue().toString());
switch (item.getCurrency()) {
case "gold":
buyButton.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_header_gold, 0, 0, 0);
break;
case "gems":
buyButton.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_header_gem, 0, 0, 0);
break;
default:
buyButton.setVisibility(View.GONE);
if (item.getCurrency().equals("gold")) {
buyButton.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_header_gold, 0, 0, 0);
} else if (item.getCurrency().equals("gems")) {
buyButton.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_header_gem, 0, 0, 0);
} else {
buyButton.setVisibility(View.GONE);
}
unlockView.setVisibility(View.GONE);
} else {
@ -194,6 +196,10 @@ public class ShopRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
unlockView.setVisibility(View.VISIBLE);
unlockView.setText(item.unlockCondition.readableUnlockConditionId());
}
if (item.getLocked()) {
buyButton.setVisibility(View.GONE);
}
}
@Override

View file

@ -3,6 +3,7 @@ package com.habitrpg.android.habitica.ui.adapter.social;
import com.habitrpg.android.habitica.HabiticaApplication;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.events.commands.CopyChatAsTodoCommand;
import com.habitrpg.android.habitica.events.commands.CopyChatMessageCommand;
import com.habitrpg.android.habitica.events.commands.DeleteChatMessageCommand;
import com.habitrpg.android.habitica.events.commands.FlagChatMessageCommand;
import com.habitrpg.android.habitica.events.commands.OpenNewPMActivityCommand;
@ -446,6 +447,12 @@ public class ChatRecyclerViewAdapter extends RecyclerView.Adapter<ChatRecyclerVi
break;
}
case R.id.menu_chat_copy: {
EventBus.getDefault().post(new CopyChatMessageCommand(groupId, currentMsg));
break;
}
}
return false;

View file

@ -3,11 +3,8 @@ package com.habitrpg.android.habitica.ui.fragments.inventory.shops;
import com.habitrpg.android.habitica.APIHelper;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.ui.activities.MainActivity;
import com.habitrpg.android.habitica.ui.adapter.inventory.ShopRecyclerAdapter;
import com.habitrpg.android.habitica.ui.adapter.inventory.StableRecyclerAdapter;
import com.habitrpg.android.habitica.ui.fragments.BaseFragment;
import com.habitrpg.android.habitica.ui.helpers.MarginDecoration;
import com.habitrpg.android.habitica.ui.helpers.RecyclerViewEmptySupport;
import com.habitrpg.android.habitica.ui.menu.DividerItemDecoration;
import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
@ -15,7 +12,6 @@ import com.magicmicky.habitrpgwrapper.lib.models.Shop;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
@ -44,7 +40,7 @@ public class ShopFragment extends BaseFragment {
public Shop shop;
@Inject
APIHelper apiHeliper;
APIHelper apiHelper;
@Nullable
@Override
@ -104,8 +100,8 @@ public class ShopFragment extends BaseFragment {
shopUrl = "seasonal";
break;
}
this.apiHeliper.apiService.fetchShopInventory(shopUrl)
.compose(this.apiHeliper.configureApiCallObserver())
this.apiHelper.apiService.fetchShopInventory(shopUrl)
.compose(this.apiHelper.configureApiCallObserver())
.subscribe(shop -> {
this.shop = shop;
this.adapter.setShop(shop);

View file

@ -4,6 +4,7 @@ import com.habitrpg.android.habitica.APIHelper;
import com.habitrpg.android.habitica.HabiticaApplication;
import com.habitrpg.android.habitica.NotificationPublisher;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.helpers.notifications.PushNotificationManager;
import com.habitrpg.android.habitica.prefs.TimePreference;
import com.habitrpg.android.habitica.ui.activities.ClassSelectionActivity;
import com.habitrpg.android.habitica.ui.activities.MainActivity;
@ -21,6 +22,8 @@ import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.util.Log;
import java.util.Calendar;
@ -33,8 +36,11 @@ public class PreferencesFragment extends BasePreferencesFragment implements
public APIHelper apiHelper;
private Context context;
private TimePreference timePreference;
private PreferenceScreen pushNotificationsPreference;
private Preference classSelectionPreference;
private HabitRPGUser user;
private PushNotificationManager pushNotificationManager;
private TransactionListener<HabitRPGUser> userTransactionListener = new TransactionListener<HabitRPGUser>() {
@Override
public void onResultReceived(HabitRPGUser habitRPGUser) {
@ -64,6 +70,7 @@ public class PreferencesFragment extends BasePreferencesFragment implements
new Select().from(HabitRPGUser.class).where(Condition.column("id").eq(userID)).async().querySingle(userTransactionListener);
}
pushNotificationManager = PushNotificationManager.getInstance(this.getActivity());
}
@Override
@ -72,6 +79,12 @@ public class PreferencesFragment extends BasePreferencesFragment implements
boolean useReminder = getPreferenceManager().getSharedPreferences().getBoolean("use_reminder", false);
timePreference.setEnabled(useReminder);
pushNotificationsPreference = (PreferenceScreen) findPreference("pushNotifications");
boolean userPushNotifications = getPreferenceManager().getSharedPreferences().getBoolean("usePushNotifications", true);
pushNotificationsPreference.setEnabled(userPushNotifications);
classSelectionPreference = findPreference("choose_class");
classSelectionPreference.setVisible(false);
}
@ -176,6 +189,14 @@ public class PreferencesFragment extends BasePreferencesFragment implements
} else if (key.equals("reminder_time")) {
removeNotifications();
scheduleNotifications();
} else if (key.equals("usePushNotifications")) {
boolean userPushNotifications = sharedPreferences.getBoolean(key, false);
pushNotificationsPreference.setEnabled(userPushNotifications);
if (userPushNotifications) {
pushNotificationManager.addPushDeviceUsingStoredToken();
} else {
pushNotificationManager.removePushDeviceUsingStoredToken();
}
}
}

View file

@ -0,0 +1,31 @@
package com.habitrpg.android.habitica.ui.fragments.preferences;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.support.v7.preference.Preference;
import android.widget.Toast;
import com.habitrpg.android.habitica.R;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* Created by keithholliday on 6/27/16.
*/
public class PushNotificationsPreferencesFragment extends BasePreferencesFragment implements
SharedPreferences.OnSharedPreferenceChangeListener {
@Override
protected void setupPreferences() {
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
}
}

View file

@ -5,6 +5,7 @@ import com.habitrpg.android.habitica.HabiticaApplication;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.events.ToggledInnStateEvent;
import com.habitrpg.android.habitica.events.commands.CopyChatMessageCommand;
import com.habitrpg.android.habitica.events.commands.DeleteChatMessageCommand;
import com.habitrpg.android.habitica.events.commands.FlagChatMessageCommand;
import com.habitrpg.android.habitica.events.commands.SendNewGroupMessageCommand;
@ -23,6 +24,9 @@ import com.raizlabs.android.dbflow.sql.language.Select;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
@ -33,6 +37,7 @@ import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.HashMap;
@ -159,6 +164,16 @@ public class ChatListFragment extends BaseFragment implements SwipeRefreshLayout
}
}
@Subscribe
public void onEvent(CopyChatMessageCommand cmd)
{
ClipboardManager clipMan = (ClipboardManager)getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
ClipData chatMessage = ClipData.newPlainText("Chat Message",cmd.chatMessage.text);
clipMan.setPrimaryClip(chatMessage);
MainActivity activity = (MainActivity) getActivity();
UiUtils.showSnackbar(activity, activity.getFloatingMenuWrapper(), getString(R.string.chat_message_copied), UiUtils.SnackbarDisplayType.NORMAL);
}
@Subscribe
public void onEvent(final FlagChatMessageCommand cmd) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

View file

@ -75,6 +75,9 @@ public interface ApiService {
@POST("user/buy-mystery-set/{key}")
Observable<Void> purchaseMysterySet(@Path("key") String itemKey);
@POST("user/buy-quest/{key}")
Observable<Void> purchaseQuest(@Path("key") String key);
@POST("user/sell/{type}/{key}")
Observable<HabitRPGUser> sellItem(@Path("type") String itemType, @Path("key") String itemKey);
@ -205,6 +208,9 @@ public interface ApiService {
@POST("groups/{gid}/invite")
Observable<Void> inviteToGroup(@Path("gid") String groupId, @Body Map<String, Object> inviteData);
@POST("groups/{gid}/reject-invite")
Observable<Void> rejectGroupInvite(@Path("gid") String groupId);
@POST("groups/{gid}/quests/accept")
Observable<Void> acceptQuest(@Path("gid") String groupId);
@ -235,10 +241,17 @@ public interface ApiService {
@POST("members/send-private-message")
Observable<PostChatMessageResult> postPrivateMessage(@Body HashMap<String, String> messageDetails);
@GET("shops/{identifier}")
Observable<Shop> fetchShopInventory(@Path("identifier") String identifier);
//Push notifications
@POST("user/push-devices")
Observable<Void> addPushDevice(@Body Map<String, String> pushDeviceData);
@DELETE("user/push-devices/{regId}")
Observable<Void> deletePushDevice(@Path("regId") String regId);
//DEBUG: These calls only work on a local development server
@POST("debug/add-ten-gems")

View file

@ -4,6 +4,7 @@ import com.google.gson.annotations.SerializedName;
import com.habitrpg.android.habitica.HabitDatabase;
import com.habitrpg.android.habitica.ui.AvatarView;
import com.magicmicky.habitrpgwrapper.lib.models.invitations.Invitations;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.TasksOrder;
import com.raizlabs.android.dbflow.annotation.Column;
@ -92,6 +93,14 @@ public class HabitRPGUser extends BaseModel {
foreignColumnName = "user_id")})
private ContributorInfo contributor;
@Column
@ForeignKey(references = {@ForeignKeyReference(columnName = "invitations_id",
columnType = String.class,
foreignColumnName = "user_id")})
private Invitations invitations;
private List<PushDevice> pushDevices = new ArrayList<PushDevice>();
private Purchases purchased;
private TasksOrder tasksOrder;
@ -145,6 +154,13 @@ public class HabitRPGUser extends BaseModel {
this.contributor = contributor;
}
public Invitations getInvitations() {
return invitations;
}
public void setInvitations(Invitations invitations) {
this.invitations = invitations;
}
public UserParty getParty() {
return party;
@ -282,6 +298,14 @@ public class HabitRPGUser extends BaseModel {
this.tasksOrder = tasksOrder;
}
public List<PushDevice> getPushDevices() {
return this.pushDevices;
}
public void setPushDevices(List<PushDevice> pushDevices) {
this.pushDevices = pushDevices;
}
@Override
public void save() {
// We need to set the user_id to all other objects
@ -293,6 +317,9 @@ public class HabitRPGUser extends BaseModel {
authentication.user_id = id;
flags.user_id = id;
if (contributor != null) { contributor.user_id = id; }
contributor.user_id = id;
invitations.user_id = id;
ArrayList<Task> allTasks = new ArrayList<Task>();
if (dailys != null) {

View file

@ -0,0 +1,34 @@
package com.magicmicky.habitrpgwrapper.lib.models;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* Created by keithholliday on 7/5/16.
*/
public class PushDevice {
@SerializedName("regId")
@Expose
private String regId;
@SerializedName("type")
@Expose
private String type;
public String getRegId() {
return this.regId;
}
public void setRegId(String regId) {
this.regId = regId;
}
public String getType() {
return this.type;
}
public void setType(String type) {
this.type = type;
}
}

View file

@ -60,6 +60,9 @@ public class ShopItem {
}
public Boolean getLocked() {
if (locked == null) {
return false;
}
return locked;
}
@ -90,4 +93,14 @@ public class ShopItem {
public void setUnlockCondition(ShopItemUnlockCondition unlockCondition) {
this.unlockCondition = unlockCondition;
}
public boolean canBuy(HabitRPGUser user) {
if (getCurrency().equals("gold")) {
return getValue() <= user.getStats().getGp();
} else if (getCurrency().equals("gems")) {
return getValue() <= (user.getBalance()*4);
} else {
return false;
}
}
}

View file

@ -0,0 +1,76 @@
package com.magicmicky.habitrpgwrapper.lib.models.invitations;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* Created by keithholliday on 7/2/16.
*/
public class GuildInvite {
@SerializedName("inviter")
@Expose
private String inviter;
@SerializedName("name")
@Expose
private String name;
@SerializedName("id")
@Expose
private String id;
/**
*
* @return
* The inviter
*/
public String getInviter() {
return inviter;
}
/**
*
* @param inviter
* The inviter
*/
public void setInviter(String inviter) {
this.inviter = inviter;
}
/**
*
* @return
* The name
*/
public String getName() {
return name;
}
/**
*
* @param name
* The name
*/
public void setName(String name) {
this.name = name;
}
/**
*
* @return
* The id
*/
public String getId() {
return id;
}
/**
*
* @param id
* The id
*/
public void setId(String id) {
this.id = id;
}
}

View file

@ -0,0 +1,71 @@
package com.magicmicky.habitrpgwrapper.lib.models.invitations;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import com.habitrpg.android.habitica.HabitDatabase;
import com.magicmicky.habitrpgwrapper.lib.models.invitations.PartyInvite;
import com.raizlabs.android.dbflow.annotation.Column;
import com.raizlabs.android.dbflow.annotation.NotNull;
import com.raizlabs.android.dbflow.annotation.PrimaryKey;
import com.raizlabs.android.dbflow.annotation.Table;
import com.raizlabs.android.dbflow.structure.BaseModel;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Created by keithholliday on 7/2/16.
*/
@Table(databaseName = HabitDatabase.NAME)
public class Invitations extends BaseModel {
@Column
@PrimaryKey
@NotNull
public String user_id;
@SerializedName("party")
@Expose
private PartyInvite party;
@SerializedName("guilds")
@Expose
private List<GuildInvite> guilds = new ArrayList<GuildInvite>();
/**
*
* @return
* The party invite
*/
public PartyInvite getParty() {
return party;
}
/**
*
* @param party
* The party
*/
public void setParty(PartyInvite party) {
this.party = party;
}
/**
*
* @return
* The guilds invite
*/
public List<GuildInvite> getGuilds() {
return guilds;
}
/**
*
* @param guilds
* The guilds
*/
public void setGuilds(List<GuildInvite> guilds) {
this.guilds = guilds;
}
}

View file

@ -0,0 +1,73 @@
package com.magicmicky.habitrpgwrapper.lib.models.invitations;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
/**
* Created by keithholliday on 7/2/16.
*/
public class PartyInvite {
@SerializedName("id")
@Expose
private String id;
@SerializedName("name")
@Expose
private String name;
@SerializedName("inviter")
@Expose
private String inviter;
/**
*
* @return
* The id
*/
public String getId() {
return id;
}
/**
*
* @param id
* The id
*/
public void setId(String id) {
this.id = id;
}
/**
*
* @return
* The name
*/
public String getName() {
return name;
}
/**
*
* @param name
* The name
*/
public void setName(String name) {
this.name = name;
}
/**
*
* @return
* The inviter
*/
public String getInviter() {
return inviter;
}
/**
*
* @param inviter
* The inviter
*/
public void setInviter(String inviter) {
this.inviter = inviter;
}
}

View file

@ -603,7 +603,7 @@ public class Task extends BaseModel {
Calendar newTime = new GregorianCalendar();
newTime.setTime(oldTime);
if (this.getFrequency().equals(FREQUENCY_DAILY) && (newTime.before(today) || newTime.equals(today))) {
if (this.getFrequency().equals(FREQUENCY_DAILY) ) {
Calendar startDate = new GregorianCalendar();
startDate.setTime(this.getStartDate());
@ -611,7 +611,9 @@ public class Task extends BaseModel {
long diffInMillies = today.getTimeInMillis() - startDate.getTimeInMillis();
long daySinceStart = timeUnit.convert(diffInMillies, TimeUnit.MILLISECONDS);
long daysUntilNextReminder = this.getEveryX() - (daySinceStart % this.getEveryX());
newTime.add(Calendar.DATE, (int) daysUntilNextReminder);
today.add(Calendar.DATE, (int) daysUntilNextReminder);
newTime.setTime(today.getTime());
} else {
int nextActiveDayOfTheWeek = newTime.get(Calendar.DAY_OF_WEEK);
while (!this.getRepeat().getForDay(nextActiveDayOfTheWeek) || newTime.before(today) || newTime.equals(today)) {

View file

@ -10,6 +10,7 @@ import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import android.os.Build;
@ -23,8 +24,8 @@ import rx.observers.TestSubscriber;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP)
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.M)
@RunWith(RobolectricTestRunner.class)
public class SocialAPITests extends BaseAPITests {
List<String> messagesIDs;

View file

@ -12,7 +12,7 @@ import android.os.Build;
import rx.observers.TestSubscriber;
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP)
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.M)
@RunWith(RobolectricGradleTestRunner.class)
public class TagAPITests extends BaseAPITests {

View file

@ -22,7 +22,7 @@ import rx.observers.TestSubscriber;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP)
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.M)
@RunWith(RobolectricGradleTestRunner.class)
public class TaskAPITests extends BaseAPITests {

View file

@ -22,7 +22,7 @@ import rx.observers.TestSubscriber;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotSame;
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP)
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.M)
@RunWith(RobolectricGradleTestRunner.class)
public class UserAPITests extends BaseAPITests {

View file

@ -12,7 +12,7 @@ public class RemindersManagerTest {
@Test
public void itCreatesRemindersItemFromDateString() {
RemindersManager remindersManager = new RemindersManager();
RemindersManager remindersManager = new RemindersManager("habits");
// RemindersItem remindersItem = remindersManager.createReminderFromDateString("dd MMMM yyyy HH:mm:ss")

View file

@ -21,12 +21,14 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@ -35,6 +37,7 @@ import static org.junit.Assert.assertNotNull;
* Created by keithholliday on 7/16/16.
*/
@Config(manifest = "AndroidManifestTesting.xml")
@RunWith(value = RobolectricTestRunner.class)
public class TaskAlarmManagerTest {
private TaskAlarmManager taskAlarmManager;
@ -59,6 +62,8 @@ public class TaskAlarmManagerTest {
List<RemindersItem> reminders = new ArrayList<RemindersItem>();
RemindersItem remindersItem1 = new RemindersItem();
UUID randomUUID = UUID.randomUUID();
remindersItem1.setId(randomUUID.toString());
Calendar cal = Calendar.getInstance();
cal.set(Calendar.MINUTE, cal.get(Calendar.MINUTE) + 1);
@ -71,14 +76,13 @@ public class TaskAlarmManagerTest {
taskAlarmManager.setAlarmsForTask(task);
Integer alarmId = reminders.get(0).getAlarmId();
int intentId = remindersItem1.getId().hashCode() & 0xfffffff;
Intent intent = new Intent(context, TaskReceiver.class);
intent.setAction(remindersItem1.getAlarmId().toString());
PendingIntent sender = PendingIntent.getBroadcast(context, alarmId, intent, PendingIntent.FLAG_NO_CREATE);
intent.setAction(remindersItem1.getId());
PendingIntent sender = PendingIntent.getBroadcast(context, intentId, intent, PendingIntent.FLAG_NO_CREATE);
boolean alarmUp = sender != null;
Assert.assertNotNull(alarmId);
Assert.assertNotNull(intentId);
Assert.assertEquals(true, alarmUp);
}
@ -89,6 +93,8 @@ public class TaskAlarmManagerTest {
List<RemindersItem> reminders = new ArrayList<RemindersItem>();
RemindersItem remindersItem1 = new RemindersItem();
UUID randomUUID = UUID.randomUUID();
remindersItem1.setId(randomUUID.toString());
Calendar cal = Calendar.getInstance();
cal.set(Calendar.MINUTE, cal.get(Calendar.MINUTE) + 1);
@ -101,9 +107,9 @@ public class TaskAlarmManagerTest {
taskAlarmManager.setAlarmsForTask(task);
Integer alarmId = reminders.get(0).getAlarmId();
int alarmId = remindersItem1.getId().hashCode() & 0xfffffff;
Intent intent = new Intent(context, TaskReceiver.class);
intent.setAction(remindersItem1.getAlarmId().toString());
intent.setAction(remindersItem1.getId());
PendingIntent sender = PendingIntent.getBroadcast(context, alarmId, intent, PendingIntent.FLAG_NO_CREATE);
boolean alarmUp = sender != null;
@ -127,7 +133,7 @@ public class TaskAlarmManagerTest {
}
taskAlarmManager.setAlarmsForTask(task);
Integer newAlarmId = reminders.get(0).getAlarmId();
int newAlarmId = reminders.get(0).getId().hashCode() & 0xfffffff;
PendingIntent senderNew = PendingIntent.getBroadcast(context, newAlarmId, intent, PendingIntent.FLAG_NO_CREATE);
boolean alarmUpNew = senderNew != null;
@ -192,6 +198,8 @@ public class TaskAlarmManagerTest {
List<RemindersItem> reminders = new ArrayList<RemindersItem>();
RemindersItem remindersItem1 = new RemindersItem();
UUID randomUUID = UUID.randomUUID();
remindersItem1.setId(randomUUID.toString());
//We try to set a reminder on Tuesday, but the manager will correct this to Monday
Calendar cal = Calendar.getInstance();
@ -206,13 +214,13 @@ public class TaskAlarmManagerTest {
taskAlarmManager.setAlarmsForTask(task);
Integer alarmId = reminders.get(0).getAlarmId();
int alarmId = reminders.get(0).getId().hashCode() & 0xfffffff;
Calendar newReminderTime = Calendar.getInstance();
newReminderTime.setTime(reminders.get(0).getTime());
Intent intent = new Intent(context, TaskReceiver.class);
intent.setAction(remindersItem1.getAlarmId().toString());
intent.setAction(remindersItem1.getId());
PendingIntent sender = PendingIntent.getBroadcast(context, alarmId, intent, PendingIntent.FLAG_NO_CREATE);
boolean alarmUp = sender != null;
@ -240,6 +248,8 @@ public class TaskAlarmManagerTest {
List<RemindersItem> reminders = new ArrayList<RemindersItem>();
RemindersItem remindersItem1 = new RemindersItem();
UUID randomUUID = UUID.randomUUID();
remindersItem1.setId(randomUUID.toString());
//We try to set a reminder for now, but by the manager will correct (because the seconds will be different)
Calendar cal = Calendar.getInstance();
@ -252,13 +262,13 @@ public class TaskAlarmManagerTest {
taskAlarmManager.setAlarmsForTask(task);
Integer alarmId = reminders.get(0).getAlarmId();
int alarmId = reminders.get(0).getId().hashCode() & 0xfffffff;
Calendar newReminderTime = Calendar.getInstance();
newReminderTime.setTime(reminders.get(0).getTime());
Intent intent = new Intent(context, TaskReceiver.class);
intent.setAction(remindersItem1.getAlarmId().toString());
intent.setAction(remindersItem1.getId());
PendingIntent sender = PendingIntent.getBroadcast(context, alarmId, intent, PendingIntent.FLAG_NO_CREATE);
boolean alarmUp = sender != null;
@ -278,13 +288,18 @@ public class TaskAlarmManagerTest {
List<RemindersItem> reminders = new ArrayList<RemindersItem>();
RemindersItem remindersItem1 = new RemindersItem();
UUID randomUUID = UUID.randomUUID();
remindersItem1.setId(randomUUID.toString());
//We try to set a reminder one day after the start date, but the manager will correct since the
// daily is every 2 days from above
Calendar cal = Calendar.getInstance();
int currentDayOfTheWeek = cal.get(Calendar.DAY_OF_WEEK);
task.setStartDate(cal.getTime());
cal.set(Calendar.DAY_OF_WEEK, currentDayOfTheWeek + 1);
cal.set(Calendar.MINUTE, cal.get(Calendar.MINUTE) + 1);
cal.set(Calendar.MINUTE, cal.get(Calendar.MINUTE));
remindersItem1.setTime(cal.getTime());
reminders.add(remindersItem1);
@ -294,13 +309,13 @@ public class TaskAlarmManagerTest {
taskAlarmManager.setAlarmsForTask(task);
Integer alarmId = reminders.get(0).getAlarmId();
int alarmId = reminders.get(0).getId().hashCode() & 0xfffffff;
Calendar newReminderTime = Calendar.getInstance();
newReminderTime.setTime(reminders.get(0).getTime());
Intent intent = new Intent(context, TaskReceiver.class);
intent.setAction(remindersItem1.getAlarmId().toString());
intent.setAction(remindersItem1.getId());
PendingIntent sender = PendingIntent.getBroadcast(context, alarmId, intent, PendingIntent.FLAG_NO_CREATE);
boolean alarmUp = sender != null;
@ -321,6 +336,8 @@ public class TaskAlarmManagerTest {
List<RemindersItem> reminders = new ArrayList<RemindersItem>();
RemindersItem remindersItem1 = new RemindersItem();
UUID randomUUID = UUID.randomUUID();
remindersItem1.setId(randomUUID.toString());
//We try to set a reminder for now, but the manager will correct since the seconds will be off
Calendar cal = Calendar.getInstance();
@ -334,13 +351,13 @@ public class TaskAlarmManagerTest {
taskAlarmManager.setAlarmsForTask(task);
Integer alarmId = reminders.get(0).getAlarmId();
int alarmId = reminders.get(0).getId().hashCode() & 0xfffffff;
Calendar newReminderTime = Calendar.getInstance();
newReminderTime.setTime(reminders.get(0).getTime());
Intent intent = new Intent(context, TaskReceiver.class);
intent.setAction(remindersItem1.getAlarmId().toString());
intent.setAction(remindersItem1.getId());
PendingIntent sender = PendingIntent.getBroadcast(context, alarmId, intent, PendingIntent.FLAG_NO_CREATE);
boolean alarmUp = sender != null;

View file

@ -12,7 +12,7 @@ It's also on Google Play:
src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png" />
</a>
Having the application installed is a good way to be notified of new releases. However, clicking "Watch" on this
Having the application installed is a good way to be notified of new releases. However, clicking "Watch" on this
repository will allow GitHub to email you whenever we publish a release.
@ -54,6 +54,7 @@ Setup Habitica build config files by simply copying the example habitica files.
$ cp habitica.properties.example habitica.properties
$ cp habitica.resources.example habitica.resources
$ cp Habitica/google-services.json.example Habitica/google-services.json (Get .json from Firebase Console)
Note: this is the default production `habitica.properties` file for habitica.com. If you
want to use a local habitica server, please modify the values in the properties file accordingly.

View file

@ -8,6 +8,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:2.1.2'
classpath 'com.android.databinding:dataBinder:1.0-rc4'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
classpath 'com.google.gms:google-services:3.0.0'
}
}

View file

@ -0,0 +1,11 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="store_short_description">Превърнете живота си в игра за повече мотивация и по-добра организация!</string>
<string name="store_description">НОВОТО приложение на Хабитика! Пренаписано изцяло за по-добро преживяване и повече функционалности.
Превърнете живота си в игра за повече мотивация и по-добра организация! С Хабитика е лесно да се забавлявате, докато изпълнявате целите си.
Въведете навиците си, ежедневните си цели и дългосрочните си задачи, а след това си създайте герой. Изпълнявайте задачите си, за да вдигате нивото на героя си и отключвате функционалности като екипировка, любимци, умения и дори мисии! Бийте се с чудовища заедно с приятели, за да държите един другиго отговорен, и използвайте златото си за награди като екипировка, или дори свои собствени такива, като например да гледате епизод от любимия си сериал. Гъвкава, забавна и с голяма общност, Хабитика е перфектният начин да се мотивирате и да постигнете всичко, което искате.
Версията в момента е „бета“, така че не се притеснявайте да ни изпращате отзивите си на mobile@habitica.com! А ако приложението Ви харесва, ще се радваме да оставите рецензията си.</string>
</resources>