Merge pull request #12 from ouchadam/reducing-min-sdk

Reducing min sdk to 7.0 (api 24)
This commit is contained in:
Adam Brown 2022-03-18 20:56:25 +00:00 committed by GitHub
commit 71bc151f32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 151 additions and 63 deletions

View File

@ -41,12 +41,18 @@ android {
} }
} }
compileOptions {
coreLibraryDesugaringEnabled true
}
packagingOptions { packagingOptions {
resources.excludes += "DebugProbesKt.bin" resources.excludes += "DebugProbesKt.bin"
} }
} }
dependencies { dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation project(":features:home") implementation project(":features:home")
implementation project(":features:directory") implementation project(":features:directory")
implementation project(":features:login") implementation project(":features:login")

View File

@ -7,10 +7,7 @@ import android.content.Intent
import app.dapk.db.DapkDb import app.dapk.db.DapkDb
import app.dapk.st.BuildConfig import app.dapk.st.BuildConfig
import app.dapk.st.SharedPreferencesDelegate import app.dapk.st.SharedPreferencesDelegate
import app.dapk.st.core.BuildMeta import app.dapk.st.core.*
import app.dapk.st.core.CoreAndroidModule
import app.dapk.st.core.CoroutineDispatchers
import app.dapk.st.core.SingletonFlows
import app.dapk.st.core.extensions.ErrorTracker import app.dapk.st.core.extensions.ErrorTracker
import app.dapk.st.core.extensions.unsafeLazy import app.dapk.st.core.extensions.unsafeLazy
import app.dapk.st.directory.DirectoryModule import app.dapk.st.directory.DirectoryModule
@ -208,7 +205,7 @@ internal class MatrixModules(
installAuthService(credentialsStore) installAuthService(credentialsStore)
installEncryptionService(store.knownDevicesStore()) installEncryptionService(store.knownDevicesStore())
val olmAccountStore = OlmPersistenceWrapper(store.olmStore()) val olmAccountStore = OlmPersistenceWrapper(store.olmStore(), AndroidBase64())
val singletonFlows = SingletonFlows(coroutineDispatchers) val singletonFlows = SingletonFlows(coroutineDispatchers)
val olm = OlmWrapper( val olm = OlmWrapper(
olmStore = olmAccountStore, olmStore = olmAccountStore,
@ -417,3 +414,13 @@ class TaskRunnerAdapter(private val matrixTaskRunner: suspend (MatrixTask) -> Ma
} }
} }
} }
class AndroidBase64 : Base64 {
override fun encode(input: ByteArray): String {
return android.util.Base64.encodeToString(input, android.util.Base64.DEFAULT)
}
override fun decode(input: String): ByteArray {
return android.util.Base64.decode(input, android.util.Base64.DEFAULT)
}
}

View File

@ -63,7 +63,7 @@ ext.applyCommonAndroidParameters = { project ->
incremental = true incremental = true
} }
android.defaultConfig { android.defaultConfig {
minSdkVersion 29 minSdkVersion 24
targetSdkVersion androidSdkVersion targetSdkVersion androidSdkVersion
} }

View File

@ -0,0 +1,6 @@
package app.dapk.st.core
interface Base64 {
fun encode(input: ByteArray): String
fun decode(input: String): ByteArray
}

View File

@ -8,15 +8,15 @@ interface TaskRunner {
suspend fun run(tasks: List<RunnableWorkTask>): List<TaskResult> suspend fun run(tasks: List<RunnableWorkTask>): List<TaskResult>
data class RunnableWorkTask( data class RunnableWorkTask(
val source: JobWorkItem, val source: JobWorkItem?,
val task: WorkTask val task: WorkTask
) )
sealed interface TaskResult { sealed interface TaskResult {
val source: JobWorkItem val source: JobWorkItem?
data class Success(override val source: JobWorkItem) : TaskResult data class Success(override val source: JobWorkItem?) : TaskResult
data class Failure(override val source: JobWorkItem, val canRetry: Boolean) : TaskResult data class Failure(override val source: JobWorkItem?, val canRetry: Boolean) : TaskResult
} }
} }

View File

