diff --git a/data/build.gradle b/data/build.gradle index f5018630..e58f1548 100644 --- a/data/build.gradle +++ b/data/build.gradle @@ -19,6 +19,7 @@ dependencies { // dagger implementation "com.google.dagger:dagger:$dagger_version" + implementation "com.google.dagger:dagger-android-support:$dagger_version" kapt "com.google.dagger:dagger-compiler:$dagger_version" compileOnly "javax.annotation:jsr250-api:1.0" diff --git a/presentation/src/main/java/receiver/DefaultSmsChangedReceiver.kt b/data/src/main/java/receiver/DefaultSmsChangedReceiver.kt similarity index 94% rename from presentation/src/main/java/receiver/DefaultSmsChangedReceiver.kt rename to data/src/main/java/receiver/DefaultSmsChangedReceiver.kt index 36d15591..edbf9276 100644 --- a/presentation/src/main/java/receiver/DefaultSmsChangedReceiver.kt +++ b/data/src/main/java/receiver/DefaultSmsChangedReceiver.kt @@ -24,7 +24,7 @@ import android.content.Intent import android.os.Build import android.provider.Telephony import android.support.annotation.RequiresApi -import injection.appComponent +import dagger.android.AndroidInjection import interactor.SyncMessages import util.Preferences import javax.inject.Inject @@ -36,7 +36,7 @@ class DefaultSmsChangedReceiver : BroadcastReceiver() { @RequiresApi(Build.VERSION_CODES.N) override fun onReceive(context: Context, intent: Intent) { - appComponent.inject(this) + AndroidInjection.inject(this, context) if (intent.getBooleanExtra(Telephony.Sms.Intents.EXTRA_IS_DEFAULT_SMS_APP, false)) { val pendingResult = goAsync() diff --git a/presentation/src/main/java/receiver/MarkReadReceiver.kt b/data/src/main/java/receiver/MarkReadReceiver.kt similarity index 93% rename from presentation/src/main/java/receiver/MarkReadReceiver.kt rename to data/src/main/java/receiver/MarkReadReceiver.kt index 60e165ed..5ee03529 100644 --- a/presentation/src/main/java/receiver/MarkReadReceiver.kt +++ b/data/src/main/java/receiver/MarkReadReceiver.kt @@ -21,7 +21,7 @@ package receiver import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import injection.appComponent +import dagger.android.AndroidInjection import interactor.MarkRead import javax.inject.Inject @@ -30,7 +30,7 @@ class MarkReadReceiver : BroadcastReceiver() { @Inject lateinit var markRead: MarkRead override fun onReceive(context: Context, intent: Intent) { - appComponent.inject(this) + AndroidInjection.inject(this, context) val threadId = intent.getLongExtra("threadId", 0) markRead.execute(threadId) diff --git a/presentation/src/main/java/receiver/MarkSeenReceiver.kt b/data/src/main/java/receiver/MarkSeenReceiver.kt similarity index 93% rename from presentation/src/main/java/receiver/MarkSeenReceiver.kt rename to data/src/main/java/receiver/MarkSeenReceiver.kt index 25c5623f..b7fc71e5 100644 --- a/presentation/src/main/java/receiver/MarkSeenReceiver.kt +++ b/data/src/main/java/receiver/MarkSeenReceiver.kt @@ -21,7 +21,7 @@ package receiver import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import injection.appComponent +import dagger.android.AndroidInjection import interactor.MarkSeen import javax.inject.Inject @@ -30,7 +30,7 @@ class MarkSeenReceiver : BroadcastReceiver() { @Inject lateinit var markSeen: MarkSeen override fun onReceive(context: Context, intent: Intent) { - appComponent.inject(this) + AndroidInjection.inject(this, context) val threadId = intent.getLongExtra("threadId", 0) markSeen.execute(threadId) diff --git a/presentation/src/main/java/receiver/MmsReceivedReceiver.kt b/data/src/main/java/receiver/MmsReceivedReceiver.kt similarity index 82% rename from presentation/src/main/java/receiver/MmsReceivedReceiver.kt rename to data/src/main/java/receiver/MmsReceivedReceiver.kt index b682ade2..44fd84c7 100644 --- a/presentation/src/main/java/receiver/MmsReceivedReceiver.kt +++ b/data/src/main/java/receiver/MmsReceivedReceiver.kt @@ -18,9 +18,11 @@ */ package receiver +import android.content.Context +import android.content.Intent import android.net.Uri import com.klinker.android.send_message.MmsReceivedReceiver -import injection.appComponent +import dagger.android.AndroidInjection import interactor.ReceiveMms import javax.inject.Inject @@ -28,9 +30,12 @@ class MmsReceivedReceiver : MmsReceivedReceiver() { @Inject lateinit var receiveMms: ReceiveMms - override fun onMessageReceived(messageUri: Uri?) { - appComponent.inject(this) + override fun onReceive(context: Context?, intent: Intent?) { + AndroidInjection.inject(this, context) + super.onReceive(context, intent) + } + override fun onMessageReceived(messageUri: Uri?) { messageUri?.let { uri -> val pendingResult = goAsync() receiveMms.execute(uri) { pendingResult.finish() } diff --git a/presentation/src/main/java/receiver/MmsReceiver.kt b/data/src/main/java/receiver/MmsReceiver.kt similarity index 100% rename from presentation/src/main/java/receiver/MmsReceiver.kt rename to data/src/main/java/receiver/MmsReceiver.kt diff --git a/presentation/src/main/java/receiver/MmsSentReceiver.kt b/data/src/main/java/receiver/MmsSentReceiver.kt similarity index 97% rename from presentation/src/main/java/receiver/MmsSentReceiver.kt rename to data/src/main/java/receiver/MmsSentReceiver.kt index bfb6870c..e3ba7959 100644 --- a/presentation/src/main/java/receiver/MmsSentReceiver.kt +++ b/data/src/main/java/receiver/MmsSentReceiver.kt @@ -29,7 +29,7 @@ import android.provider.Telephony import com.google.android.mms.MmsException import com.google.android.mms.util_alt.SqliteWrapper import com.klinker.android.send_message.Transaction -import injection.appComponent +import dagger.android.AndroidInjection import interactor.SyncMessage import timber.log.Timber import java.io.File @@ -40,7 +40,7 @@ class MmsSentReceiver : BroadcastReceiver() { @Inject lateinit var syncMessage: SyncMessage override fun onReceive(context: Context, intent: Intent) { - appComponent.inject(this) + AndroidInjection.inject(this, context) Timber.v("MMS sending result: $resultCode") val uri = Uri.parse(intent.getStringExtra(Transaction.EXTRA_CONTENT_URI)) diff --git a/presentation/src/main/java/receiver/MmsUpdatedReceiver.kt b/data/src/main/java/receiver/MmsUpdatedReceiver.kt similarity index 93% rename from presentation/src/main/java/receiver/MmsUpdatedReceiver.kt rename to data/src/main/java/receiver/MmsUpdatedReceiver.kt index 0bb21384..42bba4c3 100644 --- a/presentation/src/main/java/receiver/MmsUpdatedReceiver.kt +++ b/data/src/main/java/receiver/MmsUpdatedReceiver.kt @@ -22,7 +22,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.net.Uri -import injection.appComponent +import dagger.android.AndroidInjection import interactor.SyncMessage import javax.inject.Inject @@ -35,7 +35,8 @@ class MmsUpdatedReceiver : BroadcastReceiver() { @Inject lateinit var syncMessage: SyncMessage override fun onReceive(context: Context, intent: Intent) { - appComponent.inject(this) + AndroidInjection.inject(this, context) + intent.getStringExtra(URI)?.let { uriString -> val pendingResult = goAsync() syncMessage.execute(Uri.parse(uriString)) { pendingResult.finish() } diff --git a/presentation/src/main/java/receiver/NightModeReceiver.kt b/data/src/main/java/receiver/NightModeReceiver.kt similarity index 90% rename from presentation/src/main/java/receiver/NightModeReceiver.kt rename to data/src/main/java/receiver/NightModeReceiver.kt index ef5e4f65..c8e09819 100644 --- a/presentation/src/main/java/receiver/NightModeReceiver.kt +++ b/data/src/main/java/receiver/NightModeReceiver.kt @@ -21,19 +21,17 @@ package receiver import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import injection.appComponent -import common.util.NightModeManager +import dagger.android.AndroidInjection +import util.NightModeManager import javax.inject.Inject class NightModeReceiver : BroadcastReceiver() { @Inject lateinit var nightModeManager: NightModeManager - init { - appComponent.inject(this) - } - override fun onReceive(context: Context, intent: Intent) { + AndroidInjection.inject(this, context) + nightModeManager.updateCurrentTheme() } } \ No newline at end of file diff --git a/presentation/src/main/java/receiver/RemoteMessagingReceiver.kt b/data/src/main/java/receiver/RemoteMessagingReceiver.kt similarity index 95% rename from presentation/src/main/java/receiver/RemoteMessagingReceiver.kt rename to data/src/main/java/receiver/RemoteMessagingReceiver.kt index 6ef06b8e..fa725eb2 100644 --- a/presentation/src/main/java/receiver/RemoteMessagingReceiver.kt +++ b/data/src/main/java/receiver/RemoteMessagingReceiver.kt @@ -22,7 +22,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.support.v4.app.RemoteInput -import injection.appComponent +import dagger.android.AndroidInjection import interactor.MarkRead import interactor.SendMessage import javax.inject.Inject @@ -33,7 +33,7 @@ class RemoteMessagingReceiver : BroadcastReceiver() { @Inject lateinit var markRead: MarkRead override fun onReceive(context: Context, intent: Intent) { - appComponent.inject(this) + AndroidInjection.inject(this, context) val remoteInput = RemoteInput.getResultsFromIntent(intent) val bundle = intent.extras diff --git a/presentation/src/main/java/receiver/SendSmsReceiver.kt b/data/src/main/java/receiver/SendSmsReceiver.kt similarity index 94% rename from presentation/src/main/java/receiver/SendSmsReceiver.kt rename to data/src/main/java/receiver/SendSmsReceiver.kt index 9b9c830e..9f6c154a 100644 --- a/presentation/src/main/java/receiver/SendSmsReceiver.kt +++ b/data/src/main/java/receiver/SendSmsReceiver.kt @@ -21,7 +21,7 @@ package receiver import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import injection.appComponent +import dagger.android.AndroidInjection import interactor.RetrySending import repository.MessageRepository import javax.inject.Inject @@ -32,7 +32,7 @@ class SendSmsReceiver : BroadcastReceiver() { @Inject lateinit var retrySending: RetrySending override fun onReceive(context: Context, intent: Intent) { - appComponent.inject(this) + AndroidInjection.inject(this, context) val id = intent.getLongExtra("id", 0L) messageRepo.getMessage(id)?.let { message -> diff --git a/presentation/src/main/java/receiver/SmsDeliveredReceiver.kt b/data/src/main/java/receiver/SmsDeliveredReceiver.kt similarity index 95% rename from presentation/src/main/java/receiver/SmsDeliveredReceiver.kt rename to data/src/main/java/receiver/SmsDeliveredReceiver.kt index cd5eaaaf..41f4675d 100644 --- a/presentation/src/main/java/receiver/SmsDeliveredReceiver.kt +++ b/data/src/main/java/receiver/SmsDeliveredReceiver.kt @@ -22,7 +22,7 @@ import android.app.Activity import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import injection.appComponent +import dagger.android.AndroidInjection import interactor.MarkDelivered import interactor.MarkDeliveryFailed import javax.inject.Inject @@ -32,11 +32,9 @@ class SmsDeliveredReceiver : BroadcastReceiver() { @Inject lateinit var markDelivered: MarkDelivered @Inject lateinit var markDeliveryFailed: MarkDeliveryFailed - init { - appComponent.inject(this) - } - override fun onReceive(context: Context, intent: Intent) { + AndroidInjection.inject(this, context) + val id = intent.getLongExtra("id", 0L) when (resultCode) { diff --git a/presentation/src/main/java/receiver/SmsProviderChangedReceiver.kt b/data/src/main/java/receiver/SmsProviderChangedReceiver.kt similarity index 95% rename from presentation/src/main/java/receiver/SmsProviderChangedReceiver.kt rename to data/src/main/java/receiver/SmsProviderChangedReceiver.kt index fb73813c..877abe56 100644 --- a/presentation/src/main/java/receiver/SmsProviderChangedReceiver.kt +++ b/data/src/main/java/receiver/SmsProviderChangedReceiver.kt @@ -21,7 +21,7 @@ package receiver import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import injection.appComponent +import dagger.android.AndroidInjection import interactor.SyncMessage import javax.inject.Inject @@ -41,7 +41,7 @@ class SmsProviderChangedReceiver : BroadcastReceiver() { @Inject lateinit var syncMessage: SyncMessage override fun onReceive(context: Context, intent: Intent) { - appComponent.inject(this) + AndroidInjection.inject(this, context) // Obtain the uri for the changed data // If the value is null, don't continue diff --git a/presentation/src/main/java/receiver/SmsReceiver.kt b/data/src/main/java/receiver/SmsReceiver.kt similarity index 93% rename from presentation/src/main/java/receiver/SmsReceiver.kt rename to data/src/main/java/receiver/SmsReceiver.kt index 14198360..9c1b0644 100644 --- a/presentation/src/main/java/receiver/SmsReceiver.kt +++ b/data/src/main/java/receiver/SmsReceiver.kt @@ -22,7 +22,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.provider.Telephony.Sms -import injection.appComponent +import dagger.android.AndroidInjection import interactor.ReceiveSms import javax.inject.Inject @@ -31,7 +31,7 @@ class SmsReceiver : BroadcastReceiver() { @Inject lateinit var receiveMessage: ReceiveSms override fun onReceive(context: Context, intent: Intent) { - appComponent.inject(this) + AndroidInjection.inject(this, context) Sms.Intents.getMessagesFromIntent(intent)?.let { messages -> val pendingResult = goAsync() diff --git a/presentation/src/main/java/receiver/SmsSentReceiver.kt b/data/src/main/java/receiver/SmsSentReceiver.kt similarity index 95% rename from presentation/src/main/java/receiver/SmsSentReceiver.kt rename to data/src/main/java/receiver/SmsSentReceiver.kt index e610e5df..733fec75 100644 --- a/presentation/src/main/java/receiver/SmsSentReceiver.kt +++ b/data/src/main/java/receiver/SmsSentReceiver.kt @@ -23,7 +23,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.telephony.SmsManager -import injection.appComponent +import dagger.android.AndroidInjection import interactor.MarkFailed import interactor.MarkSent import javax.inject.Inject @@ -34,7 +34,7 @@ class SmsSentReceiver : BroadcastReceiver() { @Inject lateinit var markFailed: MarkFailed override fun onReceive(context: Context, intent: Intent) { - appComponent.inject(this) + AndroidInjection.inject(this, context) val id = intent.getLongExtra("id", 0L) diff --git a/data/src/main/java/repository/MessageRepositoryImpl.kt b/data/src/main/java/repository/MessageRepositoryImpl.kt index 93ae7e23..0852831f 100644 --- a/data/src/main/java/repository/MessageRepositoryImpl.kt +++ b/data/src/main/java/repository/MessageRepositoryImpl.kt @@ -24,14 +24,18 @@ import android.content.ContentUris import android.content.ContentValues import android.content.Context import android.content.Intent +import android.graphics.Bitmap import android.net.Uri import android.os.Build import android.provider.BaseColumns import android.provider.Telephony import android.telephony.PhoneNumberUtils import android.telephony.SmsManager +import com.google.android.mms.ContentType import com.klinker.android.send_message.BroadcastUtils +import com.klinker.android.send_message.Settings import com.klinker.android.send_message.StripAccents +import com.klinker.android.send_message.Transaction import filter.ConversationFilter import io.reactivex.Maybe import io.reactivex.android.schedulers.AndroidSchedulers @@ -43,6 +47,7 @@ import io.realm.Sort import manager.KeyManager import mapper.CursorToConversation import mapper.CursorToRecipient +import model.Attachment import model.Contact import model.Conversation import model.Message @@ -54,6 +59,7 @@ import util.extensions.anyOf import util.extensions.asMaybe import util.extensions.insertOrUpdate import util.extensions.map +import java.io.ByteArrayOutputStream import javax.inject.Inject import javax.inject.Singleton @@ -64,6 +70,7 @@ class MessageRepositoryImpl @Inject constructor( private val conversationFilter: ConversationFilter, private val cursorToConversation: CursorToConversation, private val cursorToRecipient: CursorToRecipient, + private val imageRepository: ImageRepository, private val prefs: Preferences) : MessageRepository { override fun getConversations(archived: Boolean): RealmResults { @@ -104,7 +111,7 @@ class MessageRepositoryImpl @Inject constructor( .sortedByDescending { result -> result.messages } return conversations - .filter { conversation -> conversationFilter.filter(conversation, query) } + .filter { conversation -> conversationFilter.filter(conversation, query) } .map { conversation -> SearchResult(query, conversation, 0) } .plus(messagesByConversation) } @@ -436,6 +443,60 @@ class MessageRepositoryImpl @Inject constructor( smsManager.sendMultipartTextMessage(message.address, null, parts, ArrayList(sentIntents), ArrayList(deliveredIntents)) } + override fun sendMms(threadId: Long, addresses: List, body: String, attachments: List) { + val settings = Settings() + val message = com.klinker.android.send_message.Message(body, addresses.toTypedArray()) + + // Add the GIFs as attachments. The app currently can't compress them, which may result + // in a lot of these messages failing to send + // TODO Add support for GIF compression + attachments + .filter { attachment -> attachment.isGif(context) } + .map { attachment -> attachment.getUri() } + .map { uri -> context.contentResolver.openInputStream(uri) } + .map { inputStream -> inputStream.readBytes() } + .forEach { bitmap -> message.addMedia(bitmap, ContentType.IMAGE_GIF) } + + // Compress the images and add them as attachments + var totalImageBytes = 0 + attachments + .filter { attachment -> !attachment.isGif(context) } + .mapNotNull { attachment -> attachment.getUri() } + .mapNotNull { uri -> imageRepository.loadImage(uri) } + .also { totalImageBytes = it.sumBy { it.allocationByteCount } } + .map { bitmap -> + val byteRatio = bitmap.allocationByteCount / totalImageBytes.toFloat() + shrink(bitmap, (prefs.mmsSize.get() * 1024 * byteRatio).toInt()) + } + .forEach { bitmap -> message.addMedia(bitmap, ContentType.IMAGE_JPEG) } + + val transaction = Transaction(context, settings) + transaction.sendNewMessage(message, threadId) + } + + private fun shrink(src: Bitmap, maxBytes: Int): ByteArray { + var step = 0.0 + val factor = 0.5 + val quality = 60 + + val height = src.height + val width = src.width + + val stream = ByteArrayOutputStream() + src.compress(Bitmap.CompressFormat.JPEG, 100, stream) + + while (maxBytes > 0 && stream.size() > maxBytes) { + step++ + val scale = Math.pow(factor, step) + + stream.reset() + Bitmap.createScaledBitmap(src, (width * scale).toInt(), (height * scale).toInt(), false) + .compress(Bitmap.CompressFormat.JPEG, quality, stream) + } + + return stream.toByteArray() + } + override fun cancelDelayedSms(id: Long) { val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager alarmManager.cancel(getIntentForDelayedSms(id)) diff --git a/presentation/src/main/java/service/HeadlessSmsSendService.kt b/data/src/main/java/service/HeadlessSmsSendService.kt similarity index 96% rename from presentation/src/main/java/service/HeadlessSmsSendService.kt rename to data/src/main/java/service/HeadlessSmsSendService.kt index 43996552..e2c66610 100644 --- a/presentation/src/main/java/service/HeadlessSmsSendService.kt +++ b/data/src/main/java/service/HeadlessSmsSendService.kt @@ -22,7 +22,7 @@ import android.app.IntentService import android.content.Intent import android.net.Uri import android.telephony.TelephonyManager -import injection.appComponent +import dagger.android.AndroidInjection import interactor.SendMessage import repository.MessageRepository import javax.inject.Inject @@ -35,7 +35,7 @@ class HeadlessSmsSendService : IntentService("HeadlessSmsSendService") { override fun onHandleIntent(intent: Intent?) { if (intent?.action != TelephonyManager.ACTION_RESPOND_VIA_MESSAGE) return - appComponent.inject(this) + AndroidInjection.inject(this) intent.extras?.getString(Intent.EXTRA_TEXT)?.takeIf { it.isNotBlank() }?.let { body -> val intentUri = intent.data val recipients = getRecipients(intentUri).split(";") diff --git a/presentation/src/main/java/common/util/NightModeManager.kt b/data/src/main/java/util/NightModeManager.kt similarity index 98% rename from presentation/src/main/java/common/util/NightModeManager.kt rename to data/src/main/java/util/NightModeManager.kt index e3e156bd..9c7a8e0f 100644 --- a/presentation/src/main/java/common/util/NightModeManager.kt +++ b/data/src/main/java/util/NightModeManager.kt @@ -16,15 +16,13 @@ * You should have received a copy of the GNU General Public License * along with QKSMS. If not, see . */ -package common.util +package util import android.app.AlarmManager import android.app.PendingIntent import android.content.Context import android.content.Intent import receiver.NightModeReceiver -import util.Preferences -import util.tryOrNull import java.text.SimpleDateFormat import java.util.* import java.util.concurrent.TimeUnit diff --git a/domain/src/main/java/interactor/SendMessage.kt b/domain/src/main/java/interactor/SendMessage.kt new file mode 100644 index 00000000..e03e24ae --- /dev/null +++ b/domain/src/main/java/interactor/SendMessage.kt @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017 Moez Bhatti + * + * This file is part of QKSMS. + * + * QKSMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * QKSMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with QKSMS. If not, see . + */ +package interactor + +import io.reactivex.Flowable +import model.Attachment +import repository.MessageRepository +import javax.inject.Inject + +class SendMessage @Inject constructor( + private val messageRepo: MessageRepository +) : Interactor() { + + data class Params(val threadId: Long, val addresses: List, val body: String, val attachments: List = listOf()) + + override fun buildObservable(params: Params): Flowable { + return Flowable.just(Unit) + .filter { params.addresses.isNotEmpty() } + .doOnNext { + if (params.addresses.size == 1 && params.attachments.isEmpty()) { + messageRepo.sendSmsAndPersist(params.threadId, params.addresses.first(), params.body) + } else { + messageRepo.sendMms(params.threadId, params.addresses, params.body, params.attachments) + } + } + .doOnNext { messageRepo.updateConversations(params.threadId) } + .doOnNext { messageRepo.markUnarchived(params.threadId) } + } + +} \ No newline at end of file diff --git a/presentation/src/main/java/feature/compose/Attachment.kt b/domain/src/main/java/model/Attachment.kt similarity index 86% rename from presentation/src/main/java/feature/compose/Attachment.kt rename to domain/src/main/java/model/Attachment.kt index 0a5654c9..f7401ec4 100644 --- a/presentation/src/main/java/feature/compose/Attachment.kt +++ b/domain/src/main/java/model/Attachment.kt @@ -16,13 +16,12 @@ * You should have received a copy of the GNU General Public License * along with QKSMS. If not, see . */ -package feature.compose +package model import android.content.Context import android.net.Uri import android.os.Build import android.support.v13.view.inputmethod.InputContentInfoCompat -import com.google.android.mms.ContentType data class Attachment(private val uri: Uri? = null, private val inputContent: InputContentInfoCompat? = null) { @@ -36,9 +35,9 @@ data class Attachment(private val uri: Uri? = null, private val inputContent: In fun isGif(context: Context): Boolean { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 && inputContent != null) { - inputContent.description.hasMimeType(ContentType.IMAGE_GIF) + inputContent.description.hasMimeType("image/gif") } else { - context.contentResolver.getType(uri) == ContentType.IMAGE_GIF + context.contentResolver.getType(uri) == "image/gif" } } diff --git a/domain/src/main/java/repository/MessageRepository.kt b/domain/src/main/java/repository/MessageRepository.kt index fc313ffc..bd76cc3e 100644 --- a/domain/src/main/java/repository/MessageRepository.kt +++ b/domain/src/main/java/repository/MessageRepository.kt @@ -20,6 +20,7 @@ package repository import io.reactivex.Maybe import io.realm.RealmResults +import model.Attachment import model.Conversation import model.Message import model.MmsPart @@ -100,6 +101,8 @@ interface MessageRepository { */ fun sendSms(message: Message) + fun sendMms(threadId: Long, addresses: List, body: String, attachments: List) + /** * Attempts to cancel sending the message with the given id */ diff --git a/presentation/src/main/java/common/QKApplication.kt b/presentation/src/main/java/common/QKApplication.kt index 059da7f6..5322e85b 100644 --- a/presentation/src/main/java/common/QKApplication.kt +++ b/presentation/src/main/java/common/QKApplication.kt @@ -20,6 +20,8 @@ package common */ import android.app.Activity import android.app.Application +import android.app.Service +import android.content.BroadcastReceiver import android.support.text.emoji.EmojiCompat import android.support.text.emoji.FontRequestEmojiCompatConfig import android.support.v4.provider.FontRequest @@ -27,10 +29,12 @@ import com.bugsnag.android.Bugsnag import com.bugsnag.android.Configuration import com.moez.QKSMS.BuildConfig import com.moez.QKSMS.R -import common.util.NightModeManager +import util.NightModeManager import dagger.android.AndroidInjector import dagger.android.DispatchingAndroidInjector import dagger.android.HasActivityInjector +import dagger.android.HasBroadcastReceiverInjector +import dagger.android.HasServiceInjector import injection.AppComponentManager import injection.appComponent import io.realm.Realm @@ -40,7 +44,7 @@ import migration.QkRealmMigration import timber.log.Timber import javax.inject.Inject -class QKApplication : Application(), HasActivityInjector { +class QKApplication : Application(), HasActivityInjector, HasBroadcastReceiverInjector, HasServiceInjector { /** * Inject this so that it is forced to initialize @@ -48,7 +52,9 @@ class QKApplication : Application(), HasActivityInjector { @Suppress("unused") @Inject lateinit var analyticsManager: AnalyticsManager - @Inject lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector + @Inject lateinit var dispatchingActivityInjector: DispatchingAndroidInjector + @Inject lateinit var dispatchingBroadcastReceiverInjector: DispatchingAndroidInjector + @Inject lateinit var dispatchingServiceInjector: DispatchingAndroidInjector @Inject lateinit var nightModeManager: NightModeManager private val packages = arrayOf("common", "feature", "injection", "filter", "interactor", "manager", "mapper", @@ -88,7 +94,15 @@ class QKApplication : Application(), HasActivityInjector { } override fun activityInjector(): AndroidInjector { - return dispatchingAndroidInjector + return dispatchingActivityInjector + } + + override fun broadcastReceiverInjector(): AndroidInjector { + return dispatchingBroadcastReceiverInjector + } + + override fun serviceInjector(): AndroidInjector { + return dispatchingServiceInjector } } \ No newline at end of file diff --git a/presentation/src/main/java/feature/compose/AttachmentAdapter.kt b/presentation/src/main/java/feature/compose/AttachmentAdapter.kt index 09bf5cd5..903d8a56 100644 --- a/presentation/src/main/java/feature/compose/AttachmentAdapter.kt +++ b/presentation/src/main/java/feature/compose/AttachmentAdapter.kt @@ -31,6 +31,7 @@ import io.reactivex.disposables.CompositeDisposable import io.reactivex.subjects.PublishSubject import io.reactivex.subjects.Subject import kotlinx.android.synthetic.main.attachment_list_item.view.* +import model.Attachment import javax.inject.Inject class AttachmentAdapter @Inject constructor( diff --git a/presentation/src/main/java/feature/compose/ComposeActivity.kt b/presentation/src/main/java/feature/compose/ComposeActivity.kt index 1594b717..0bf5ea52 100644 --- a/presentation/src/main/java/feature/compose/ComposeActivity.kt +++ b/presentation/src/main/java/feature/compose/ComposeActivity.kt @@ -50,6 +50,7 @@ import io.reactivex.Observable import io.reactivex.subjects.PublishSubject import io.reactivex.subjects.Subject import kotlinx.android.synthetic.main.compose_activity.* +import model.Attachment import model.Contact import model.Message import java.text.SimpleDateFormat diff --git a/presentation/src/main/java/feature/compose/ComposeState.kt b/presentation/src/main/java/feature/compose/ComposeState.kt index 5ed38897..7c3a9186 100644 --- a/presentation/src/main/java/feature/compose/ComposeState.kt +++ b/presentation/src/main/java/feature/compose/ComposeState.kt @@ -19,6 +19,7 @@ package feature.compose import io.realm.RealmResults +import model.Attachment import model.Contact import model.Conversation import model.Message diff --git a/presentation/src/main/java/feature/compose/ComposeView.kt b/presentation/src/main/java/feature/compose/ComposeView.kt index 25e29af1..21455250 100644 --- a/presentation/src/main/java/feature/compose/ComposeView.kt +++ b/presentation/src/main/java/feature/compose/ComposeView.kt @@ -23,6 +23,7 @@ import android.support.v13.view.inputmethod.InputContentInfoCompat import common.base.QkView import io.reactivex.Observable import io.reactivex.subjects.Subject +import model.Attachment import model.Contact import model.Message diff --git a/presentation/src/main/java/feature/compose/ComposeViewModel.kt b/presentation/src/main/java/feature/compose/ComposeViewModel.kt index 480ae84a..bed6ab45 100644 --- a/presentation/src/main/java/feature/compose/ComposeViewModel.kt +++ b/presentation/src/main/java/feature/compose/ComposeViewModel.kt @@ -49,6 +49,7 @@ import io.reactivex.subjects.PublishSubject import io.reactivex.subjects.Subject import io.realm.RealmList import manager.PermissionManager +import model.Attachment import model.Contact import model.Conversation import model.Message diff --git a/presentation/src/main/java/feature/settings/SettingsViewModel.kt b/presentation/src/main/java/feature/settings/SettingsViewModel.kt index 04eb024f..87cbe866 100644 --- a/presentation/src/main/java/feature/settings/SettingsViewModel.kt +++ b/presentation/src/main/java/feature/settings/SettingsViewModel.kt @@ -27,7 +27,7 @@ import common.base.QkViewModel import common.util.BillingManager import common.util.Colors import common.util.DateFormatter -import common.util.NightModeManager +import util.NightModeManager import interactor.SyncMessages import io.reactivex.rxkotlin.plusAssign import io.reactivex.rxkotlin.withLatestFrom diff --git a/presentation/src/main/java/feature/widget/WidgetProvider.kt b/presentation/src/main/java/feature/widget/WidgetProvider.kt index 60509555..d88478c6 100644 --- a/presentation/src/main/java/feature/widget/WidgetProvider.kt +++ b/presentation/src/main/java/feature/widget/WidgetProvider.kt @@ -31,9 +31,9 @@ import android.widget.RemoteViews import com.moez.QKSMS.R import common.util.Colors import common.util.extensions.getColorCompat +import dagger.android.AndroidInjection import feature.compose.ComposeActivity import feature.main.MainActivity -import injection.appComponent import manager.WidgetManager import timber.log.Timber import util.Preferences @@ -44,11 +44,9 @@ class WidgetProvider : AppWidgetProvider() { @Inject lateinit var colors: Colors @Inject lateinit var prefs: Preferences - init { - appComponent.inject(this) - } - override fun onReceive(context: Context, intent: Intent) { + AndroidInjection.inject(this, context) + when (intent.action) { WidgetManager.ACTION_NOTIFY_DATASET_CHANGED -> updateData(context) else -> super.onReceive(context, intent) diff --git a/presentation/src/main/java/injection/BuildersModule.kt b/presentation/src/main/java/injection/ActivityBuilderModule.kt similarity index 98% rename from presentation/src/main/java/injection/BuildersModule.kt rename to presentation/src/main/java/injection/ActivityBuilderModule.kt index a9ec9ac6..52f3f57f 100644 --- a/presentation/src/main/java/injection/BuildersModule.kt +++ b/presentation/src/main/java/injection/ActivityBuilderModule.kt @@ -26,7 +26,7 @@ import feature.themepicker.ThemePickerActivity import feature.themepicker.ThemePickerActivityModule @Module -abstract class BuildersModule { +abstract class ActivityBuilderModule { @ActivityScope @ContributesAndroidInjector(modules = [MainActivityModule::class]) diff --git a/presentation/src/main/java/injection/AppComponent.kt b/presentation/src/main/java/injection/AppComponent.kt index 86300ce8..3f8eefcd 100644 --- a/presentation/src/main/java/injection/AppComponent.kt +++ b/presentation/src/main/java/injection/AppComponent.kt @@ -31,25 +31,15 @@ import dagger.Component import dagger.android.support.AndroidSupportInjectionModule import feature.compose.DetailedChipView import feature.widget.WidgetAdapter -import feature.widget.WidgetProvider -import receiver.DefaultSmsChangedReceiver -import receiver.MarkReadReceiver -import receiver.MarkSeenReceiver -import receiver.MmsReceivedReceiver -import receiver.MmsSentReceiver -import receiver.MmsUpdatedReceiver -import receiver.NightModeReceiver -import receiver.RemoteMessagingReceiver -import receiver.SendSmsReceiver -import receiver.SmsDeliveredReceiver -import receiver.SmsProviderChangedReceiver -import receiver.SmsReceiver -import receiver.SmsSentReceiver -import service.HeadlessSmsSendService import javax.inject.Singleton @Singleton -@Component(modules = [AndroidSupportInjectionModule::class, AppModule::class, BuildersModule::class]) +@Component(modules = [ + AndroidSupportInjectionModule::class, + AppModule::class, + ActivityBuilderModule::class, + BroadcastReceiverBuilderModule::class, + ServiceBuilderModule::class]) interface AppComponent { fun inject(application: QKApplication) @@ -58,22 +48,6 @@ interface AppComponent { fun inject(fetcher: ContactImageLoader.ContactImageFetcher) - fun inject(receiver: DefaultSmsChangedReceiver) - fun inject(receiver: SmsDeliveredReceiver) - fun inject(receiver: SmsSentReceiver) - fun inject(receiver: MarkSeenReceiver) - fun inject(receiver: MarkReadReceiver) - fun inject(receiver: MmsReceivedReceiver) - fun inject(receiver: MmsSentReceiver) - fun inject(receiver: MmsUpdatedReceiver) - fun inject(receiver: NightModeReceiver) - fun inject(receiver: RemoteMessagingReceiver) - fun inject(receiver: SendSmsReceiver) - fun inject(receiver: SmsProviderChangedReceiver) - fun inject(receiver: SmsReceiver) - fun inject(receiver: WidgetProvider) - - fun inject(service: HeadlessSmsSendService) fun inject(service: WidgetAdapter) fun inject(view: AvatarView) diff --git a/presentation/src/main/java/injection/BroadcastReceiverBuilderModule.kt b/presentation/src/main/java/injection/BroadcastReceiverBuilderModule.kt new file mode 100644 index 00000000..e173bce2 --- /dev/null +++ b/presentation/src/main/java/injection/BroadcastReceiverBuilderModule.kt @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2017 Moez Bhatti + * + * This file is part of QKSMS. + * + * QKSMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * QKSMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with QKSMS. If not, see . + */ +package injection + +import dagger.Module +import dagger.android.ContributesAndroidInjector +import feature.widget.WidgetProvider +import receiver.DefaultSmsChangedReceiver +import receiver.MarkReadReceiver +import receiver.MarkSeenReceiver +import receiver.MmsReceivedReceiver +import receiver.MmsReceiver +import receiver.MmsSentReceiver +import receiver.MmsUpdatedReceiver +import receiver.NightModeReceiver +import receiver.RemoteMessagingReceiver +import receiver.SendSmsReceiver +import receiver.SmsDeliveredReceiver +import receiver.SmsProviderChangedReceiver +import receiver.SmsReceiver +import receiver.SmsSentReceiver + +@Module +abstract class BroadcastReceiverBuilderModule { + + @ActivityScope + @ContributesAndroidInjector() + abstract fun bindDefaultSmsChangedReceiver(): DefaultSmsChangedReceiver + + @ActivityScope + @ContributesAndroidInjector() + abstract fun bindMarkReadReceiver(): MarkReadReceiver + + @ActivityScope + @ContributesAndroidInjector() + abstract fun bindMarkSeenReceiver(): MarkSeenReceiver + + @ActivityScope + @ContributesAndroidInjector() + abstract fun bindMmsReceivedReceiver(): MmsReceivedReceiver + + @ActivityScope + @ContributesAndroidInjector() + abstract fun bindMmsReceiver(): MmsReceiver + + @ActivityScope + @ContributesAndroidInjector() + abstract fun bindMmsSentReceiver(): MmsSentReceiver + + @ActivityScope + @ContributesAndroidInjector() + abstract fun bindMmsUpdatedReceiver(): MmsUpdatedReceiver + + @ActivityScope + @ContributesAndroidInjector() + abstract fun bindNightModeReceiver(): NightModeReceiver + + @ActivityScope + @ContributesAndroidInjector() + abstract fun bindRemoteMessagingReceiver(): RemoteMessagingReceiver + + @ActivityScope + @ContributesAndroidInjector() + abstract fun bindSendSmsReceiver(): SendSmsReceiver + + @ActivityScope + @ContributesAndroidInjector() + abstract fun bindSmsDeliveredReceiver(): SmsDeliveredReceiver + + @ActivityScope + @ContributesAndroidInjector() + abstract fun bindSmsProviderChangedReceiver(): SmsProviderChangedReceiver + + @ActivityScope + @ContributesAndroidInjector() + abstract fun bindSmsReceiver(): SmsReceiver + + @ActivityScope + @ContributesAndroidInjector() + abstract fun bindSmsSentReceiver(): SmsSentReceiver + + @ActivityScope + @ContributesAndroidInjector() + abstract fun bindWidgetProvider(): WidgetProvider + +} \ No newline at end of file diff --git a/presentation/src/main/java/injection/ServiceBuilderModule.kt b/presentation/src/main/java/injection/ServiceBuilderModule.kt new file mode 100644 index 00000000..208b1772 --- /dev/null +++ b/presentation/src/main/java/injection/ServiceBuilderModule.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2017 Moez Bhatti + * + * This file is part of QKSMS. + * + * QKSMS is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * QKSMS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with QKSMS. If not, see . + */ +package injection + +import dagger.Module +import dagger.android.ContributesAndroidInjector +import service.HeadlessSmsSendService + +@Module +abstract class ServiceBuilderModule { + + @ActivityScope + @ContributesAndroidInjector() + abstract fun bindHeadlessSmsSendService(): HeadlessSmsSendService + +} \ No newline at end of file diff --git a/presentation/src/main/java/interactor/MigratePreferences.kt b/presentation/src/main/java/interactor/MigratePreferences.kt index 701e4ee2..ba91bfcc 100644 --- a/presentation/src/main/java/interactor/MigratePreferences.kt +++ b/presentation/src/main/java/interactor/MigratePreferences.kt @@ -19,8 +19,8 @@ package interactor import com.f2prateek.rx.preferences2.RxSharedPreferences -import common.util.NightModeManager import io.reactivex.Flowable +import util.NightModeManager import util.Preferences import javax.inject.Inject diff --git a/presentation/src/main/java/interactor/SendMessage.kt b/presentation/src/main/java/interactor/SendMessage.kt deleted file mode 100644 index 13741b49..00000000 --- a/presentation/src/main/java/interactor/SendMessage.kt +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2017 Moez Bhatti - * - * This file is part of QKSMS. - * - * QKSMS is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * QKSMS is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with QKSMS. If not, see . - */ -package interactor - -import android.content.Context -import android.graphics.Bitmap -import com.google.android.mms.ContentType -import com.klinker.android.send_message.Message -import com.klinker.android.send_message.Settings -import com.klinker.android.send_message.Transaction -import feature.compose.Attachment -import io.reactivex.Flowable -import repository.ImageRepository -import repository.MessageRepository -import util.Preferences -import java.io.ByteArrayOutputStream -import javax.inject.Inject - -class SendMessage @Inject constructor( - private val context: Context, - private val imageRepository: ImageRepository, - private val prefs: Preferences, - private val messageRepo: MessageRepository -) : Interactor() { - - data class Params(val threadId: Long, val addresses: List, val body: String, val attachments: List = listOf()) - - override fun buildObservable(params: Params): Flowable { - return Flowable.just(Unit) - .filter { params.addresses.isNotEmpty() } - .doOnNext { - if (params.addresses.size == 1 && params.attachments.isEmpty()) { - messageRepo.sendSmsAndPersist(params.threadId, params.addresses.first(), params.body) - } else { - sendMms(params.threadId, params.addresses, params.body, params.attachments) - } - } - .doOnNext { messageRepo.updateConversations(params.threadId) } - .doOnNext { messageRepo.markUnarchived(params.threadId) } - } - - private fun sendMms(threadId: Long, addresses: List, body: String, attachments: List) { - val settings = Settings() - val message = Message(body, addresses.toTypedArray()) - - // Add the GIFs as attachments. The app currently can't compress them, which may result - // in a lot of these messages failing to send - // TODO Add support for GIF compression - attachments - .filter { attachment -> attachment.isGif(context) } - .map { attachment -> attachment.getUri() } - .map { uri -> context.contentResolver.openInputStream(uri) } - .map { inputStream -> inputStream.readBytes() } - .forEach { bitmap -> message.addMedia(bitmap, ContentType.IMAGE_GIF) } - - // Compress the images and add them as attachments - var totalImageBytes = 0 - attachments - .filter { attachment -> !attachment.isGif(context) } - .mapNotNull { attachment -> attachment.getUri() } - .mapNotNull { uri -> imageRepository.loadImage(uri) } - .also { totalImageBytes = it.sumBy { it.allocationByteCount } } - .map { bitmap -> - val byteRatio = bitmap.allocationByteCount / totalImageBytes.toFloat() - shrink(bitmap, (prefs.mmsSize.get() * 1024 * byteRatio).toInt()) - } - .forEach { bitmap -> message.addMedia(bitmap, ContentType.IMAGE_JPEG) } - - val transaction = Transaction(context, settings) - transaction.sendNewMessage(message, threadId) - } - - private fun shrink(src: Bitmap, maxBytes: Int): ByteArray { - var step = 0.0 - val factor = 0.5 - val quality = 60 - - val height = src.height - val width = src.width - - val stream = ByteArrayOutputStream() - src.compress(Bitmap.CompressFormat.JPEG, 100, stream) - - while (maxBytes > 0 && stream.size() > maxBytes) { - step++ - val scale = Math.pow(factor, step) - - stream.reset() - Bitmap.createScaledBitmap(src, (width * scale).toInt(), (height * scale).toInt(), false) - .compress(Bitmap.CompressFormat.JPEG, quality, stream) - } - - return stream.toByteArray() - } - -} \ No newline at end of file