diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
index 23e0ed69e..296aaeec4 100644
--- a/.github/workflows/android.yml
+++ b/.github/workflows/android.yml
@@ -41,10 +41,10 @@ jobs:
arguments: testProdDebugUnitTest
# ui-test:
-# runs-on: macos-latest
+# runs-on: ubuntu-latest
# strategy:
# matrix:
-# api-level: [24, 26, 28, 29, 30, 31]
+# api-level: [28, 29, 30, 31, 32]
# steps:
# - uses: actions/checkout@v2
# - name: set up JDK 11
@@ -55,8 +55,6 @@ jobs:
# cache: gradle
# - name: Prepare
# run: ./.github/prepare-workflow
-# - name: Validate Gradle wrapper
-# uses: gradle/wrapper-validation-action@e6e38bacfdf1a337459f332974bb2327a31aaf4b
# - name: run tests
# uses: reactivecircus/android-emulator-runner@v2
# with:
diff --git a/Habitica/AndroidManifest.xml b/Habitica/AndroidManifest.xml
index 3427583b4..a7962ee36 100644
--- a/Habitica/AndroidManifest.xml
+++ b/Habitica/AndroidManifest.xml
@@ -17,7 +17,7 @@
android:name=".HabiticaApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
- android:roundIcon="@mipmap/ic_launcher_round"
+ android:roundIcon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/MainAppTheme"
android:fullBackupContent="@xml/backup_descriptor"
diff --git a/Habitica/build.gradle b/Habitica/build.gradle
index c11905213..d59eefc20 100644
--- a/Habitica/build.gradle
+++ b/Habitica/build.gradle
@@ -7,6 +7,8 @@ plugins {
id 'com.google.firebase.crashlytics'
id 'androidx.navigation.safeargs'
id 'com.google.firebase.firebase-perf'
+
+ id 'jacoco-report-aggregation'
}
apply plugin: 'kotlin-android'
@@ -16,7 +18,6 @@ repositories {
mavenLocal()
mavenCentral()
google()
- maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
maven { url "https://jitpack.io" }
}
@@ -67,15 +68,19 @@ dependencies {
androidTestImplementation ('com.kaspersky.android-components:kaspresso:1.5.1') {
exclude module: "protobuf-lite"
}
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
+ androidTestImplementation "com.kaspersky.android-components:kaspresso-compose-support:1.5.1"
+
androidTestImplementation 'androidx.test:runner:1.5.2'
androidTestImplementation 'androidx.test:rules:1.5.0'
debugImplementation 'androidx.fragment:fragment-testing:1.5.5'
- androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test:core-ktx:1.5.0'
+ debugImplementation "androidx.test:monitor:1.6.1"
androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.5'
androidTestImplementation "io.mockk:mockk-android:$mockk_version"
+ androidTestImplementation "io.mockk:mockk-agent:$mockk_version"
androidTestImplementation "io.kotest:kotest-assertions-core:$kotest_version"
+ androidTestImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
+
androidTestUtil("androidx.test:orchestrator:1.4.2")
implementation 'com.facebook.shimmer:shimmer:0.5.0'
@@ -285,6 +290,10 @@ android {
disable 'MissingTranslation', 'InvalidPackage'
enable 'LogConditional', 'IconExpectedSize', 'MissingRegistered', 'TypographyQuotes'
}
+
+ packagingOptions {
+ resources.excludes.add("META-INF/*")
+ }
}
android.testOptions {
diff --git a/Habitica/proguard-rules.pro b/Habitica/proguard-rules.pro
index ff92c0284..b14baf632 100644
--- a/Habitica/proguard-rules.pro
+++ b/Habitica/proguard-rules.pro
@@ -65,7 +65,7 @@
-keep class io.realm.internal.Keep
-keep @io.realm.internal.Keep class *
-dontwarn javax.**
--keep io.realm.**
+-dontwarn io.realm.**
#crashlytic
-keepattributes SourceFile,LineNumberTable
diff --git a/Habitica/res/drawable/ic_launcher_foreground.xml b/Habitica/res/drawable/ic_launcher_foreground.xml
index 4fc0d90b9..a481c0d49 100644
--- a/Habitica/res/drawable/ic_launcher_foreground.xml
+++ b/Habitica/res/drawable/ic_launcher_foreground.xml
@@ -5,14 +5,14 @@
android:viewportHeight="430">
diff --git a/Habitica/res/drawable/ic_launcher_monochrome.xml b/Habitica/res/drawable/ic_launcher_monochrome.xml
new file mode 100644
index 000000000..a9ff16a44
--- /dev/null
+++ b/Habitica/res/drawable/ic_launcher_monochrome.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/Habitica/res/mipmap-anydpi-v26/ic_launcher.xml b/Habitica/res/mipmap-anydpi-v26/ic_launcher.xml
index 7353dbd1f..1413a3145 100644
--- a/Habitica/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/Habitica/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -2,4 +2,5 @@
+
\ No newline at end of file
diff --git a/Habitica/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Habitica/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 7353dbd1f..000000000
--- a/Habitica/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/Habitica/res/mipmap-hdpi/ic_launcher_round.webp b/Habitica/res/mipmap-hdpi/ic_launcher_round.webp
deleted file mode 100644
index 066112991..000000000
Binary files a/Habitica/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/Habitica/res/mipmap-mdpi/ic_launcher_round.webp b/Habitica/res/mipmap-mdpi/ic_launcher_round.webp
deleted file mode 100644
index ea8d0062b..000000000
Binary files a/Habitica/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/Habitica/res/mipmap-xhdpi/ic_launcher_round.webp b/Habitica/res/mipmap-xhdpi/ic_launcher_round.webp
deleted file mode 100644
index 618438ce0..000000000
Binary files a/Habitica/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/Habitica/res/mipmap-xxhdpi/ic_launcher_round.webp b/Habitica/res/mipmap-xxhdpi/ic_launcher_round.webp
deleted file mode 100644
index c56f1c90f..000000000
Binary files a/Habitica/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/Habitica/res/mipmap-xxxhdpi/ic_launcher_round.webp b/Habitica/res/mipmap-xxxhdpi/ic_launcher_round.webp
deleted file mode 100644
index 0f31f5c50..000000000
Binary files a/Habitica/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/Habitica/res/values/colors.xml b/Habitica/res/values/colors.xml
index 3d62b6b47..d030d9199 100644
--- a/Habitica/res/values/colors.xml
+++ b/Habitica/res/values/colors.xml
@@ -63,7 +63,8 @@
#313131
#4cfedead
#D9784A00
- #331960
+ #5A2CC8
+ #E0D3FF
@color/content_background_offset
@color/window_background
@color/offset_background_30
diff --git a/Habitica/src/androidTest/java/com/habitrpg/android/habitica/HabiticaTestCase.kt b/Habitica/src/androidTest/java/com/habitrpg/android/habitica/HabiticaTestCase.kt
index 9e8e1d5d7..a18aac87d 100644
--- a/Habitica/src/androidTest/java/com/habitrpg/android/habitica/HabiticaTestCase.kt
+++ b/Habitica/src/androidTest/java/com/habitrpg/android/habitica/HabiticaTestCase.kt
@@ -13,7 +13,7 @@ import com.habitrpg.android.habitica.data.TaskRepository
import com.habitrpg.android.habitica.data.TutorialRepository
import com.habitrpg.android.habitica.data.UserRepository
import com.habitrpg.android.habitica.helpers.AppConfigManager
-import com.habitrpg.android.habitica.helpers.ExceptionHandler
+import com.habitrpg.common.habitica.helpers.ExceptionHandler
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.helpers.NotificationsManager
import com.habitrpg.android.habitica.helpers.SoundManager
@@ -26,7 +26,7 @@ import com.habitrpg.android.habitica.models.inventory.Food
import com.habitrpg.android.habitica.models.inventory.HatchingPotion
import com.habitrpg.android.habitica.models.inventory.QuestContent
import com.habitrpg.android.habitica.models.user.User
-import com.habitrpg.android.habitica.proxy.AnalyticsManager
+import com.habitrpg.common.habitica.helpers.AnalyticsManager
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
import com.habitrpg.common.habitica.api.HostConfig
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
diff --git a/Habitica/src/androidTest/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivityTest.kt b/Habitica/src/androidTest/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivityTest.kt
index 9b01fd857..82af0c4ca 100644
--- a/Habitica/src/androidTest/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivityTest.kt
+++ b/Habitica/src/androidTest/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivityTest.kt
@@ -24,13 +24,14 @@ import io.github.kakaocup.kakao.spinner.KSpinner
import io.github.kakaocup.kakao.spinner.KSpinnerItem
import io.github.kakaocup.kakao.text.KButton
import io.github.kakaocup.kakao.toolbar.KToolbar
+import io.mockk.coVerify
import io.mockk.every
import io.mockk.justRun
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.slot
import io.mockk.verify
-import io.reactivex.rxjava3.core.Flowable
+import kotlinx.coroutines.flow.flowOf
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -162,7 +163,7 @@ class TaskFormActivityTest : ActivityTestCase() {
device.activities.isCurrent(TaskFormActivity::class.java)
textEditText.typeText("New Habit")
KButton { withId(R.id.action_save) }.click()
- verify(exactly = 1) { taskRepository.createTaskInBackground(any(), assignChanges) }
+ verify(exactly = 1) { taskRepository.createTaskInBackground(any(), emptyMap()) }
}
}
@@ -176,7 +177,7 @@ class TaskFormActivityTest : ActivityTestCase() {
val bundle = Bundle()
bundle.putString(TaskFormActivity.TASK_TYPE_KEY, TaskType.HABIT.value)
bundle.putString(TaskFormActivity.TASK_ID_KEY, task.id!!)
- every { taskRepository.getUnmanagedTask(any()) } returns Flowable.just(task)
+ every { taskRepository.getUnmanagedTask(any()) } returns flowOf(task)
val intent = Intent(ApplicationProvider.getApplicationContext(), TaskFormActivity::class.java)
intent.putExtras(bundle)
@@ -184,7 +185,7 @@ class TaskFormActivityTest : ActivityTestCase() {
screen {
toolbar {
KView { withId(R.id.action_save) }.click()
- verify(exactly = 1) { taskRepository.updateTaskInBackground(any(), assignChanges) }
+ verify(exactly = 1) { taskRepository.updateTaskInBackground(any(), emptyMap()) }
}
}
}
@@ -200,7 +201,7 @@ class TaskFormActivityTest : ActivityTestCase() {
bundle.putString(TaskFormActivity.TASK_TYPE_KEY, TaskType.DAILY.value)
bundle.putString(TaskFormActivity.TASK_TYPE_KEY, TaskType.DAILY.value)
bundle.putString(TaskFormActivity.TASK_ID_KEY, task.id!!)
- every { taskRepository.getUnmanagedTask(any()) } returns Flowable.just(task)
+ every { taskRepository.getUnmanagedTask(any()) } returns flowOf(task)
val intent = Intent(ApplicationProvider.getApplicationContext(), TaskFormActivity::class.java)
intent.putExtras(bundle)
@@ -209,7 +210,7 @@ class TaskFormActivityTest : ActivityTestCase() {
device.activities.isCurrent(TaskFormActivity::class.java)
KButton { withId(R.id.action_delete) }.click()
KButton { withText(R.string.delete_task) }.click()
- verify(exactly = 1) { taskRepository.deleteTask(task.id!!) }
+ coVerify(exactly = 1) { taskRepository.deleteTask(task.id!!) }
}
}
@@ -287,8 +288,8 @@ class TaskFormActivityTest : ActivityTestCase() {
val bundle = Bundle()
bundle.putString(TaskFormActivity.TASK_TYPE_KEY, TaskType.DAILY.value)
bundle.putString(TaskFormActivity.TASK_ID_KEY, task.id!!)
- every { taskRepository.getUnmanagedTask(any()) } returns Flowable.just(task)
- justRun { taskRepository.updateTaskInBackground(capture(taskSlot), assignChanges) }
+ every { taskRepository.getUnmanagedTask(any()) } returns flowOf(task)
+ justRun { taskRepository.updateTaskInBackground(capture(taskSlot), emptyMap()) }
val intent = Intent(ApplicationProvider.getApplicationContext(), TaskFormActivity::class.java)
intent.putExtras(bundle)
@@ -313,7 +314,7 @@ class TaskFormActivityTest : ActivityTestCase() {
typeText("3")
}
KButton { withId(R.id.action_save) }.click()
- verify { taskRepository.updateTaskInBackground(any(), assignChanges) }
+ verify { taskRepository.updateTaskInBackground(any(), emptyMap()) }
assert(taskSlot.captured.everyX == 3)
assert(taskSlot.captured.frequency == Frequency.WEEKLY)
}
diff --git a/Habitica/src/androidTest/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragmentTest.kt b/Habitica/src/androidTest/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragmentTest.kt
index 8dd625cfa..96be8d381 100644
--- a/Habitica/src/androidTest/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragmentTest.kt
+++ b/Habitica/src/androidTest/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragmentTest.kt
@@ -22,12 +22,13 @@ import io.github.kakaocup.kakao.text.KTextView
import io.kotest.matchers.shouldBe
import io.mockk.CapturingSlot
import io.mockk.clearMocks
+import io.mockk.coEvery
+import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.spyk
-import io.mockk.verify
-import io.reactivex.rxjava3.core.Flowable
+import kotlinx.coroutines.flow.flowOf
import org.hamcrest.Matcher
import org.hamcrest.Matchers.isA
import org.junit.Test
@@ -70,10 +71,10 @@ class ItemScreen : Screen() {
internal class ItemRecyclerFragmentTest : FragmentTestCase(false) {
override fun makeFragment() {
every { inventoryRepository.getOwnedItems("eggs") } answers {
- Flowable.just(user.items?.eggs!!.filter { it.numberOwned > 0 })
+ flowOf(user.items?.eggs!!.filter { it.numberOwned > 0 })
}
every { inventoryRepository.getOwnedItems("hatchingPotions") } answers {
- Flowable.just(user.items?.hatchingPotions!!.filter { it.numberOwned > 0 })
+ flowOf(user.items?.hatchingPotions!!.filter { it.numberOwned > 0 })
}
fragment = spyk()
fragment.shouldInitializeComponent = false
@@ -113,10 +114,10 @@ internal class ItemRecyclerFragmentTest : FragmentTestCase 0 }
items = (items + items).sortedBy { it.key }
- Flowable.just(items)
+ flowOf(items)
}
every { inventoryRepository.getItems(Food::class.java, any()) } answers {
- Flowable.just((content.eggs + content.eggs).sortedBy { it.key })
+ flowOf((content.eggs + content.eggs).sortedBy { it.key })
}
fragment.itemType = "food"
val foundItems = mutableListOf()
@@ -134,7 +135,7 @@ internal class ItemRecyclerFragmentTest : FragmentTestCase()
- every { hatchPetUseCase.callInteractor(capture(slot)) } returns mockk(relaxed = true)
+ coEvery { hatchPetUseCase.callInteractor(capture(slot)) } returns mockk(relaxed = true)
fragment.itemType = "eggs"
launchFragment()
screen {
@@ -142,7 +143,7 @@ internal class ItemRecyclerFragmentTest : FragmentTestCase { withDescendant { withText("Wolf") } }.click()
KView { withText(R.string.hatch_with_potion) }.click()
KView { withText("Shade") }.click()
- verify { hatchPetUseCase.callInteractor(any()) }
+ coVerify { hatchPetUseCase.callInteractor(any()) }
slot.captured.egg.key shouldBe "Wolf"
slot.captured.potion.key shouldBe "Shade"
}
@@ -152,7 +153,7 @@ internal class ItemRecyclerFragmentTest : FragmentTestCase()
- every { hatchPetUseCase.callInteractor(capture(slot)) } returns mockk(relaxed = true)
+ coEvery { hatchPetUseCase.callInteractor(capture(slot)) } returns mockk(relaxed = true)
fragment.itemType = "hatchingPotions"
launchFragment()
screen {
@@ -160,7 +161,7 @@ internal class ItemRecyclerFragmentTest : FragmentTestCase { withDescendant { withText("Shade") } }.click()
KView { withText(R.string.hatch_egg) }.click()
KView { withText("Wolf") }.click()
- verify { hatchPetUseCase.callInteractor(any()) }
+ coVerify { hatchPetUseCase.callInteractor(any()) }
slot.captured.egg.key shouldBe "Wolf"
slot.captured.potion.key shouldBe "Shade"
}
@@ -170,19 +171,19 @@ internal class ItemRecyclerFragmentTest : FragmentTestCase()
- every { inventoryRepository.sellItem(capture(slot)) } returns mockk(relaxed = true)
+ coEvery { inventoryRepository.sellItem(capture(slot)) } returns mockk(relaxed = true)
fragment.itemType = "eggs"
launchFragment()
screen {
recycler {
childWith { withDescendant { withText("Cactus") } }.click()
KView { withText("Sell (3 Gold)") }.click()
- verify { inventoryRepository.sellItem(any()) }
+ coVerify { inventoryRepository.sellItem(any()) }
slot.captured.key shouldBe "Cactus"
childWith { withDescendant { withText("Panda Cub") } }.click()
KView { withText("Sell (3 Gold)") }.click()
- verify { inventoryRepository.sellItem(any()) }
+ coVerify { inventoryRepository.sellItem(any()) }
slot.captured.key shouldBe "PandaCub"
}
}
diff --git a/Habitica/src/androidTest/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragmentTest.kt b/Habitica/src/androidTest/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragmentTest.kt
index ce7701288..73e5383fa 100644
--- a/Habitica/src/androidTest/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragmentTest.kt
+++ b/Habitica/src/androidTest/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragmentTest.kt
@@ -14,10 +14,11 @@ import io.github.kakaocup.kakao.recycler.KRecyclerItem
import io.github.kakaocup.kakao.recycler.KRecyclerView
import io.github.kakaocup.kakao.screen.Screen
import io.github.kakaocup.kakao.text.KTextView
+import io.mockk.coEvery
+import io.mockk.coVerify
import io.mockk.every
import io.mockk.spyk
-import io.mockk.verify
-import io.reactivex.rxjava3.core.Flowable
+import kotlinx.coroutines.flow.flowOf
import org.hamcrest.Matcher
import org.junit.Test
@@ -54,7 +55,7 @@ internal class TaskRecyclerViewFragmentTest : FragmentTestCase(1) {
KView(this.parent) {
withId(R.id.checkBoxHolder)
}.click()
- verify(exactly = 1) { taskRepository.taskChecked(any(), dailies[1].id!!, false, false, any()) }
+ coVerify(exactly = 1) { taskRepository.taskChecked(any(), dailies[1].id!!, false, false, any()) }
}
}
}
@@ -245,7 +246,7 @@ internal class TaskRecyclerViewFragmentTest : FragmentTestCase> {
@Throws(JsonParseException::class)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/AvatarStatsWidgetProvider.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/AvatarStatsWidgetProvider.kt
index 05212ec49..d660bc611 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/AvatarStatsWidgetProvider.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/AvatarStatsWidgetProvider.kt
@@ -11,7 +11,7 @@ import android.widget.RemoteViews
import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.withImmutableFlag
-import com.habitrpg.android.habitica.helpers.launchCatching
+import com.habitrpg.common.habitica.helpers.launchCatching
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetProvider.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetProvider.kt
index 2684fd174..f03306d11 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetProvider.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetProvider.kt
@@ -8,7 +8,7 @@ import android.widget.RemoteViews
import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.TaskRepository
-import com.habitrpg.android.habitica.helpers.ExceptionHandler
+import com.habitrpg.common.habitica.helpers.ExceptionHandler
import com.habitrpg.shared.habitica.models.responses.TaskDirection
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.flow.firstOrNull
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetService.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetService.kt
index c4ee5e4c2..6f2a8e705 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetService.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetService.kt
@@ -16,7 +16,7 @@ import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.TaskRepository
import com.habitrpg.android.habitica.extensions.withImmutableFlag
-import com.habitrpg.android.habitica.helpers.ExceptionHandler
+import com.habitrpg.common.habitica.helpers.ExceptionHandler
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.common.habitica.helpers.MarkdownParser
import com.habitrpg.shared.habitica.models.responses.TaskDirection
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListFactory.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListFactory.kt
index b3269f2f8..b088f1a08 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListFactory.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListFactory.kt
@@ -10,7 +10,7 @@ import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.TaskRepository
import com.habitrpg.android.habitica.data.UserRepository
-import com.habitrpg.android.habitica.helpers.ExceptionHandler
+import com.habitrpg.common.habitica.helpers.ExceptionHandler
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.common.habitica.helpers.MarkdownParser
import com.habitrpg.shared.habitica.models.tasks.TaskType
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListWidgetProvider.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListWidgetProvider.kt
index 72bda08f9..d920c48ab 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListWidgetProvider.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListWidgetProvider.kt
@@ -12,7 +12,7 @@ import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.TaskRepository
import com.habitrpg.android.habitica.extensions.withImmutableFlag
import com.habitrpg.android.habitica.extensions.withMutableFlag
-import com.habitrpg.android.habitica.helpers.ExceptionHandler
+import com.habitrpg.common.habitica.helpers.ExceptionHandler
import com.habitrpg.android.habitica.ui.activities.MainActivity
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.flow.firstOrNull
diff --git a/Habitica/src/release/java/com/habitrpg/android/habitica/ReleaseDeveloperModule.kt b/Habitica/src/release/java/com/habitrpg/android/habitica/ReleaseDeveloperModule.kt
index 4805b5f74..aa1742cc3 100644
--- a/Habitica/src/release/java/com/habitrpg/android/habitica/ReleaseDeveloperModule.kt
+++ b/Habitica/src/release/java/com/habitrpg/android/habitica/ReleaseDeveloperModule.kt
@@ -2,7 +2,7 @@ package com.habitrpg.android.habitica
import android.content.Context
import com.habitrpg.android.habitica.modules.DeveloperModule
-import com.habitrpg.android.habitica.proxy.AnalyticsManager
+import com.habitrpg.common.habitica.helpers.AnalyticsManager
import com.habitrpg.android.habitica.proxy.AnalyticsManagerImpl
class ReleaseDeveloperModule : DeveloperModule() {
diff --git a/Habitica/src/release/java/com/habitrpg/android/habitica/proxy/AnalyticsManagerImpl.kt b/Habitica/src/release/java/com/habitrpg/android/habitica/proxy/AnalyticsManagerImpl.kt
index 9faf96f41..6f957f2e3 100644
--- a/Habitica/src/release/java/com/habitrpg/android/habitica/proxy/AnalyticsManagerImpl.kt
+++ b/Habitica/src/release/java/com/habitrpg/android/habitica/proxy/AnalyticsManagerImpl.kt
@@ -5,6 +5,7 @@ import android.os.Bundle
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.habitrpg.android.habitica.helpers.AmplitudeManager
+import com.habitrpg.common.habitica.helpers.AnalyticsManager
class AnalyticsManagerImpl(context: Context) : AnalyticsManager {
diff --git a/Habitica/src/test/java/com/habitrpg/android/habitica/helpers/NumberAbbreviatorTest.kt b/Habitica/src/test/java/com/habitrpg/android/habitica/helpers/NumberAbbreviatorTest.kt
index 136c8a23a..a932b70ef 100644
--- a/Habitica/src/test/java/com/habitrpg/android/habitica/helpers/NumberAbbreviatorTest.kt
+++ b/Habitica/src/test/java/com/habitrpg/android/habitica/helpers/NumberAbbreviatorTest.kt
@@ -4,6 +4,7 @@ import android.content.Context
import com.habitrpg.android.habitica.R
import com.habitrpg.common.habitica.helpers.NumberAbbreviator.abbreviate
import io.kotest.core.spec.style.StringSpec
+import io.kotest.datatest.withData
import io.kotest.matchers.shouldBe
import io.mockk.clearMocks
import io.mockk.every
@@ -12,50 +13,39 @@ import java.util.Locale
class NumberAbbreviatorTest : StringSpec({
val mockContext = mockk()
- beforeEach {
+ beforeSpec {
Locale.setDefault(Locale.US)
every { mockContext.getString(R.string.thousand_abbrev) } returns "k"
every { mockContext.getString(R.string.million_abbrev) } returns "m"
every { mockContext.getString(R.string.billion_abbrev) } returns "b"
every { mockContext.getString(R.string.trillion_abbrev) } returns "t"
+ every { mockContext.getString(R.string.quadrillion_abbrev) } returns "q"
}
- "should not abbreviate small numbers" {
- abbreviate(mockContext, 215.0, 2) shouldBe "215"
- abbreviate(mockContext, 2.05, 2) shouldBe "2.05"
+ withData(
+ Triple(215.0, "215", 2),
+ Triple(2.05, "2.05", 2),
+ Triple(5.406, "5.4", 2),
+ Triple(-20.42, "-20.42", 2),
+ Triple(2550.0, "2.55k", 2),
+ Triple(-1020.42, "-1.02k", 2),
+ Triple(9990000.0, "9.99m", 2),
+ Triple(1990000000.0, "1.99b", 2),
+ Triple(1990000000000.0, "1.99t", 2),
+ Triple(-1990000000.42, "-1.99b", 2),
+ Triple(1000.0, "1k", 2),
+ Triple(1500.0, "1.5k", 2),
+ Triple(1500.0, "1k", 0),
+ Triple(-1302.42, "-1.3k", 2),
+ Triple(9999.0, "9.99k", 2),
+ Triple(-20.42, "-20", 0),
+ Triple(40.2412, "40", 0),
+ Triple(0.5, "0.5", 0),
+ Triple(0.328, "0.32", 0),
+ Triple(-0.99, "-0.99", 0)
+ ) { (input, output, decimals) ->
+ abbreviate(mockContext, input, decimals) shouldBe output
}
- "should abbreviate thousands" {
- abbreviate(mockContext, 1550.0, 2) shouldBe "1.55k"
- }
-
- "should abbreviate millions" {
- abbreviate(mockContext, 9990000.0, 2) shouldBe "9.99m"
- }
-
- "should abbreviate billions" {
- abbreviate(mockContext, 1990000000.0, 2) shouldBe "1.99b"
- }
-
- "should abbreviate trillions" {
- abbreviate(mockContext, 1990000000000.0, 2) shouldBe "1.99t"
- }
-
- "should abbreviate thousands without additional decimals" {
- abbreviate(mockContext, 1000.0, 2) shouldBe "1k"
- abbreviate(mockContext, 1500.0, 2) shouldBe "1.5k"
- abbreviate(mockContext, 1500.0, 0) shouldBe "1k"
- }
-
- "should round correctly" {
- abbreviate(mockContext, 9999.0, 2) shouldBe "9.99k"
- }
-
- "should force decimals for numbers between -1 and 1" {
- abbreviate(mockContext, 0.5, 0) shouldBe "0.5"
- abbreviate(mockContext, 0.3248, 0) shouldBe "0.32"
- abbreviate(mockContext, -0.99, 0) shouldBe "-0.99"
- }
-
- afterEach { clearMocks(mockContext) }
+ afterSpec { clearMocks(mockContext) }
})
diff --git a/Habitica/src/test/java/com/habitrpg/android/habitica/utils/DateDeserializerTest.kt b/Habitica/src/test/java/com/habitrpg/android/habitica/utils/DateDeserializerTest.kt
index a730571ee..73eae5a80 100644
--- a/Habitica/src/test/java/com/habitrpg/android/habitica/utils/DateDeserializerTest.kt
+++ b/Habitica/src/test/java/com/habitrpg/android/habitica/utils/DateDeserializerTest.kt
@@ -6,25 +6,13 @@ import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import io.kotest.core.spec.style.WordSpec
import io.kotest.matchers.shouldBe
-import java.lang.reflect.Type
+import io.mockk.mockk
import java.util.Date
class DateDeserializerTest : WordSpec({
val deserializer = DateDeserializer()
- val deserializationContext = object : JsonDeserializationContext {
- override fun deserialize(json: JsonElement, typeOfT: Type): T? {
- return null
- }
- }
- val serializationContext = object : JsonSerializationContext {
- override fun serialize(src: Any): JsonElement? {
- return null
- }
-
- override fun serialize(src: Any, typeOfSrc: Type): JsonElement? {
- return null
- }
- }
+ val deserializationContext: JsonDeserializationContext = mockk(relaxed = true)
+ val serializationContext: JsonSerializationContext = mockk(relaxed = true)
val referenceTimestamp: Long = 1443445200000
"deserialize" should {
diff --git a/build.gradle b/build.gradle
index a72bb46a5..0c5408e6a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -42,7 +42,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:7.4.1'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
classpath 'com.google.gms:google-services:4.3.15'
- classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2'
+ classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.4'
classpath "io.realm:realm-gradle-plugin:10.11.0"
classpath("io.realm.kotlin:gradle-plugin:$realm_version")
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/Base-Extensions.kt b/common/src/main/java/com/habitrpg/common/habitica/extensions/Base-Extensions.kt
similarity index 87%
rename from Habitica/src/main/java/com/habitrpg/android/habitica/extensions/Base-Extensions.kt
rename to common/src/main/java/com/habitrpg/common/habitica/extensions/Base-Extensions.kt
index d2514ac9c..651c60e54 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/Base-Extensions.kt
+++ b/common/src/main/java/com/habitrpg/common/habitica/extensions/Base-Extensions.kt
@@ -1,6 +1,6 @@
package com.habitrpg.android.habitica.extensions
-import com.habitrpg.android.habitica.helpers.launchCatching
+import com.habitrpg.common.habitica.helpers.launchCatching
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.delay
import kotlin.time.DurationUnit
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/proxy/AnalyticsManager.kt b/common/src/main/java/com/habitrpg/common/habitica/helpers/AnalyticsManager.kt
similarity index 86%
rename from Habitica/src/main/java/com/habitrpg/android/habitica/proxy/AnalyticsManager.kt
rename to common/src/main/java/com/habitrpg/common/habitica/helpers/AnalyticsManager.kt
index 8bd54c8f9..f303bb1ae 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/proxy/AnalyticsManager.kt
+++ b/common/src/main/java/com/habitrpg/common/habitica/helpers/AnalyticsManager.kt
@@ -1,4 +1,4 @@
-package com.habitrpg.android.habitica.proxy
+package com.habitrpg.common.habitica.helpers
import android.os.Bundle
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/ExceptionHandler.kt b/common/src/main/java/com/habitrpg/common/habitica/helpers/ExceptionHandler.kt
similarity index 86%
rename from Habitica/src/main/java/com/habitrpg/android/habitica/helpers/ExceptionHandler.kt
rename to common/src/main/java/com/habitrpg/common/habitica/helpers/ExceptionHandler.kt
index 2a3b0c0a2..6782352b7 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/ExceptionHandler.kt
+++ b/common/src/main/java/com/habitrpg/common/habitica/helpers/ExceptionHandler.kt
@@ -1,13 +1,12 @@
-package com.habitrpg.android.habitica.helpers
+package com.habitrpg.common.habitica.helpers
import android.util.Log
-import com.habitrpg.android.habitica.BuildConfig
-import com.habitrpg.android.habitica.proxy.AnalyticsManager
+import coil.network.HttpException
+import com.habitrpg.common.habitica.BuildConfig
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
-import retrofit2.HttpException
import java.io.IOException
class ExceptionHandler {
@@ -17,7 +16,7 @@ class ExceptionHandler {
private var instance = ExceptionHandler()
- fun init(analyticsManager: AnalyticsManager) {
+ fun init(analyticsManager: AnalyticsManager? = null) {
instance.analyticsManager = analyticsManager
}
diff --git a/common/src/main/java/com/habitrpg/common/habitica/helpers/NumberAbbreviator.kt b/common/src/main/java/com/habitrpg/common/habitica/helpers/NumberAbbreviator.kt
index 42e9b25dc..889034a90 100644
--- a/common/src/main/java/com/habitrpg/common/habitica/helpers/NumberAbbreviator.kt
+++ b/common/src/main/java/com/habitrpg/common/habitica/helpers/NumberAbbreviator.kt
@@ -4,6 +4,7 @@ import android.content.Context
import com.habitrpg.common.habitica.R
import java.math.RoundingMode
import java.text.DecimalFormat
+import kotlin.math.abs
object NumberAbbreviator {
@@ -13,9 +14,10 @@ object NumberAbbreviator {
fun abbreviate(context: Context?, number: Double, numberOfDecimals: Int = 2, minForAbbrevation: Int = 0): String {
val decimalCount = if (number != 0.0 && number > -1 && number < 1 && numberOfDecimals == 0) 2 else numberOfDecimals
- var usedNumber = number
+ val absNumber = abs(number)
+ var usedNumber = absNumber
var counter = 0
- while (usedNumber >= 1000 && number >= minForAbbrevation) {
+ while (usedNumber >= 1000 && absNumber >= minForAbbrevation) {
counter++
usedNumber /= 1000
}
@@ -23,6 +25,9 @@ object NumberAbbreviator {
if (decimalCount > 0) {
pattern = ("$pattern.").padEnd(4 + decimalCount, '#')
}
+ if (number < 0) {
+ pattern = "-$pattern"
+ }
val formatter = DecimalFormat(
pattern + abbreviationForCounter(context, counter)
.replace(".", "")