mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 01:08:50 -06:00
Implement runtime notification permission; bump targetSdk to 33
This commit is contained in:
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
@@ -79,8 +79,8 @@ class AndroidDataView(
|
||||
}
|
||||
|
||||
override fun onFling(
|
||||
e1: MotionEvent?,
|
||||
e2: MotionEvent?,
|
||||
e1: MotionEvent,
|
||||
e2: MotionEvent,
|
||||
velocityX: Float,
|
||||
velocityY: Float
|
||||
): Boolean {
|
||||
@@ -100,7 +100,7 @@ class AndroidDataView(
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onAnimationUpdate(animation: ValueAnimator?) {
|
||||
override fun onAnimationUpdate(animation: ValueAnimator) {
|
||||
if (!scroller.isFinished) {
|
||||
scroller.computeScrollOffset()
|
||||
updateDataOffset()
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
|
||||
@@ -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() {
|
||||
@@ -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()
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user