[go: nahoru, domu]

Skip to content

Commit

Permalink
Reorganize packages, better error definitions
Browse files Browse the repository at this point in the history
  • Loading branch information
oblakr24 committed Mar 18, 2024
1 parent 0ddccf4 commit 60828f3
Show file tree
Hide file tree
Showing 56 changed files with 193 additions and 127 deletions.
24 changes: 12 additions & 12 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ android {
applicationId = "com.rokoblak.chatbackup"
minSdk = 29
targetSdk = 34
versionCode = 6
versionCode = 7
versionName = "1.0.4"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
Expand Down Expand Up @@ -59,8 +59,9 @@ android {

dependencies {
val composeHiltNavigationVersion = "1.2.0"
val nav_version = "2.7.7"
val compose_ui_version = "1.6.3"
val navVersion = "2.7.7"
val composeUiVersion = "1.6.3"
val timberVersion = "5.0.1"

// Core/activity/lifecycle
implementation("androidx.core:core-ktx:1.12.0")
Expand All @@ -71,19 +72,19 @@ dependencies {
kapt("com.google.dagger:hilt-compiler:2.49")

// Compose
implementation("androidx.compose.ui:ui:$compose_ui_version")
implementation("androidx.compose.ui:ui-tooling-preview:$compose_ui_version")
implementation("androidx.compose.ui:ui:$composeUiVersion")
implementation("androidx.compose.ui:ui-tooling-preview:$composeUiVersion")
implementation("androidx.compose.material:material:1.6.3")
// Compose constraint layout
implementation("androidx.constraintlayout:constraintlayout-compose:1.0.1")
// Compose tooling
debugImplementation("androidx.compose.ui:ui-tooling:$compose_ui_version")
debugImplementation("androidx.compose.ui:ui-test-manifest:$compose_ui_version")
debugImplementation("androidx.compose.ui:ui-tooling:$composeUiVersion")
debugImplementation("androidx.compose.ui:ui-test-manifest:$composeUiVersion")
// Compose Navigation
implementation("androidx.navigation:navigation-compose:$nav_version")
implementation("androidx.navigation:navigation-compose:$navVersion")
implementation("androidx.hilt:hilt-navigation-compose:$composeHiltNavigationVersion")
// Compose permissions
implementation("com.google.accompanist:accompanist-permissions:0.31.2-alpha")
implementation("com.google.accompanist:accompanist-permissions:0.35.0-alpha")
// Compose extended material icons
implementation("androidx.compose.material:material-icons-extended:1.6.3")
// Compose Material 3
Expand All @@ -93,8 +94,7 @@ dependencies {
implementation("io.coil-kt:coil-compose:2.6.0")

// Timber
val timber = "5.0.1"
implementation("com.jakewharton.timber:timber:$timber")
implementation("com.jakewharton.timber:timber:$timberVersion")

// Datastore
implementation("androidx.datastore:datastore-preferences:1.0.0")
Expand All @@ -112,7 +112,7 @@ dependencies {
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation("androidx.compose.ui:ui-test-junit4:$compose_ui_version")
androidTestImplementation("androidx.compose.ui:ui-test-junit4:$composeUiVersion")
}

kapt {
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
</receiver>

<activity
android:name="com.rokoblak.chatbackup.main.MainActivity"
android:name="com.rokoblak.chatbackup.feature.main.MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/Theme.ChatBackup">
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.rokoblak.chatbackup.data.model.Conversations
import com.rokoblak.chatbackup.data.model.Message
import com.rokoblak.chatbackup.data.model.MinimalContact
import com.rokoblak.chatbackup.data.model.OperationResult
import com.rokoblak.chatbackup.data.model.RootError
import com.rokoblak.chatbackup.data.util.ConversationBuilder
import com.rokoblak.chatbackup.di.AppScope
import com.rokoblak.chatbackup.domain.usecases.RetrieveContactUseCase
Expand Down Expand Up @@ -174,7 +175,7 @@ class MessagesDataSource @Inject constructor(
)
}

suspend fun saveMessages(messages: List<Message>): Flow<OperationResult<Int>> {
suspend fun saveMessages(messages: List<Message>): Flow<OperationResult<Int, MessageInsertionError>> {
val (incomingMsgs, sentMsgs) = messages.partition { it.incoming }
val incomingUrl = Telephony.Sms.Inbox.CONTENT_URI
val sentUrl = Telephony.Sms.Sent.CONTENT_URI
Expand All @@ -187,7 +188,7 @@ class MessagesDataSource @Inject constructor(
private suspend fun saveMessagesForUri(
messages: List<Message>,
uri: Uri
): Flow<OperationResult<Int>> =
): Flow<OperationResult<Int, MessageInsertionError>> =
withContext(Dispatchers.IO) {
val allValues = messages.map {
createMsgValues(
Expand All @@ -202,7 +203,7 @@ class MessagesDataSource @Inject constructor(
Timber.i("Inserted: $it out of ${valuesList.size}")
}
if (inserted != valuesList.size) {
emit(OperationResult.Error("Failed to insert messages: $inserted out of ${valuesList.size}"))
emit(OperationResult.Error(MessageInsertionError.InsertionError(totalToInsert = valuesList.size, inserted = inserted)))
return@forEach
} else {
emit(OperationResult.Done(inserted))
Expand All @@ -212,7 +213,7 @@ class MessagesDataSource @Inject constructor(
}


suspend fun deleteMessages(ids: Set<String>): OperationResult<Unit> =
suspend fun deleteMessages(ids: Set<String>): OperationResult<Unit, MessageDeletionError> =
withContext(Dispatchers.IO) {
val cr = appScope.appContext.contentResolver

Expand All @@ -228,13 +229,16 @@ class MessagesDataSource @Inject constructor(
}
if (anyFailed) {
val totalDeleted = results.sumOf { it.count ?: 0 }
OperationResult.Error("Error deleting ${ids.size} messages: only deleted $totalDeleted")
OperationResult.Error(MessageDeletionError.DeletionError(
totalToDelete = ids.size,
deleted = totalDeleted
))
} else {
OperationResult.Done(Unit)
}
} catch (e: Throwable) {
e.printStackTrace()
OperationResult.Error(e.message ?: "Error deleting: unknown error")
OperationResult.Error(MessageDeletionError.GenericDeletionError(e.message ?: "Error deleting: unknown error"))
}
}

Expand Down Expand Up @@ -303,3 +307,12 @@ class MessagesDataSource @Inject constructor(
}
}
}

sealed interface MessageDeletionError: RootError {
data class DeletionError(val totalToDelete: Int, val deleted: Int): MessageDeletionError
data class GenericDeletionError(val message: String): MessageDeletionError
}

sealed interface MessageInsertionError: RootError {
data class InsertionError(val totalToInsert: Int, val inserted: Int): MessageInsertionError
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.rokoblak.chatbackup.data.model

sealed interface OperationResult<out T : Any?> {
data class Done<out T : Any>(val data: T) : OperationResult<T>
data class Error(val msg: String) : OperationResult<Nothing>
interface RootError

fun <R : Any> map(mapper: (T) -> R): OperationResult<R> = when (this) {
sealed interface OperationResult<out T : Any?, out E: RootError> {
data class Done<out T : Any, out E: RootError>(val data: T) : OperationResult<T, E>
data class Error<out E: RootError>(val error: E) : OperationResult<Nothing, E>

fun <R : Any> map(mapper: (T) -> R): OperationResult<R, E> = when (this) {
is Done -> Done(mapper(data))
is Error -> this
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.rokoblak.chatbackup.ui.commonui.PreviewDataUtils.obfuscate
import com.rokoblak.chatbackup.data.model.Contact
import com.rokoblak.chatbackup.data.model.PhoneType
import com.rokoblak.chatbackup.data.model.OperationResult
import com.rokoblak.chatbackup.data.model.RootError
import com.rokoblak.chatbackup.di.AppScope
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
Expand All @@ -20,7 +21,7 @@ class ContactsRepository @Inject constructor(

private val scope = CoroutineScope(Dispatchers.Main + Job())

val contactsFlow: StateFlow<OperationResult<List<Contact>>?> = flow {
val contactsFlow: StateFlow<OperationResult<List<Contact>, ContactsLoadError>?> = flow {
emit(loadContacts())
}.stateIn(scope, SharingStarted.WhileSubscribed(5000), OperationResult.Done(emptyList()))

Expand Down Expand Up @@ -67,10 +68,10 @@ class ContactsRepository @Inject constructor(
name?.contains(query, ignoreCase = true) == true
|| number.contains(query, ignoreCase = true)

private suspend fun loadContacts(): OperationResult<List<Contact>> =
private suspend fun loadContacts(): OperationResult<List<Contact>, ContactsLoadError> =
withContext(Dispatchers.IO) {
val cursor =
createCursor() ?: return@withContext OperationResult.Error("Could not open cursor")
createCursor() ?: return@withContext OperationResult.Error(ContactsLoadError.CursorOpenError)
OperationResult.Done(
cursor.parseContacts().let { contacts ->
if (AppConstants.OBFUSCATE) contacts.map { it.obfuscate() } else contacts
Expand Down Expand Up @@ -180,4 +181,8 @@ class ContactsRepository @Inject constructor(
Phone.PHOTO_URI
)
}
}

sealed interface ContactsLoadError: RootError {
data object CursorOpenError: ContactsLoadError
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.rokoblak.chatbackup.data.repo

import com.rokoblak.chatbackup.data.datasources.MessageDeletionError
import com.rokoblak.chatbackup.data.model.Conversation
import com.rokoblak.chatbackup.data.model.Conversations
import com.rokoblak.chatbackup.data.model.OperationResult
Expand Down Expand Up @@ -72,7 +73,7 @@ class ConversationsRepository @Inject constructor(
deviceLoadConvsFlow(it.emitInitial)
}.stateIn(scope, SharingStarted.WhileSubscribed(5000), null)

suspend fun deleteDeviceConvs(contactIds: Set<String>): OperationResult<Unit> {
suspend fun deleteDeviceConvs(contactIds: Set<String>): OperationResult<Unit, MessageDeletionError> {
deletionsFlow.update { contactIds }

val msgIds = contactIds.flatMap {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ data class SearchResults(
)

sealed interface MatchedContact {
object NotMatched : MatchedContact
data object NotMatched : MatchedContact
data class MatchingInName(val contact: Contact, val matchingLastMsg: Message?) : MatchedContact
data class MatchingInMessage(val contact: Contact, val last: Message) : MatchedContact
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.rokoblak.chatbackup.domain.usecases

import com.rokoblak.chatbackup.data.model.Contact
import com.rokoblak.chatbackup.data.model.OperationResult
import com.rokoblak.chatbackup.data.repo.ContactsLoadError
import com.rokoblak.chatbackup.data.repo.ContactsRepository
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
Expand All @@ -27,7 +28,7 @@ class ContactsFilteringUseCase @Inject constructor(
if (q.isBlank()) 0L else 100L
}

fun filteredContacts(): Flow<OperationResult<List<Contact>>?> {
fun filteredContacts(): Flow<OperationResult<List<Contact>, ContactsLoadError>?> {
return contactsRepo.contactsFlow.flatMapLatest { res ->
when (res) {
is OperationResult.Done -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.rokoblak.chatbackup.domain.usecases

import com.rokoblak.chatbackup.data.datasources.MessageDeletionError
import com.rokoblak.chatbackup.data.model.Conversations
import com.rokoblak.chatbackup.data.model.OperationResult
import com.rokoblak.chatbackup.data.util.ConversationSearcher
Expand Down Expand Up @@ -99,7 +100,7 @@ class ConversationsSearchUseCase @Inject constructor(
_editState.update { it.copy(editing = false) }
}

suspend fun deleteSelected(): OperationResult<Unit> {
suspend fun deleteSelected(): OperationResult<Unit, MessageDeletionError> {
val selectedIds = _selections.value.filter { it.value }.keys
return conversationsRepo.deleteDeviceConvs(selectedIds)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.rokoblak.chatbackup.domain.usecases

import com.rokoblak.chatbackup.data.datasources.MessageInsertionError
import com.rokoblak.chatbackup.data.model.OperationResult
import com.rokoblak.chatbackup.data.repo.ConversationsRepository
import com.rokoblak.chatbackup.data.datasources.MessagesDataSource
Expand Down Expand Up @@ -91,7 +92,10 @@ class DownloadConversationUseCase @Inject constructor(
}

is OperationResult.Error -> {
onProgressMsg(chunkRes.msg)
val msg = when (val e = chunkRes.error) {
is MessageInsertionError.InsertionError -> "Inserted ${e.inserted} out of ${e.totalToInsert} total"
}
onProgressMsg(msg)
}
}
}.collect()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.rokoblak.chatbackup.domain.usecases

import com.rokoblak.chatbackup.conversation.ConversationRoute
import com.rokoblak.chatbackup.createchat.CreateChatRoute
import com.rokoblak.chatbackup.feature.conversation.ConversationRoute
import com.rokoblak.chatbackup.feature.createchat.CreateChatRoute
import com.rokoblak.chatbackup.ui.navigation.RouteNavigator
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import android.telephony.SmsManager
import androidx.core.content.ContextCompat
import com.rokoblak.chatbackup.data.datasources.MessagesDataSource
import com.rokoblak.chatbackup.data.model.OperationResult
import com.rokoblak.chatbackup.data.model.RootError
import com.rokoblak.chatbackup.di.AppScope
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
Expand All @@ -23,7 +24,7 @@ class SMSSendUseCase @Inject constructor(
private val eventsUseCase: AppEventsUseCase,
) {

suspend fun send(address: String, body: String): OperationResult<Unit> {
suspend fun send(address: String, body: String): OperationResult<Unit, SMSSendError> {
val context = appScope.appContext
val smsManager = ContextCompat.getSystemService(context, SmsManager::class.java)
?: throw IllegalAccessError("No SMS manager")
Expand All @@ -33,7 +34,7 @@ class SMSSendUseCase @Inject constructor(
PendingIntent.getBroadcast(context, 0, Intent(action), PendingIntent.FLAG_IMMUTABLE)

val res = withTimeoutOrNull(5000L) {
suspendCancellableCoroutine<OperationResult<Unit>> { cont ->
suspendCancellableCoroutine<OperationResult<Unit, SMSSendError>> { cont ->
val sentIntentReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
val resultCode = resultCode
Expand All @@ -46,15 +47,15 @@ class SMSSendUseCase @Inject constructor(
smsManager.sendTextMessage(address, null, body, sendPendingIntent, null)
appScope.appContext.registerReceiver(sentIntentReceiver, IntentFilter(action))
}
} ?: OperationResult.Error("Error sending: timeout")
} ?: OperationResult.Error(SMSSendError.Timeout)

// TODO: Handle error cases
when (res) {
is OperationResult.Done -> {
Timber.i("Message sent: $body to $address")
}
is OperationResult.Error -> {
Timber.e("Send failure: ${res.msg}")
Timber.e("Send failure: ${res.error}")
}
}
MessagesDataSource.saveSingle(context, incoming = false, body = body, address = address)
Expand All @@ -64,16 +65,25 @@ class SMSSendUseCase @Inject constructor(
return res
}

private fun Int.mapCodeToResult() = when (this) {
private fun Int.mapCodeToResult(): OperationResult<Unit, SMSSendError> = when (this) {
Activity.RESULT_OK -> OperationResult.Done(Unit)
SmsManager.RESULT_ERROR_GENERIC_FAILURE -> OperationResult.Error("Generic failure")
SmsManager.RESULT_ERROR_NO_SERVICE -> OperationResult.Error("No service")
SmsManager.RESULT_ERROR_NULL_PDU -> OperationResult.Error("Null PDU")
SmsManager.RESULT_ERROR_RADIO_OFF -> OperationResult.Error("Radio off")
else -> OperationResult.Error("Send failure")
SmsManager.RESULT_ERROR_GENERIC_FAILURE -> OperationResult.Error(SMSSendError.GenericFailure)
SmsManager.RESULT_ERROR_NO_SERVICE -> OperationResult.Error(SMSSendError.NoService)
SmsManager.RESULT_ERROR_NULL_PDU -> OperationResult.Error(SMSSendError.NullPDU)
SmsManager.RESULT_ERROR_RADIO_OFF -> OperationResult.Error(SMSSendError.RadioOff)
else -> OperationResult.Error(SMSSendError.OtherError)
}

companion object {
private const val ACTION_SENT = "sms-action-sent"
}
}

sealed interface SMSSendError: RootError {
data object GenericFailure: SMSSendError
data object NoService: SMSSendError
data object NullPDU: SMSSendError
data object RadioOff: SMSSendError
data object OtherError: SMSSendError
data object Timeout: SMSSendError
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.rokoblak.chatbackup.conversation
package com.rokoblak.chatbackup.feature.conversation

sealed interface ConversationAction {
data class InputChanged(val input: String) : ConversationAction
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.rokoblak.chatbackup.conversation
package com.rokoblak.chatbackup.feature.conversation

import androidx.compose.runtime.Composable
import androidx.hilt.navigation.compose.hiltViewModel
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.rokoblak.chatbackup.conversation
package com.rokoblak.chatbackup.feature.conversation

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
Expand Down
Loading

0 comments on commit 60828f3

Please sign in to comment.