diff --git a/.gitignore b/.gitignore index 6e6e04e44..80f87efef 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,4 @@ fabric.properties Habitica/res/values/secret_strings.xml habitica.properties habitica.resources +Habitica/google-services.json diff --git a/.travis.yml b/.travis.yml index 15b9c450c..52df7c594 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 diff --git a/Habitica/AndroidManifest.xml b/Habitica/AndroidManifest.xml index 82496d97e..9e84ff798 100644 --- a/Habitica/AndroidManifest.xml +++ b/Habitica/AndroidManifest.xml @@ -2,8 +2,8 @@ @@ -132,12 +132,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Habitica/build.gradle b/Habitica/build.gradle index e0d8ff629..6639214d8 100644 --- a/Habitica/build.gradle +++ b/Habitica/build.gradle @@ -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' \ No newline at end of file diff --git a/Habitica/google-services.json.example b/Habitica/google-services.json.example new file mode 100644 index 000000000..a7499e800 --- /dev/null +++ b/Habitica/google-services.json.example @@ -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" +} diff --git a/Habitica/res/layout/activity_full_profile.xml b/Habitica/res/layout/activity_full_profile.xml index ac46afd9f..f73142c29 100644 --- a/Habitica/res/layout/activity_full_profile.xml +++ b/Habitica/res/layout/activity_full_profile.xml @@ -2,9 +2,9 @@ + android:layout_height="match_parent"> + android:layout_height="wrap_content"> + android:stretchColumns="0,1,2,3,4"> @@ -279,5 +279,38 @@ android:indeterminate="true" /> + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Habitica/res/menu/chat_message.xml b/Habitica/res/menu/chat_message.xml index 3f9d520ba..e9e41012e 100644 --- a/Habitica/res/menu/chat_message.xml +++ b/Habitica/res/menu/chat_message.xml @@ -9,6 +9,11 @@ android:icon="@drawable/ic_action_chat" android:title="@string/send_pm" /> + + + Задачи + Умения + Общност + Входящи + Кръчма + Група + Купуване на диаманти + Гилдии + Предизвикателства + Чанта + Герой + Екипировка + Конюшня + Новини + Настройки + Относно + Магазини + diff --git a/Habitica/res/values-bg/strings.tutorial.xml b/Habitica/res/values-bg/strings.tutorial.xml new file mode 100644 index 000000000..3224530e4 --- /dev/null +++ b/Habitica/res/values-bg/strings.tutorial.xml @@ -0,0 +1,14 @@ + + + Отбелязвайте положителните си навици, за да получавате злато и опит! Отрицателните навици ще наранят героя Ви, ако ги докоснете, така че ги избягвайте в истинския живот! + Побеждавайте своите повтарящи се ежедневни задачи, за да печелите злато и опит. Внимание! Ежедневните задачи ще наранят героя Ви, ако не ги изпълните навреме. + Завършвайте задачите си за изпълнение в истинския живот, а след това ги отбелязвайте тук за ЗЛАТО и ОПИТ; така ще можете да отключвате награди и нови функционалности! + Това са наградите Ви! Печелете злато като изпълнявате истинските си навици, ежедневни задачи и задачи за изпълнение. След това го харчете за награди в играта или персонализирани такива за истинския живот! + Когато купувате екипировка, тя се появява тук. Бойното снаряжение променя атрибутите Ви, а костюмът (ако е включен) влияе на това какво носи героят Ви. + Печелете предмети като изпълнявате задачи и качвате нива. Докоснете предмет, за да го използвате! + Когато изпълнявате задачите си в истинския живот, имате шанс на случаен принцип да Ви се паднат яйца и отвари. Комбинирайте ги, за да си излюпите любимци. + Уменията са специални способности, които имат мощни ефекти! Докоснете умение, за да го използвате. Това ще Ви струва малко мана (синята лента), която се възстановява при влизане в играта всеки ден, както и като изпълнявате задачите от истинския си живот. Прегледайте ЧЗВ в менюто за повече информация! + Тук е мястото, където заедно с приятелите си можете взаимно да се държите отговорни за изпълнението на целите си, както и да се биете срещу чудовища със задачите си! + Добре дошли в кръчмата — обществено място за разговори, достъпно за хора от всички възрасти! Тук можете да разговаряте за продуктивността си и да задавате въпроси. Приятно прекарване! + Изберете дали да бъдете воин, магьосник, лечител или мошеник! Всеки клас има уникална екипировка и умения. Докоснете (?), за да научите повече! + diff --git a/Habitica/res/values-bg/strings.xml b/Habitica/res/values-bg/strings.xml new file mode 100644 index 000000000..29549d18a --- /dev/null +++ b/Habitica/res/values-bg/strings.xml @@ -0,0 +1,344 @@ + + + Хабитика + Опресняване + Опит + Здраве + Мана + Настройките не могат да бъдат заредени от кода + + Настройки + Вашият персонализиран сървър + Потребителски идентификатор + Вашият потребителски идентификатор + Жетон за ППИ + Вашият жетон за ППИ + Свържете се с мен + Възникна грешка… + Профил + Ежедневно напомняне + Включване на напомнянето + Задаване на времето за напомняне + + Стойност + Нова задача за изпълнение + Нова награда + Нова ежедневна задача + Нов навик + Редактиране + Изтриване + Отказ + Вход + Регистрация + Потребителско име + Е-поща или потребителско име + Парола + Е-поща + Потвърдете паролата + Изход + Излизане от Вашия профил + Подробности за профила + Добре дошли + Съживяване + Моля, свържете се чрез приложението, преди да използвате джаджа + Относно + Библиотеки + История на версиите + Хабитика е софтуер с отворен код, който е наличен в Github + Оценете приложението + Изпратете ни отзивите си! + Докладване на грешка + Изходен код + + Грешка при свързване + Нямате връзка с Интернет. + Изглежда има проблем със сървъра. Опитайте отново по-късно. + Грешка при удостоверяване + Потребителското име и/или паролата е грешна. + Грешка при проверяването + Трябва да попълните всички полета. + Запазване + Бележки + Текст + Трудност + Етикети + Елементарно + Лесно + Средно + Трудно + Начална дата + Положителен ( + ) + Отрицателен ( - ) + Подзадачи + Напомняния + Действия + Атрибути + Физическа + Умствена + Обществена + Други + Честота + В определени дни от седмицата + На всеки Х дни + Понеделник + Вторник + Сряда + Четвъртък + Петък + Събота + Неделя + Вдигнахте ниво! + Изпълнявайки целите си в истинския живот, Вие достигнахте ниво %1$d! + Вие бяхте излекуван(а) напълно! + Ура! + Не се отчайвайте! + Изгубихте ниво, всичкото си злато и един предмет от екипировката си, но можете да си ги върнете с усърдна работа! Късмет — ще се справите. + Здравето Ви свърши! + Възстановете здравето си и опитайте отново + Филтър + Профилно изображение + %d ТМ + Използвахте %1$s за %2$d точки мана. + нова подзадача + Добавяне + Не забравяйте да изпълнявате ежедневните си задачи! + Използване на умение + Очаквайте скоро + Наистина ли искате да докладвате това съобщение за нарушение? + Докладване на съобщението + Оключва се на ниво 11 + Вие не участвате в група. За да се присъедините към група, моля, посетете уеб сайта. + Забравена парола + Забравена парола? Моля, използвайте мобилния уеб сайт. + Включете отново ежедневните си задачи + Пауза на ежедн. + Купуване + Премахване + Група + Чат + Членове + Навици + Ежедневни + Задачи + Награди + Сигурен/на ли сте? + Наистина ли искате да изтриете това? + Да + Не + Мисия + Тяло + Коса + Риза + Кожа + Животински уши + Основа + Цвят + Брада + Мустаци + Цвете + Бретон + Няма нищо + Размер + Слаб + Широк + Фон + Купуване на персонализация + Отключване на комплекта за %d диамант(а) + Купуване + Купуване на комплекта %s + Крайна дата + Приемане + Отказване + Започване на мисия + Отказване на поканата + Прекратяване на мисията + Напускане на мисията + В очакване + Приета + Отказана + Участници + Версия %s (%d) + Помощ и ЧЗВ + Разбрано! + Напомнете ми отново + Добре дошли в Хабитика + Над 1 100 000 хора се забавляват, докато вършат нещата си. Присъединете се към тях! Създайте си герой и следете задачите от истинския си живот. + Напредък в играта = напредък в живота + Отключвайте функционалностите на играта като изпълнявате задачите в истинския си живот. Печелете екипировка, любимци и още, като награда за постигането на целите си! + Общност и битки с чудовища + Приятелите Ви могат да Ви помогнат да постигнете целите си. Подкрепяйте се взаимно в живота и битките, за да израснете заедно! + Започване + Назад + Напред + Пропускане + Добре дошли в Хабитика, където напредването в играта ще подобри истинския Ви живот! Докато изпълнявате реалните си цели, ще отключвате екипировка, любимци, мисии и още. + Добре дошли + Първо, ще Ви трябва герой, който да Ви представлява в играта! Нещата, които вършите в истинския си живот, ще се отразяват на здравето, нивото и златото на героя Ви. + Чудесно! А сега да създадем няколко задачи, за да започнете да печелите опит и злато. + +Кои области от живота си искате да подобрите? + Работа + Упражнения + Здравословно състояние + Училище + Екипи + Домакинска работа + Изобретателност + Обработка на е-поща + Най-важната задача + Работен проект + 10 мин аеробни упражнения + Разтягане + Да организирам тренировките си + Да ям полезна/вредна храна + Да изчистя зъбите си с конец + Да се запиша за преглед + Учене/отлагане + Да напиша домашното си + Да завърша проекта за училище + Да проверя какво прави екипът + Да уведомя екипа за напредъка си + Да завърша екипния проект + 10 минути чистене + Да измия чиниите + Да подредя килера + Да се уча от майстор в занаята + Да работя по проекта си + Да завърша проекта си + Искате ли да помогнете на Хабитика да продължи да съществува? Можете да подкрепите разработчиците като купите диаманти!\n\nС диамантите можете да си купувате забавни неща за профила си, като: \n\n — Интересни костюми за героя си;\n — Страхотни фонове;\n — Забавни мисии, от които ще получите яйца за любимци;\n — Възможността да променяте класа си преди да достигнете ниво 100.\n\nБлагодарим Ви за това, че помагате на Хабитика да се развива. Подкрепата Ви означава много за нас! + Моите гилдии + Обществени гилдии + Гилдия + Напускане + Присъединяване + Водач + Диаманти + Копиране като задача + Изпращане на ЛС + Докладване + Изтриване + Име + Описание + Добавяне на етикет + Поверителност + Напишете съобщение + Изпр. + Срок: %s + текуща серия: %d + Задачата има краен срок + Бойно снаряжение + Костюм + Глава + Аксесоар за глава + Предмет за очи + Броня + Гръб + Тяло + Щит + Оръжие + Костюм + Екипирано + Наистина ли искате да прекратите мисията? Всички приети покани ще бъдат загубени. Притежателят на мисията ще си запази свитъка ѝ. + Покана за мисия + Наистина ли искате да започнете мисията? След като мисията започне, никой повече няма да може да се присъедини към нея. + Получихте покана за участие в мисия! + Преди 1 ден + Преди %d дни + Преди 1 минута + Преди %d минути + Преди %d часа + Преди 1 час + Днес + Предмети + Яйца + Излюпващи отвари + Храна + Мисии + Любимци + Превози + Намерихте рядък предмет в гардероба: %s! Страхотно! + Тършувате из гардероба и намирате %1$s %2$s. Какво ли прави това там? + Сборвате се с гардероба и получавате опит. Така му се пада! + Отворете гардероба, за да получите случайно избрана специална екипировка, опит или храна! Оставащи предмети: %d + Намерихте последния рядък предмет за екипиране в омагьосания гардероб. + В гардероба ще се появяват нови предмети през първата седмица на всеки месец. До тогава продължавайте да щракате за опит и храна! + Продаване (%s злато) + Излюпване с отвара + Даване на любимец + Излюпване с яйце + Поканване на групата + Хранене на %1$s %2$s с: + Използване + Хранене + Излюпване на любимец с %s + Вие излюпихте %1$s %2$s! + Затваряне + Споделяне + Вие нахранихте своя(та/то) %1$s %2$s! + %s беше закупен(а/о) + Обикновени любимци + Любимци от магически отвари + Любимци от мисии + Редки любимци + Обикновени превози + Превози от магически отвари + Превози от мисии + Редки превози + Световна мисия + Починете в странноприемницата, за да не могат да Ви наранят неизпълнените Ви ежедневни задачи! (Забележка: това не Ви защитава от получаването на щети от главатари.) + Нямате %s + Ниво %s + Ниво %1$s - %2$s + Воин + Мошеник + Магьосник + Лечител + Воините нанасят повече и по-силни „критични удари“, които на случаен принцип дават злато, опит и шанс за падане на предмет при изпълнение на задача. Те също така нанасят сериозни щети на чудовищата-главатари. Играйте като воин, ако Ви мотивират изненадващите награди, или ако искате да раздавате правосъдие в мисиите с главатари. + Магьосниците се учат лесно, тъй като придобиват опит и нива по-бързо от останалите класове. Те имат много мана за специалните си умения. Играйте като магьосник, ако обичате тактическите страни на навиците, или ако се мотивирате от вдигане на нива и отключване на специални функционалности! + Мошениците обичат да трупат богатства, печелят повече злато повече от останалите и са майстори в намирането на случайни предмети. Отличителното им умение „Невидимост“ им позволява да избегнат последствията от пропуснати ежедневни задачи. Играйте като мошеник, ако Ви мотивират наградите и постиженията, и обичате плячката и значките! + Лечителите трудно могат да бъдат наранени, и разпростират защитата си върху останалите. Пропуснатите ежедневни задачи и лошите навици не ги смущават толкова много; те винаги могат да възстановят здравето си след провал. Играйте като лечител, ако обичате да помагате на останалите в групата си, или ако искате да изиграете смъртта чрез усърдна работа! + Изберете клас + Отказване + Не Ви се занимава с класове? Искате да изберете по-късно? Откажете се от тях — ще бъдете воин без специални умения. Можете да прочетете относно класовата система по-късно в уикито, както и да включите класовете когато пожелаете. + Наистина ли искате да бъдете %s? + Избиране на класа + Назад + Наистина ли искате да се откажете? + Промяна на класа + Променете класа си и възстановете атрибутните си точки за 3 диаманта. Можете да разпределите атрибутните си точки през уеб сайта + Включване на класовата система + Промяна на класа + По е-поща + Поканете съществуващи потребители + Изпращане + Поканете приятели + Ако имате приятели, които вече използват Хабитика, поканете ги чрез потребителски идентификатор тук. + Ако приятел се присъедини към Хабитика през Вашето е-писмо, той автоматично ще бъде поканен в групата Ви! + Добавяне на покани + Потребителски ид. + Е-поща + Поканване в групата + Споделяне чрез + Достигнах ниво %s в Хабитика, като подобрих навиците си в истинския живот! + Току-що излюпих любимец — %1$s %2$s в Хабитика, като изпълнявах задачите си в истинския живот! + Току-що се сдобих с превоз — %1$s %2$s в Хабитика, като изпълнявах задачите си в истинския живот! + Отваряне в магазина + Наистина ли искате да промените класа си? Това ще Ви струва 3 диаманта. + Съобщение от %1$s + Потвърждаване + Пазар + Пътешественици във времето + Сезонен магазин + Нямате входящи съобщения. Можете да изпратите ново съобщение на потребител от екрана с неговите публични съобщения в чата! + Отключете, като поканите приятели + Нямате достатъчно злато + Няма нужда да купувате лечебна отвара + Закупено успешно: %1$s + Потвърждаване на покупката + Купуване на %1$s за %2$s %3$s + диамант + пясъчен часовник + пясъчни часовници + злато + злато + diff --git a/Habitica/res/values-de/strings.sidebar.xml b/Habitica/res/values-de/strings.sidebar.xml index 3b829eeeb..0b8d2d778 100644 --- a/Habitica/res/values-de/strings.sidebar.xml +++ b/Habitica/res/values-de/strings.sidebar.xml @@ -16,4 +16,5 @@ Neuigkeiten Einstellungen Über + Märkte diff --git a/Habitica/res/values-de/strings.xml b/Habitica/res/values-de/strings.xml index 650bc23d3..7dfc43820 100644 --- a/Habitica/res/values-de/strings.xml +++ b/Habitica/res/values-de/strings.xml @@ -53,7 +53,7 @@ Verbindungsfehler Keine Internetverbindung. Es scheint ein Problem mit dem Server zu geben. Versuch es später noch mal. - Autentifikationsfehler + Authentifikationsfehler Ungültiger Benutzername und/oder Passwort Validierungsfehler Bitte fülle alle Felder aus. @@ -166,14 +166,14 @@ Spielfortschritt = Lebensfortschritt 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! Werde sozial und kämpfe gegen Monster - Behalte Deine Ziele im Auge mit Hilfe Deiner Freunde. Unterstützt Euch gegenseitig im Leben und im Kampf während Ihr Euch gemeinsam verbessert! + Behalte Deine Ziele im Auge mit Hilfe Deiner Freunde. Unterstützt Euch gegenseitig im Leben und im Kampf, während Ihr Euch gemeinsam verbessert! Anfangen Zurück Weiter Überspringen 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. Willkommen - 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. + 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. 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? @@ -330,4 +330,15 @@ Für den Anfang: Welche Bereiche Deines Lebens möchtest Du verbessern? Mysteriöse Zeitreisende Jahreszeitenmarkt Du hast keine Nachrichten in Deinem Posteingang. Du kannst einem anderen Benutzer eine neue Nachricht von deren öffentlichen Chatnachrichten aus senden! + Durch Einladen von Freunden freischalten + Nicht genügend Gold + Du musst keinen Heiltrank kaufen + %1$s gekauft + Kauf bestätigen + %1$s für %2$s %3$s kaufen + Edelstein + Sanduhr + Sanduhren + Gold + Gold diff --git a/Habitica/res/values-es/strings.sidebar.xml b/Habitica/res/values-es/strings.sidebar.xml index 5c050d41f..8fdcef7af 100644 --- a/Habitica/res/values-es/strings.sidebar.xml +++ b/Habitica/res/values-es/strings.sidebar.xml @@ -16,4 +16,5 @@ Noticias Ajustes Información + Tiendas diff --git a/Habitica/res/values-es/strings.xml b/Habitica/res/values-es/strings.xml index 6ed6ec16f..1a13cada1 100644 --- a/Habitica/res/values-es/strings.xml +++ b/Habitica/res/values-es/strings.xml @@ -325,4 +325,9 @@ Para empezar, ¿qué aspectos de tu vida te gustaría mejorar? Abrir en Play Store ¿Seguro que quieres cambiar de clase? Te costará 3 gemas. Mensaje de %1$s + Confirmar + Mercado + Viajeros del tiempo + Tienda estacional + 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. diff --git a/Habitica/res/values-fr/strings.xml b/Habitica/res/values-fr/strings.xml index d955cc841..ed038d36d 100644 --- a/Habitica/res/values-fr/strings.xml +++ b/Habitica/res/values-fr/strings.xml @@ -329,5 +329,16 @@ Pour commencer, quels aspects de votre vie souhaitez-vous améliorer ? Marché Voyageurs temporels Boutique saisonnière - 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. + 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. + Débloquer en invitant des ami•e•s + Pas assez d\'or + Vous n\'avez pas besoin d\'acheter une potion de santé + %1$s acheté avec succès + Confirmer l\'achat + Acheter %1$s for %2$s %3$s + gemme + sablier + sabliers + or + or diff --git a/Habitica/res/values-ja/strings.sidebar.xml b/Habitica/res/values-ja/strings.sidebar.xml index e48775131..d11938451 100644 --- a/Habitica/res/values-ja/strings.sidebar.xml +++ b/Habitica/res/values-ja/strings.sidebar.xml @@ -16,4 +16,5 @@ お知らせ 設定 Habitica について + ショップ diff --git a/Habitica/res/values-ja/strings.xml b/Habitica/res/values-ja/strings.xml index 93a890f10..b1c97b0f2 100644 --- a/Habitica/res/values-ja/strings.xml +++ b/Habitica/res/values-ja/strings.xml @@ -326,4 +326,20 @@ Play ストアで開く クラスを変更します。いいですか? 3 ジェムかかります。 %1$s からのメッセージ + 確認 + 市場 + タイムトラベラー + 期間限定ショップ + 受信箱にメッセージがありません。Habitica ユーザーあてにメッセージを送ることができます。公開チャットに投稿しているユーザーにメッセージを送ってみてはどうですか? + 友達を招待するとアンロック + ゴールドが足りません + 体力回復の薬を買う必要がありません + %1$s を買いました + 購入の確認 + %2$s %3$s で %1$s を買う + ジェム + 砂時計 + 砂時計 + ゴールド + ゴールド diff --git a/Habitica/res/values-nl/strings.xml b/Habitica/res/values-nl/strings.xml index 9aad792c3..909442ced 100644 --- a/Habitica/res/values-nl/strings.xml +++ b/Habitica/res/values-nl/strings.xml @@ -330,4 +330,15 @@ Om te beginnen, welke delen van je leven wil je verbeteren? Tijd Reizigers Seizoenswinkel Je hebt geen Inbox berichten. Je kan een gebruiker een nieuw bericht sturen vanuit hun publieke chat berichten. + Ontgrendel door vrienden uit te nodigen + Niet genoeg goud + Je hoeft geen gezondheidsdrankje te kopen + %1$s succesvol gekocht + Bevestig aankoop + Koop %1$s voor %2$s %3$s + edelsteen + zandloper + zandlopers + goud + goud diff --git a/Habitica/res/values-pt-rBR/strings.xml b/Habitica/res/values-pt-rBR/strings.xml index c3f3a8d65..4ef30acc5 100644 --- a/Habitica/res/values-pt-rBR/strings.xml +++ b/Habitica/res/values-pt-rBR/strings.xml @@ -326,4 +326,12 @@ Para começar, quais partes da sua vida você quer melhorar? Abra na Play Store Você tem certeza que quer mudar sua classe? Isso irá custar 3 gemas. Mensagem de %1$s + Confirmar + Mercado + Você não precisa comprar uma poção de vida + %1$s comprado com sucesso + Confirmar compra + gema + ouro + ouro diff --git a/Habitica/res/values-zh-rTW/strings.sidebar.xml b/Habitica/res/values-zh-rTW/strings.sidebar.xml index 91fdc8ee6..0138aa5a9 100644 --- a/Habitica/res/values-zh-rTW/strings.sidebar.xml +++ b/Habitica/res/values-zh-rTW/strings.sidebar.xml @@ -16,4 +16,5 @@ 新聞 設定 關於 + 商店 diff --git a/Habitica/res/values-zh-rTW/strings.xml b/Habitica/res/values-zh-rTW/strings.xml index 32fe2a607..d9a5c3829 100644 --- a/Habitica/res/values-zh-rTW/strings.xml +++ b/Habitica/res/values-zh-rTW/strings.xml @@ -113,7 +113,7 @@ 重啟每日任務 暫停每日任務 購買 - 解散 + 取消 隊伍 聊天 成員 @@ -322,5 +322,12 @@ 我在Habitica中通過改善我現實生活中的習慣到達了%s級! 我剛剛在Habitica中通完成我現實生活中的任務孵化了一隻寵物%1$s %2$s! 我剛剛在Habitica中通完成我現實生活中的任務得到了一匹坐騎%1$s %2$s! + 開啟Play sotre 你確定要變更職業嗎?這會花費3寶石。 + 訊息來自%1$s + 確認 + 市集 + 時光旅人 + 季節限定商店 + 你的收件匣裡面沒有任何信息。你可以利用聊天去傳送訊息給大家 diff --git a/Habitica/res/values-zh/strings.sidebar.xml b/Habitica/res/values-zh/strings.sidebar.xml index 6580fb2f5..7992a695c 100644 --- a/Habitica/res/values-zh/strings.sidebar.xml +++ b/Habitica/res/values-zh/strings.sidebar.xml @@ -16,4 +16,5 @@ 新闻 设置 关于 + 商店 diff --git a/Habitica/res/values-zh/strings.xml b/Habitica/res/values-zh/strings.xml index 6f21deddc..f6692148d 100644 --- a/Habitica/res/values-zh/strings.xml +++ b/Habitica/res/values-zh/strings.xml @@ -325,4 +325,20 @@ 在应用商店开放 你确定想要转变职业吗?这将花费3颗宝石。 从%1$s来的消息 + 确认 + 市场 + 时光旅行 + 四季商店 + 收件箱里没有消息。您可以在用户的公共聊天窗口中给他发送新消息! + 通过邀请朋友来解锁 + 没有足够的金币 + 你不需要购买生命药水 + 已购买 %1$s + 确认购买 + 为 %2$s %3$s 购买 %1$s + 宝石 + 沙漏 + 沙漏 + 金币 + 金币 diff --git a/Habitica/res/values/strings.constants.xml b/Habitica/res/values/strings.constants.xml index 2c56c1eb5..b7d64b085 100644 --- a/Habitica/res/values/strings.constants.xml +++ b/Habitica/res/values/strings.constants.xml @@ -7,4 +7,12 @@ Username E-mail https://habitica.com + + + ACCEPT_PARTY_INVITE + REJECT_PARTY_INVITE + ACCEPT_GUILD_INVITE + REJECT_GUILD_INVITE + ACCEPT_QUEST_INVITE + REJECT_QUEST_INVITE \ No newline at end of file diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml index 390d4b3b9..0aa13106f 100644 --- a/Habitica/res/values/strings.xml +++ b/Habitica/res/values/strings.xml @@ -22,6 +22,17 @@ Activate Reminder Set Reminder Time + User Push Notifications + Push Notifications + You won a Challenge! + Received a Private Message + Gifted Gems + Gifted Subscription + Invited to Party + Invited to Guiild + Your Quest has Begun + Invited to Quest + Value New todo @@ -363,4 +374,6 @@ To start, which parts of your life do you want to improve? hourglasses gold gold + Message copied to Clipboard + Copy to clipboard diff --git a/Habitica/res/xml/preferences_fragment.xml b/Habitica/res/xml/preferences_fragment.xml index e88c4312f..46d5b2e1b 100644 --- a/Habitica/res/xml/preferences_fragment.xml +++ b/Habitica/res/xml/preferences_fragment.xml @@ -75,4 +75,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/APIHelper.java b/Habitica/src/main/java/com/habitrpg/android/habitica/APIHelper.java index 1b5926217..4db72a780 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/APIHelper.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/APIHelper.java @@ -163,17 +163,22 @@ public class APIHelper implements Action1 { 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 { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/HabitDatabase.java b/Habitica/src/main/java/com/habitrpg/android/habitica/HabitDatabase.java index c0c6af461..f4d1cc3d4 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/HabitDatabase.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/HabitDatabase.java @@ -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; } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/HabiticaApplication.java b/Habitica/src/main/java/com/habitrpg/android/habitica/HabiticaApplication.java index 21b755a6f..d10318b2d 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/HabiticaApplication.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/HabiticaApplication.java @@ -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() { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java b/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java index 0527d3509..41996eac5 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java @@ -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); } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/events/commands/CopyChatMessageCommand.java b/Habitica/src/main/java/com/habitrpg/android/habitica/events/commands/CopyChatMessageCommand.java new file mode 100644 index 000000000..575fff4f0 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/events/commands/CopyChatMessageCommand.java @@ -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); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/GuildInviteLocalNotification.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/GuildInviteLocalNotification.java new file mode 100644 index 000000000..a728ac0cd --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/GuildInviteLocalNotification.java @@ -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); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseInstanceIDService.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseInstanceIDService.java new file mode 100644 index 000000000..c9504930a --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseInstanceIDService.java @@ -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); + } + +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.java new file mode 100644 index 000000000..a4c66251c --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.java @@ -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); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaLocalNotification.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaLocalNotification.java new file mode 100644 index 000000000..bad509da4 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaLocalNotification.java @@ -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 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 data) { + this.data = data; + } + + protected abstract void setNotificationActions(); +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaLocalNotificationFactory.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaLocalNotificationFactory.java new file mode 100644 index 000000000..3abb8db9d --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaLocalNotificationFactory.java @@ -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; + } + +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/PartyInviteLocalNotification.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/PartyInviteLocalNotification.java new file mode 100644 index 000000000..3b1e2d5e3 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/PartyInviteLocalNotification.java @@ -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); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/PushNotificationManager.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/PushNotificationManager.java new file mode 100644 index 000000000..8b0ec84a0 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/PushNotificationManager.java @@ -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 pushDeviceData = new HashMap(); + 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); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/QuestBegunLocalNotification.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/QuestBegunLocalNotification.java new file mode 100644 index 000000000..f49d32ec5 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/QuestBegunLocalNotification.java @@ -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); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/QuestInviteLocalNotification.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/QuestInviteLocalNotification.java new file mode 100644 index 000000000..87ddbc61e --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/QuestInviteLocalNotification.java @@ -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); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/ReceivedGemsGiftLocalNotification.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/ReceivedGemsGiftLocalNotification.java new file mode 100644 index 000000000..7e4d394c4 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/ReceivedGemsGiftLocalNotification.java @@ -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); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/ReceivedPrivateMessageLocalNotification.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/ReceivedPrivateMessageLocalNotification.java new file mode 100644 index 000000000..e36f9b605 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/ReceivedPrivateMessageLocalNotification.java @@ -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); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/ReceivedSubscriptionGiftLocalNotification.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/ReceivedSubscriptionGiftLocalNotification.java new file mode 100644 index 000000000..e2658ff6f --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/ReceivedSubscriptionGiftLocalNotification.java @@ -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); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/WonChallengeLocalNotification.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/WonChallengeLocalNotification.java new file mode 100644 index 000000000..62e709169 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/WonChallengeLocalNotification.java @@ -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); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/LocalNotificationActionReceiver.java b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/LocalNotificationActionReceiver.java new file mode 100644 index 000000000..506b41c67 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/LocalNotificationActionReceiver.java @@ -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 -> {}); + } + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.java index 364968838..a36181a04 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.java @@ -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() { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.java index e92d26178..a893d5615 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.java @@ -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, 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, 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, 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, Ha @Subscribe public void onEvent(final BuyGemItemCommand event) { - Observable 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 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, Ha } @Subscribe - public void onEvent(OpenGemPurchaseFragmentCommand cmd) { + public void openGemPurchaseFragment(OpenGemPurchaseFragmentCommand cmd) { drawer.setSelection(MainDrawerBuilder.SIDEBAR_PURCHASE); } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PrefsActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PrefsActivity.java index cf74940b9..37e2f01d2 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PrefsActivity.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PrefsActivity.java @@ -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; } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.java index 559a5e7be..18431b772 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.java @@ -162,6 +162,10 @@ public class ShopRecyclerAdapter extends RecyclerView.Adapter { this.shop = shop; this.adapter.setShop(shop); diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PreferencesFragment.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PreferencesFragment.java index 1498261ce..72bfc2cf7 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PreferencesFragment.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PreferencesFragment.java @@ -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 userTransactionListener = new TransactionListener() { @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(); + } } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PushNotificationsPreferencesFragment.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PushNotificationsPreferencesFragment.java new file mode 100644 index 000000000..92f1074e0 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PushNotificationsPreferencesFragment.java @@ -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) { + + } +} \ No newline at end of file diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatListFragment.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatListFragment.java index 821576520..62c4bcc5b 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatListFragment.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatListFragment.java @@ -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()); diff --git a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/api/ApiService.java b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/api/ApiService.java index 9c283b46e..7bc6c5031 100644 --- a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/api/ApiService.java +++ b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/api/ApiService.java @@ -75,6 +75,9 @@ public interface ApiService { @POST("user/buy-mystery-set/{key}") Observable purchaseMysterySet(@Path("key") String itemKey); + @POST("user/buy-quest/{key}") + Observable purchaseQuest(@Path("key") String key); + @POST("user/sell/{type}/{key}") Observable sellItem(@Path("type") String itemType, @Path("key") String itemKey); @@ -205,6 +208,9 @@ public interface ApiService { @POST("groups/{gid}/invite") Observable inviteToGroup(@Path("gid") String groupId, @Body Map inviteData); + @POST("groups/{gid}/reject-invite") + Observable rejectGroupInvite(@Path("gid") String groupId); + @POST("groups/{gid}/quests/accept") Observable acceptQuest(@Path("gid") String groupId); @@ -235,10 +241,17 @@ public interface ApiService { @POST("members/send-private-message") Observable postPrivateMessage(@Body HashMap messageDetails); - + @GET("shops/{identifier}") Observable fetchShopInventory(@Path("identifier") String identifier); + //Push notifications + @POST("user/push-devices") + Observable addPushDevice(@Body Map pushDeviceData); + + @DELETE("user/push-devices/{regId}") + Observable deletePushDevice(@Path("regId") String regId); + //DEBUG: These calls only work on a local development server @POST("debug/add-ten-gems") diff --git a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/HabitRPGUser.java b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/HabitRPGUser.java index 211d0ee4a..1092cfd45 100644 --- a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/HabitRPGUser.java +++ b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/HabitRPGUser.java @@ -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 pushDevices = new ArrayList(); + 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 getPushDevices() { + return this.pushDevices; + } + + public void setPushDevices(List 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 allTasks = new ArrayList(); if (dailys != null) { diff --git a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/PushDevice.java b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/PushDevice.java new file mode 100644 index 000000000..c70ec2a06 --- /dev/null +++ b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/PushDevice.java @@ -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; + } +} diff --git a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/ShopItem.java b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/ShopItem.java index b7c1b47f1..d844cc194 100644 --- a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/ShopItem.java +++ b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/ShopItem.java @@ -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; + } + } } diff --git a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/invitations/GuildInvite.java b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/invitations/GuildInvite.java new file mode 100644 index 000000000..e3d1a7291 --- /dev/null +++ b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/invitations/GuildInvite.java @@ -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; + } +} diff --git a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/invitations/Invitations.java b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/invitations/Invitations.java new file mode 100644 index 000000000..d09fda0e8 --- /dev/null +++ b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/invitations/Invitations.java @@ -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 guilds = new ArrayList(); + + /** + * + * @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 getGuilds() { + return guilds; + } + + /** + * + * @param guilds + * The guilds + */ + public void setGuilds(List guilds) { + this.guilds = guilds; + } +} diff --git a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/invitations/PartyInvite.java b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/invitations/PartyInvite.java new file mode 100644 index 000000000..05bb1e08c --- /dev/null +++ b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/invitations/PartyInvite.java @@ -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; + } +} diff --git a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/tasks/Task.java b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/tasks/Task.java index 8434e1df0..1fa61fb47 100644 --- a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/tasks/Task.java +++ b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/tasks/Task.java @@ -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)) { diff --git a/Habitica/src/test/java/com/habitrpg/android/habitica/api/SocialAPITests.java b/Habitica/src/test/java/com/habitrpg/android/habitica/api/SocialAPITests.java index c5f6214c9..bf57867f3 100644 --- a/Habitica/src/test/java/com/habitrpg/android/habitica/api/SocialAPITests.java +++ b/Habitica/src/test/java/com/habitrpg/android/habitica/api/SocialAPITests.java @@ -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 messagesIDs; diff --git a/Habitica/src/test/java/com/habitrpg/android/habitica/api/TagAPITests.java b/Habitica/src/test/java/com/habitrpg/android/habitica/api/TagAPITests.java index 2e941984e..498bedba6 100644 --- a/Habitica/src/test/java/com/habitrpg/android/habitica/api/TagAPITests.java +++ b/Habitica/src/test/java/com/habitrpg/android/habitica/api/TagAPITests.java @@ -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 { diff --git a/Habitica/src/test/java/com/habitrpg/android/habitica/api/TaskAPITests.java b/Habitica/src/test/java/com/habitrpg/android/habitica/api/TaskAPITests.java index b67faf621..82488ff96 100644 --- a/Habitica/src/test/java/com/habitrpg/android/habitica/api/TaskAPITests.java +++ b/Habitica/src/test/java/com/habitrpg/android/habitica/api/TaskAPITests.java @@ -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 { diff --git a/Habitica/src/test/java/com/habitrpg/android/habitica/api/UserAPITests.java b/Habitica/src/test/java/com/habitrpg/android/habitica/api/UserAPITests.java index 7b3d394a9..215872af2 100644 --- a/Habitica/src/test/java/com/habitrpg/android/habitica/api/UserAPITests.java +++ b/Habitica/src/test/java/com/habitrpg/android/habitica/api/UserAPITests.java @@ -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 { diff --git a/Habitica/src/test/java/com/habitrpg/android/habitica/helpers/RemindersManagerTest.java b/Habitica/src/test/java/com/habitrpg/android/habitica/helpers/RemindersManagerTest.java index a8620a660..31feb3fa6 100644 --- a/Habitica/src/test/java/com/habitrpg/android/habitica/helpers/RemindersManagerTest.java +++ b/Habitica/src/test/java/com/habitrpg/android/habitica/helpers/RemindersManagerTest.java @@ -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") diff --git a/Habitica/src/test/java/com/habitrpg/android/habitica/helpers/TaskAlarmManagerTest.java b/Habitica/src/test/java/com/habitrpg/android/habitica/helpers/TaskAlarmManagerTest.java index 9b2c7be36..ec08f64a2 100644 --- a/Habitica/src/test/java/com/habitrpg/android/habitica/helpers/TaskAlarmManagerTest.java +++ b/Habitica/src/test/java/com/habitrpg/android/habitica/helpers/TaskAlarmManagerTest.java @@ -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 reminders = new ArrayList(); 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 reminders = new ArrayList(); 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 reminders = new ArrayList(); 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 reminders = new ArrayList(); 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 reminders = new ArrayList(); 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 reminders = new ArrayList(); 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; diff --git a/README.md b/README.md index e671d23ce..284422afb 100644 --- a/README.md +++ b/README.md @@ -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" /> -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. diff --git a/build.gradle b/build.gradle index 2ca638a10..0e19da6e8 100644 --- a/build.gradle +++ b/build.gradle @@ -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' } } diff --git a/translations/store_strings-bg.xml b/translations/store_strings-bg.xml new file mode 100644 index 000000000..2f0fe195e --- /dev/null +++ b/translations/store_strings-bg.xml @@ -0,0 +1,11 @@ + + + Превърнете живота си в игра за повече мотивация и по-добра организация! + НОВОТО приложение на Хабитика! Пренаписано изцяло за по-добро преживяване и повече функционалности. + +Превърнете живота си в игра за повече мотивация и по-добра организация! С Хабитика е лесно да се забавлявате, докато изпълнявате целите си. + +Въведете навиците си, ежедневните си цели и дългосрочните си задачи, а след това си създайте герой. Изпълнявайте задачите си, за да вдигате нивото на героя си и отключвате функционалности като екипировка, любимци, умения и дори мисии! Бийте се с чудовища заедно с приятели, за да държите един другиго отговорен, и използвайте златото си за награди като екипировка, или дори свои собствени такива, като например да гледате епизод от любимия си сериал. Гъвкава, забавна и с голяма общност, Хабитика е перфектният начин да се мотивирате и да постигнете всичко, което искате. + +Версията в момента е „бета“, така че не се притеснявайте да ни изпращате отзивите си на mobile@habitica.com! А ако приложението Ви харесва, ще се радваме да оставите рецензията си. +