Merge pull request #12 from ouchadam/reducing-min-sdk
Reducing min sdk to 7.0 (api 24)
This commit is contained in:
commit
71bc151f32
|
@ -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")
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -63,7 +63,7 @@ ext.applyCommonAndroidParameters = { project ->
|
||||||
incremental = true
|
incremental = true
|
||||||
}
|
}
|
||||||
android.defaultConfig {
|
android.defaultConfig {
|
||||||
minSdkVersion 29
|
minSdkVersion 24
|
||||||
targetSdkVersion androidSdkVersion
|
targetSdkVersion androidSdkVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
package app.dapk.st.core
|
||||||
|
|
||||||
|
interface Base64 {
|
||||||
|
fun encode(input: ByteArray): String
|
||||||
|
fun decode(input: String): ByteArray
|
||||||
|
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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,6 +45,7 @@ class WorkAndroidService : JobService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun JobParameters.collectAllTasks(): List<RunnableWorkTask> {
|
private fun JobParameters.collectAllTasks(): List<RunnableWorkTask> {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
var work: JobWorkItem?
|
var work: JobWorkItem?
|
||||||
val tasks = mutableListOf<RunnableWorkTask>()
|
val tasks = mutableListOf<RunnableWorkTask>()
|
||||||
do {
|
do {
|
||||||
|
@ -58,6 +64,18 @@ class WorkAndroidService : JobService() {
|
||||||
}
|
}
|
||||||
} while (work != null)
|
} while (work != null)
|
||||||
return tasks
|
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")!!,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStopJob(params: JobParameters): Boolean {
|
override fun onStopJob(params: JobParameters): Boolean {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
val item = JobWorkItem(
|
val item = JobWorkItem(
|
||||||
Intent()
|
Intent()
|
||||||
.putExtra("task-type", task.type)
|
.putExtra("task-type", task.type)
|
||||||
.putExtra("task-payload", task.jsonPayload)
|
.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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.getEncoder().encode(baos.toByteArray()).toString(Charsets.UTF_8)
|
return base64.encode(baos.toByteArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
@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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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,6 +11,7 @@ class NotificationChannels(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun initChannels() {
|
fun initChannels() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
if (notificationManager.getNotificationChannel(channelId) == null) {
|
if (notificationManager.getNotificationChannel(channelId) == null) {
|
||||||
notificationManager.createNotificationChannel(
|
notificationManager.createNotificationChannel(
|
||||||
NotificationChannel(
|
NotificationChannel(
|
||||||
|
@ -20,5 +22,6 @@ class NotificationChannels(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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>)
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue