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(".", "")