update minSdk to 26

This commit is contained in:
Phillip Thelen 2025-01-31 11:59:41 +01:00
parent 2638001513
commit 6d062aa7b6
39 changed files with 136 additions and 345 deletions

View file

@ -156,12 +156,8 @@ abstract class HabiticaBaseApplication : Application(), Application.ActivityLife
val resources = resources
val configuration: Configuration = resources.configuration
val languageHelper = LanguageHelper(sharedPrefs.getString("language", "en"))
if (if (SDK_INT >= Build.VERSION_CODES.N) {
if (
configuration.locales.isEmpty || configuration.locales[0] != languageHelper.locale
} else {
@Suppress("DEPRECATION")
configuration.locale != languageHelper.locale
}
) {
configuration.setLocale(languageHelper.locale)
resources.updateConfiguration(configuration, null)

View file

@ -1,20 +0,0 @@
package com.habitrpg.android.habitica.extensions
import android.app.PendingIntent
import android.os.Build
fun withImmutableFlag(flags: Int): Int {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
flags + PendingIntent.FLAG_IMMUTABLE
} else {
flags
}
}
fun withMutableFlag(flags: Int): Int {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
flags + PendingIntent.FLAG_MUTABLE
} else {
flags
}
}

View file

@ -15,9 +15,7 @@ fun Resources.forceLocale(
Locale.setDefault(locale)
val configuration = Configuration()
configuration.setLocale(locale)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
activity.createConfigurationContext(configuration)
}
activity.createConfigurationContext(configuration)
updateConfiguration(configuration, displayMetrics)
try {

View file

@ -13,12 +13,8 @@ fun Window.updateStatusBarColor(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
return
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
statusBarColor = color
@Suppress("DEPRECATION")
decorView.systemUiVisibility =
if (isLight) View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR else View.SYSTEM_UI_FLAG_VISIBLE
} else {
statusBarColor = context.getThemeColor(R.attr.colorPrimaryDark)
}
statusBarColor = color
@Suppress("DEPRECATION")
decorView.systemUiVisibility =
if (isLight) View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR else View.SYSTEM_UI_FLAG_VISIBLE
}

View file

