diff --git a/app/build.gradle b/app/build.gradle index d9184e1..aad5e53 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -35,6 +35,7 @@ dependencies { implementation 'com.afollestad.material-dialogs:core:' + versions.materialDialogs + implementation 'com.jakewharton.timber:timber:' + versions.timber testImplementation 'junit:junit:' + versions.junit testImplementation 'org.mockito:mockito-core:' + versions.mockito testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:' + versions.mockitoKotlin diff --git a/app/src/main/java/com/afollestad/nocknock/NockNockApp.kt b/app/src/main/java/com/afollestad/nocknock/NockNockApp.kt index 5370d4b..f50be42 100644 --- a/app/src/main/java/com/afollestad/nocknock/NockNockApp.kt +++ b/app/src/main/java/com/afollestad/nocknock/NockNockApp.kt @@ -6,7 +6,6 @@ package com.afollestad.nocknock import android.app.Application -import android.util.Log import com.afollestad.nocknock.di.AppComponent import com.afollestad.nocknock.di.DaggerAppComponent import com.afollestad.nocknock.engine.statuscheck.BootReceiver @@ -18,19 +17,14 @@ import com.afollestad.nocknock.ui.viewsite.ViewSiteActivity import com.afollestad.nocknock.utilities.Injector import com.afollestad.nocknock.utilities.ext.systemService import okhttp3.OkHttpClient +import timber.log.Timber +import timber.log.Timber.DebugTree import javax.inject.Inject +import timber.log.Timber.d as log /** @author Aidan Follestad (@afollestad) */ class NockNockApp : Application(), Injector { - companion object { - private fun log(message: String) { - if (BuildConfig.DEBUG) { - Log.d("NockNockApp", message) - } - } - } - private lateinit var appComponent: AppComponent @Inject lateinit var nockNotificationManager: NockNotificationManager @@ -39,6 +33,10 @@ class NockNockApp : Application(), Injector { override fun onCreate() { super.onCreate() + if (BuildConfig.DEBUG) { + Timber.plant(DebugTree()) + } + val okHttpClient = OkHttpClient.Builder() .addNetworkInterceptor { chain -> val request = chain.request() diff --git a/dependencies.gradle b/dependencies.gradle index 7f63786..46a4a43 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -22,6 +22,7 @@ ext.versions = [ materialDialogs : '2.0.0-rc3', rxkPrefs : '1.2.0', + timber : '4.7.1', junit : '4.12', mockito : '2.23.0', mockitoKotlin : '2.0.0-RC1', diff --git a/engine/build.gradle b/engine/build.gradle index ae51e07..c26b99b 100644 --- a/engine/build.gradle +++ b/engine/build.gradle @@ -27,6 +27,7 @@ dependencies { implementation 'com.google.dagger:dagger:' + versions.dagger kapt 'com.google.dagger:dagger-compiler:' + versions.dagger + implementation 'com.jakewharton.timber:timber:' + versions.timber testImplementation 'junit:junit:' + versions.junit testImplementation 'org.mockito:mockito-core:' + versions.mockito testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:' + versions.mockitoKotlin diff --git a/engine/src/main/java/com/afollestad/nocknock/engine/db/ServerModelStore.kt b/engine/src/main/java/com/afollestad/nocknock/engine/db/ServerModelStore.kt index e5050d2..cd40c5a 100644 --- a/engine/src/main/java/com/afollestad/nocknock/engine/db/ServerModelStore.kt +++ b/engine/src/main/java/com/afollestad/nocknock/engine/db/ServerModelStore.kt @@ -7,13 +7,13 @@ package com.afollestad.nocknock.engine.db import android.app.Application import android.database.Cursor -import android.util.Log import com.afollestad.nocknock.data.ServerModel import com.afollestad.nocknock.data.ServerModel.Companion.COLUMN_ID import com.afollestad.nocknock.data.ServerModel.Companion.DEFAULT_SORT_ORDER import com.afollestad.nocknock.data.ServerModel.Companion.TABLE_NAME -import com.afollestad.nocknock.engine.BuildConfig import com.afollestad.nocknock.utilities.ext.diffFrom +import timber.log.Timber.d as log +import timber.log.Timber.w as warn import javax.inject.Inject /** @author Aidan Follestad (@afollestad) */ @@ -35,21 +35,6 @@ interface ServerModelStore { /** @author Aidan Follestad (@afollestad) */ class RealServerModelStore @Inject constructor(app: Application) : ServerModelStore { - companion object { - private fun log( - message: String, - warning: Boolean = false - ) { - if (BuildConfig.DEBUG) { - if (warning) { - Log.w("ServerModelStore", message) - } else { - Log.d("ServerModelStore", message) - } - } - } - } - private val dbHelper = ServerModelDbHelper(app) override suspend fun get(id: Int?): List { @@ -115,7 +100,7 @@ class RealServerModelStore @Inject constructor(app: Application) : ServerModelSt val valuesDiff = oldValues.diffFrom(newValues) if (valuesDiff.size() == 0) { - log("Nothing has changed - nothing to update!", warning = true) + warn("Nothing has changed - nothing to update!") return 0 } diff --git a/engine/src/main/java/com/afollestad/nocknock/engine/statuscheck/BootReceiver.kt b/engine/src/main/java/com/afollestad/nocknock/engine/statuscheck/BootReceiver.kt index 558779d..accc18d 100644 --- a/engine/src/main/java/com/afollestad/nocknock/engine/statuscheck/BootReceiver.kt +++ b/engine/src/main/java/com/afollestad/nocknock/engine/statuscheck/BootReceiver.kt @@ -9,8 +9,6 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.Intent.ACTION_BOOT_COMPLETED -import android.util.Log -import com.afollestad.nocknock.engine.BuildConfig import com.afollestad.nocknock.utilities.ext.injector import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main @@ -18,18 +16,11 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async import kotlinx.coroutines.launch import javax.inject.Inject +import timber.log.Timber.d as log /** @author Aidan Follestad (@afollestad) */ class BootReceiver : BroadcastReceiver() { - companion object { - private fun log(message: String) { - if (BuildConfig.DEBUG) { - Log.d("BootReceiver", message) - } - } - } - @Inject lateinit var checkStatusManager: CheckStatusManager override fun onReceive( diff --git a/engine/src/main/java/com/afollestad/nocknock/engine/statuscheck/CheckStatusJob.kt b/engine/src/main/java/com/afollestad/nocknock/engine/statuscheck/CheckStatusJob.kt index 0a7d3fe..d7f6e80 100644 --- a/engine/src/main/java/com/afollestad/nocknock/engine/statuscheck/CheckStatusJob.kt +++ b/engine/src/main/java/com/afollestad/nocknock/engine/statuscheck/CheckStatusJob.kt @@ -8,7 +8,6 @@ package com.afollestad.nocknock.engine.statuscheck import android.app.job.JobParameters import android.app.job.JobService import android.content.Intent -import android.util.Log import com.afollestad.nocknock.data.ServerModel import com.afollestad.nocknock.data.ServerStatus import com.afollestad.nocknock.data.ServerStatus.CHECKING @@ -21,7 +20,6 @@ import com.afollestad.nocknock.data.isPending import com.afollestad.nocknock.engine.BuildConfig.APPLICATION_ID import com.afollestad.nocknock.engine.db.ServerModelStore import com.afollestad.nocknock.notifications.NockNotificationManager -import com.afollestad.nocknock.utilities.BuildConfig import com.afollestad.nocknock.utilities.ext.injector import com.afollestad.nocknock.utilities.js.JavaScript import kotlinx.coroutines.Dispatchers.IO @@ -32,6 +30,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.lang.System.currentTimeMillis import javax.inject.Inject +import timber.log.Timber.d as log /** @author Aidan Follestad (@afollestad)*/ class CheckStatusJob : JobService() { @@ -41,12 +40,6 @@ class CheckStatusJob : JobService() { const val ACTION_JOB_RUNNING = "$APPLICATION_ID.STATUS_JOB_RUNNING" const val KEY_UPDATE_MODEL = "site_model" const val KEY_SITE_ID = "site.id" - - private fun log(message: String) { - if (BuildConfig.DEBUG) { - Log.d("CheckStatusJob", message) - } - } } @Inject lateinit var modelStore: ServerModelStore diff --git a/engine/src/main/java/com/afollestad/nocknock/engine/statuscheck/CheckStatusManager.kt b/engine/src/main/java/com/afollestad/nocknock/engine/statuscheck/CheckStatusManager.kt index 5af52d1..11e21a4 100644 --- a/engine/src/main/java/com/afollestad/nocknock/engine/statuscheck/CheckStatusManager.kt +++ b/engine/src/main/java/com/afollestad/nocknock/engine/statuscheck/CheckStatusManager.kt @@ -10,20 +10,19 @@ import android.app.job.JobInfo.NETWORK_TYPE_ANY import android.app.job.JobScheduler import android.app.job.JobScheduler.RESULT_SUCCESS import android.os.PersistableBundle -import android.util.Log import com.afollestad.nocknock.data.ServerModel import com.afollestad.nocknock.data.ServerStatus.ERROR import com.afollestad.nocknock.data.ServerStatus.OK import com.afollestad.nocknock.engine.R import com.afollestad.nocknock.engine.db.ServerModelStore import com.afollestad.nocknock.engine.statuscheck.CheckStatusJob.Companion.KEY_SITE_ID -import com.afollestad.nocknock.utilities.BuildConfig import com.afollestad.nocknock.utilities.providers.StringProvider import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import java.net.SocketTimeoutException import javax.inject.Inject +import timber.log.Timber.d as log /** @author Aidan Follestad (@afollestad) */ data class CheckResult( @@ -56,14 +55,6 @@ class RealCheckStatusManager @Inject constructor( private val siteStore: ServerModelStore ) : CheckStatusManager { - companion object { - private fun log(message: String) { - if (BuildConfig.DEBUG) { - Log.d("CheckStatusManager", message) - } - } - } - override suspend fun ensureScheduledChecks() { val sites = siteStore.get() if (sites.isEmpty()) { diff --git a/notifications/build.gradle b/notifications/build.gradle index e3d7838..9578137 100644 --- a/notifications/build.gradle +++ b/notifications/build.gradle @@ -26,6 +26,7 @@ dependencies { implementation 'com.google.dagger:dagger:' + versions.dagger kapt 'com.google.dagger:dagger-compiler:' + versions.dagger + implementation 'com.jakewharton.timber:timber:' + versions.timber testImplementation 'junit:junit:' + versions.junit testImplementation 'org.mockito:mockito-core:' + versions.mockito testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:' + versions.mockitoKotlin diff --git a/notifications/src/main/java/com/afollestad/nocknock/notifications/Channel.kt b/notifications/src/main/java/com/afollestad/nocknock/notifications/Channel.kt index 4865efc..1510489 100644 --- a/notifications/src/main/java/com/afollestad/nocknock/notifications/Channel.kt +++ b/notifications/src/main/java/com/afollestad/nocknock/notifications/Channel.kt @@ -5,10 +5,6 @@ */ package com.afollestad.nocknock.notifications -import android.app.NotificationChannel -import android.content.Context -import android.os.Build.VERSION_CODES -import androidx.annotation.RequiresApi import androidx.core.app.NotificationManagerCompat.IMPORTANCE_DEFAULT /** @author Aidan Follestad (@afollestad) */ @@ -25,14 +21,3 @@ enum class Channel( importance = IMPORTANCE_DEFAULT ) } - -/** @author Aidan Follestad (@afollestad) */ -@RequiresApi(VERSION_CODES.O) -fun Channel.toNotificationChannel(context: Context): NotificationChannel { - val titleText = context.getString(this.title) - val descriptionText = context.getString(this.description) - return NotificationChannel(this.id, titleText, this.importance) - .apply { - description = descriptionText - } -} diff --git a/notifications/src/main/java/com/afollestad/nocknock/notifications/NockNotificationManager.kt b/notifications/src/main/java/com/afollestad/nocknock/notifications/NockNotificationManager.kt index 295c2ec..589cf9f 100644 --- a/notifications/src/main/java/com/afollestad/nocknock/notifications/NockNotificationManager.kt +++ b/notifications/src/main/java/com/afollestad/nocknock/notifications/NockNotificationManager.kt @@ -6,20 +6,19 @@ package com.afollestad.nocknock.notifications import android.annotation.TargetApi -import android.app.Application import android.app.NotificationManager import android.os.Build.VERSION_CODES -import android.util.Log -import androidx.core.app.NotificationCompat.DEFAULT_VIBRATE import com.afollestad.nocknock.data.ServerModel import com.afollestad.nocknock.notifications.Channel.CheckFailures import com.afollestad.nocknock.utilities.providers.BitmapProvider import com.afollestad.nocknock.utilities.providers.IntentProvider +import com.afollestad.nocknock.utilities.providers.NotificationChannelProvider +import com.afollestad.nocknock.utilities.providers.NotificationProvider import com.afollestad.nocknock.utilities.providers.RealIntentProvider.Companion.BASE_NOTIFICATION_REQUEST_CODE import com.afollestad.nocknock.utilities.providers.StringProvider import com.afollestad.nocknock.utilities.qualifiers.AppIconRes -import com.afollestad.nocknock.utilities.util.hasOreo import javax.inject.Inject +import timber.log.Timber.d as log /** @author Aidan Follestad (@afollestad) */ interface NockNotificationManager { @@ -37,22 +36,15 @@ interface NockNotificationManager { /** @author Aidan Follestad (@afollestad) */ class RealNockNotificationManager @Inject constructor( - private val app: Application, @AppIconRes private val appIconRes: Int, private val stockManager: NotificationManager, private val bitmapProvider: BitmapProvider, private val stringProvider: StringProvider, - private val intentProvider: IntentProvider + private val intentProvider: IntentProvider, + private val channelProvider: NotificationChannelProvider, + private val notificationProvider: NotificationProvider ) : NockNotificationManager { - companion object { - private fun log(message: String) { - if (BuildConfig.DEBUG) { - Log.d("NockNotificationManager", message) - } - } - } - private var isAppOpen = false override fun setIsAppOpen(open: Boolean) { @@ -73,15 +65,14 @@ class RealNockNotificationManager @Inject constructor( log("Posting status notification for site ${model.id}...") val intent = intentProvider.getPendingIntentForViewSite(model) - val newNotification = notification(app, CheckFailures) { - setContentTitle(model.name) - setContentText(stringProvider.get(R.string.something_wrong)) - setContentIntent(intent) - setSmallIcon(R.drawable.ic_notification) - setLargeIcon(bitmapProvider.get(appIconRes)) - setAutoCancel(true) - setDefaults(DEFAULT_VIBRATE) - } + val newNotification = notificationProvider.create( + channelId = CheckFailures.id, + title = model.name, + content = stringProvider.get(R.string.something_wrong), + intent = intent, + smallIcon = R.drawable.ic_notification, + largeIcon = bitmapProvider.get(appIconRes) + ) stockManager.notify(model.url, model.notificationId(), newNotification) log("Posted status notification for site ${model.notificationId()}.") @@ -96,13 +87,13 @@ class RealNockNotificationManager @Inject constructor( @TargetApi(VERSION_CODES.O) private fun createChannel(channel: Channel) { - if (!hasOreo()) { - log("Not running Android O, channels won't be created.") - return - } - - val notificationChannel = channel.toNotificationChannel(app) - stockManager.createNotificationChannel(notificationChannel) + val notificationChannel = channelProvider.create( + id = channel.id, + title = stringProvider.get(channel.title), + description = stringProvider.get(channel.description), + importance = channel.importance + ) + notificationChannel?.let(stockManager::createNotificationChannel) log("Created notification channel ${channel.id}") } diff --git a/notifications/src/main/java/com/afollestad/nocknock/notifications/Notification.kt b/notifications/src/main/java/com/afollestad/nocknock/notifications/Notification.kt deleted file mode 100644 index aeb24f2..0000000 --- a/notifications/src/main/java/com/afollestad/nocknock/notifications/Notification.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Licensed under Apache-2.0 - * - * Designed and developed by Aidan Follestad (@afollestad) - */ -package com.afollestad.nocknock.notifications - -import android.app.Notification -import android.content.Context -import androidx.core.app.NotificationCompat - -typealias NotificationBuilder = NotificationCompat.Builder -typealias NotificationConstructor = NotificationBuilder.() -> Unit - -/** @author Aidan Follestad (@afollestad) */ -fun notification( - context: Context, - channel: Channel, - builder: NotificationConstructor -): Notification { - val newNotification = NotificationCompat.Builder(context, channel.id) - builder(newNotification) - return newNotification.build() -} diff --git a/notifications/src/test/java/com/afollestad/nocknock/notifications/ExampleUnitTest.java b/notifications/src/test/java/com/afollestad/nocknock/notifications/ExampleUnitTest.java deleted file mode 100644 index 8af7ccc..0000000 --- a/notifications/src/test/java/com/afollestad/nocknock/notifications/ExampleUnitTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.afollestad.nocknock.notifications; - -import static org.junit.Assert.*; - -import org.junit.Test; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() { - assertEquals(4, 2 + 2); - } -} diff --git a/notifications/src/test/java/com/afollestad/nocknock/notifications/NockNotificationManagerTest.kt b/notifications/src/test/java/com/afollestad/nocknock/notifications/NockNotificationManagerTest.kt new file mode 100644 index 0000000..362274d --- /dev/null +++ b/notifications/src/test/java/com/afollestad/nocknock/notifications/NockNotificationManagerTest.kt @@ -0,0 +1,151 @@ +/* + * Licensed under Apache-2.0 + * + * Designed and developed by Aidan Follestad (@afollestad) + */ +package com.afollestad.nocknock.notifications + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.graphics.Bitmap +import com.afollestad.nocknock.data.ServerModel +import com.afollestad.nocknock.data.ValidationMode.STATUS_CODE +import com.afollestad.nocknock.notifications.Channel.CheckFailures +import com.afollestad.nocknock.utilities.providers.BitmapProvider +import com.afollestad.nocknock.utilities.providers.IntentProvider +import com.afollestad.nocknock.utilities.providers.NotificationChannelProvider +import com.afollestad.nocknock.utilities.providers.NotificationProvider +import com.afollestad.nocknock.utilities.providers.RealIntentProvider.Companion.BASE_NOTIFICATION_REQUEST_CODE +import com.afollestad.nocknock.utilities.providers.StringProvider +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.times +import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions +import com.nhaarman.mockitokotlin2.whenever +import org.junit.Before +import org.junit.Test + +class NockNotificationManagerTest { + + private val appIconRes = 1024 + private val somethingWentWrong = "something went wrong" + + private val stockManager = mock() + private val appIcon = mock() + private val bitmapProvider = mock { + on { get(appIconRes) } doReturn appIcon + } + private val stringProvider = mock { + on { get(R.string.something_wrong) } doReturn somethingWentWrong + } + private val intentProvider = mock() + private val channelProvider = mock() + private val notificationProvider = mock() + + private val manager = RealNockNotificationManager( + appIconRes, + stockManager, + bitmapProvider, + stringProvider, + intentProvider, + channelProvider, + notificationProvider + ) + + @Before fun setup() { + whenever(channelProvider.create(any(), any(), any(), any())).doAnswer { inv -> + val id = inv.getArgument(0) + val title = inv.getArgument(1) + val description = inv.getArgument(2) + val important = inv.getArgument(3) + return@doAnswer mock { + on { this.id } doReturn id + on { this.name } doReturn title + on { this.description } doReturn description + on { this.importance } doReturn important + } + } + } + + @Test fun createChannels() { + whenever(stringProvider.get(any())).doReturn("") + val createdChannel = mock { + on { this.id } doReturn CheckFailures.id + } + whenever(channelProvider.create(any(), any(), any(), any())) + .doReturn(createdChannel) + manager.createChannels() + + val captor = argumentCaptor() + verify(stockManager, times(1)).createNotificationChannel(captor.capture()) + + val channel = captor.allValues.single() + assertThat(channel.id).isEqualTo(CheckFailures.id) + + verifyNoMoreInteractions(stockManager) + } + + @Test fun postStatusNotification_appIsOpen() { + manager.setIsAppOpen(true) + manager.postStatusNotification(fakeModel()) + + verifyNoMoreInteractions(stockManager) + } + + @Test fun postStatusNotification_appNotOpen() { + manager.setIsAppOpen(false) + val model = fakeModel() + + val pendingIntent = mock() + whenever(intentProvider.getPendingIntentForViewSite(model)) + .doReturn(pendingIntent) + + val notification = mock() + whenever( + notificationProvider.create( + CheckFailures.id, + model.name, + somethingWentWrong, + pendingIntent, + R.drawable.ic_notification, + appIcon + ) + ).doReturn(notification) + + manager.postStatusNotification(model) + + verify(stockManager).notify( + model.url, + BASE_NOTIFICATION_REQUEST_CODE + model.id, + notification + ) + verifyNoMoreInteractions(stockManager) + } + + @Test fun cancelStatusNotification() { + val model = fakeModel() + manager.cancelStatusNotification(model) + verify(stockManager).cancel(BASE_NOTIFICATION_REQUEST_CODE + model.id) + verifyNoMoreInteractions(stockManager) + } + + @Test fun cancelStatusNotifications() { + manager.cancelStatusNotifications() + verify(stockManager).cancelAll() + verifyNoMoreInteractions(stockManager) + } + + private fun fakeModel() = ServerModel( + id = 1, + url = "https://hello.com", + name = "Testing", + validationMode = STATUS_CODE + ) +} diff --git a/utilities/build.gradle b/utilities/build.gradle index 627bbfc..433cdf4 100644 --- a/utilities/build.gradle +++ b/utilities/build.gradle @@ -20,6 +20,7 @@ android { dependencies { implementation 'androidx.annotation:annotation:' + versions.androidx + implementation 'androidx.appcompat:appcompat:' + versions.androidx implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:' + versions.kotlin api 'org.jetbrains.kotlinx:kotlinx-coroutines-core:' + versions.coroutines diff --git a/utilities/src/main/java/com/afollestad/nocknock/utilities/UtilitiesModule.kt b/utilities/src/main/java/com/afollestad/nocknock/utilities/UtilitiesModule.kt index af4a35a..6e7defe 100644 --- a/utilities/src/main/java/com/afollestad/nocknock/utilities/UtilitiesModule.kt +++ b/utilities/src/main/java/com/afollestad/nocknock/utilities/UtilitiesModule.kt @@ -7,9 +7,15 @@ package com.afollestad.nocknock.utilities import com.afollestad.nocknock.utilities.providers.BitmapProvider import com.afollestad.nocknock.utilities.providers.IntentProvider +import com.afollestad.nocknock.utilities.providers.NotificationChannelProvider +import com.afollestad.nocknock.utilities.providers.NotificationProvider import com.afollestad.nocknock.utilities.providers.RealBitmapProvider import com.afollestad.nocknock.utilities.providers.RealIntentProvider +import com.afollestad.nocknock.utilities.providers.RealNotificationChannelProvider +import com.afollestad.nocknock.utilities.providers.RealNotificationProvider +import com.afollestad.nocknock.utilities.providers.RealSdkProvider import com.afollestad.nocknock.utilities.providers.RealStringProvider +import com.afollestad.nocknock.utilities.providers.SdkProvider import com.afollestad.nocknock.utilities.providers.StringProvider import dagger.Binds import dagger.Module @@ -19,6 +25,12 @@ import javax.inject.Singleton @Module abstract class UtilitiesModule { + @Binds + @Singleton + abstract fun provideSdkProvider( + sdkProvider: RealSdkProvider + ): SdkProvider + @Binds @Singleton abstract fun provideBitmapProvider( @@ -36,4 +48,16 @@ abstract class UtilitiesModule { abstract fun provideIntentProvider( intentProvider: RealIntentProvider ): IntentProvider + + @Binds + @Singleton + abstract fun provideChannelProvider( + channelProvider: RealNotificationChannelProvider + ): NotificationChannelProvider + + @Binds + @Singleton + abstract fun provideNotificationProvider( + notificationProvider: RealNotificationProvider + ): NotificationProvider } diff --git a/utilities/src/main/java/com/afollestad/nocknock/utilities/js/JavaScript.kt b/utilities/src/main/java/com/afollestad/nocknock/utilities/js/JavaScript.kt index 7e25f1c..6d735e2 100644 --- a/utilities/src/main/java/com/afollestad/nocknock/utilities/js/JavaScript.kt +++ b/utilities/src/main/java/com/afollestad/nocknock/utilities/js/JavaScript.kt @@ -5,8 +5,6 @@ */ package com.afollestad.nocknock.utilities.js -import android.util.Log -import com.afollestad.nocknock.utilities.BuildConfig import org.mozilla.javascript.Context import org.mozilla.javascript.EvaluatorException import org.mozilla.javascript.Function @@ -62,9 +60,6 @@ object JavaScript { } } - log( - "Evaluated to $message ($success): $code" - ) return if (!success) message else null } finally { Context.exit() @@ -73,10 +68,4 @@ object JavaScript { return e.message } } - - private fun log(message: String) { - if (BuildConfig.DEBUG) { - Log.d("JavaScript", message) - } - } } diff --git a/utilities/src/main/java/com/afollestad/nocknock/utilities/providers/NotificationChannelProvider.kt b/utilities/src/main/java/com/afollestad/nocknock/utilities/providers/NotificationChannelProvider.kt new file mode 100644 index 0000000..694ef8a --- /dev/null +++ b/utilities/src/main/java/com/afollestad/nocknock/utilities/providers/NotificationChannelProvider.kt @@ -0,0 +1,45 @@ +/* + * Licensed under Apache-2.0 + * + * Designed and developed by Aidan Follestad (@afollestad) + */ +package com.afollestad.nocknock.utilities.providers + +import android.annotation.TargetApi +import android.app.NotificationChannel +import android.os.Build.VERSION_CODES +import javax.inject.Inject + +/** @author Aidan Follestad (@afollestad) */ +interface NotificationChannelProvider { + + /** @return null if the device doesn't have Android O. */ + fun create( + id: String, + title: String, + description: String, + importance: Int + ): NotificationChannel? +} + +/** @author Aidan Follestad (@afollestad) */ +class RealNotificationChannelProvider @Inject constructor( + private val sdkProvider: SdkProvider +) : NotificationChannelProvider { + + @TargetApi(VERSION_CODES.O) + override fun create( + id: String, + title: String, + description: String, + importance: Int + ): NotificationChannel? { + if (!sdkProvider.hasOreo()) { + return null + } + return NotificationChannel(id, title, importance) + .apply { + this.description = description + } + } +} diff --git a/utilities/src/main/java/com/afollestad/nocknock/utilities/providers/NotificationProvider.kt b/utilities/src/main/java/com/afollestad/nocknock/utilities/providers/NotificationProvider.kt new file mode 100644 index 0000000..68f162f --- /dev/null +++ b/utilities/src/main/java/com/afollestad/nocknock/utilities/providers/NotificationProvider.kt @@ -0,0 +1,52 @@ +/* + * Licensed under Apache-2.0 + * + * Designed and developed by Aidan Follestad (@afollestad) + */ +package com.afollestad.nocknock.utilities.providers + +import android.app.Application +import android.app.Notification +import android.app.PendingIntent +import android.graphics.Bitmap +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationCompat.DEFAULT_VIBRATE +import javax.inject.Inject + +/** @author Aidan Follestad (@afollestad) */ +interface NotificationProvider { + + fun create( + channelId: String, + title: String, + content: String, + intent: PendingIntent, + smallIcon: Int, + largeIcon: Bitmap + ): Notification +} + +/** @author Aidan Follestad (@afollestad) */ +class RealNotificationProvider @Inject constructor( + private val app: Application +) : NotificationProvider { + + override fun create( + channelId: String, + title: String, + content: String, + intent: PendingIntent, + smallIcon: Int, + largeIcon: Bitmap + ): Notification { + return NotificationCompat.Builder(app, channelId) + .setContentTitle(title) + .setContentText(content) + .setContentIntent(intent) + .setSmallIcon(smallIcon) + .setLargeIcon(largeIcon) + .setAutoCancel(true) + .setDefaults(DEFAULT_VIBRATE) + .build() + } +} diff --git a/utilities/src/main/java/com/afollestad/nocknock/utilities/providers/SdkProvider.kt b/utilities/src/main/java/com/afollestad/nocknock/utilities/providers/SdkProvider.kt new file mode 100644 index 0000000..5c1ea7c --- /dev/null +++ b/utilities/src/main/java/com/afollestad/nocknock/utilities/providers/SdkProvider.kt @@ -0,0 +1,22 @@ +/* + * Licensed under Apache-2.0 + * + * Designed and developed by Aidan Follestad (@afollestad) + */ +package com.afollestad.nocknock.utilities.providers + +import android.os.Build.VERSION.SDK_INT +import android.os.Build.VERSION_CODES.O +import javax.inject.Inject + +/** @author Aidan Follestad (@afollestad) */ +interface SdkProvider { + + fun hasOreo(): Boolean +} + +/** @author Aidan Follestad (@afollestad) */ +class RealSdkProvider @Inject constructor() : SdkProvider { + + override fun hasOreo() = SDK_INT >= O +} diff --git a/utilities/src/main/java/com/afollestad/nocknock/utilities/util/SdkUtil.kt b/utilities/src/main/java/com/afollestad/nocknock/utilities/util/SdkUtil.kt deleted file mode 100644 index 4078558..0000000 --- a/utilities/src/main/java/com/afollestad/nocknock/utilities/util/SdkUtil.kt +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Licensed under Apache-2.0 - * - * Designed and developed by Aidan Follestad (@afollestad) - */ -package com.afollestad.nocknock.utilities.util - -import android.os.Build.VERSION.SDK_INT -import android.os.Build.VERSION_CODES.O - -fun hasOreo() = SDK_INT >= O