@ -3,6 +3,7 @@ package app.dapk.st.work
import android.app.job.JobParameters import android.app.job.JobParameters
import android.app.job.JobService import android.app.job.JobService
import android.app.job.JobWorkItem import android.app.job.JobWorkItem
import android.os.Build
import app.dapk.st.core.extensions.Scope import app.dapk.st.core.extensions.Scope
import app.dapk.st.core.extensions.unsafeLazy import app.dapk.st.core.extensions.unsafeLazy
import app.dapk.st.core.module import app.dapk.st.core.module
@ -24,11 +25,15 @@ class WorkAndroidService : JobService() {
when (it) { when (it) {
is TaskRunner.TaskResult.Failure -> { is TaskRunner.TaskResult.Failure -> {
if (!it.canRetry) { if (!it.canRetry) {
params.completeWork(it.source) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
params.completeWork(it.source!!)
}
} }
} }
is TaskRunner.TaskResult.Success -> { is TaskRunner.TaskResult.Success -> {
params.completeWork(it.source) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
params.completeWork(it.source!!)
}
} }
} }
} }
@ -40,24 +45,37 @@ class WorkAndroidService : JobService() {
} }
private fun JobParameters.collectAllTasks(): List<RunnableWorkTask> { private fun JobParameters.collectAllTasks(): List<RunnableWorkTask> {
var work: JobWorkItem? if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val tasks = mutableListOf<RunnableWorkTask>() var work: JobWorkItem?
do { val tasks = mutableListOf<RunnableWorkTask>()
work = this.dequeueWork() do {
work?.intent?.also { intent -> work = this.dequeueWork()
tasks.add( work?.intent?.also { intent ->
RunnableWorkTask( tasks.add(
source = work, RunnableWorkTask(
task = WorkTask( source = work,
jobId = this.jobId, task = WorkTask(
type = intent.getStringExtra("task-type")!!, jobId = this.jobId,
jsonPayload = intent.getStringExtra("task-payload")!!, type = intent.getStringExtra("task-type")!!,
jsonPayload = intent.getStringExtra("task-payload")!!,
)
) )
) )
}
} while (work != null)
return tasks
} else {
return listOf(
RunnableWorkTask(
source = null,
task = WorkTask(
jobId = this.jobId,
type = this.extras.getString("task-type")!!,
jsonPayload = this.extras.getString("task-payload")!!,
)
) )
} )
} while (work != null) }
return tasks
} }
override fun onStopJob(params: JobParameters): Boolean { override fun onStopJob(params: JobParameters): Boolean {

View File

@ -6,6 +6,7 @@ import android.app.job.JobWorkItem
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build
internal class WorkSchedulingJobScheduler( internal class WorkSchedulingJobScheduler(
private val context: Context, private val context: Context,
@ -23,12 +24,17 @@ internal class WorkSchedulingJobScheduler(
.setRequiresDeviceIdle(false) .setRequiresDeviceIdle(false)
.build() .build()
val item = JobWorkItem( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent() val item = JobWorkItem(
.putExtra("task-type", task.type) Intent()
.putExtra("task-payload", task.jsonPayload) .putExtra("task-type", task.type)
) .putExtra("task-payload", task.jsonPayload)
)
jobScheduler.enqueue(job, item) jobScheduler.enqueue(job, item)
} else {
job.extras.putString("task-type", task.type)
job.extras.putString("task-payload", task.jsonPayload)
jobScheduler.schedule(job)
}
} }
} }

View File

