Skip to content

Commit

Permalink
Support Android 31 API level
Browse files Browse the repository at this point in the history
  • Loading branch information
AChep committed Jan 29, 2022
1 parent 1bad745 commit d36b163
Show file tree
Hide file tree
Showing 12 changed files with 129 additions and 42 deletions.
13 changes: 7 additions & 6 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -115,20 +115,21 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0'
implementation 'com.artemchep.config:config:2.2.0'
implementation 'com.afollestad.material-dialogs:core:3.3.0'
implementation 'com.google.android.material:material:1.6.0-alpha01'
implementation 'com.google.android.material:material:1.6.0-alpha02'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
implementation 'com.eightbitlab:blurview:1.6.6'
implementation "ch.acra:acra-http:5.8.4"
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.browser:browser:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
implementation "androidx.work:work-runtime-ktx:2.7.0"
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation "androidx.work:work-runtime-ktx:2.7.1"
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0'
implementation 'com.mikepenz:fastadapter:3.3.1'
compileOnly 'org.solovyev.android:checkout:1.2.3'
playstoreImplementation 'org.solovyev.android:checkout:1.2.3'
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>

Expand Down
25 changes: 20 additions & 5 deletions app/src/main/java/com/artemchep/pocketmode/Heart.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,24 @@ import android.app.Application
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.work.*
import com.artemchep.config.Config
import com.artemchep.pocketmode.analytics.Analytics
import com.artemchep.pocketmode.analytics.AnalyticsHolder
import com.artemchep.pocketmode.analytics.AnalyticsStub
import com.artemchep.pocketmode.analytics.createAnalytics
import com.artemchep.pocketmode.services.PocketService
import com.artemchep.pocketmode.services.PocketServiceRestartWorker
import com.google.android.material.color.DynamicColors
import org.acra.ACRA
import org.acra.annotation.AcraCore
import org.acra.annotation.AcraHttpSender
import org.acra.data.StringFormat
import org.acra.sender.HttpSender
import org.solovyev.android.checkout.Billing
import java.time.Duration

