Migrate services and receivers to data package

This commit is contained in:
Moez Bhatti
2018-05-29 11:11:38 -04:00
parent 554791db49
commit c0968e4b42
35 changed files with 323 additions and 200 deletions
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import android.provider.Telephony
import android.support.annotation.RequiresApi
import dagger.android.AndroidInjection
import interactor.SyncMessages
import util.Preferences
import javax.inject.Inject
class DefaultSmsChangedReceiver : BroadcastReceiver() {
@Inject lateinit var prefs: Preferences
@Inject lateinit var syncMessages: SyncMessages
@RequiresApi(Build.VERSION_CODES.N)
override fun onReceive(context: Context, intent: Intent) {
AndroidInjection.inject(this, context)
if (intent.getBooleanExtra(Telephony.Sms.Intents.EXTRA_IS_DEFAULT_SMS_APP, false)) {
val pendingResult = goAsync()
syncMessages.execute(Unit, { pendingResult.finish() })
}
}
}
@@ -0,0 +1,39 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import dagger.android.AndroidInjection
import interactor.MarkRead
import javax.inject.Inject
class MarkReadReceiver : BroadcastReceiver() {
@Inject lateinit var markRead: MarkRead
override fun onReceive(context: Context, intent: Intent) {
AndroidInjection.inject(this, context)
val threadId = intent.getLongExtra("threadId", 0)
markRead.execute(threadId)
}
}
@@ -0,0 +1,39 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import dagger.android.AndroidInjection
import interactor.MarkSeen
import javax.inject.Inject
class MarkSeenReceiver : BroadcastReceiver() {
@Inject lateinit var markSeen: MarkSeen
override fun onReceive(context: Context, intent: Intent) {
AndroidInjection.inject(this, context)
val threadId = intent.getLongExtra("threadId", 0)
markSeen.execute(threadId)
}
}
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package receiver
import android.content.Context
import android.content.Intent
import android.net.Uri
import com.klinker.android.send_message.MmsReceivedReceiver
import dagger.android.AndroidInjection
import interactor.ReceiveMms
import javax.inject.Inject
class MmsReceivedReceiver : MmsReceivedReceiver() {
@Inject lateinit var receiveMms: ReceiveMms
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() }
}
}
}
@@ -0,0 +1,23 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package receiver
import com.android.mms.transaction.PushReceiver
class MmsReceiver : PushReceiver()
@@ -0,0 +1,91 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package receiver
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.ContentUris
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.net.Uri
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 dagger.android.AndroidInjection
import interactor.SyncMessage
import timber.log.Timber
import java.io.File
import javax.inject.Inject
class MmsSentReceiver : BroadcastReceiver() {
@Inject lateinit var syncMessage: SyncMessage
override fun onReceive(context: Context, intent: Intent) {
AndroidInjection.inject(this, context)
Timber.v("MMS sending result: $resultCode")
val uri = Uri.parse(intent.getStringExtra(Transaction.EXTRA_CONTENT_URI))
Timber.v(uri.toString())
when (resultCode) {
Activity.RESULT_OK -> {
Timber.v("MMS has finished sending, marking it as so in the database")
val values = ContentValues(1)
values.put(Telephony.Mms.MESSAGE_BOX, Telephony.Mms.MESSAGE_BOX_SENT)
SqliteWrapper.update(context, context.contentResolver, uri, values, null, null)
}
else -> {
Timber.v("MMS has failed to send, marking it as so in the database")
try {
val messageId = ContentUris.parseId(uri)
val values = ContentValues(1)
values.put(Telephony.Mms.MESSAGE_BOX, Telephony.Mms.MESSAGE_BOX_FAILED)
SqliteWrapper.update(context, context.contentResolver, Telephony.Mms.CONTENT_URI, values,
"${Telephony.Mms._ID} = ?", arrayOf(messageId.toString()))
// TODO this query isn't able to find any results
// Need to figure out why the message isn't appearing in the PendingMessages Uri,
// so that we can properly assign the error type
val errorTypeValues = ContentValues(1)
errorTypeValues.put(Telephony.MmsSms.PendingMessages.ERROR_TYPE, Telephony.MmsSms.ERR_TYPE_GENERIC_PERMANENT)
SqliteWrapper.update(context, context.contentResolver, Telephony.MmsSms.PendingMessages.CONTENT_URI,
errorTypeValues, "${Telephony.MmsSms.PendingMessages.MSG_ID} = ?", arrayOf(messageId.toString()))
} catch (e: MmsException) {
e.printStackTrace()
}
}
}
val filePath = intent.getStringExtra(Transaction.EXTRA_FILE_PATH)
Timber.v(filePath)
File(filePath).delete()
Uri.parse(intent.getStringExtra("content_uri"))?.let { uri ->
val pendingResult = goAsync()
syncMessage.execute(uri) { pendingResult.finish() }
}
}
}
@@ -0,0 +1,46 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.net.Uri
import dagger.android.AndroidInjection
import interactor.SyncMessage
import javax.inject.Inject
class MmsUpdatedReceiver : BroadcastReceiver() {
companion object {
const val URI = "uri"
}
@Inject lateinit var syncMessage: SyncMessage
override fun onReceive(context: Context, intent: Intent) {
AndroidInjection.inject(this, context)
intent.getStringExtra(URI)?.let { uriString ->
val pendingResult = goAsync()
syncMessage.execute(Uri.parse(uriString)) { pendingResult.finish() }
}
}
}
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import dagger.android.AndroidInjection
import util.NightModeManager
import javax.inject.Inject
class NightModeReceiver : BroadcastReceiver() {
@Inject lateinit var nightModeManager: NightModeManager
override fun onReceive(context: Context, intent: Intent) {
AndroidInjection.inject(this, context)
nightModeManager.updateCurrentTheme()
}
}
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.support.v4.app.RemoteInput
import dagger.android.AndroidInjection
import interactor.MarkRead
import interactor.SendMessage
import javax.inject.Inject
class RemoteMessagingReceiver : BroadcastReceiver() {
@Inject lateinit var sendMessage: SendMessage
@Inject lateinit var markRead: MarkRead
override fun onReceive(context: Context, intent: Intent) {
AndroidInjection.inject(this, context)
val remoteInput = RemoteInput.getResultsFromIntent(intent)
val bundle = intent.extras
if (remoteInput != null && bundle != null) {
val address = bundle.getString("address")
val threadId = bundle.getLong("threadId")
val body = remoteInput.getCharSequence("body").toString()
markRead.execute(threadId)
sendMessage.execute(SendMessage.Params(threadId, listOf(address), body))
}
}
}
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import dagger.android.AndroidInjection
import interactor.RetrySending
import repository.MessageRepository
import javax.inject.Inject
class SendSmsReceiver : BroadcastReceiver() {
@Inject lateinit var messageRepo: MessageRepository
@Inject lateinit var retrySending: RetrySending
override fun onReceive(context: Context, intent: Intent) {
AndroidInjection.inject(this, context)
val id = intent.getLongExtra("id", 0L)
messageRepo.getMessage(id)?.let { message ->
val result = goAsync()
retrySending.execute(message) { result.finish() }
}
}
}
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package receiver
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import dagger.android.AndroidInjection
import interactor.MarkDelivered
import interactor.MarkDeliveryFailed
import javax.inject.Inject
class SmsDeliveredReceiver : BroadcastReceiver() {
@Inject lateinit var markDelivered: MarkDelivered
@Inject lateinit var markDeliveryFailed: MarkDeliveryFailed
override fun onReceive(context: Context, intent: Intent) {
AndroidInjection.inject(this, context)
val id = intent.getLongExtra("id", 0L)
when (resultCode) {
// TODO notify about delivery
Activity.RESULT_OK -> {
val pendingResult = goAsync()
markDelivered.execute(id) { pendingResult.finish() }
}
// TODO notify about delivery failure
Activity.RESULT_CANCELED -> {
val pendingResult = goAsync()
markDeliveryFailed.execute(MarkDeliveryFailed.Params(id, resultCode)) { pendingResult.finish() }
}
}
}
}
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import dagger.android.AndroidInjection
import interactor.SyncMessage
import javax.inject.Inject
/**
* When the SMS contentprovider is changed by a process other than us, we need to sync the Uri that
* was changed.
*
* This can happen if a message is sent through something like Pushbullet or Google Assistant.
*
* This only works on API 24+, so to fully solve this problem we'll need a smarter way of running
* partial syncs on older devices.
*
* https://developer.android.com/reference/android/provider/Telephony.Sms.Intents.html#ACTION_EXTERNAL_PROVIDER_CHANGE
*/
class SmsProviderChangedReceiver : BroadcastReceiver() {
@Inject lateinit var syncMessage: SyncMessage
override fun onReceive(context: Context, intent: Intent) {
AndroidInjection.inject(this, context)
// Obtain the uri for the changed data
// If the value is null, don't continue
val uri = intent.data ?: return
// Sync the message to our realm
val pendingResult = goAsync()
syncMessage.execute(uri) { pendingResult.finish() }
}
}
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.provider.Telephony.Sms
import dagger.android.AndroidInjection
import interactor.ReceiveSms
import javax.inject.Inject
class SmsReceiver : BroadcastReceiver() {
@Inject lateinit var receiveMessage: ReceiveSms
override fun onReceive(context: Context, intent: Intent) {
AndroidInjection.inject(this, context)
Sms.Intents.getMessagesFromIntent(intent)?.let { messages ->
val pendingResult = goAsync()
receiveMessage.execute(messages, { pendingResult.finish() })
}
}
}
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package receiver
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.telephony.SmsManager
import dagger.android.AndroidInjection
import interactor.MarkFailed
import interactor.MarkSent
import javax.inject.Inject
class SmsSentReceiver : BroadcastReceiver() {
@Inject lateinit var markSent: MarkSent
@Inject lateinit var markFailed: MarkFailed
override fun onReceive(context: Context, intent: Intent) {
AndroidInjection.inject(this, context)
val id = intent.getLongExtra("id", 0L)
when (resultCode) {
Activity.RESULT_OK -> {
val pendingResult = goAsync()
markSent.execute(id) { pendingResult.finish() }
}
SmsManager.RESULT_ERROR_GENERIC_FAILURE,
SmsManager.RESULT_ERROR_NO_SERVICE,
SmsManager.RESULT_ERROR_NULL_PDU,
SmsManager.RESULT_ERROR_RADIO_OFF -> {
val pendingResult = goAsync()
markFailed.execute(MarkFailed.Params(id, resultCode)) { pendingResult.finish() }
}
}
}
}
@@ -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<Conversation> {
@@ -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<String>, body: String, attachments: List<Attachment>) {
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))
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package service
import android.app.IntentService
import android.content.Intent
import android.net.Uri
import android.telephony.TelephonyManager
import dagger.android.AndroidInjection
import interactor.SendMessage
import repository.MessageRepository
import javax.inject.Inject
class HeadlessSmsSendService : IntentService("HeadlessSmsSendService") {
@Inject lateinit var messageRepo: MessageRepository
@Inject lateinit var sendMessage: SendMessage
override fun onHandleIntent(intent: Intent?) {
if (intent?.action != TelephonyManager.ACTION_RESPOND_VIA_MESSAGE) return
AndroidInjection.inject(this)
intent.extras?.getString(Intent.EXTRA_TEXT)?.takeIf { it.isNotBlank() }?.let { body ->
val intentUri = intent.data
val recipients = getRecipients(intentUri).split(";")
val threadId = messageRepo.getOrCreateConversation(recipients).blockingGet()?.id ?: 0L
sendMessage.execute(SendMessage.Params(threadId, recipients, body))
}
}
private fun getRecipients(uri: Uri): String {
val base = uri.schemeSpecificPart
val position = base.indexOf('?')
return if (position == -1) base else base.substring(0, position)
}
}
+130
View File
@@ -0,0 +1,130 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package util
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import receiver.NightModeReceiver
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class NightModeManager @Inject constructor(
private val context: Context,
private val prefs: Preferences) {
fun updateCurrentTheme() {
// If night mode is not on auto, then there's nothing to do here
if (prefs.nightMode.get() != Preferences.NIGHT_MODE_AUTO) {
return
}
val nightStartTime = getPreviousInstanceOfTime(prefs.nightStart.get())
val nightEndTime = getPreviousInstanceOfTime(prefs.nightEnd.get())
// If the last nightStart was more recent than the last nightEnd, then it's night time
prefs.night.set(nightStartTime > nightEndTime)
}
fun updateNightMode(mode: Int) {
prefs.nightMode.set(mode)
// If it's not on auto mode, set the appropriate night mode
if (mode != Preferences.NIGHT_MODE_AUTO) {
prefs.night.set(mode == Preferences.NIGHT_MODE_ON)
}
updateAlarms()
}
fun setNightStart(hour: Int, minute: Int) {
prefs.nightStart.set("$hour:$minute")
updateAlarms()
}
fun setNightEnd(hour: Int, minute: Int) {
prefs.nightEnd.set("$hour:$minute")
updateAlarms()
}
private fun updateAlarms() {
val dayCalendar = createCalendar(prefs.nightEnd.get())
val day = Intent(context, NightModeReceiver::class.java)
val dayIntent = PendingIntent.getBroadcast(context, 0, day, 0)
val nightCalendar = createCalendar(prefs.nightStart.get())
val night = Intent(context, NightModeReceiver::class.java)
val nightIntent = PendingIntent.getBroadcast(context, 1, night, 0)
context.sendBroadcast(day)
context.sendBroadcast(night)
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
if (prefs.nightMode.get() == Preferences.NIGHT_MODE_AUTO) {
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, dayCalendar.timeInMillis, AlarmManager.INTERVAL_DAY, dayIntent)
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, nightCalendar.timeInMillis, AlarmManager.INTERVAL_DAY, nightIntent)
} else {
alarmManager.cancel(dayIntent)
alarmManager.cancel(nightIntent)
}
}
private fun createCalendar(time: String): Calendar {
val calendar = parseTime(time)
return Calendar.getInstance().apply {
set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY))
set(Calendar.MINUTE, calendar.get(Calendar.MINUTE))
}
}
/**
* Parses the hour and minute out of the [time], which should be formatted h:mm
*/
fun parseTime(time: String): Calendar {
return tryOrNull {
val parsedTime = SimpleDateFormat("H:mm", Locale.US).parse(time)
Calendar.getInstance().apply { this.time = parsedTime }
} ?: tryOrNull {
// Parse the legacy timestamp format (<=3.1.3)
val parsedTime = SimpleDateFormat("h:mm a", Locale.US).parse(time)
Calendar.getInstance().apply { this.time = parsedTime }
} ?: Calendar.getInstance().apply { set(Calendar.HOUR_OF_DAY, 18) }
}
/**
* Returns a Calendar set to the most recent occurrence of this time
*/
private fun getPreviousInstanceOfTime(time: String): Calendar {
val currentTime = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(10)
val calendar = createCalendar(time)
while (calendar.timeInMillis > currentTime) {
calendar.add(Calendar.DAY_OF_YEAR, -1)
}
return calendar
}
}