diff --git a/app/build.gradle b/app/build.gradle index 0427f5a0..9f2e1856 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -104,6 +104,7 @@ dependencies { implementation 'com.google.android.material:material:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0' + implementation 'androidx.work:work-runtime-ktx:2.3.4' // Places implementation 'com.google.android.libraries.places:places:1.1.0' @@ -142,5 +143,8 @@ dependencies { testImplementation 'com.android.support.test:runner:1.0.2' testImplementation 'com.android.support.test:rules:1.0.2' + // gross, somehow needed to make the stop notif work + implementation 'com.google.guava:guava:27.0.1-android' + } diff --git a/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt b/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt index 96b37bf7..ce9399f0 100644 --- a/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt +++ b/app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt @@ -76,14 +76,9 @@ class PokerAnalyticsApplication : Application() { if (BuildConfig.DEBUG) { Timber.d("UserPreferences.defaultCurrency: ${UserDefaults.currency.symbol}") -// this.createFakeSessions() + this.createFakeSessions() } -// CashGameOptimalDurationCalculator.start(true) { -// val hours = it / 3600 / 1000 -// Timber.d("Optimal duration = ${it}, $hours") -// } - Patcher.patchAll(this.applicationContext) val locale = Locale.getDefault() @@ -102,7 +97,7 @@ class PokerAnalyticsApplication : Application() { if (sessionsCount < 10) { GlobalScope.launch { - FakeDataManager.createFakeSessions(400) + FakeDataManager.createFakeSessions(500) } } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/session/SessionFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/session/SessionFragment.kt index 7d1aa705..32bc7f62 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/session/SessionFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/session/SessionFragment.kt @@ -9,6 +9,9 @@ import androidx.appcompat.app.AlertDialog import androidx.interpolator.view.animation.FastOutSlowInInterpolator import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.DiffUtil +import androidx.work.Data +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager import com.crashlytics.android.Crashlytics import kotlinx.android.synthetic.main.fragment_session.* import kotlinx.coroutines.Dispatchers @@ -38,12 +41,14 @@ import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentableDiffCallback import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager import net.pokeranalytics.android.ui.view.rowrepresentable.SessionRow +import net.pokeranalytics.android.util.NotificationSchedule import net.pokeranalytics.android.util.Preferences import net.pokeranalytics.android.util.extensions.findById import net.pokeranalytics.android.util.extensions.formattedHourlyDuration import net.pokeranalytics.android.util.extensions.getNextMinuteInMilliseconds import timber.log.Timber import java.util.* +import java.util.concurrent.TimeUnit import kotlin.coroutines.CoroutineContext @@ -395,7 +400,10 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate { if (!isDetached) { optimalDuration?.let { - // TODO setup a notification + + val delay = 1000L // it.toLong() + scheduleNotification(delay, "stop notif tag") + val formattedDuration = (it / 3600 / 1000).formattedHourlyDuration() Timber.d("Setting stop notification in: $formattedDuration") val message = requireContext().getString(R.string.stop_notification_in_, formattedDuration) @@ -406,6 +414,24 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate { } + private fun scheduleNotification(timeDelay: Long, tag: String) { + + val title = requireContext().getString(R.string.stop_notification_title) + val body = requireContext().getString(R.string.stop_notification_body) + + val data = Data.Builder() + .putString(NotificationSchedule.ParamKeys.TITLE.value, title) + .putString(NotificationSchedule.ParamKeys.BODY.value, body) + + val work = OneTimeWorkRequestBuilder() + .setInitialDelay(timeDelay, TimeUnit.MILLISECONDS) + .setInputData(data.build()) + .addTag(tag) + .build() + + WorkManager.getInstance().enqueue(work) + } + /** * Stop the current session */ diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/SettingRow.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/SettingRow.kt index 00baf8a6..97e89283 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/SettingRow.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/SettingRow.kt @@ -1,5 +1,6 @@ package net.pokeranalytics.android.ui.view.rowrepresentable +import android.os.Build import net.pokeranalytics.android.R import net.pokeranalytics.android.model.LiveData import net.pokeranalytics.android.ui.view.RowRepresentable @@ -63,8 +64,10 @@ enum class SettingRow : RowRepresentable { rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.reports)) rows.addAll(arrayListOf(BANKROLL_REPORT, TOP_10, PLAYERS)) - rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.cash_game)) - rows.addAll(arrayListOf(STOP_NOTIFICATION, STOP_NOTIFICATION_MESSAGE)) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // notif mechanism requires 26+ + rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.cash_game)) + rows.addAll(arrayListOf(STOP_NOTIFICATION, STOP_NOTIFICATION_MESSAGE)) + } rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.information)) rows.addAll(arrayListOf(SUBSCRIPTION, VERSION, RATE_APP, CONTACT_US, BUG_REPORT, DISCORD)) diff --git a/app/src/main/java/net/pokeranalytics/android/util/NotificationScheduling.kt b/app/src/main/java/net/pokeranalytics/android/util/NotificationScheduling.kt new file mode 100644 index 00000000..d5aec554 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/util/NotificationScheduling.kt @@ -0,0 +1,84 @@ +package net.pokeranalytics.android.util + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import android.graphics.Color +import android.media.RingtoneManager +import android.os.Build +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import androidx.work.Worker +import androidx.work.WorkerParameters +import net.pokeranalytics.android.R +import timber.log.Timber +import java.util.* + +class NotificationSchedule(var context: Context, var params: WorkerParameters) : Worker(context, params) { + + enum class ParamKeys(val value: String) { + TITLE("title"), + BODY("body") + } + + override fun doWork(): Result { + val data = params.inputData + val title = data.getString(ParamKeys.TITLE.value) + val body = data.getString(ParamKeys.BODY.value) + + if (title != null && body != null) { + TriggerNotification(context, title, body) + } else { + Timber.d("Missing title and or title for notification") + } + + return Result.success() + } +} + +class TriggerNotification(context: Context, title: String, body: String) { + + init { + sendNotification(context, title, body) + } + + private fun createNotificationChannel(context: Context, name: String, description: String): String { + // Create the NotificationChannel, but only on API 26+ because + // the NotificationChannel class is new and not in the support library + val channelId = UUID.randomUUID().toString() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val importance = NotificationManager.IMPORTANCE_HIGH + val channel = NotificationChannel(channelId, name, importance) + channel.enableLights(true) + channel.enableVibration(true) + channel.description = description + channel.lightColor = Color.GREEN + channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC + + val notificationManager = context.getSystemService(NotificationManager::class.java) + notificationManager?.createNotificationChannel(channel) + } + + return channelId + } + + private fun sendNotification(context: Context, title: String, body: String) { + + val notificationManager = NotificationManagerCompat.from(context) + val mBuilder = NotificationCompat.Builder(context, createNotificationChannel(context, title, body)) + val notificationId = (System.currentTimeMillis() and 0xfffffff).toInt() + + mBuilder.setDefaults(Notification.DEFAULT_ALL) + .setContentTitle(title) + .setContentText(body) + .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setSmallIcon(R.drawable.release_note_icon) +// .setContentInfo("Content Info") + .setAutoCancel(true) + + notificationManager.notify(notificationId, mBuilder.build()) + } + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/util/Preferences.kt b/app/src/main/java/net/pokeranalytics/android/util/Preferences.kt index ea44b3e9..73479cdb 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/Preferences.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/Preferences.kt @@ -1,6 +1,7 @@ package net.pokeranalytics.android.util import android.content.Context +import android.os.Build import android.preference.PreferenceManager import android.view.View import io.realm.Realm @@ -152,7 +153,8 @@ class Preferences { } fun showStopNotifications(context: Context): Boolean { - return getBoolean(Keys.SHOW_STOP_NOTIFICATIONS, context, true) + val defaultValue = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + return getBoolean(Keys.SHOW_STOP_NOTIFICATIONS, context, defaultValue) } fun setStopShowingMessage(message: FeedMessage, context: Context) { diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 15d52c7d..fa96f1df 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -651,7 +651,7 @@ Dites nous ce que vous en pensez Notez-nous ! Non merci - D\'après votre historique, il apparait que vous commencez à fatiguer. Il est peut-être temps de s\'arrêter! + D\'après votre historique, il est peut-être temps de s\'arrêter! C\'est l\'heure! Notifications d\'arrêt Une notification d\'arrêt sera envoyée dans %s diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 26cc4172..a4f27dcd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -645,7 +645,7 @@ Rate us! No thanks It\'s time! - Based on your history, it looks like your start to fatigue. It might be time to stop! + You might want to stop the session now! Stop notifications A stop notification has been set in %s Save & authorize