@ -1,5 +1,6 @@
package app.dapk.st.olm package app.dapk.st.olm
import app.dapk.st.core.Base64
import app.dapk.st.domain.OlmPersistence import app.dapk.st.domain.OlmPersistence
import app.dapk.st.domain.SerializedObject import app.dapk.st.domain.SerializedObject
import app.dapk.st.matrix.common.Curve25519 import app.dapk.st.matrix.common.Curve25519
@ -10,10 +11,10 @@ import org.matrix.olm.OlmInboundGroupSession
import org.matrix.olm.OlmOutboundGroupSession import org.matrix.olm.OlmOutboundGroupSession
import org.matrix.olm.OlmSession import org.matrix.olm.OlmSession
import java.io.* import java.io.*
import java.util.*
class OlmPersistenceWrapper( class OlmPersistenceWrapper(
private val olmPersistence: OlmPersistence, private val olmPersistence: OlmPersistence,
private val base64: Base64,
) : OlmStore { ) : OlmStore {
override suspend fun read(): OlmAccount? { override suspend fun read(): OlmAccount? {
@ -49,21 +50,21 @@ class OlmPersistenceWrapper(
override suspend fun readInbound(sessionId: SessionId): OlmInboundGroupSession? { override suspend fun readInbound(sessionId: SessionId): OlmInboundGroupSession? {
return olmPersistence.readInbound(sessionId)?.value?.deserialize() return olmPersistence.readInbound(sessionId)?.value?.deserialize()
} }
}
private fun <T : Serializable> T.serialize(): String { private fun <T : Serializable> T.serialize(): String {
val baos = ByteArrayOutputStream() val baos = ByteArrayOutputStream()
ObjectOutputStream(baos).use { ObjectOutputStream(baos).use {
it.writeObject(this) it.writeObject(this)
}
return base64.encode(baos.toByteArray())
} }
return Base64.getEncoder().encode(baos.toByteArray()).toString(Charsets.UTF_8)
}
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
private fun <T : Serializable> String.deserialize(): T { private fun <T : Serializable> String.deserialize(): T {
val decoded = Base64.getDecoder().decode(this) val decoded = base64.decode(this)
val baos = ByteArrayInputStream(decoded) val baos = ByteArrayInputStream(decoded)
return ObjectInputStream(baos).use { return ObjectInputStream(baos).use {
it.readObject() as T it.readObject() as T
}
} }
} }

View File

@ -2,6 +2,7 @@ package app.dapk.st.notifications
import android.app.NotificationChannel import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
import android.os.Build
private const val channelId = "message" private const val channelId = "message"
@ -10,14 +11,16 @@ class NotificationChannels(
) { ) {
fun initChannels() { fun initChannels() {
if (notificationManager.getNotificationChannel(channelId) == null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager.createNotificationChannel( if (notificationManager.getNotificationChannel(channelId) == null) {
NotificationChannel( notificationManager.createNotificationChannel(
channelId, NotificationChannel(
"messages", channelId,
NotificationManager.IMPORTANCE_HIGH, "messages",
NotificationManager.IMPORTANCE_HIGH,
)
) )
) }
} }
} }

View File

@ -4,6 +4,8 @@ import android.app.Notification
import android.app.PendingIntent import android.app.PendingIntent
import android.app.Person import android.app.Person
import android.content.Context import android.content.Context
import android.os.Build
import androidx.annotation.RequiresApi
import app.dapk.st.imageloader.IconLoader import app.dapk.st.imageloader.IconLoader
import app.dapk.st.matrix.sync.RoomEvent import app.dapk.st.matrix.sync.RoomEvent
import app.dapk.st.matrix.sync.RoomOverview import app.dapk.st.matrix.sync.RoomOverview
@ -46,7 +48,7 @@ class NotificationFactory(
} }
} }
return Notification.Builder(context, channelId) return builder()
.setStyle(summaryInboxStyle) .setStyle(summaryInboxStyle)
.setSmallIcon(R.drawable.ic_notification_small_icon) .setSmallIcon(R.drawable.ic_notification_small_icon)
.setCategory(Notification.CATEGORY_MESSAGE) .setCategory(Notification.CATEGORY_MESSAGE)
@ -55,7 +57,8 @@ class NotificationFactory(
.build() .build()
} }
private suspend fun createNotification(events: List<RoomEvent.Message>, roomOverview: RoomOverview): NotificationDelegate { @RequiresApi(Build.VERSION_CODES.P)
private suspend fun createMessageStyle(events: List<RoomEvent.Message>, roomOverview: RoomOverview): Notification.MessagingStyle {
val messageStyle = Notification.MessagingStyle( val messageStyle = Notification.MessagingStyle(
Person.Builder() Person.Builder()
.setName("me") .setName("me")
@ -66,7 +69,7 @@ class NotificationFactory(
messageStyle.conversationTitle = roomOverview.roomName.takeIf { roomOverview.isGroup } messageStyle.conversationTitle = roomOverview.roomName.takeIf { roomOverview.isGroup }
messageStyle.isGroupConversation = roomOverview.isGroup messageStyle.isGroupConversation = roomOverview.isGroup
events.sortedBy { it.utcTimestamp }.forEach { message -> events.forEach { message ->
val sender = Person.Builder() val sender = Person.Builder()
.setName(message.author.displayName ?: message.author.id.value) .setName(message.author.displayName ?: message.author.id.value)
.setIcon(message.author.avatarUrl?.let { iconLoader.load(it.value) }) .setIcon(message.author.avatarUrl?.let { iconLoader.load(it.value) })
@ -80,6 +83,21 @@ class NotificationFactory(
) )
) )
} }
return messageStyle
}
private suspend fun createNotification(events: List<RoomEvent.Message>, roomOverview: RoomOverview): NotificationDelegate {
val sortedEvents = events.sortedBy { it.utcTimestamp }
val messageStyle = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
createMessageStyle(sortedEvents, roomOverview)
} else {
val inboxStyle = Notification.InboxStyle()
events.forEach {
inboxStyle.addLine("${it.author.displayName ?: it.author.id.value}: ${it.content}")
}
inboxStyle
}
val openRoomIntent = PendingIntent.getActivity( val openRoomIntent = PendingIntent.getActivity(
context, context,
@ -89,25 +107,37 @@ class NotificationFactory(
) )
return NotificationDelegate.Room( return NotificationDelegate.Room(
Notification.Builder(context, channelId) builder()
.setWhen(messageStyle.messages.last().timestamp) .setWhen(sortedEvents.last().utcTimestamp)
.setShowWhen(true) .setShowWhen(true)
.setGroup(GROUP_ID) .setGroup(GROUP_ID)
.setOnlyAlertOnce(roomOverview.isGroup) .setOnlyAlertOnce(roomOverview.isGroup)
.setContentIntent(openRoomIntent) .setContentIntent(openRoomIntent)
.setStyle(messageStyle) .setStyle(messageStyle)
.setCategory(Notification.CATEGORY_MESSAGE) .setCategory(Notification.CATEGORY_MESSAGE)
.setShortcutId(roomOverview.roomId.value) .run {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
this.setShortcutId(roomOverview.roomId.value)
} else {
this
}
}
.setSmallIcon(R.drawable.ic_notification_small_icon) .setSmallIcon(R.drawable.ic_notification_small_icon)
.setLargeIcon(roomOverview.roomAvatarUrl?.let { iconLoader.load(it.value) }) .setLargeIcon(roomOverview.roomAvatarUrl?.let { iconLoader.load(it.value) })
.setAutoCancel(true) .setAutoCancel(true)
.build(), .build(),
roomId = roomOverview.roomId, roomId = roomOverview.roomId,
summary = messageStyle.messages.last().text.toString() summary = events.last().content
) )
} }
private fun builder() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder(context, channelId)
} else {
Notification.Builder(context)
}
} }
data class Notifications(val summaryNotification: Notification?, val delegates: List<NotificationDelegate>) data class Notifications(val summaryNotification: Notification?, val delegates: List<NotificationDelegate>)