@ -7,7 +7,6 @@ import android.content.Intent
import android.os.Build
import androidx.preference.PreferenceManager
import com.habitrpg.android.habitica.data.TaskRepository
import com.habitrpg.android.habitica.extensions.withImmutableFlag
import com.habitrpg.android.habitica.models.tasks.RemindersItem
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.modules.AuthenticationHandler
@ -166,7 +165,7 @@ class TaskAlarmManager(
context,
intentId,
intent,
withImmutableFlag(PendingIntent.FLAG_NO_CREATE)
PendingIntent.FLAG_NO_CREATE + PendingIntent.FLAG_IMMUTABLE
)
if (previousSender != null) {
previousSender.cancel()
@ -178,7 +177,7 @@ class TaskAlarmManager(
context,
intentId,
intent,
withImmutableFlag(PendingIntent.FLAG_CANCEL_CURRENT)
PendingIntent.FLAG_CANCEL_CURRENT + PendingIntent.FLAG_IMMUTABLE
)
CoroutineScope(Dispatchers.IO).launch {
@ -209,7 +208,7 @@ class TaskAlarmManager(
context,
intentId,
intent,
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_IMMUTABE
)
val am = context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
sender.cancel()
@ -250,7 +249,7 @@ class TaskAlarmManager(
context,
0,
notificationIntent,
withImmutableFlag(PendingIntent.FLAG_NO_CREATE)
PendingIntent.FLAG_NO_CREATE + PendingIntent.FLAG_IMMUTABLE
)
if (previousSender != null) {
previousSender.cancel()
@ -262,7 +261,7 @@ class TaskAlarmManager(
context,
0,
notificationIntent,
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_IMMUTABE
)
setAlarm(context, triggerTime, pendingIntent)
@ -273,7 +272,7 @@ class TaskAlarmManager(
val notificationIntent = Intent(context, NotificationPublisher::class.java)
val alarmManager = context?.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
val displayIntent =
PendingIntent.getBroadcast(context, 0, notificationIntent, withImmutableFlag(0))
PendingIntent.getBroadcast(context, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE)
alarmManager?.cancel(displayIntent)
}
@ -289,45 +288,41 @@ class TaskAlarmManager(
}
val notificationType = AlarmManager.RTC_WAKEUP
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
try {
var canScheduleExact = true
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
canScheduleExact = alarmManager.canScheduleExactAlarms()
}
if (canScheduleExact) {
alarmManager.setExactAndAllowWhileIdle(notificationType, time, pendingIntent)
HLogger.log(
LogLevel.DEBUG,
"TaskAlarmManager",
"setAlarm: Scheduling for $time using setExact ${Date().time}"
)
} else {
alarmManager.setAndAllowWhileIdle(notificationType, time, pendingIntent)
HLogger.log(
LogLevel.DEBUG,
"TaskAlarmManager",
"setAlarm: Scheduling for $time using setAndAllowWhileIdle"
try {
var canScheduleExact = true
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
canScheduleExact = alarmManager.canScheduleExactAlarms()
}
if (canScheduleExact) {
alarmManager.setExactAndAllowWhileIdle(notificationType, time, pendingIntent)
HLogger.log(
LogLevel.DEBUG,
"TaskAlarmManager",
"setAlarm: Scheduling for $time using setExact ${Date().time}"
)
} else {
alarmManager.setAndAllowWhileIdle(notificationType, time, pendingIntent)
HLogger.log(
LogLevel.DEBUG,
"TaskAlarmManager",
"setAlarm: Scheduling for $time using setAndAllowWhileIdle"
)
}
} catch (ex: Exception) {
when (ex) {
is IllegalStateException, is SecurityException -> {
alarmManager.setWindow(
notificationType,
time,
600000,
pendingIntent
)
}
} catch (ex: Exception) {
when (ex) {
is IllegalStateException, is SecurityException -> {
alarmManager.setWindow(
notificationType,
time,
600000,
pendingIntent
)
}
else -> {
throw ex
}
else -> {
throw ex
}
}
} else {
alarmManager.set(notificationType, time, pendingIntent)
}
}
}

View file

@ -130,12 +130,8 @@ class TaskDescriptionBuilder(private val context: Context) {
}
private fun withOrdinal(day: Int): String {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val formatter = MessageFormat("{0,ordinal}", LanguageHelper.systemLocale)
formatter.format(arrayOf(day))
} else {
day.toString()
}
val formatter = MessageFormat("{0,ordinal}", LanguageHelper.systemLocale)
return formatter.format(arrayOf(day))
}
private fun describeRepeatInterval(

View file

@ -11,7 +11,6 @@ import androidx.core.app.Person
import androidx.core.app.RemoteInput
import androidx.core.os.bundleOf
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.withMutableFlag
import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver
import com.habitrpg.common.habitica.helpers.EmojiParser
import java.text.SimpleDateFormat
@ -35,11 +34,7 @@ class GroupActivityNotification(context: Context, identifier: String?) :
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager
val existingNotifications =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
notificationManager?.activeNotifications?.filter { it.id == getNotificationID(data) }
} else {
null
}
notificationManager?.activeNotifications?.filter { it.id == getNotificationID(data) }
val oldMessages =
existingNotifications?.firstOrNull()?.notification?.extras?.getBundle("messages")
?.get("messages") as? ArrayList<Map<String, String>> ?: arrayListOf()
@ -89,7 +84,7 @@ class GroupActivityNotification(context: Context, identifier: String?) :
context,
groupID.hashCode(),
intent,
withMutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_MUTABLE
)
val action: NotificationCompat.Action =

View file

@ -4,12 +4,8 @@ import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.withImmutableFlag
import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver
/**
* Created by keithholliday on 7/1/16.
*/
class GuildInviteLocalNotification(context: Context, identifier: String?) :
HabiticaLocalNotification(context, identifier) {
override fun configureMainIntent(intent: Intent) {
@ -34,7 +30,7 @@ class GuildInviteLocalNotification(context: Context, identifier: String?) :
context,
groupID.hashCode(),
acceptInviteIntent,
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_IMMUTABLE
)
notificationBuilder.addAction(0, "Accept", pendingIntentAccept)
@ -47,7 +43,7 @@ class GuildInviteLocalNotification(context: Context, identifier: String?) :
context,
groupID.hashCode() + 1,
rejectInviteIntent,
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_IMMUTABLE
)
notificationBuilder.addAction(0, "Reject", pendingIntentReject)
}

View file

