Skip to content

Commit

Permalink
Implement runtime notification permission; bump targetSdk to 33
Browse files Browse the repository at this point in the history
  • Loading branch information
iSoron committed Nov 25, 2023
1 parent 7735247 commit ed8c60e
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 18 deletions.
4 changes: 2 additions & 2 deletions uhabits-android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ kotlin {

android {

compileSdk = 32
compileSdk = 33

defaultConfig {
versionCode = 20200
versionName = "2.2.0"
minSdk = 28
targetSdk = 32
targetSdk = 33
applicationId = "org.isoron.uhabits"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package org.isoron.uhabits.activities.common.views

import android.view.MotionEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.isoron.uhabits.BaseViewTest
Expand Down Expand Up @@ -52,7 +53,8 @@ class FrequencyChartTest : BaseViewTest() {
@Test
@Throws(Throwable::class)
fun testRender_withDataOffset() {
view.onScroll(null, null, -dpToPixels(150), 0f)
val e = MotionEvent.obtain(0, 0, 0, 0f, 0f, 0)
view.onScroll(e, e, -dpToPixels(150), 0f)
view.invalidate()
assertRenders(view, BASE_PATH + "renderDataOffset.png")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package org.isoron.uhabits.activities.common.views

import android.view.MotionEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.isoron.uhabits.BaseViewTest
Expand Down Expand Up @@ -63,7 +64,8 @@ class ScoreChartTest : BaseViewTest() {
@Test
@Throws(Throwable::class)
fun testRender_withDataOffset() {
view.onScroll(null, null, -dpToPixels(150), 0f)
val e = MotionEvent.obtain(0, 0, 0, 0f, 0f, 0)
view.onScroll(e, e, -dpToPixels(150), 0f)
view.invalidate()
assertRenders(view, BASE_PATH + "renderDataOffset.png")
}
Expand Down
1 change: 1 addition & 0 deletions uhabits-android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<application
android:name=".HabitsApplication"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,21 @@ class AndroidDataView(
addUpdateListener(this@AndroidDataView)
}

override fun onTouchEvent(event: MotionEvent?) = detector.onTouchEvent(event)
override fun onDown(e: MotionEvent?) = true
override fun onShowPress(e: MotionEvent?) = Unit
override fun onTouchEvent(event: MotionEvent) = detector.onTouchEvent(event)
override fun onDown(e: MotionEvent) = true
override fun onShowPress(e: MotionEvent) = Unit

override fun onSingleTapUp(e: MotionEvent?): Boolean {
override fun onSingleTapUp(e: MotionEvent): Boolean {
return handleClick(e, true)
}

override fun onLongPress(e: MotionEvent?) {
override fun onLongPress(e: MotionEvent) {
handleClick(e)
}

override fun onScroll(
e1: MotionEvent?,
e2: MotionEvent?,
e1: MotionEvent,
e2: MotionEvent,
dx: Float,
dy: Float
): Boolean {
Expand All @@ -79,8 +79,8 @@ class AndroidDataView(
}

override fun onFling(
e1: MotionEvent?,
e2: MotionEvent?,
e1: MotionEvent,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
): Boolean {
Expand All @@ -100,7 +100,7 @@ class AndroidDataView(
return false
}

override fun onAnimationUpdate(animation: ValueAnimator?) {
override fun onAnimationUpdate(animation: ValueAnimator) {
if (!scroller.isFinished) {
scroller.computeScrollOffset()
updateDataOffset()
Expand All @@ -127,11 +127,11 @@ class AndroidDataView(
}
}

private fun handleClick(e: MotionEvent?, isSingleTap: Boolean = false): Boolean {
private fun handleClick(e: MotionEvent, isSingleTap: Boolean = false): Boolean {
val x: Float
val y: Float
try {
val pointerId = e!!.getPointerId(0)
val pointerId = e.getPointerId(0)
x = e.getX(pointerId)
y = e.getY(pointerId)
} catch (ex: RuntimeException) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ abstract class ScrollableChart : View, GestureDetector.OnGestureListener, Animat
return BundleSavedState(superState, bundle)
}

override fun onScroll(e1: MotionEvent?, e2: MotionEvent?, dx: Float, dy: Float): Boolean {
override fun onScroll(e1: MotionEvent, e2: MotionEvent, dx: Float, dy: Float): Boolean {
var dx = dx
if (scrollerBucketSize == 0) return false
if (abs(dx) > abs(dy)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@

package org.isoron.uhabits.activities.habits.list

import android.Manifest.permission.POST_NOTIFICATIONS
import android.content.Intent
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat.checkSelfPermission
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import org.isoron.uhabits.BaseExceptionHandler
Expand Down Expand Up @@ -56,6 +61,16 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
lateinit var midnightTimer: MidnightTimer
private val scope = CoroutineScope(Dispatchers.Main)

private var permissionAlreadyRequested = false
private val permissionLauncher =
registerForActivityResult(RequestPermission()) { isGranted: Boolean ->
if (isGranted) {
scheduleReminders()
} else {
Log.i("ListHabitsActivity", "POST_NOTIFICATIONS denied")
}
}

private lateinit var menu: ListHabitsMenu

override fun onQuestionMarksChanged() {
Expand Down Expand Up @@ -101,7 +116,26 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
screen.onAttached()
rootView.postInvalidate()
midnightTimer.onResume()
appComponent.reminderScheduler.scheduleAll()

if (appComponent.reminderScheduler.hasHabitsWithReminders()) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
scheduleReminders()
} else {
if (checkSelfPermission(this, POST_NOTIFICATIONS) == PERMISSION_GRANTED) {
scheduleReminders()
} else {
// If we have not requested the permission yet, request it. Otherwide do
// nothing. This check is necessary to avoid an infinite onResume loop in case
// the user denies the permission.
if (!permissionAlreadyRequested) {
Log.i("ListHabitsActivity", "Requestion permission: POST_NOTIFICATIONS")
permissionLauncher.launch(POST_NOTIFICATIONS)
permissionAlreadyRequested = true
}
}
}
}

taskRunner.run {
try {
AutoBackup(this@ListHabitsActivity).run()
Expand All @@ -117,6 +151,10 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
super.onResume()
}

private fun scheduleReminders() {
appComponent.reminderScheduler.scheduleAll()
}

override fun onCreateOptionsMenu(m: Menu): Boolean {
menu.onCreate(menuInflater, m)
return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ class ReminderScheduler @Inject constructor(
for (habit in reminderHabits) schedule(habit)
}

@Synchronized
fun hasHabitsWithReminders(): Boolean {
return !habitList.getFiltered(HabitMatcher.WITH_ALARM).isEmpty
}

@Synchronized
fun startListening() {
commandRunner.addListener(this)
Expand Down

0 comments on commit ed8c60e

Please sign in to comment.