View File

@ -1,6 +1,7 @@
package test package test
import TestUser import TestUser
import app.dapk.st.core.Base64
import app.dapk.st.core.CoroutineDispatchers import app.dapk.st.core.CoroutineDispatchers
import app.dapk.st.core.SingletonFlows import app.dapk.st.core.SingletonFlows
import app.dapk.st.domain.StoreModule import app.dapk.st.domain.StoreModule
@ -81,7 +82,7 @@ class TestMatrix(
installAuthService(storeModule.credentialsStore(), AuthConfig(forceHttp = false)) installAuthService(storeModule.credentialsStore(), AuthConfig(forceHttp = false))
installEncryptionService(storeModule.knownDevicesStore()) installEncryptionService(storeModule.knownDevicesStore())
val olmAccountStore = OlmPersistenceWrapper(storeModule.olmStore()) val olmAccountStore = OlmPersistenceWrapper(storeModule.olmStore(), JavaBase64())
val olm = OlmWrapper( val olm = OlmWrapper(
olmStore = olmAccountStore, olmStore = olmAccountStore,
singletonFlows = SingletonFlows(coroutineDispatchers), singletonFlows = SingletonFlows(coroutineDispatchers),
@ -271,3 +272,13 @@ class TestMatrix(
suspend fun deviceId() = storeModule.credentialsStore().credentials()!!.deviceId suspend fun deviceId() = storeModule.credentialsStore().credentials()!!.deviceId
suspend fun userId() = storeModule.credentialsStore().credentials()!!.userId suspend fun userId() = storeModule.credentialsStore().credentials()!!.userId
} }
class JavaBase64 : Base64 {
override fun encode(input: ByteArray): String {
return java.util.Base64.getEncoder().encode(input).toString(Charsets.UTF_8)
}
override fun decode(input: String): ByteArray {
return java.util.Base64.getDecoder().decode(input)
}
}