diff --git a/app/src/main/java/com/afollestad/nocknock/ui/DarkModeSwitchActivity.kt b/app/src/main/java/com/afollestad/nocknock/ui/DarkModeSwitchActivity.kt index c43b729..5785fd3 100644 --- a/app/src/main/java/com/afollestad/nocknock/ui/DarkModeSwitchActivity.kt +++ b/app/src/main/java/com/afollestad/nocknock/ui/DarkModeSwitchActivity.kt @@ -15,10 +15,15 @@ */ package com.afollestad.nocknock.ui +import android.content.res.Configuration +import android.os.Build import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import com.afollestad.nocknock.R import com.afollestad.nocknock.koin.PREF_DARK_MODE +import com.afollestad.nocknock.ui.NightMode.DISABLED +import com.afollestad.nocknock.ui.NightMode.ENABLED +import com.afollestad.nocknock.ui.NightMode.UNKNOWN import com.afollestad.nocknock.utilities.rx.attachLifecycle import com.afollestad.rxkprefs.Pref import org.koin.android.ext.android.inject @@ -35,13 +40,27 @@ abstract class DarkModeSwitchActivity : AppCompatActivity() { setTheme(themeRes()) super.onCreate(savedInstanceState) - darkModePref.observe() - .filter { it != isDarkModeEnabled } - .subscribe { - log("Theme changed, recreating Activity.") - recreate() - } - .attachLifecycle(this) + if (getCurrentNightMode() == UNKNOWN) { + darkModePref.observe() + .filter { it != isDarkModeEnabled } + .subscribe { + log("Theme changed, recreating Activity.") + recreate() + } + .attachLifecycle(this) + } + } + + protected fun getCurrentNightMode(): NightMode { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + return UNKNOWN + } + val currentNightMode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK + return when (currentNightMode) { + Configuration.UI_MODE_NIGHT_YES -> return ENABLED + Configuration.UI_MODE_NIGHT_NO -> return DISABLED + else -> UNKNOWN + } } protected fun isDarkMode() = darkModePref.get() diff --git a/app/src/main/java/com/afollestad/nocknock/ui/NightMode.kt b/app/src/main/java/com/afollestad/nocknock/ui/NightMode.kt new file mode 100644 index 0000000..fdbbcc2 --- /dev/null +++ b/app/src/main/java/com/afollestad/nocknock/ui/NightMode.kt @@ -0,0 +1,11 @@ +package com.afollestad.nocknock.ui + +/** @author Aidan Follestad (@afollestad) */ +enum class NightMode { + /** Night mode is on at the system level. */ + ENABLED, + /** Night mode is off at the system level. */ + DISABLED, + /** We don't know about night mode, fallback to custom impl. */ + UNKNOWN +} \ No newline at end of file diff --git a/app/src/main/java/com/afollestad/nocknock/ui/main/MainActivity.kt b/app/src/main/java/com/afollestad/nocknock/ui/main/MainActivity.kt index bd7796c..75647e2 100644 --- a/app/src/main/java/com/afollestad/nocknock/ui/main/MainActivity.kt +++ b/app/src/main/java/com/afollestad/nocknock/ui/main/MainActivity.kt @@ -33,6 +33,7 @@ import com.afollestad.nocknock.data.model.Site import com.afollestad.nocknock.dialogs.AboutDialog import com.afollestad.nocknock.notifications.NockNotificationManager import com.afollestad.nocknock.ui.DarkModeSwitchActivity +import com.afollestad.nocknock.ui.NightMode.UNKNOWN import com.afollestad.nocknock.utilities.providers.IntentProvider import com.afollestad.nocknock.utilities.ui.toast import com.afollestad.nocknock.viewUrl @@ -93,7 +94,13 @@ class MainActivity : DarkModeSwitchActivity() { toolbar.run { inflateMenu(R.menu.menu_main) menu.findItem(R.id.dark_mode) - .isChecked = isDarkMode() + .apply { + if (getCurrentNightMode() == UNKNOWN) { + isVisible = false + } else { + isChecked = isDarkMode() + } + } setOnMenuItemClickListener { item -> when (item.itemId) { R.id.about -> AboutDialog.show(this@MainActivity) diff --git a/app/src/main/java/com/afollestad/nocknock/ui/viewsite/ViewSiteViewModel.kt b/app/src/main/java/com/afollestad/nocknock/ui/viewsite/ViewSiteViewModel.kt index f41a9a7..b5c9f93 100644 --- a/app/src/main/java/com/afollestad/nocknock/ui/viewsite/ViewSiteViewModel.kt +++ b/app/src/main/java/com/afollestad/nocknock/ui/viewsite/ViewSiteViewModel.kt @@ -223,7 +223,7 @@ class ViewSiteViewModel( private fun getUpdatedDbModel(): Site? { val timeout = timeout.value ?: 10_000 - val cleanedTags = tags.value?.split(',')?.joinToString { it.trim() } ?: "" + val cleanedTags = tags.value?.split(',')?.joinToString(separator = ",") ?: "" val newSettings = site.settings!!.copy( validationIntervalMs = getCheckIntervalMs(), diff --git a/app/src/test/java/com/afollestad/nocknock/ui/addsite/AddSiteViewModelTest.kt b/app/src/test/java/com/afollestad/nocknock/ui/addsite/AddSiteViewModelTest.kt index b62f9a6..1319310 100644 --- a/app/src/test/java/com/afollestad/nocknock/ui/addsite/AddSiteViewModelTest.kt +++ b/app/src/test/java/com/afollestad/nocknock/ui/addsite/AddSiteViewModelTest.kt @@ -17,20 +17,21 @@ package com.afollestad.nocknock.ui.addsite import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.afollestad.nocknock.R +import com.afollestad.nocknock.data.model.Header import com.afollestad.nocknock.data.model.Site import com.afollestad.nocknock.data.model.SiteSettings +import com.afollestad.nocknock.data.model.Status.WAITING import com.afollestad.nocknock.data.model.ValidationMode.JAVASCRIPT import com.afollestad.nocknock.data.model.ValidationMode.STATUS_CODE import com.afollestad.nocknock.data.model.ValidationMode.TERM_SEARCH +import com.afollestad.nocknock.data.model.ValidationResult import com.afollestad.nocknock.engine.validation.ValidationExecutor import com.afollestad.nocknock.mockDatabase import com.afollestad.nocknock.utilities.ext.MINUTE import com.afollestad.nocknock.utilities.livedata.test import com.google.common.truth.Truth.assertThat -import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.argumentCaptor import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.never import com.nhaarman.mockitokotlin2.verify import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -159,19 +160,24 @@ class AddSiteViewModelTest { val siteCaptor = argumentCaptor() val settingsCaptor = argumentCaptor() + val validationResultCaptor = argumentCaptor() isLoading.assertValues(true, false) verify(database.siteDao()).insert(siteCaptor.capture()) verify(database.siteSettingsDao()).insert(settingsCaptor.capture()) - verify(database.validationResultsDao(), never()).insert(any()) + verify(database.validationResultsDao()).insert(validationResultCaptor.capture()) val settings = settingsCaptor.firstValue + val result = validationResultCaptor.firstValue.copy(siteId = 1) val model = siteCaptor.firstValue.copy( id = 1, // fill it in because our insert captor doesn't catch this settings = settings, - lastResult = null + lastResult = result ) + assertThat(result.reason).isNull() + assertThat(result.status).isEqualTo(WAITING) + verify(validationManager).scheduleValidation( site = model, rightNow = true, @@ -191,5 +197,10 @@ class AddSiteViewModelTest { validationScript.value = null checkIntervalValue.value = 60 checkIntervalUnit.value = 1000 + tags.value = "one,two" + headers.value = listOf( + Header(2L, 1L, key = "Content-Type", value = "text/html"), + Header(3L, 1L, key = "User-Agent", value = "NockNock") + ) } } diff --git a/app/src/test/java/com/afollestad/nocknock/ui/viewsite/ViewSiteViewModelTest.kt b/app/src/test/java/com/afollestad/nocknock/ui/viewsite/ViewSiteViewModelTest.kt index d7bc8a8..47fe6e0 100644 --- a/app/src/test/java/com/afollestad/nocknock/ui/viewsite/ViewSiteViewModelTest.kt +++ b/app/src/test/java/com/afollestad/nocknock/ui/viewsite/ViewSiteViewModelTest.kt @@ -18,6 +18,8 @@ package com.afollestad.nocknock.ui.viewsite import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.afollestad.nocknock.MOCK_MODEL_1 import com.afollestad.nocknock.R +import com.afollestad.nocknock.data.model.Header +import com.afollestad.nocknock.data.model.RetryPolicy import com.afollestad.nocknock.data.model.Site import com.afollestad.nocknock.data.model.SiteSettings import com.afollestad.nocknock.data.model.Status.CHECKING @@ -29,6 +31,7 @@ import com.afollestad.nocknock.data.model.ValidationMode.STATUS_CODE import com.afollestad.nocknock.data.model.ValidationMode.TERM_SEARCH import com.afollestad.nocknock.data.model.ValidationResult import com.afollestad.nocknock.engine.validation.ValidationExecutor +import com.afollestad.nocknock.fakeRetryPolicy import com.afollestad.nocknock.mockDatabase import com.afollestad.nocknock.notifications.NockNotificationManager import com.afollestad.nocknock.utilities.livedata.test @@ -38,8 +41,10 @@ import com.google.common.truth.Truth.assertThat import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.argumentCaptor import com.nhaarman.mockitokotlin2.doAnswer +import com.nhaarman.mockitokotlin2.doReturn import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.whenever import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking @@ -255,6 +260,8 @@ class ViewSiteViewModelTest { } @Test fun commit_success() = runBlocking { + whenever(database.retryPolicyDao().forSite(any())).doReturn(listOf(fakeRetryPolicy(1))) + val isLoading = viewModel.onIsLoading() .test() @@ -267,11 +274,13 @@ class ViewSiteViewModelTest { val siteCaptor = argumentCaptor() val settingsCaptor = argumentCaptor() val resultCaptor = argumentCaptor() + val retryPolicyCaptor = argumentCaptor() isLoading.assertValues(true, false) verify(database.siteDao()).update(siteCaptor.capture()) verify(database.siteSettingsDao()).update(settingsCaptor.capture()) verify(database.validationResultsDao()).update(resultCaptor.capture()) + verify(database.retryPolicyDao()).update(retryPolicyCaptor.capture()) // From fillInModel() below val updatedSettings = MOCK_MODEL_1.settings!!.copy( @@ -284,11 +293,13 @@ class ViewSiteViewModelTest { val updatedResult = MOCK_MODEL_1.lastResult!!.copy( status = WAITING ) + val retryPolicy = retryPolicyCaptor.firstValue val updatedModel = MOCK_MODEL_1.copy( name = "Hello There", url = "https://www.hellothere.com", settings = updatedSettings, - lastResult = updatedResult + lastResult = updatedResult, + retryPolicy = retryPolicy ) assertThat(siteCaptor.firstValue).isEqualTo(updatedModel) @@ -373,5 +384,12 @@ class ViewSiteViewModelTest { validationScript.value = "throw 'Oh no!'" checkIntervalValue.value = 24 checkIntervalUnit.value = 60000 + tags.value = "one,two" + retryPolicyTimes.value = 5 + retryPolicyMinutes.value = 5 + headers.value = listOf( + Header(2L, 1L, key = "Content-Type", value = "text/html"), + Header(3L, 1L, key = "User-Agent", value = "NockNock") + ) } } diff --git a/data/src/androidTest/java/com/afollestad/nocknock/data/TestUtil.kt b/data/src/androidTest/java/com/afollestad/nocknock/data/TestUtil.kt index 6fc9796..2866b50 100644 --- a/data/src/androidTest/java/com/afollestad/nocknock/data/TestUtil.kt +++ b/data/src/androidTest/java/com/afollestad/nocknock/data/TestUtil.kt @@ -35,7 +35,8 @@ fun fakeSettingsModel( validationMode = validationMode, validationArgs = null, disabled = false, - networkTimeout = 10000 + networkTimeout = 10000, + certificate = null ) fun fakeResultModel(