Loading data/src/main/java/com/moez/QKSMS/migration/QkRealmMigration.kt +22 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ */ package com.moez.QKSMS.migration import android.annotation.SuppressLint import com.f2prateek.rx.preferences2.RxSharedPreferences import com.moez.QKSMS.extensions.map import com.moez.QKSMS.mapper.CursorToContactImpl import io.realm.DynamicRealm Loading @@ -29,13 +31,15 @@ import io.realm.Sort import javax.inject.Inject class QkRealmMigration @Inject constructor( private val cursorToContact: CursorToContactImpl private val cursorToContact: CursorToContactImpl, private val prefs: RxSharedPreferences ) : RealmMigration { companion object { const val SchemaVersion: Long = 9 } @SuppressLint("ApplySharedPref") override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { var version = oldVersion Loading Loading @@ -169,6 +173,23 @@ class QkRealmMigration @Inject constructor( realmContact.setString("photoUri", photoUri) } // Migrate conversation themes val recipients = mutableMapOf<Long, Int>() // Map of recipientId:theme realm.where("Conversation").findAll().forEach { conversation -> val pref = prefs.getInteger("theme_${conversation.getLong("id")}") if (pref.isSet) { conversation.getList("Recipient").forEach { recipient -> recipients[recipient.getLong("id")] = pref.get() } pref.delete() } } recipients.forEach { (recipientId, theme) -> prefs.getInteger("theme_$recipientId").set(theme) } version++ } Loading data/src/main/java/com/moez/QKSMS/repository/MessageRepositoryImpl.kt +58 −37 Original line number Diff line number Diff line Loading @@ -28,6 +28,8 @@ import android.media.MediaScannerConnection import android.os.Build import android.os.Environment import android.provider.Telephony import android.provider.Telephony.Mms import android.provider.Telephony.Sms import android.telephony.SmsManager import android.webkit.MimeTypeMap import androidx.core.content.contentValuesOf Loading Loading @@ -110,6 +112,25 @@ class MessageRepositoryImpl @Inject constructor( .findFirst() } override fun getLastIncomingMessage(threadId: Long): RealmResults<Message> { return Realm.getDefaultInstance() .where(Message::class.java) .equalTo("threadId", threadId) .beginGroup() .beginGroup() .equalTo("type", "sms") .`in`("boxId", arrayOf(Sms.MESSAGE_TYPE_INBOX, Sms.MESSAGE_TYPE_ALL)) .endGroup() .or() .beginGroup() .equalTo("type", "mms") .`in`("boxId", arrayOf(Mms.MESSAGE_BOX_INBOX, Mms.MESSAGE_BOX_ALL)) .endGroup() .endGroup() .sort("date", Sort.DESCENDING) .findAll() } override fun getUnreadCount(): Long { return Realm.getDefaultInstance().use { realm -> realm.refresh() Loading Loading @@ -239,13 +260,13 @@ class MessageRepositoryImpl @Inject constructor( } val values = ContentValues() values.put(Telephony.Sms.SEEN, true) values.put(Telephony.Sms.READ, true) values.put(Sms.SEEN, true) values.put(Sms.READ, true) threadIds.forEach { threadId -> try { val uri = ContentUris.withAppendedId(Telephony.MmsSms.CONTENT_CONVERSATIONS_URI, threadId) context.contentResolver.update(uri, values, "${Telephony.Sms.READ} = 0", null) context.contentResolver.update(uri, values, "${Sms.READ} = 0", null) } catch (exception: Exception) { Timber.w(exception) } Loading Loading @@ -421,7 +442,7 @@ class MessageRepositoryImpl @Inject constructor( this.subId = subId id = messageIds.newId() boxId = Telephony.Sms.MESSAGE_TYPE_OUTBOX boxId = Sms.MESSAGE_TYPE_OUTBOX type = "sms" read = true seen = true Loading @@ -432,20 +453,20 @@ class MessageRepositoryImpl @Inject constructor( // Insert the message to the native content provider val values = contentValuesOf( Telephony.Sms.ADDRESS to address, Telephony.Sms.BODY to body, Telephony.Sms.DATE to System.currentTimeMillis(), Telephony.Sms.READ to true, Telephony.Sms.SEEN to true, Telephony.Sms.TYPE to Telephony.Sms.MESSAGE_TYPE_OUTBOX, Telephony.Sms.THREAD_ID to threadId Sms.ADDRESS to address, Sms.BODY to body, Sms.DATE to System.currentTimeMillis(), Sms.READ to true, Sms.SEEN to true, Sms.TYPE to Sms.MESSAGE_TYPE_OUTBOX, Sms.THREAD_ID to threadId ) if (prefs.canUseSubId.get()) { values.put(Telephony.Sms.SUBSCRIPTION_ID, message.subId) values.put(Sms.SUBSCRIPTION_ID, message.subId) } val uri = context.contentResolver.insert(Telephony.Sms.CONTENT_URI, values) val uri = context.contentResolver.insert(Sms.CONTENT_URI, values) // Update the contentId after the message has been inserted to the content provider // The message might have been deleted by now, so only proceed if it's valid Loading Loading @@ -479,7 +500,7 @@ class MessageRepositoryImpl @Inject constructor( id = messageIds.newId() threadId = TelephonyCompat.getOrCreateThreadId(context, address) boxId = Telephony.Sms.MESSAGE_TYPE_INBOX boxId = Sms.MESSAGE_TYPE_INBOX type = "sms" read = activeConversationManager.getActiveConversation() == threadId } Loading @@ -489,16 +510,16 @@ class MessageRepositoryImpl @Inject constructor( // Insert the message to the native content provider val values = contentValuesOf( Telephony.Sms.ADDRESS to address, Telephony.Sms.BODY to body, Telephony.Sms.DATE_SENT to sentTime Sms.ADDRESS to address, Sms.BODY to body, Sms.DATE_SENT to sentTime ) if (prefs.canUseSubId.get()) { values.put(Telephony.Sms.SUBSCRIPTION_ID, message.subId) values.put(Sms.SUBSCRIPTION_ID, message.subId) } context.contentResolver.insert(Telephony.Sms.Inbox.CONTENT_URI, values)?.lastPathSegment?.toLong()?.let { id -> context.contentResolver.insert(Sms.Inbox.CONTENT_URI, values)?.lastPathSegment?.toLong()?.let { id -> // Update the contentId after the message has been inserted to the content provider realm.executeTransaction { managedMessage?.contentId = id } } Loading @@ -520,15 +541,15 @@ class MessageRepositoryImpl @Inject constructor( // Update the message in realm realm.executeTransaction { message.boxId = when (message.isSms()) { true -> Telephony.Sms.MESSAGE_TYPE_OUTBOX false -> Telephony.Mms.MESSAGE_BOX_OUTBOX true -> Sms.MESSAGE_TYPE_OUTBOX false -> Mms.MESSAGE_BOX_OUTBOX } } // Update the message in the native ContentProvider val values = when (message.isSms()) { true -> contentValuesOf(Telephony.Sms.TYPE to Telephony.Sms.MESSAGE_TYPE_OUTBOX) false -> contentValuesOf(Telephony.Mms.MESSAGE_BOX to Telephony.Mms.MESSAGE_BOX_OUTBOX) true -> contentValuesOf(Sms.TYPE to Sms.MESSAGE_TYPE_OUTBOX) false -> contentValuesOf(Mms.MESSAGE_BOX to Mms.MESSAGE_BOX_OUTBOX) } context.contentResolver.update(message.getUri(), values, null, null) } Loading @@ -543,12 +564,12 @@ class MessageRepositoryImpl @Inject constructor( message?.let { // Update the message in realm realm.executeTransaction { message.boxId = Telephony.Sms.MESSAGE_TYPE_SENT message.boxId = Sms.MESSAGE_TYPE_SENT } // Update the message in the native ContentProvider val values = ContentValues() values.put(Telephony.Sms.TYPE, Telephony.Sms.MESSAGE_TYPE_SENT) values.put(Sms.TYPE, Sms.MESSAGE_TYPE_SENT) context.contentResolver.update(message.getUri(), values, null, null) } } Loading @@ -562,14 +583,14 @@ class MessageRepositoryImpl @Inject constructor( message?.let { // Update the message in realm realm.executeTransaction { message.boxId = Telephony.Sms.MESSAGE_TYPE_FAILED message.boxId = Sms.MESSAGE_TYPE_FAILED message.errorCode = resultCode } // Update the message in the native ContentProvider val values = ContentValues() values.put(Telephony.Sms.TYPE, Telephony.Sms.MESSAGE_TYPE_FAILED) values.put(Telephony.Sms.ERROR_CODE, resultCode) values.put(Sms.TYPE, Sms.MESSAGE_TYPE_FAILED) values.put(Sms.ERROR_CODE, resultCode) context.contentResolver.update(message.getUri(), values, null, null) } } Loading @@ -583,16 +604,16 @@ class MessageRepositoryImpl @Inject constructor( message?.let { // Update the message in realm realm.executeTransaction { message.deliveryStatus = Telephony.Sms.STATUS_COMPLETE message.deliveryStatus = Sms.STATUS_COMPLETE message.dateSent = System.currentTimeMillis() message.read = true } // Update the message in the native ContentProvider val values = ContentValues() values.put(Telephony.Sms.STATUS, Telephony.Sms.STATUS_COMPLETE) values.put(Telephony.Sms.DATE_SENT, System.currentTimeMillis()) values.put(Telephony.Sms.READ, true) values.put(Sms.STATUS, Sms.STATUS_COMPLETE) values.put(Sms.DATE_SENT, System.currentTimeMillis()) values.put(Sms.READ, true) context.contentResolver.update(message.getUri(), values, null, null) } } Loading @@ -606,7 +627,7 @@ class MessageRepositoryImpl @Inject constructor( message?.let { // Update the message in realm realm.executeTransaction { message.deliveryStatus = Telephony.Sms.STATUS_FAILED message.deliveryStatus = Sms.STATUS_FAILED message.dateSent = System.currentTimeMillis() message.read = true message.errorCode = resultCode Loading @@ -614,10 +635,10 @@ class MessageRepositoryImpl @Inject constructor( // Update the message in the native ContentProvider val values = ContentValues() values.put(Telephony.Sms.STATUS, Telephony.Sms.STATUS_FAILED) values.put(Telephony.Sms.DATE_SENT, System.currentTimeMillis()) values.put(Telephony.Sms.READ, true) values.put(Telephony.Sms.ERROR_CODE, resultCode) values.put(Sms.STATUS, Sms.STATUS_FAILED) values.put(Sms.DATE_SENT, System.currentTimeMillis()) values.put(Sms.READ, true) values.put(Sms.ERROR_CODE, resultCode) context.contentResolver.update(message.getUri(), values, null, null) } } Loading domain/src/main/java/com/moez/QKSMS/repository/MessageRepository.kt +2 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ interface MessageRepository { fun getMessageForPart(id: Long): Message? fun getLastIncomingMessage(threadId: Long): RealmResults<Message> fun getUnreadCount(): Long fun getPart(id: Long): MmsPart? Loading domain/src/main/java/com/moez/QKSMS/util/Preferences.kt +27 −5 Original line number Diff line number Diff line Loading @@ -19,16 +19,22 @@ package com.moez.QKSMS.util import android.content.Context import android.content.SharedPreferences import android.os.Build import android.provider.Settings import com.f2prateek.rx.preferences2.Preference import com.f2prateek.rx.preferences2.RxSharedPreferences import com.moez.QKSMS.common.util.extensions.versionCode import io.reactivex.Observable import javax.inject.Inject import javax.inject.Singleton @Singleton class Preferences @Inject constructor(context: Context, private val rxPrefs: RxSharedPreferences) { class Preferences @Inject constructor( context: Context, private val rxPrefs: RxSharedPreferences, private val sharedPrefs: SharedPreferences ) { companion object { const val NIGHT_MODE_SYSTEM = 0 Loading Loading @@ -118,12 +124,28 @@ class Preferences @Inject constructor(context: Context, private val rxPrefs: RxS } } fun theme(threadId: Long = 0): Preference<Int> { /** * Returns a stream of preference keys for changing preferences */ val keyChanges: Observable<String> = Observable.create<String> { emitter -> // Making this a lambda would cause it to be GCd val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key -> emitter.onNext(key) } emitter.setCancellable { sharedPrefs.unregisterOnSharedPreferenceChangeListener(listener) } sharedPrefs.registerOnSharedPreferenceChangeListener(listener) }.share() fun theme(recipientId: Long = 0): Preference<Int> { val default = rxPrefs.getInteger("theme", 0xFF0097A7.toInt()) return when (threadId) { return when (recipientId) { 0L -> default else -> rxPrefs.getInteger("theme_$threadId", default.get()) else -> rxPrefs.getInteger("theme_$recipientId", default.get()) } } Loading presentation/src/main/java/com/moez/QKSMS/common/base/QkThemedActivity.kt +40 −2 Original line number Diff line number Diff line Loading @@ -30,6 +30,11 @@ import com.moez.QKSMS.R import com.moez.QKSMS.common.util.Colors import com.moez.QKSMS.common.util.extensions.resolveThemeBoolean import com.moez.QKSMS.common.util.extensions.resolveThemeColor import com.moez.QKSMS.extensions.asObservable import com.moez.QKSMS.extensions.mapNotNull import com.moez.QKSMS.repository.ConversationRepository import com.moez.QKSMS.repository.MessageRepository import com.moez.QKSMS.util.PhoneNumberUtils import com.moez.QKSMS.util.Preferences import com.uber.autodispose.android.lifecycle.scope import com.uber.autodispose.autoDisposable Loading @@ -51,6 +56,9 @@ import javax.inject.Inject abstract class QkThemedActivity : QkActivity() { @Inject lateinit var colors: Colors @Inject lateinit var conversationRepo: ConversationRepository @Inject lateinit var messageRepo: MessageRepository @Inject lateinit var phoneNumberUtils: PhoneNumberUtils @Inject lateinit var prefs: Preferences /** Loading @@ -61,10 +69,40 @@ abstract class QkThemedActivity : QkActivity() { /** * Switch the theme if the threadId changes * Set it based on the latest message in the conversation */ val theme = threadId val theme: Observable<Colors.Theme> = threadId .distinctUntilChanged() .switchMap { threadId -> colors.themeObservable(threadId) } .switchMap { threadId -> val conversation = conversationRepo.getConversation(threadId) when { conversation == null -> Observable.just(0L) conversation.recipients.size == 1 -> Observable.just(conversation.recipients.first()?.id ?: 0L) else -> messageRepo.getLastIncomingMessage(conversation.id) .asObservable() .mapNotNull { messages -> messages.firstOrNull() } .distinctUntilChanged { message -> message.address } .mapNotNull { message -> conversation.recipients.find { recipient -> phoneNumberUtils.compare(recipient.address, message.address) } } .map { recipient -> recipient.id } .startWith(conversation.recipients.firstOrNull()?.id ?: 0) .distinctUntilChanged() } } .switchMap { colors.themeObservable(it) } /** * Emits an event whenever any theme changes, whether it be global or for some recipient. This is useful * for invalidating recyclerviews */ val allThemes by lazy { prefs.keyChanges.filter { key -> key.contains("theme") } } @SuppressLint("InlinedApi") override fun onCreate(savedInstanceState: Bundle?) { Loading Loading
data/src/main/java/com/moez/QKSMS/migration/QkRealmMigration.kt +22 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ */ package com.moez.QKSMS.migration import android.annotation.SuppressLint import com.f2prateek.rx.preferences2.RxSharedPreferences import com.moez.QKSMS.extensions.map import com.moez.QKSMS.mapper.CursorToContactImpl import io.realm.DynamicRealm Loading @@ -29,13 +31,15 @@ import io.realm.Sort import javax.inject.Inject class QkRealmMigration @Inject constructor( private val cursorToContact: CursorToContactImpl private val cursorToContact: CursorToContactImpl, private val prefs: RxSharedPreferences ) : RealmMigration { companion object { const val SchemaVersion: Long = 9 } @SuppressLint("ApplySharedPref") override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { var version = oldVersion Loading Loading @@ -169,6 +173,23 @@ class QkRealmMigration @Inject constructor( realmContact.setString("photoUri", photoUri) } // Migrate conversation themes val recipients = mutableMapOf<Long, Int>() // Map of recipientId:theme realm.where("Conversation").findAll().forEach { conversation -> val pref = prefs.getInteger("theme_${conversation.getLong("id")}") if (pref.isSet) { conversation.getList("Recipient").forEach { recipient -> recipients[recipient.getLong("id")] = pref.get() } pref.delete() } } recipients.forEach { (recipientId, theme) -> prefs.getInteger("theme_$recipientId").set(theme) } version++ } Loading
data/src/main/java/com/moez/QKSMS/repository/MessageRepositoryImpl.kt +58 −37 Original line number Diff line number Diff line Loading @@ -28,6 +28,8 @@ import android.media.MediaScannerConnection import android.os.Build import android.os.Environment import android.provider.Telephony import android.provider.Telephony.Mms import android.provider.Telephony.Sms import android.telephony.SmsManager import android.webkit.MimeTypeMap import androidx.core.content.contentValuesOf Loading Loading @@ -110,6 +112,25 @@ class MessageRepositoryImpl @Inject constructor( .findFirst() } override fun getLastIncomingMessage(threadId: Long): RealmResults<Message> { return Realm.getDefaultInstance() .where(Message::class.java) .equalTo("threadId", threadId) .beginGroup() .beginGroup() .equalTo("type", "sms") .`in`("boxId", arrayOf(Sms.MESSAGE_TYPE_INBOX, Sms.MESSAGE_TYPE_ALL)) .endGroup() .or() .beginGroup() .equalTo("type", "mms") .`in`("boxId", arrayOf(Mms.MESSAGE_BOX_INBOX, Mms.MESSAGE_BOX_ALL)) .endGroup() .endGroup() .sort("date", Sort.DESCENDING) .findAll() } override fun getUnreadCount(): Long { return Realm.getDefaultInstance().use { realm -> realm.refresh() Loading Loading @@ -239,13 +260,13 @@ class MessageRepositoryImpl @Inject constructor( } val values = ContentValues() values.put(Telephony.Sms.SEEN, true) values.put(Telephony.Sms.READ, true) values.put(Sms.SEEN, true) values.put(Sms.READ, true) threadIds.forEach { threadId -> try { val uri = ContentUris.withAppendedId(Telephony.MmsSms.CONTENT_CONVERSATIONS_URI, threadId) context.contentResolver.update(uri, values, "${Telephony.Sms.READ} = 0", null) context.contentResolver.update(uri, values, "${Sms.READ} = 0", null) } catch (exception: Exception) { Timber.w(exception) } Loading Loading @@ -421,7 +442,7 @@ class MessageRepositoryImpl @Inject constructor( this.subId = subId id = messageIds.newId() boxId = Telephony.Sms.MESSAGE_TYPE_OUTBOX boxId = Sms.MESSAGE_TYPE_OUTBOX type = "sms" read = true seen = true Loading @@ -432,20 +453,20 @@ class MessageRepositoryImpl @Inject constructor( // Insert the message to the native content provider val values = contentValuesOf( Telephony.Sms.ADDRESS to address, Telephony.Sms.BODY to body, Telephony.Sms.DATE to System.currentTimeMillis(), Telephony.Sms.READ to true, Telephony.Sms.SEEN to true, Telephony.Sms.TYPE to Telephony.Sms.MESSAGE_TYPE_OUTBOX, Telephony.Sms.THREAD_ID to threadId Sms.ADDRESS to address, Sms.BODY to body, Sms.DATE to System.currentTimeMillis(), Sms.READ to true, Sms.SEEN to true, Sms.TYPE to Sms.MESSAGE_TYPE_OUTBOX, Sms.THREAD_ID to threadId ) if (prefs.canUseSubId.get()) { values.put(Telephony.Sms.SUBSCRIPTION_ID, message.subId) values.put(Sms.SUBSCRIPTION_ID, message.subId) } val uri = context.contentResolver.insert(Telephony.Sms.CONTENT_URI, values) val uri = context.contentResolver.insert(Sms.CONTENT_URI, values) // Update the contentId after the message has been inserted to the content provider // The message might have been deleted by now, so only proceed if it's valid Loading Loading @@ -479,7 +500,7 @@ class MessageRepositoryImpl @Inject constructor( id = messageIds.newId() threadId = TelephonyCompat.getOrCreateThreadId(context, address) boxId = Telephony.Sms.MESSAGE_TYPE_INBOX boxId = Sms.MESSAGE_TYPE_INBOX type = "sms" read = activeConversationManager.getActiveConversation() == threadId } Loading @@ -489,16 +510,16 @@ class MessageRepositoryImpl @Inject constructor( // Insert the message to the native content provider val values = contentValuesOf( Telephony.Sms.ADDRESS to address, Telephony.Sms.BODY to body, Telephony.Sms.DATE_SENT to sentTime Sms.ADDRESS to address, Sms.BODY to body, Sms.DATE_SENT to sentTime ) if (prefs.canUseSubId.get()) { values.put(Telephony.Sms.SUBSCRIPTION_ID, message.subId) values.put(Sms.SUBSCRIPTION_ID, message.subId) } context.contentResolver.insert(Telephony.Sms.Inbox.CONTENT_URI, values)?.lastPathSegment?.toLong()?.let { id -> context.contentResolver.insert(Sms.Inbox.CONTENT_URI, values)?.lastPathSegment?.toLong()?.let { id -> // Update the contentId after the message has been inserted to the content provider realm.executeTransaction { managedMessage?.contentId = id } } Loading @@ -520,15 +541,15 @@ class MessageRepositoryImpl @Inject constructor( // Update the message in realm realm.executeTransaction { message.boxId = when (message.isSms()) { true -> Telephony.Sms.MESSAGE_TYPE_OUTBOX false -> Telephony.Mms.MESSAGE_BOX_OUTBOX true -> Sms.MESSAGE_TYPE_OUTBOX false -> Mms.MESSAGE_BOX_OUTBOX } } // Update the message in the native ContentProvider val values = when (message.isSms()) { true -> contentValuesOf(Telephony.Sms.TYPE to Telephony.Sms.MESSAGE_TYPE_OUTBOX) false -> contentValuesOf(Telephony.Mms.MESSAGE_BOX to Telephony.Mms.MESSAGE_BOX_OUTBOX) true -> contentValuesOf(Sms.TYPE to Sms.MESSAGE_TYPE_OUTBOX) false -> contentValuesOf(Mms.MESSAGE_BOX to Mms.MESSAGE_BOX_OUTBOX) } context.contentResolver.update(message.getUri(), values, null, null) } Loading @@ -543,12 +564,12 @@ class MessageRepositoryImpl @Inject constructor( message?.let { // Update the message in realm realm.executeTransaction { message.boxId = Telephony.Sms.MESSAGE_TYPE_SENT message.boxId = Sms.MESSAGE_TYPE_SENT } // Update the message in the native ContentProvider val values = ContentValues() values.put(Telephony.Sms.TYPE, Telephony.Sms.MESSAGE_TYPE_SENT) values.put(Sms.TYPE, Sms.MESSAGE_TYPE_SENT) context.contentResolver.update(message.getUri(), values, null, null) } } Loading @@ -562,14 +583,14 @@ class MessageRepositoryImpl @Inject constructor( message?.let { // Update the message in realm realm.executeTransaction { message.boxId = Telephony.Sms.MESSAGE_TYPE_FAILED message.boxId = Sms.MESSAGE_TYPE_FAILED message.errorCode = resultCode } // Update the message in the native ContentProvider val values = ContentValues() values.put(Telephony.Sms.TYPE, Telephony.Sms.MESSAGE_TYPE_FAILED) values.put(Telephony.Sms.ERROR_CODE, resultCode) values.put(Sms.TYPE, Sms.MESSAGE_TYPE_FAILED) values.put(Sms.ERROR_CODE, resultCode) context.contentResolver.update(message.getUri(), values, null, null) } } Loading @@ -583,16 +604,16 @@ class MessageRepositoryImpl @Inject constructor( message?.let { // Update the message in realm realm.executeTransaction { message.deliveryStatus = Telephony.Sms.STATUS_COMPLETE message.deliveryStatus = Sms.STATUS_COMPLETE message.dateSent = System.currentTimeMillis() message.read = true } // Update the message in the native ContentProvider val values = ContentValues() values.put(Telephony.Sms.STATUS, Telephony.Sms.STATUS_COMPLETE) values.put(Telephony.Sms.DATE_SENT, System.currentTimeMillis()) values.put(Telephony.Sms.READ, true) values.put(Sms.STATUS, Sms.STATUS_COMPLETE) values.put(Sms.DATE_SENT, System.currentTimeMillis()) values.put(Sms.READ, true) context.contentResolver.update(message.getUri(), values, null, null) } } Loading @@ -606,7 +627,7 @@ class MessageRepositoryImpl @Inject constructor( message?.let { // Update the message in realm realm.executeTransaction { message.deliveryStatus = Telephony.Sms.STATUS_FAILED message.deliveryStatus = Sms.STATUS_FAILED message.dateSent = System.currentTimeMillis() message.read = true message.errorCode = resultCode Loading @@ -614,10 +635,10 @@ class MessageRepositoryImpl @Inject constructor( // Update the message in the native ContentProvider val values = ContentValues() values.put(Telephony.Sms.STATUS, Telephony.Sms.STATUS_FAILED) values.put(Telephony.Sms.DATE_SENT, System.currentTimeMillis()) values.put(Telephony.Sms.READ, true) values.put(Telephony.Sms.ERROR_CODE, resultCode) values.put(Sms.STATUS, Sms.STATUS_FAILED) values.put(Sms.DATE_SENT, System.currentTimeMillis()) values.put(Sms.READ, true) values.put(Sms.ERROR_CODE, resultCode) context.contentResolver.update(message.getUri(), values, null, null) } } Loading
domain/src/main/java/com/moez/QKSMS/repository/MessageRepository.kt +2 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ interface MessageRepository { fun getMessageForPart(id: Long): Message? fun getLastIncomingMessage(threadId: Long): RealmResults<Message> fun getUnreadCount(): Long fun getPart(id: Long): MmsPart? Loading
domain/src/main/java/com/moez/QKSMS/util/Preferences.kt +27 −5 Original line number Diff line number Diff line Loading @@ -19,16 +19,22 @@ package com.moez.QKSMS.util import android.content.Context import android.content.SharedPreferences import android.os.Build import android.provider.Settings import com.f2prateek.rx.preferences2.Preference import com.f2prateek.rx.preferences2.RxSharedPreferences import com.moez.QKSMS.common.util.extensions.versionCode import io.reactivex.Observable import javax.inject.Inject import javax.inject.Singleton @Singleton class Preferences @Inject constructor(context: Context, private val rxPrefs: RxSharedPreferences) { class Preferences @Inject constructor( context: Context, private val rxPrefs: RxSharedPreferences, private val sharedPrefs: SharedPreferences ) { companion object { const val NIGHT_MODE_SYSTEM = 0 Loading Loading @@ -118,12 +124,28 @@ class Preferences @Inject constructor(context: Context, private val rxPrefs: RxS } } fun theme(threadId: Long = 0): Preference<Int> { /** * Returns a stream of preference keys for changing preferences */ val keyChanges: Observable<String> = Observable.create<String> { emitter -> // Making this a lambda would cause it to be GCd val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key -> emitter.onNext(key) } emitter.setCancellable { sharedPrefs.unregisterOnSharedPreferenceChangeListener(listener) } sharedPrefs.registerOnSharedPreferenceChangeListener(listener) }.share() fun theme(recipientId: Long = 0): Preference<Int> { val default = rxPrefs.getInteger("theme", 0xFF0097A7.toInt()) return when (threadId) { return when (recipientId) { 0L -> default else -> rxPrefs.getInteger("theme_$threadId", default.get()) else -> rxPrefs.getInteger("theme_$recipientId", default.get()) } } Loading
presentation/src/main/java/com/moez/QKSMS/common/base/QkThemedActivity.kt +40 −2 Original line number Diff line number Diff line Loading @@ -30,6 +30,11 @@ import com.moez.QKSMS.R import com.moez.QKSMS.common.util.Colors import com.moez.QKSMS.common.util.extensions.resolveThemeBoolean import com.moez.QKSMS.common.util.extensions.resolveThemeColor import com.moez.QKSMS.extensions.asObservable import com.moez.QKSMS.extensions.mapNotNull import com.moez.QKSMS.repository.ConversationRepository import com.moez.QKSMS.repository.MessageRepository import com.moez.QKSMS.util.PhoneNumberUtils import com.moez.QKSMS.util.Preferences import com.uber.autodispose.android.lifecycle.scope import com.uber.autodispose.autoDisposable Loading @@ -51,6 +56,9 @@ import javax.inject.Inject abstract class QkThemedActivity : QkActivity() { @Inject lateinit var colors: Colors @Inject lateinit var conversationRepo: ConversationRepository @Inject lateinit var messageRepo: MessageRepository @Inject lateinit var phoneNumberUtils: PhoneNumberUtils @Inject lateinit var prefs: Preferences /** Loading @@ -61,10 +69,40 @@ abstract class QkThemedActivity : QkActivity() { /** * Switch the theme if the threadId changes * Set it based on the latest message in the conversation */ val theme = threadId val theme: Observable<Colors.Theme> = threadId .distinctUntilChanged() .switchMap { threadId -> colors.themeObservable(threadId) } .switchMap { threadId -> val conversation = conversationRepo.getConversation(threadId) when { conversation == null -> Observable.just(0L) conversation.recipients.size == 1 -> Observable.just(conversation.recipients.first()?.id ?: 0L) else -> messageRepo.getLastIncomingMessage(conversation.id) .asObservable() .mapNotNull { messages -> messages.firstOrNull() } .distinctUntilChanged { message -> message.address } .mapNotNull { message -> conversation.recipients.find { recipient -> phoneNumberUtils.compare(recipient.address, message.address) } } .map { recipient -> recipient.id } .startWith(conversation.recipients.firstOrNull()?.id ?: 0) .distinctUntilChanged() } } .switchMap { colors.themeObservable(it) } /** * Emits an event whenever any theme changes, whether it be global or for some recipient. This is useful * for invalidating recyclerviews */ val allThemes by lazy { prefs.keyChanges.filter { key -> key.contains("theme") } } @SuppressLint("InlinedApi") override fun onCreate(savedInstanceState: Bundle?) { Loading