/**
* @author Artem Chepurnoy
Expand All @@ -33,14 +38,20 @@ import org.solovyev.android.checkout.Billing
httpMethod = HttpSender.Method.POST,
)
class Heart : Application() {
companion object {
private const val WORK_RESTART_ID = "PocketService::restart"
private const val WORK_RESTART_PERIOD = 2 * 60 * 60 * 1000L // 2h
}

private val cfgObserver = object : Config.OnConfigChangedListener<String> {
override fun onConfigChanged(keys: Set<String>) {
if (Cfg.KEY_ENABLED in keys) {
// Start or stop the service depending on the
// config.
val shouldBeEnabled = Cfg.isEnabled
if (shouldBeEnabled) {
startForegroundService(pocketServiceIntent)
if (ProcessLifecycleOwner.get().lifecycle.currentState >= Lifecycle.State.STARTED)
startForegroundService(pocketServiceIntent)
}
// Else service should monitor the config and
// kill itself.
Expand Down Expand Up @@ -106,9 +117,13 @@ class Heart : Application() {
}
})

// Start the service.
if (Cfg.isEnabled) {
startForegroundService(pocketServiceIntent)
}
// Start a scheduler to restart service in
// few hours in cause it was killed.
val request = PeriodicWorkRequestBuilder<PocketServiceRestartWorker>(
Duration.ofMillis(WORK_RESTART_PERIOD)
).build()
WorkManager
.getInstance(this)
.enqueueUniquePeriodicWork(WORK_RESTART_ID, ExistingPeriodicWorkPolicy.REPLACE, request)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@ import com.artemchep.pocketmode.Heart
*/
class BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action != Intent.ACTION_BOOT_COMPLETED) {
if (intent.action != Intent.ACTION_BOOT_COMPLETED &&
intent.action != Intent.ACTION_LOCKED_BOOT_COMPLETED &&
intent.action != Intent.ACTION_MY_PACKAGE_REPLACED
) {
return
}

// We can start foreground services at this point, see
// https://developer.android.com/guide/components/foreground-services#background-start-restriction-exemptions
if (Cfg.isEnabled) {
val heart = context.applicationContext as Heart
val pocketServiceIntent = heart.pocketServiceIntent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ fun flowOfLockScreen(
is Proximity.Far -> flowOf(Idle)
// When the sensor gets covered, wait for a bit and if it
// does not get uncovered -> send the lock screen event.
is Proximity.Near -> flow {
is Proximity.Near -> flow<LockScreenEvent> {
emit(BeforeLockScreen)
delay(Cfg.lockScreenDelay)
emit(OnLockScreen)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,10 @@ import kotlin.coroutines.CoroutineContext
*/
class PocketService : Service(), CoroutineScope {
companion object {
private const val WORK_RESTART_ID = "PocketService::restart"
private const val WORK_RESTART_PERIOD = 60 * 60 * 1000L // ms

private const val NOTIFICATION_CHANNEL = "pocket_notification_channel"
private const val NOTIFICATION_ID = 112

var running = false
}

private lateinit var job: Job
Expand Down Expand Up @@ -128,6 +127,7 @@ class PocketService : Service(), CoroutineScope {

override fun onCreate() {
job = Job()
running = true
super.onCreate()
Cfg.observe(configObserver)
stopSelfIfPocketServiceIsDisabled()
Expand All @@ -148,15 +148,6 @@ class PocketService : Service(), CoroutineScope {
overlayObserver.onChanged(it)
}
}

// Start a scheduler to restart service in
// few hours in cause it was killed.
val request = PeriodicWorkRequestBuilder<PocketServiceRestartWorker>(
Duration.ofMillis(WORK_RESTART_PERIOD)
).build()
WorkManager
.getInstance(this)
.enqueueUniquePeriodicWork(WORK_RESTART_ID, ExistingPeriodicWorkPolicy.KEEP, request)
}

private fun stopSelfIfPocketServiceIsDisabled() {
Expand Down Expand Up @@ -199,6 +190,7 @@ class PocketService : Service(), CoroutineScope {
super.onDestroy()
job.cancel()
overlayExitJob?.cancel()
running = false
}

override fun onBind(intent: Intent?): IBinder? = null
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,95 @@
package com.artemchep.pocketmode.services

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import androidx.work.Worker
import androidx.core.app.NotificationCompat
import androidx.core.content.getSystemService
import androidx.lifecycle.asFlow
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.artemchep.pocketmode.viewmodels.PocketViewModel
import com.artemchep.pocketmode.Cfg
import com.artemchep.pocketmode.R
import com.artemchep.pocketmode.sensors.AccessAccessibilityLiveData
import com.artemchep.pocketmode.ui.activities.MainActivity
import kotlinx.coroutines.flow.first

/**
* @author Artem Chepurnoy
*/
class PocketServiceRestartWorker(
context: Context,
workerParams: WorkerParameters
) : Worker(context, workerParams) {
override fun doWork(): Result {
val intent = Intent(applicationContext, PocketViewModel::class.java)
applicationContext.startForegroundService(intent)
) : CoroutineWorker(context, workerParams) {
companion object {
private const val NOTIFICATION_CHANNEL = "pocket_restart_channel"
private const val NOTIFICATION_ID = 212
}

override suspend fun doWork(): Result {
val accessibilityGranted = AccessAccessibilityLiveData(applicationContext)
.asFlow()
.first()
if (Cfg.isEnabled && (!PocketService.running || !accessibilityGranted)) {
val nm = applicationContext.getSystemService<NotificationManager>()!!
val n = createNotification(
accessibilityGranted = accessibilityGranted,
)
nm.notify(NOTIFICATION_ID, n)
}

return Result.success()
}

private fun createNotification(
accessibilityGranted: Boolean,
): Notification {
// Create notification channel
val channelName = applicationContext.getString(R.string.notification_restart_channel)
val channel =
NotificationChannel(
NOTIFICATION_CHANNEL,
channelName,
NotificationManager.IMPORTANCE_HIGH
)
val nm = applicationContext.getSystemService<NotificationManager>()!!
nm.createNotificationChannel(channel)

val pi = if (accessibilityGranted) {
val intent = Intent(applicationContext, PocketService::class.java)
PendingIntent.getForegroundService(
applicationContext,
NOTIFICATION_ID,
intent,
PendingIntent.FLAG_IMMUTABLE
)
} else {
// We can not restart the service, because the system has taken the accessibility
// permission of the app.
val intent = Intent(applicationContext, MainActivity::class.java)
PendingIntent.getActivity(
applicationContext,
NOTIFICATION_ID,
intent,
PendingIntent.FLAG_IMMUTABLE
)
}

val description = applicationContext.getString(R.string.notification_restart_description)
return NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_outline_lock)
.setContentIntent(pi)
.setColor(0xFFf4ff81.toInt())
.setShowWhen(false)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setContentTitle(applicationContext.getString(R.string.notification_restart_title))
.setContentText(description)
.setStyle(NotificationCompat.BigTextStyle().bigText(description))
.build()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,7 @@ inline fun <T> observerFlow(
try {
unregister = withContext(Dispatchers.Main) {
val setter = { value: T ->
try {
offer(value)
} catch (e: Exception) {
// Do nothing.
}
trySend(value)
}
register(setter)
}
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/res/layout/layout_access.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@
android:paddingBottom="12dp"
android:text="@string/access_accessibility_service"
android:textAppearance="?textAppearanceBodyLarge"
android:textColor="?colorTertiary"
android:textColor="?colorError"
app:drawableStartCompat="@drawable/ic_outline_accessibility"
app:drawableTint="?colorTertiary"
app:drawableTint="?colorError"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/access" />
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@
<string name="notification_pocket_channel">Pocket mode service notification</string>
<string name="notification_pocket_title">Pocket mode is running</string>
<string name="notification_pocket_description">Long press the notification and hide it, if you don\'t want to see it constantly.</string>
<string name="notification_restart_channel">Pocket mode restart notification</string>
<string name="notification_restart_title">Pocket mode has been stopped by the system</string>
<string name="notification_restart_description">Tap to restart the service</string>

<string name="coming_soon">Coming soon</string>
<string name="error">Oh, snap!</string>
Expand Down
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
ext.kotlin_version = '1.5.31'
ext.kotlin_version = '1.6.10'
ext.lifecycle_version = "2.4.0"
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.0.3'
classpath 'com.android.tools.build:gradle:7.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.3.10'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.0'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip

0 comments on commit d36b163

Please sign in to comment.