Skip to content

Commit

Permalink
fixes for API level 29
Browse files Browse the repository at this point in the history
- Use wakelock to prevent device sleep stopping timer.
- Ask for SYSTEM_ALERT_WINDOW permission to return to home screen, see https://developer.android.com/guide/components/activities/background-starts
TimoWilhelm committed Jun 12, 2020
1 parent 42b64e0 commit 13bca1d
Showing 5 changed files with 55 additions and 36 deletions.
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -46,6 +46,7 @@ dependencies {
implementation 'androidx.preference:preference:1.1.1'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.legacy:legacy-preference-v14:1.0.0'
implementation 'androidx.media:media:1.1.0'
implementation 'com.github.Triggertrap:SeekArc:v1.1'
implementation project(':preferencecompatextensions')

4 changes: 3 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -3,8 +3,10 @@
package="com.timowilhelm.sleeptimer"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.USES_POLICY_FORCE_LOCK" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.USES_POLICY_FORCE_LOCK" />

<application
tools:replace="android:icon,android:theme"
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.timowilhelm.sleeptimer

import android.content.*
import android.net.Uri
import android.os.Bundle
import android.os.IBinder
import android.provider.Settings
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager
@@ -126,12 +128,18 @@ class SleepTimerActivity : AppCompatActivity() {
}

fun startTimer(@Suppress("UNUSED_PARAMETER")view: View) {
val timerValueInMinutes = seekArc.progress
lastUsedTimePreference = timerValueInMinutes
val serviceToStart = Intent(this, SleepTimerService::class.java)
startService(serviceToStart)
sleepTimerService?.startTimer(timerValueInMinutes)
updateUiTimerRunning()
if(!Settings.canDrawOverlays(this)) {
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:$packageName"))
startActivity(intent)
} else {
val timerValueInMinutes = seekArc.progress
lastUsedTimePreference = timerValueInMinutes
val serviceToStart = Intent(this, SleepTimerService::class.java)
startService(serviceToStart)
sleepTimerService?.startTimer(timerValueInMinutes)
updateUiTimerRunning()
}
}

fun extendTimer(@Suppress("UNUSED_PARAMETER")view: View) {
64 changes: 36 additions & 28 deletions app/src/main/kotlin/com/timowilhelm/sleeptimer/SleepTimerService.kt
Original file line number Diff line number Diff line change
@@ -6,13 +6,18 @@ import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.media.AudioAttributes
import android.media.AudioFocusRequest
import android.media.AudioManager
import android.os.Binder
import android.os.CountDownTimer
import android.os.IBinder
import android.os.PowerManager
import android.provider.Settings
import androidx.core.app.ActivityCompat.startActivityForResult
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.media.AudioAttributesCompat
import androidx.media.AudioFocusRequestCompat
import androidx.media.AudioManagerCompat
import androidx.media.AudioManagerCompat.requestAudioFocus
import androidx.preference.PreferenceManager
import java.text.MessageFormat
import kotlin.math.roundToInt
@@ -26,15 +31,15 @@ class SleepTimerService : Service() {
}

private val NOTIFICATION_ID = 15

private lateinit var notificationHelper: NotificationHelper
private lateinit var wakeLock: PowerManager.WakeLock
private var countDownTimer: CountDownTimer? = null
var timeLeft = 0
var running = false
private var lowerMediaVolumeTask: LowerMediaVolumeTask? = null

private val myBinder = LocalBinder()

var timeLeft = 0
var running = false

inner class LocalBinder : Binder() {
fun getService(): SleepTimerService? {
return this@SleepTimerService
@@ -55,16 +60,21 @@ class SleepTimerService : Service() {

override fun onCreate() {
super.onCreate()
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "com.timowilhelm.sleeptimer::WakeLock")
}
notificationHelper = NotificationHelper(this)
}

override fun onDestroy() {
notificationHelper.cancel(1)
wakeLock.release()
super.onDestroy()
}

fun startTimer(timerValueInMinutes: Int) {
running = true
this.wakeLock.acquire(timerValueInMinutes * 60 * 1000L)
startForeground(NOTIFICATION_ID, notificationHelper.notificationBuilder.build())

val timerValueInMs = (timerValueInMinutes * 60 * 1000).toLong()
@@ -117,27 +127,25 @@ class SleepTimerService : Service() {
stopForeground(true)
sendTimerFinishedBroadcast()
stopSelf()
wakeLock.release()
}

private fun stopPlayback() {
val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val playbackAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
val playbackAttributes = AudioAttributesCompat.Builder()
.setUsage(AudioAttributesCompat.USAGE_MEDIA)
.setContentType(AudioAttributesCompat.CONTENT_TYPE_MUSIC)
.build()
val res: Int
res = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
audioManager.requestAudioFocus(AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(playbackAttributes)
.build())
} else {
@Suppress("DEPRECATION")
audioManager.requestAudioFocus(null,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN)
}

if (res == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
val focusRequest = AudioFocusRequestCompat.Builder(AudioManagerCompat.AUDIOFOCUS_GAIN)
.setAudioAttributes(playbackAttributes ?: return)
.setWillPauseWhenDucked(false)
.setOnAudioFocusChangeListener { _ ->
{}
}
.build()

if (requestAudioFocus(audioManager, focusRequest) != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
//Todo: Throw Error
}
}
@@ -146,32 +154,32 @@ class SleepTimerService : Service() {
val startMain = Intent(Intent.ACTION_MAIN)
startMain.addCategory(Intent.CATEGORY_HOME)
startMain.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(startMain)
applicationContext.startActivity(startMain)
}

private fun turnOffScreen() {
private fun turnOffScreen(): Boolean {
val policyManager = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val adminReceiver = ComponentName(this,
SleepTimerAdminReceiver::class.java)
val admin = policyManager.isAdminActive(adminReceiver)
if (admin) {
return if (admin) {
policyManager.lockNow()
true
} else {
//TODO: Throw Error
false
}
}

private fun sendTimerUpdateBroadcast() {
val timerUpdateBroadcast = Intent(SleepTimerActivity.ACTION_TIMER_UPDATE)
.putExtra("timeLeft", timeLeft)
LocalBroadcastManager.getInstance(this@SleepTimerService)
LocalBroadcastManager.getInstance(this)
.sendBroadcast(timerUpdateBroadcast)
}

private fun sendTimerFinishedBroadcast() {
val timerFinishedBroadcast = Intent(SleepTimerActivity.ACTION_TIMER_FINISH)
LocalBroadcastManager.getInstance(this@SleepTimerService)
LocalBroadcastManager.getInstance(this)
.sendBroadcast(timerFinishedBroadcast)
}

}
2 changes: 1 addition & 1 deletion preferencecompatextensions/build.gradle
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ android {
}
buildTypes {
release {
minifyEnabled false
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
zipAlignEnabled true
}

0 comments on commit 13bca1d

Please sign in to comment.