@ -9,7 +9,6 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.withImmutableFlag
import com.habitrpg.android.habitica.ui.activities.MainActivity
import java.util.Date
@ -81,7 +80,7 @@ abstract class HabiticaLocalNotification(
context,
3000,
intent,
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_IMMUTABE
)
notificationBuilder.setContentIntent(pendingIntent)
}

View file

@ -4,12 +4,8 @@ import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.withImmutableFlag
import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver
/**
* Created by keithholliday on 6/28/16.
*/
class PartyInviteLocalNotification(context: Context, identifier: String?) :
HabiticaLocalNotification(context, identifier) {
override fun setNotificationActions(
@ -29,7 +25,7 @@ class PartyInviteLocalNotification(context: Context, identifier: String?) :
context,
groupID.hashCode(),
acceptInviteIntent,
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_IMMUTABLE
)
notificationBuilder.addAction(0, context.getString(R.string.accept), pendingIntentAccept)
@ -42,7 +38,7 @@ class PartyInviteLocalNotification(context: Context, identifier: String?) :
context,
groupID.hashCode() + 1,
rejectInviteIntent,
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_IMMUTABLE
)
notificationBuilder.addAction(0, context.getString(R.string.reject), pendingIntentReject)
}

View file

@ -2,8 +2,5 @@ package com.habitrpg.android.habitica.helpers.notifications
import android.content.Context
/**
* Created by keithholliday on 7/1/16.
*/
class QuestBegunLocalNotification(context: Context, identifier: String?) :
HabiticaLocalNotification(context, identifier)

View file

