Update to Android 12

android-12
Adam Niederer 5 months ago
parent d724719514
commit 40a6028c4d
  1. 22
      app/build.gradle
  2. 17
      app/src/main/AndroidManifest.xml
  3. 2
      app/src/main/java/com/jmstudios/redmoon/Config.kt
  4. 162
      app/src/main/java/com/jmstudios/redmoon/KPreferences.kt
  5. 15
      app/src/main/java/com/jmstudios/redmoon/Notification.kt
  6. 6
      app/src/main/java/com/jmstudios/redmoon/RedMoonApplication.kt
  7. 3
      app/src/main/java/com/jmstudios/redmoon/Whitelist.kt
  8. 2
      app/src/main/java/com/jmstudios/redmoon/fragment/SettingsFragment.kt
  9. 2
      app/src/main/java/com/jmstudios/redmoon/preference/ProfileSelectorPreference.kt
  10. 26
      app/src/main/java/com/jmstudios/redmoon/receiver/ScheduleReceiver.kt
  11. 8
      build.gradle
  12. 2
      gradle/wrapper/gradle-wrapper.properties
  13. 4
      timepickerpreference/build.gradle

@ -28,13 +28,12 @@ if(rootProject.file("keystore.properties").exists()) {
}
android {
compileSdkVersion 29
buildToolsVersion '29.0.2'
compileSdkVersion 32
defaultConfig {
applicationId "com.jmstudios.redmoon"
minSdkVersion 14
targetSdkVersion 29
targetSdkVersion 32
versionCode 38
versionName "3.5.0"
}
@ -62,23 +61,22 @@ android {
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions {
disable 'NewApi','ExpiredTargetSdkVersion'
lint {
disable 'NewApi', 'ExpiredTargetSdkVersion'
}
}
dependencies {
// Android/platform deps
implementation 'androidx.core:core:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.preference:preference:1.1.1'
implementation 'androidx.preference:preference-ktx:1.1.1'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.core:core:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'androidx.preference:preference:1.2.0'
implementation 'androidx.preference:preference-ktx:1.2.0'
implementation 'com.google.android.material:material:1.5.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// Written by me
implementation "me.smichel.android:kpreferences:0.7.0"
implementation project(":timepickerpreference")
// 3rd party

@ -44,7 +44,9 @@
<uses-permission
android:name="android.permission.WRITE_SETTINGS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
@ -66,7 +68,8 @@
<activity
android:name="com.jmstudios.redmoon.activity.MainActivity"
android:label="@string/activity_main">
android:label="@string/activity_main"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@ -103,27 +106,28 @@
<activity
android:name=".widget.ShortcutCreationActivity"
android:label="@string/activity_shortcut"
android:theme="@style/TransparentTheme">
android:theme="@style/TransparentTheme"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<receiver android:name="com.jmstudios.redmoon.receiver.BootReceiver">
<receiver android:name="com.jmstudios.redmoon.receiver.BootReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
<receiver android:name="com.jmstudios.redmoon.receiver.TimeZoneChangeReceiver">
<receiver android:name="com.jmstudios.redmoon.receiver.TimeZoneChangeReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
</intent-filter>
</receiver>
<receiver android:name="com.jmstudios.redmoon.widget.SwitchAppWidgetProvider">
<receiver android:name="com.jmstudios.redmoon.widget.SwitchAppWidgetProvider" android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
@ -143,7 +147,8 @@
android:name=".widget.TileReceiver"
android:icon="@drawable/notification_icon_half_moon"
android:label="@string/app_name"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
android:exported="true">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>

@ -35,8 +35,6 @@ import com.luckycatlabs.sunrisesunset.SunriseSunsetCalculator
import java.util.Calendar
import java.util.TimeZone
import me.smichel.android.KPreferences.Preferences
private const val BROADCAST_ACTION = "com.jmstudios.redmoon.RED_MOON_TOGGLED"
private const val BROADCAST_FIELD = "jmstudios.bundle.key.FILTER_IS_ON"

@ -0,0 +1,162 @@
/*
* Copyright (c) 2017 Stephen Michel <s@smichel.me>
* SPDX-License-Identifier: GPL-3.0+
*/
package com.jmstudios.redmoon
import android.preference.PreferenceManager.*
import android.content.Context
import android.content.SharedPreferences
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
private typealias Callback<T> = (T) -> Unit
// These are to cut down on verbosity in Preference subclasses.
// For clarity, they should not be used elsewhere.
private typealias B = Boolean
private typealias L = Long
private typealias I = Int
private typealias S = String
private typealias SS = Set<String>
abstract class Preferences : SharedPreferences.OnSharedPreferenceChangeListener {
val prefs: SharedPreferences
val context: Context
constructor(context: Context) {
this.context = context
prefs = getDefaultSharedPreferences(context)
}
constructor(context: Context, name: String, mode: Int = Context.MODE_PRIVATE) {
this.context = context
prefs = context.getSharedPreferences(name, mode)
}
private val callbacks = mutableMapOf<String, () -> Unit>()
var listeningForChanges: Boolean = false
set(value) {
if (value != field) {
field = value
if (value) {
prefs.registerOnSharedPreferenceChangeListener(this)
} else {
prefs.unregisterOnSharedPreferenceChangeListener(this)
}
}
}
protected fun registerCallback(key: String, onChange: () -> Unit) {
callbacks[key] = onChange
listeningForChanges = true
}
protected fun registerCallbacks(keys: List<String>, onChange: () -> Unit) {
keys.forEach { key ->
registerCallback(key, onChange)
}
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
callbacks[key]?.invoke()
}
private fun str(resId: Int): String = context.getString(resId)
inner abstract class Preference<T: Any?>(inline val key: String, inline val default: T) : ReadWriteProperty<Any?, T> {
constructor(resId: Int, default: T) : this(str(resId), default)
constructor(key: String, default: T, onChange: Callback<T>) : this(key, default) {
registerCallback(key){ onChange(prefValue) }
}
constructor(resId: Int, default: T, onChange: Callback<T>) : this(str(resId), default, onChange)
protected abstract var prefValue: T
override fun getValue(thisRef: Any?, property: KProperty<*>) = prefValue
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
prefValue = value
}
}
inner class BooleanPreference : Preference<B> {
constructor(resId: Int, default: B) : super(resId, default)
constructor(key: String, default: B) : super(key, default)
constructor(key: String, default: B, onChange: Callback<B>) : super(key, default, onChange)
constructor(resId: Int, default: B, onChange: Callback<B>) : super(resId, default, onChange)
override var prefValue: B
get() = prefs.getBoolean(key, default)
set(value) = prefs.edit().putBoolean(key, value).apply()
}
inner class IntPreference : Preference<I> {
constructor(key: String, default: I) : super(key, default)
constructor(resId: I, default: I) : super(resId, default)
constructor(key: String, default: I, onChange: Callback<I>) : super(key, default, onChange)
constructor(resId: I, default: I, onChange: Callback<I>) : super(resId, default, onChange)
override var prefValue: I
get() = prefs.getInt(key, default)
set(value) = prefs.edit().putInt(key, value).apply()
}
inner class LongPreference : Preference<L> {
constructor(key: String, default: L) : super(key, default)
constructor(resId: Int, default: L) : super(resId, default)
constructor(key: String, default: L, onChange: Callback<L>) : super(key, default, onChange)
constructor(resId: Int, default: L, onChange: Callback<L>) : super(resId, default, onChange)
override var prefValue: L
get() = prefs.getLong(key, default)
set(value) = prefs.edit().putLong(key, value).apply()
}
inner class StringPreference : Preference<S> {
constructor(key: String, default: S) : super(key, default)
constructor(resId: Int, default: S) : super(resId, default)
constructor(key: String, default: S, onChange: Callback<S>) : super(key, default, onChange)
constructor(resId: Int, default: S, onChange: Callback<S>) : super(resId, default, onChange)
override var prefValue: S
get() = prefs.getString(key, default)!!
set(value) = prefs.edit().putString(key, value).apply()
}
inner class StringSetPreference : Preference<SS> {
constructor(key: String, default: SS) : super(key, default)
constructor(resId: Int, default: SS) : super(resId, default)
constructor(key: String, default: SS, onChange: Callback<SS>) : super(key, default, onChange)
constructor(resId: Int, default: SS, onChange: Callback<SS>) : super(resId, default, onChange)
override var prefValue: SS
get() = prefs.getStringSet(key, default)!!
set(value) = prefs.edit().putStringSet(key, value).apply()
}
inner class StringOrNullPreference : Preference<S?> {
constructor(key: String, default: S? = null) : super(key, default)
constructor(resId: Int, default: S? = null) : super(resId, default)
constructor(key: String, default: S? = null, onChange: Callback<S?>) : super(key, default, onChange)
constructor(resId: Int, default: S? = null, onChange: Callback<S?>) : super(resId, default, onChange)
override var prefValue: S?
get() = prefs.getString(key, default)
set(value) = prefs.edit().putString(key, value).apply()
}
inner class StringSetOrNullPreference : Preference<SS?> {
constructor(key: String, default: SS? = null) : super(key, default)
constructor(resId: Int, default: SS? = null) : super(resId, default)
constructor(key: String, default: SS? = null, onChange: Callback<SS?>) : super(key, default, onChange)
constructor(resId: Int, default: SS? = null, onChange: Callback<SS?>) : super(resId, default, onChange)
override var prefValue: SS?
get() = prefs.getStringSet(key, default)
set(value) = prefs.edit().putStringSet(key, value).apply()
}
}

@ -5,12 +5,14 @@
*/
package com.jmstudios.redmoon
import android.annotation.SuppressLint
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 android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
@ -87,13 +89,26 @@ class Notification(
}
//region wrappers for readability
@SuppressLint("UnspecifiedImmutableFlag")
private fun servicePI(code: Int, intent: Intent) =
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
PendingIntent.getService(context, code, intent, PendingIntent.FLAG_UPDATE_CURRENT.or(PendingIntent.FLAG_IMMUTABLE))
else
PendingIntent.getService(context, code, intent, PendingIntent.FLAG_UPDATE_CURRENT)
@SuppressLint("UnspecifiedImmutableFlag")
private fun activityPI(code: Int, intent: Intent) =
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
PendingIntent.getActivity(context, code, intent, PendingIntent.FLAG_UPDATE_CURRENT.or(PendingIntent.FLAG_IMMUTABLE))
else
PendingIntent.getActivity(context, code, intent, PendingIntent.FLAG_UPDATE_CURRENT)
@SuppressLint("UnspecifiedImmutableFlag")
private fun broadcastPI(code: Int, intent: Intent) =
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
PendingIntent.getBroadcast(context, code, intent, PendingIntent.FLAG_IMMUTABLE)
else
PendingIntent.getBroadcast(context, code, intent, 0)
private fun NotificationCompat.Builder.addAction(icon: Int, title: Int, intent: PendingIntent) =

@ -6,19 +6,15 @@
package com.jmstudios.redmoon
import android.app.Application
import com.jmstudios.redmoon.helper.Logger
import android.content.Context
import android.content.SharedPreferences
import androidx.appcompat.app.AppCompatDelegate
import androidx.preference.PreferenceManager
import com.jmstudios.redmoon.helper.Logger
import com.jmstudios.redmoon.receiver.ScheduleReceiver
import org.json.JSONObject
class RedMoonApplication: Application() {
override fun onCreate() {
Log.i("onCreate -- Initializing appContext")
app = this

@ -4,11 +4,8 @@
*/
package com.jmstudios.redmoon
import com.jmstudios.redmoon.App
import com.jmstudios.redmoon.helper.KLogging
import me.smichel.android.KPreferences.Preferences
private const val WHITELIST_PREF: String = "com.jmstudios.redmoon.WHITELIST"
object Whitelist : Preferences(appContext, WHITELIST_PREF) {

@ -123,7 +123,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
updateSecureSuspendSummary()
}
override fun onDisplayPreferenceDialog(p: Preference?) {
override fun onDisplayPreferenceDialog(p: Preference) {
if (p is TimePreference) {
TimePreferenceDialogFragmentCompat.newInstance(p.key).let {
it.setTargetFragment(this, 0)

@ -43,7 +43,7 @@ class ProfileSelectorPreference(ctx: Context, attrs: AttributeSet) : Preference(
layoutResource = R.layout.preference_profile_selector
}
override fun onBindViewHolder(holder: PreferenceViewHolder?) {
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
mProfileSpinner = holder?.findViewById(R.id.profile_spinner) as Spinner
mProfileActionButton = holder?.findViewById(R.id.profile_action_button) as Button

@ -5,12 +5,15 @@
*/
package com.jmstudios.redmoon.receiver
import android.annotation.SuppressLint
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.provider.Settings
import androidx.core.app.AlarmManagerCompat
import com.jmstudios.redmoon.Command
import com.jmstudios.redmoon.appContext
@ -23,7 +26,6 @@ import java.util.Calendar
import java.util.GregorianCalendar
class ScheduleReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.i("Alarm received")
@ -57,6 +59,7 @@ class ScheduleReceiver : BroadcastReceiver() {
cancelAlarm(false)
}
@SuppressLint("UnspecifiedImmutableFlag")
private fun scheduleNextCommand(turnOn: Boolean) {
if (Config.scheduleOn) {
Log.d("Scheduling alarm to turn filter ${if (turnOn) "on" else "off"}")
@ -82,8 +85,18 @@ class ScheduleReceiver : BroadcastReceiver() {
Log.i("Scheduling alarm for " + calendar.toString())
val pendingIntent = PendingIntent.getBroadcast(appContext, 0, command, 0)
val pendingIntent =
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
PendingIntent.getBroadcast(appContext, 0, command, PendingIntent.FLAG_IMMUTABLE)
else
PendingIntent.getBroadcast(appContext, 0, command, 0)
if(
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
!(appContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager).canScheduleExactAlarms()
) {
appContext.startActivity(Intent().apply { action = Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM })
}
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC,
calendar.timeInMillis, pendingIntent)
} else {
@ -91,12 +104,17 @@ class ScheduleReceiver : BroadcastReceiver() {
}
}
@SuppressLint("UnspecifiedImmutableFlag")
private fun cancelAlarm(turnOn: Boolean) {
Log.d("Canceling alarm to turn filter ${if (turnOn) "on" else "off"}")
val command = intent.apply {
data = Uri.parse(if (turnOn) "turnOnIntent" else "offIntent")
}
val pendingIntent = PendingIntent.getBroadcast(appContext, 0, command, 0)
val pendingIntent =
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
PendingIntent.getBroadcast(appContext, 0, command, PendingIntent.FLAG_IMMUTABLE)
else
PendingIntent.getBroadcast(appContext, 0, command, 0)
alarmManager.cancel(pendingIntent)
}
}

@ -1,14 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.4.21'
ext.kotlin_version = '1.6.10'
ext.grgit_version = "4.1.0"
repositories {
jcenter()
mavenCentral()
google()
}
dependencies {
// Platform dependencies
classpath 'com.android.tools.build:gradle:4.1.1'
classpath 'com.android.tools.build:gradle:7.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// 3rd party
@ -37,7 +37,7 @@ dependencyUpdates {
allprojects {
repositories {
jcenter()
mavenCentral()
google()
maven { url 'https://jitpack.io' }
}

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

@ -1,16 +1,12 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
minSdkVersion 14
targetSdkVersion 29
versionCode 1
versionName "0.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'

Loading…
Cancel
Save