@ -7,9 +7,6 @@ import android.os.Build
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver
/**
* Created by keithholliday on 7/1/16.
*/
class QuestInviteLocalNotification(context: Context, identifier: String?) :
HabiticaLocalNotification(context, identifier) {
override fun getNotificationID(data: MutableMap<String, String>): Int {
@ -27,11 +24,7 @@ class QuestInviteLocalNotification(context: Context, identifier: String?) :
acceptInviteIntent.action = res.getString(R.string.accept_quest_invite)
acceptInviteIntent.putExtra("NOTIFICATION_ID", notificationId)
val flags =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_IMMUTABLE
} else {
PendingIntent.FLAG_UPDATE_CURRENT
}
PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_IMMUTABLE
val pendingIntentAccept =
PendingIntent.getBroadcast(
context,

View file

@ -2,8 +2,5 @@ package com.habitrpg.android.habitica.helpers.notifications
import android.content.Context
/**
* Created by keithholliday on 7/1/16.
*/
class ReceivedGemsGiftLocalNotification(context: Context, identifier: String?) :
HabiticaLocalNotification(context, identifier)

View file

@ -10,7 +10,6 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.RemoteInput
import androidx.core.os.bundleOf
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.withMutableFlag
import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver
import com.habitrpg.common.habitica.helpers.EmojiParser
@ -20,11 +19,7 @@ class ReceivedPrivateMessageLocalNotification(context: Context, identifier: Stri
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager
val existingNotifications =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
notificationManager?.activeNotifications?.filter { it.id == getNotificationID(data) }
} else {
null
}
notificationManager?.activeNotifications?.filter { it.id == getNotificationID(data) }
val messageText = EmojiParser.parseEmojis(data["message"]?.trim { it <= ' ' })
val oldMessages =
existingNotifications?.firstOrNull()?.notification?.extras?.getStringArrayList("messages")
@ -100,7 +95,7 @@ class ReceivedPrivateMessageLocalNotification(context: Context, identifier: Stri
context,
senderID.hashCode(),
intent,
withMutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_MUTABLE
)
val action: NotificationCompat.Action =

View file

@ -2,8 +2,5 @@ package com.habitrpg.android.habitica.helpers.notifications
import android.content.Context
/**
* Created by keithholliday on 7/1/16.
*/
class ReceivedSubscriptionGiftLocalNotification(context: Context, identifier: String?) :
HabiticaLocalNotification(context, identifier)

View file

@ -13,7 +13,6 @@ import androidx.core.content.edit
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.TaskRepository
import com.habitrpg.android.habitica.data.UserRepository
import com.habitrpg.android.habitica.extensions.withImmutableFlag
import com.habitrpg.android.habitica.helpers.TaskAlarmManager
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.common.habitica.helpers.launchCatching
@ -142,7 +141,7 @@ class NotificationPublisher : BroadcastReceiver() {
thisContext,
0,
notificationIntent,
withImmutableFlag(0)
PendingIntent.FLAG_IMMUTABLE
)
builder.setContentIntent(intent)

View file

@ -15,7 +15,6 @@ import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.TaskRepository
import com.habitrpg.android.habitica.extensions.withImmutableFlag
import com.habitrpg.android.habitica.helpers.TaskAlarmManager
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.ui.activities.MainActivity
@ -73,7 +72,7 @@ class TaskReceiver : BroadcastReceiver() {
context,
System.currentTimeMillis().toInt(),
intent,
withImmutableFlag(0)
PendingIntent.FLAG_IMMUTABLE
)
val soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
@ -91,9 +90,7 @@ class TaskReceiver : BroadcastReceiver() {
.setAutoCancel(true)
.setContentIntent(pendingIntent)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
notificationBuilder = notificationBuilder.setCategory(Notification.CATEGORY_REMINDER)
}
notificationBuilder = notificationBuilder.setCategory(Notification.CATEGORY_REMINDER)
if (task.type == TaskType.DAILY || task.type == TaskType.TODO) {
val completeIntent =
@ -107,7 +104,7 @@ class TaskReceiver : BroadcastReceiver() {
context,
task.id.hashCode(),
completeIntent,
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_IMMUTABE
)
notificationBuilder.addAction(
0,

View file

@ -87,9 +87,7 @@ class AdventureGuideActivity : BaseActivity() {
val completed = achievements.count { it.earned }
binding.progressBar.max = achievements.size
binding.progressBar.progress = completed
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
binding.progressBar.progressBackgroundTintMode = PorterDuff.Mode.SRC_OVER
}
binding.progressBar.progressBackgroundTintMode = PorterDuff.Mode.SRC_OVER
if (completed > 0) {
binding.progressTextview.text =

View file

@ -186,10 +186,8 @@ class LoginActivity : BaseActivity() {
private fun configureForRegistering() {
binding.submitButton.text = getString(R.string.register_btn)
binding.username.setHint(R.string.username)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
binding.username.setAutofillHints("newUsername")
binding.password.setAutofillHints("newPassword")
}
binding.username.setAutofillHints("newUsername")
binding.password.setAutofillHints("newPassword")
binding.password.imeOptions = EditorInfo.IME_ACTION_NEXT
binding.googleLoginButton.setText(R.string.register_btn_google)
@ -199,10 +197,8 @@ class LoginActivity : BaseActivity() {
private fun configureForLogin() {
binding.submitButton.text = getString(R.string.login_btn)
binding.username.setHint(R.string.email_username)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
binding.username.setAutofillHints("username")
binding.password.setAutofillHints("password")
}
binding.username.setAutofillHints("username")
binding.password.setAutofillHints("password")
binding.password.imeOptions = EditorInfo.IME_ACTION_DONE
binding.googleLoginButton.setText(R.string.login_btn_google)
this.resetLayout()
@ -409,9 +405,7 @@ class LoginActivity : BaseActivity() {
private fun onForgotPasswordClicked() {
val input = EditText(this)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
input.setAutofillHints(EditText.AUTOFILL_HINT_EMAIL_ADDRESS)
}
input.setAutofillHints(EditText.AUTOFILL_HINT_EMAIL_ADDRESS)
input.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
input.hint = getString(R.string.forgot_password_hint_example)
input.textSize = 16f

View file

@ -114,6 +114,7 @@ import java.util.Date
import javax.inject.Inject
import kotlin.time.DurationUnit
import kotlin.time.toDuration
import androidx.core.view.isVisible
var mainActivityCreatedAt: Date? = null
@ -458,22 +459,20 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
}
private fun setupNotifications() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelId = "default"
val channel =
NotificationChannel(
channelId,
"Habitica Notifications",
NotificationManager.IMPORTANCE_DEFAULT
)
val manager = getSystemService(NotificationManager::class.java)
manager?.createNotificationChannel(channel)
}
val channelId = "default"
val channel =
NotificationChannel(
channelId,
"Habitica Notifications",
NotificationManager.IMPORTANCE_DEFAULT
)
val manager = getSystemService(NotificationManager::class.java)
manager?.createNotificationChannel(channel)
}
private fun setupBottomnavigationLayoutListener() {
binding.content.bottomNavigation.viewTreeObserver.addOnGlobalLayoutListener {
if (binding.content.bottomNavigation.visibility == View.VISIBLE) {
if (binding.content.bottomNavigation.isVisible) {
snackbarContainer.setPadding(
0,
0,

View file

@ -118,32 +118,17 @@ class ReportMessageActivity : BaseActivity() {
private fun setStatusBarDim(dim: Boolean) {
if (dim) {
binding.appbar.elevation = 0f
window.statusBarColor = getThemeColor(R.attr.colorPrimaryDark)
binding.closeButton.visibility = View.GONE
binding.toolbarTitle.setTypeface(null, Typeface.BOLD)
} else {
binding.appbar.elevation = 8f
window.statusBarColor = ContextCompat.getColor(this, R.color.offset_background)
binding.closeButton.visibility = View.VISIBLE
binding.toolbarTitle.setTypeface(null, Typeface.NORMAL)
}
if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
setSystemBarTheme(dim)
}
}
override fun finish() {
dismissKeyboard()
super.finish()
}
@RequiresApi(api = VERSION_CODES.M)
fun setSystemBarTheme(isDark: Boolean) {
// Fetch the current flags.
val lFlags = window.decorView.systemUiVisibility
// Update the SystemUiVisibility dependening on whether we want a Light or Dark theme.
window.decorView.systemUiVisibility =
if (isDark) lFlags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() else lFlags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
}
}

View file

@ -135,13 +135,9 @@ class TaskFormActivity : BaseActivity() {
alert.setMessage(R.string.push_notification_system_settings_description)
alert.addButton(R.string.settings, isPrimary = true, isDestructive = false) { _, _ ->
val notifSettingIntent: Intent =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(Settings.EXTRA_APP_PACKAGE, applicationContext?.packageName)
} else {
return@addButton
}
Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(Settings.EXTRA_APP_PACKAGE, applicationContext?.packageName)
startActivity(notifSettingIntent)
}
alert.addButton(R.string.cancel, false) { _, _ ->

View file

@ -80,9 +80,7 @@ class HabiticaAccountDialog(private var thisContext: Context) :
private fun showForgotPasswordDialog() {
val input = EditText(requireContext())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
input.setAutofillHints(EditText.AUTOFILL_HINT_EMAIL_ADDRESS)
}
input.setAutofillHints(EditText.AUTOFILL_HINT_EMAIL_ADDRESS)
input.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
input.hint = getString(R.string.forgot_password_hint_example)
input.textSize = 16f

View file

@ -271,17 +271,15 @@ class PreferencesFragment :
val alert = context?.let { HabiticaAlertDialog(it) }
alert?.setTitle(R.string.push_notification_system_settings_title)
alert?.setMessage(R.string.push_notification_system_settings_description)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
alert?.addButton(R.string.open_settings, true, false) { _, _ ->
val notifSettingIntent: Intent =
Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(
Settings.EXTRA_APP_PACKAGE,
context?.applicationContext?.packageName
)
startActivity(notifSettingIntent)
}
alert?.addButton(R.string.open_settings, true, false) { _, _ ->
val notifSettingIntent: Intent =
Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(
Settings.EXTRA_APP_PACKAGE,
context?.applicationContext?.packageName
)
startActivity(notifSettingIntent)
}
alert?.addButton(R.string.cancel, false) { _, _ ->
alert.dismiss()

View file

@ -22,15 +22,8 @@ class TimePreferenceDialogFragment : PreferenceDialogFragmentCompat() {
get() {
val lastHour: Int
val lastMinute: Int
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
lastHour = picker.hour
lastMinute = picker.minute
} else {
@Suppress("DEPRECATION")
lastHour = picker.currentHour
@Suppress("DEPRECATION")
lastMinute = picker.currentMinute
}
lastHour = picker.hour
lastMinute = picker.minute
return lastHour.toString() + ":" + String.format(Locale.UK, "%02d", lastMinute)
}
@ -46,15 +39,8 @@ class TimePreferenceDialogFragment : PreferenceDialogFragmentCompat() {
val preference = timePreference
val lastHour = preference.lastHour
val lastMinute = preference.lastMinute
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
picker.hour = lastHour
picker.minute = lastMinute
} else {
@Suppress("DEPRECATION")
picker.currentHour = lastHour
@Suppress("DEPRECATION")
picker.currentMinute = lastMinute
}
picker.hour = lastHour
picker.minute = lastMinute
}
override fun onDialogClosed(positiveResult: Boolean) {

View file

@ -50,9 +50,6 @@ object ToolbarColorHelper {
backgroundColor ?: activity.getThemeColor(R.attr.headerBackgroundColor)
)
val toolbarIconsColor = iconColor ?: activity.getThemeColor(R.attr.headerTextColor)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
activity.window.statusBarColor = activity.getThemeColor(R.attr.colorPrimaryDark)
}
val colorFilter = PorterDuffColorFilter(toolbarIconsColor, PorterDuff.Mode.MULTIPLY)
for (i in 0 until toolbar.childCount) {
when (val v = toolbar.getChildAt(i)) {

View file

@ -20,6 +20,7 @@ import com.habitrpg.common.habitica.extensions.DataBindingUtils
import com.habitrpg.common.habitica.extensions.inflate
import com.habitrpg.shared.habitica.models.responses.FeedResponse
import dagger.hilt.android.internal.managers.ViewComponentManager
import androidx.core.graphics.drawable.toDrawable
class PetViewHolder(
parent: ViewGroup,
@ -92,9 +93,7 @@ class PetViewHolder(
binding.imageView.alpha = 0.2f
}
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
binding.trainedProgressBar.progressBackgroundTintMode = PorterDuff.Mode.SRC_OVER
}
binding.trainedProgressBar.progressBackgroundTintMode = PorterDuff.Mode.SRC_OVER
binding.imageView.background = null
binding.activeIndicator.visibility =
if (currentPet.equals(animal?.key)) View.VISIBLE else View.GONE
@ -103,10 +102,7 @@ class PetViewHolder(
val resources = itemView.context.resources ?: return@loadImage
val drawable =
if (trained == 0 && canRaiseToMount) {
BitmapDrawable(
resources,
it.toBitmap().extractAlpha()
)
it.toBitmap().extractAlpha().toDrawable(resources)
} else {
it
}

View file

@ -157,7 +157,7 @@ class AuthenticationViewModel @Inject constructor(
sharedPrefs.edit {
putString("UserID", user)
val encryptedKey =
if (keyHelper != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (keyHelper != null) {
try {
keyHelper.encrypt(api)
} catch (e: Exception) {

View file

@ -179,10 +179,8 @@ class SubscriptionOptionView(context: Context, attrs: AttributeSet) : FrameLayou
binding.priceLabel.setTextColor(textColor)
}
binding.descriptionTextView.setTextColor(textColor)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
TextViewCompat.setCompoundDrawableTintList(binding.gemCapTextView, ContextCompat.getColorStateList(context, R.color.yellow_100))
TextViewCompat.setCompoundDrawableTintList(binding.hourglassTextView, ContextCompat.getColorStateList(context, R.color.yellow_100))
}
TextViewCompat.setCompoundDrawableTintList(binding.gemCapTextView, ContextCompat.getColorStateList(context, R.color.yellow_100))
TextViewCompat.setCompoundDrawableTintList(binding.hourglassTextView, ContextCompat.getColorStateList(context, R.color.yellow_100))
} else {
binding.selectedIndicator.animate()
.alpha(0f)
@ -215,10 +213,8 @@ class SubscriptionOptionView(context: Context, attrs: AttributeSet) : FrameLayou
R.color.brand_600
)
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
TextViewCompat.setCompoundDrawableTintList(binding.gemCapTextView, ContextCompat.getColorStateList(context, R.color.brand_400))
TextViewCompat.setCompoundDrawableTintList(binding.hourglassTextView, ContextCompat.getColorStateList(context, R.color.brand_400))
}
TextViewCompat.setCompoundDrawableTintList(binding.gemCapTextView, ContextCompat.getColorStateList(context, R.color.brand_400))
TextViewCompat.setCompoundDrawableTintList(binding.hourglassTextView, ContextCompat.getColorStateList(context, R.color.brand_400))
}
}
}

View file

@ -420,23 +420,13 @@ constructor(
weekdays =
if (daysOfMonth?.isNotEmpty() == true) {
val date = startDateCalendar.get(Calendar.DATE)
val formattedDate =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val formatter = MessageFormat("{0,ordinal}", LanguageHelper.systemLocale)
formatter.format(arrayOf(date))
} else {
date.toString()
}
val formatter = MessageFormat("{0,ordinal}", LanguageHelper.systemLocale)
val formattedDate = formatter.format(arrayOf(date))
" on the $formattedDate"
} else {
val week = startDateCalendar.get(Calendar.WEEK_OF_MONTH)
val formattedWeek =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val formatter = MessageFormat("{0,ordinal}", LanguageHelper.systemLocale)
formatter.format(arrayOf(week))
} else {
week.toString()
}
val formatter = MessageFormat("{0,ordinal}", LanguageHelper.systemLocale)
val formattedWeek = formatter.format(arrayOf(week))
val dayLongName =
startDateCalendar.getDisplayName(
Calendar.DAY_OF_WEEK,

View file

@ -23,11 +23,7 @@ class DateDeserializer : JsonDeserializer<Date>, JsonSerializer<Date> {
addFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
addFormat("E MMM dd yyyy HH:mm:ss zzzz")
addFormat("yyyy-MM-dd'T'HH:mm:sszzz")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
addFormat("yyyy-MM-dd'T'HH:mmX")
} else {
addFormat("yyyy-MM-dd'T'HH:mm")
}
addFormat("yyyy-MM-dd'T'HH:mmX")
addFormat("yyyy-MM-dd")
}

View file

@ -8,7 +8,6 @@ import android.content.Intent
import android.view.View
import android.widget.RemoteViews
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.withImmutableFlag
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
@ -203,7 +202,7 @@ class AvatarStatsWidgetProvider : BaseWidgetProvider() {
context,
0,
openAppIntent,
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_IMMUTABE
)
remoteViews.setOnClickPendingIntent(android.R.id.background, openApp)

View file

@ -14,7 +14,6 @@ import android.widget.RemoteViews
import androidx.core.content.ContextCompat
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.TaskRepository
import com.habitrpg.android.habitica.extensions.withImmutableFlag
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.common.habitica.helpers.ExceptionHandler
import com.habitrpg.common.habitica.helpers.MarkdownParser
@ -144,7 +143,7 @@ class HabitButtonWidgetService : Service() {
context,
widgetId + direction.hashCode(),
taskIntent,
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_IMMUTABE
)
}
}

View file

@ -9,8 +9,6 @@ import android.net.Uri
import android.widget.RemoteViews
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.TaskRepository
import com.habitrpg.android.habitica.extensions.withImmutableFlag
import com.habitrpg.android.habitica.extensions.withMutableFlag
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.common.habitica.helpers.ExceptionHandler
import dagger.hilt.android.AndroidEntryPoint
@ -95,7 +93,7 @@ abstract class TaskListWidgetProvider : BaseWidgetProvider() {
context,
0,
openAppIntent,
withImmutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_IMMUTABE
)
rv.setOnClickPendingIntent(R.id.widget_title, openApp)
@ -108,7 +106,7 @@ abstract class TaskListWidgetProvider : BaseWidgetProvider() {
context,
0,
taskIntent,
withMutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_MUTABLE
)
rv.setPendingIntentTemplate(R.id.list_view, toastPendingIntent)

View file

@ -54,7 +54,7 @@ class HostConfig {
}
} else {
val key = sharedPreferences.getString("APIToken", null)
if (key?.isNotBlank() == true && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (key?.isNotBlank() == true) {
val encryptedKey = keyHelper?.encrypt(key)
sharedPreferences.edit {
putString(userID, encryptedKey)

View file

@ -9,12 +9,7 @@ import android.util.Patterns
import java.util.Locale
fun String.fromHtml(): CharSequence {
return if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
Html.fromHtml(this, Html.FROM_HTML_MODE_LEGACY)
} else {
@Suppress("DEPRECATION")
Html.fromHtml(this)
}
return Html.fromHtml(this, Html.FROM_HTML_MODE_LEGACY)
}
fun String.addZeroWidthSpace(): CharSequence {

View file

@ -34,9 +34,5 @@ object HealthFormatter {
}
private fun getDefaultLocale() =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Locale.getDefault(Locale.Category.FORMAT)
} else {
Locale.getDefault()
}
Locale.getDefault(Locale.Category.FORMAT)
}

View file

@ -49,55 +49,26 @@ constructor(ctx: Context, var sharedPreferences: SharedPreferences, var keyStore
}
init {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
this.generateEncryptKey(ctx)
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
try {
this.generateAESKey()
} catch (e: Exception) {
HLogger.logException("KeyHelper", "Error initializing", e)
}
}
this.generateEncryptKey(ctx)
}
@Throws(NoSuchProviderException::class, NoSuchAlgorithmException::class, InvalidAlgorithmParameterException::class, KeyStoreException::class, CertificateException::class, IOException::class)
private fun generateEncryptKey(ctx: Context) {
keyStore?.load(null)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (keyStore?.containsAlias(KEY_ALIAS) == false) {
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE)
keyGenerator.init(
KeyGenParameterSpec.Builder(
KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(false)
.build()
if (keyStore?.containsAlias(KEY_ALIAS) == false) {
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE)
keyGenerator.init(
KeyGenParameterSpec.Builder(
KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
keyGenerator.generateKey()
}
} else {
if (keyStore?.containsAlias(KEY_ALIAS) == false) {
// Generate a key pair for encryption
val start = Calendar.getInstance()
val end = Calendar.getInstance()
end.add(Calendar.YEAR, 30)
val spec =
KeyPairGeneratorSpec.Builder(ctx)
.setAlias(KEY_ALIAS)
.setSubject(X500Principal("CN=$KEY_ALIAS"))
.setSerialNumber(BigInteger.TEN)
.setStartDate(start.time)
.setEndDate(end.time)
.build()
val kpg = KeyPairGenerator.getInstance("RSA", ANDROID_KEY_STORE)
kpg.initialize(spec)
kpg.generateKeyPair()
}
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(false)
.build()
)
keyGenerator.generateKey()
}
}
@ -158,20 +129,11 @@ constructor(ctx: Context, var sharedPreferences: SharedPreferences, var keyStore
val c: Cipher
val publicIV = getRandomIV()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
c = Cipher.getInstance(AES_MODE_M)
try {
c.init(Cipher.ENCRYPT_MODE, aesKeyFromKS, GCMParameterSpec(128, Base64.decode(publicIV, Base64.DEFAULT)))
} catch (e: Exception) {
HLogger.logException("KeyHelper", "Error encrypting", e)
}
} else {
c = Cipher.getInstance(AES_MODE_M)
try {
c.init(Cipher.ENCRYPT_MODE, getSecretKey(), GCMParameterSpec(128, Base64.decode(publicIV, Base64.DEFAULT)))
} catch (e: Exception) {
HLogger.logException("KeyHelper", "Error encrypting", e)
}
c = Cipher.getInstance(AES_MODE_M)
try {
c.init(Cipher.ENCRYPT_MODE, aesKeyFromKS, GCMParameterSpec(128, Base64.decode(publicIV, Base64.DEFAULT)))
} catch (e: Exception) {
HLogger.logException("KeyHelper", "Error encrypting", e)
}
val encodedBytes = c.doFinal(input.toByteArray(charset("UTF-8")))
return Base64.encodeToString(encodedBytes, Base64.DEFAULT)
@ -182,20 +144,11 @@ constructor(ctx: Context, var sharedPreferences: SharedPreferences, var keyStore
val c: Cipher
val publicIV = getRandomIV()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
c = Cipher.getInstance(AES_MODE_M)
try {
c.init(Cipher.DECRYPT_MODE, aesKeyFromKS, GCMParameterSpec(128, Base64.decode(publicIV, Base64.DEFAULT)))
} catch (e: Exception) {
HLogger.logException("KeyHelper", "Error decrypting", e)
}
} else {
c = Cipher.getInstance(AES_MODE_M)
try {
c.init(Cipher.DECRYPT_MODE, getSecretKey(), GCMParameterSpec(128, Base64.decode(publicIV, Base64.DEFAULT)))
} catch (e: Exception) {
HLogger.logException("KeyHelper", "Error decrypting", e)
}
c = Cipher.getInstance(AES_MODE_M)
try {
c.init(Cipher.DECRYPT_MODE, aesKeyFromKS, GCMParameterSpec(128, Base64.decode(publicIV, Base64.DEFAULT)))
} catch (e: Exception) {
HLogger.logException("KeyHelper", "Error decrypting", e)
}
return try {

View file

@ -1,5 +1,5 @@
[versions]
minSdk = "21"
minSdk = "26"
targetSdk = "35"
wearOsTargetSdk = "34"