Merge pull request #353 from FossifyOrg/remove_storage_permissions
Removed storage permission requirement
This commit is contained in:
commit
9d45c9d81f
11 changed files with 168 additions and 153 deletions
|
|
@ -15,10 +15,6 @@
|
|||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission
|
||||
android:name="android.permission.USE_BIOMETRIC"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package org.fossify.messages.activities
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.role.RoleManager
|
||||
import android.content.Intent
|
||||
import android.content.pm.ShortcutInfo
|
||||
|
|
@ -208,7 +207,7 @@ class MainActivity : SimpleActivity() {
|
|||
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, resultData)
|
||||
if (requestCode == MAKE_DEFAULT_APP_REQUEST) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
askPermissions()
|
||||
} else {
|
||||
finish()
|
||||
|
|
@ -251,7 +250,8 @@ class MainActivity : SimpleActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
// while SEND_SMS and READ_SMS permissions are mandatory, READ_CONTACTS is optional. If we don't have it, we just won't be able to show the contact name in some cases
|
||||
// while SEND_SMS and READ_SMS permissions are mandatory, READ_CONTACTS is optional.
|
||||
// If we don't have it, we just won't be able to show the contact name in some cases
|
||||
private fun askPermissions() {
|
||||
handlePermission(PERMISSION_READ_SMS) {
|
||||
if (it) {
|
||||
|
|
@ -271,7 +271,7 @@ class MainActivity : SimpleActivity() {
|
|||
bus = EventBus.getDefault()
|
||||
try {
|
||||
bus!!.register(this)
|
||||
} catch (ignored: Exception) {
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -302,20 +302,22 @@ class MainActivity : SimpleActivity() {
|
|||
ensureBackgroundThread {
|
||||
val conversations = try {
|
||||
conversationsDB.getNonArchived().toMutableList() as ArrayList<Conversation>
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
ArrayList()
|
||||
}
|
||||
|
||||
val archived = try {
|
||||
conversationsDB.getAllArchived()
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
listOf()
|
||||
}
|
||||
|
||||
updateUnreadCountBadge(conversations)
|
||||
runOnUiThread {
|
||||
setupConversations(conversations, cached = true)
|
||||
getNewConversations((conversations + archived).toMutableList() as ArrayList<Conversation>)
|
||||
getNewConversations(
|
||||
(conversations + archived).toMutableList() as ArrayList<Conversation>
|
||||
)
|
||||
}
|
||||
conversations.forEach {
|
||||
clearExpiredScheduledMessages(it.threadId)
|
||||
|
|
@ -349,11 +351,14 @@ class MainActivity : SimpleActivity() {
|
|||
val newConversation =
|
||||
conversations.find { it.phoneNumber == cachedConversation.phoneNumber }
|
||||
if (isTemporaryThread && newConversation != null) {
|
||||
// delete the original temporary thread and move any scheduled messages to the new thread
|
||||
// delete the original temporary thread and move any scheduled messages
|
||||
// to the new thread
|
||||
conversationsDB.deleteThreadId(threadId)
|
||||
messagesDB.getScheduledThreadMessages(threadId)
|
||||
.forEach { message ->
|
||||
messagesDB.insertOrUpdate(message.copy(threadId = newConversation.threadId))
|
||||
messagesDB.insertOrUpdate(
|
||||
message.copy(threadId = newConversation.threadId)
|
||||
)
|
||||
}
|
||||
insertOrUpdateConversation(newConversation, cachedConversation)
|
||||
}
|
||||
|
|
@ -366,7 +371,8 @@ class MainActivity : SimpleActivity() {
|
|||
)
|
||||
}
|
||||
if (conv != null) {
|
||||
// FIXME: Scheduled message date is being reset here. Conversations with scheduled messages will have their original date.
|
||||
// FIXME: Scheduled message date is being reset here. Conversations with
|
||||
// scheduled messages will have their original date.
|
||||
insertOrUpdateConversation(conv)
|
||||
}
|
||||
}
|
||||
|
|
@ -412,15 +418,18 @@ class MainActivity : SimpleActivity() {
|
|||
|
||||
private fun setupConversations(
|
||||
conversations: ArrayList<Conversation>,
|
||||
cached: Boolean = false
|
||||
cached: Boolean = false,
|
||||
) {
|
||||
val sortedConversations = conversations.sortedWith(
|
||||
compareByDescending<Conversation> { config.pinnedConversations.contains(it.threadId.toString()) }
|
||||
.thenByDescending { it.date }
|
||||
).toMutableList() as ArrayList<Conversation>
|
||||
val sortedConversations = conversations
|
||||
.sortedWith(
|
||||
compareByDescending<Conversation> {
|
||||
config.pinnedConversations.contains(it.threadId.toString())
|
||||
}.thenByDescending { it.date }
|
||||
).toMutableList() as ArrayList<Conversation>
|
||||
|
||||
if (cached && config.appRunCount == 1) {
|
||||
// there are no cached conversations on the first run so we show the loading placeholder and progress until we are done loading from telephony
|
||||
// there are no cached conversations on the first run so we show the
|
||||
// loading placeholder and progress until we are done loading from telephony
|
||||
showOrHideProgress(conversations.isEmpty())
|
||||
} else {
|
||||
showOrHideProgress(false)
|
||||
|
|
@ -435,7 +444,7 @@ class MainActivity : SimpleActivity() {
|
|||
}
|
||||
}
|
||||
}
|
||||
} catch (ignored: Exception) {
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -498,7 +507,7 @@ class MainActivity : SimpleActivity() {
|
|||
try {
|
||||
manager.dynamicShortcuts = listOf(newConversation)
|
||||
config.lastHandledShortcutColor = appIconColor
|
||||
} catch (ignored: Exception) {
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,24 +5,17 @@ import android.net.Uri
|
|||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import org.fossify.commons.dialogs.ExportBlockedNumbersDialog
|
||||
import org.fossify.commons.dialogs.FilePickerDialog
|
||||
import org.fossify.commons.extensions.beVisibleIf
|
||||
import org.fossify.commons.extensions.getFileOutputStream
|
||||
import org.fossify.commons.extensions.getProperPrimaryColor
|
||||
import org.fossify.commons.extensions.getTempFile
|
||||
import org.fossify.commons.extensions.showErrorToast
|
||||
import org.fossify.commons.extensions.toFileDirItem
|
||||
import org.fossify.commons.extensions.toast
|
||||
import org.fossify.commons.extensions.underlineText
|
||||
import org.fossify.commons.extensions.updateTextColors
|
||||
import org.fossify.commons.extensions.viewBinding
|
||||
import org.fossify.commons.helpers.ExportResult
|
||||
import org.fossify.commons.helpers.NavigationIcon
|
||||
import org.fossify.commons.helpers.PERMISSION_READ_STORAGE
|
||||
import org.fossify.commons.helpers.PERMISSION_WRITE_STORAGE
|
||||
import org.fossify.commons.helpers.ensureBackgroundThread
|
||||
import org.fossify.commons.helpers.isQPlus
|
||||
import org.fossify.commons.interfaces.RefreshRecyclerViewListener
|
||||
import org.fossify.messages.R
|
||||
import org.fossify.messages.databinding.ActivityManageBlockedKeywordsBinding
|
||||
|
|
@ -96,7 +89,7 @@ class ManageBlockedKeywordsActivity : SimpleActivity(), RefreshRecyclerViewListe
|
|||
}
|
||||
}
|
||||
|
||||
private val exportActivityResultLauncher =
|
||||
private val createDocument =
|
||||
registerForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) { uri ->
|
||||
try {
|
||||
val outputStream = uri?.let { contentResolver.openOutputStream(it) }
|
||||
|
|
@ -108,7 +101,7 @@ class ManageBlockedKeywordsActivity : SimpleActivity(), RefreshRecyclerViewListe
|
|||
}
|
||||
}
|
||||
|
||||
private val importActivityResultLauncher =
|
||||
private val getContent =
|
||||
registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
|
||||
try {
|
||||
if (uri != null) {
|
||||
|
|
@ -120,27 +113,13 @@ class ManageBlockedKeywordsActivity : SimpleActivity(), RefreshRecyclerViewListe
|
|||
}
|
||||
|
||||
private fun tryImportBlockedKeywords() {
|
||||
if (isQPlus()) {
|
||||
val mimeType = "text/plain"
|
||||
try {
|
||||
importActivityResultLauncher.launch(mimeType)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
toast(org.fossify.commons.R.string.system_service_disabled, Toast.LENGTH_LONG)
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
}
|
||||
} else {
|
||||
handlePermission(PERMISSION_READ_STORAGE) { isAllowed ->
|
||||
if (isAllowed) {
|
||||
pickFileToImportBlockedKeywords()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun pickFileToImportBlockedKeywords() {
|
||||
FilePickerDialog(this) {
|
||||
importBlockedKeywords(it)
|
||||
val mimeType = "text/plain"
|
||||
try {
|
||||
getContent.launch(mimeType)
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
toast(org.fossify.commons.R.string.system_service_disabled, Toast.LENGTH_LONG)
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -200,32 +179,20 @@ class ManageBlockedKeywordsActivity : SimpleActivity(), RefreshRecyclerViewListe
|
|||
}
|
||||
|
||||
private fun tryExportBlockedNumbers() {
|
||||
if (isQPlus()) {
|
||||
ExportBlockedKeywordsDialog(this, config.lastBlockedKeywordExportPath, true) { file ->
|
||||
try {
|
||||
exportActivityResultLauncher.launch(file.name)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
toast(
|
||||
org.fossify.commons.R.string.system_service_disabled,
|
||||
Toast.LENGTH_LONG
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
handlePermission(PERMISSION_WRITE_STORAGE) { isAllowed ->
|
||||
if (isAllowed) {
|
||||
ExportBlockedNumbersDialog(
|
||||
this,
|
||||
config.lastBlockedKeywordExportPath,
|
||||
false
|
||||
) { file ->
|
||||
getFileOutputStream(file.toFileDirItem(this), true) { out ->
|
||||
exportBlockedKeywordsTo(out)
|
||||
}
|
||||
}
|
||||
}
|
||||
ExportBlockedKeywordsDialog(
|
||||
activity = this,
|
||||
path = config.lastBlockedKeywordExportPath,
|
||||
hidePath = true
|
||||
) { file ->
|
||||
try {
|
||||
createDocument.launch(file.name)
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
toast(
|
||||
org.fossify.commons.R.string.system_service_disabled,
|
||||
Toast.LENGTH_LONG
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,8 +89,9 @@ abstract class BaseConversationsAdapter(
|
|||
|
||||
override fun getSelectableItemCount() = itemCount
|
||||
|
||||
protected fun getSelectedItems() =
|
||||
currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
|
||||
protected fun getSelectedItems() = currentList.filter {
|
||||
selectedKeys.contains(it.hashCode())
|
||||
} as ArrayList<Conversation>
|
||||
|
||||
override fun getIsItemSelectable(position: Int) = true
|
||||
|
||||
|
|
@ -143,7 +144,9 @@ abstract class BaseConversationsAdapter(
|
|||
draftIndicator.beVisibleIf(!smsDraft.isNullOrEmpty())
|
||||
draftIndicator.setTextColor(properPrimaryColor)
|
||||
|
||||
pinIndicator.beVisibleIf(activity.config.pinnedConversations.contains(conversation.threadId.toString()))
|
||||
pinIndicator.beVisibleIf(
|
||||
activity.config.pinnedConversations.contains(conversation.threadId.toString())
|
||||
)
|
||||
pinIndicator.applyColorFilter(textColor)
|
||||
|
||||
conversationFrame.isSelected = selectedKeys.contains(conversation.hashCode())
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import org.fossify.commons.extensions.setupDialogStuff
|
|||
import org.fossify.commons.extensions.showErrorToast
|
||||
import org.fossify.commons.extensions.toast
|
||||
import org.fossify.commons.extensions.value
|
||||
import org.fossify.commons.helpers.MEDIUM_ALPHA
|
||||
import org.fossify.commons.helpers.ensureBackgroundThread
|
||||
import org.fossify.messages.R
|
||||
import org.fossify.messages.activities.SimpleActivity
|
||||
|
|
@ -72,7 +73,7 @@ class ExportMessagesDialog(
|
|||
getButton(AlertDialog.BUTTON_NEGATIVE)
|
||||
).forEach {
|
||||
it.isEnabled = false
|
||||
it.alpha = 0.6f
|
||||
it.alpha = MEDIUM_ALPHA
|
||||
}
|
||||
|
||||
binding.exportProgress.setIndicatorColor(activity.getProperPrimaryColor())
|
||||
|
|
@ -112,7 +113,7 @@ class ExportMessagesDialog(
|
|||
// delete the file to avoid leaving behind an empty/corrupt file
|
||||
try {
|
||||
DocumentsContract.deleteDocument(activity.contentResolver, uri)
|
||||
} catch (ignored: Exception) {
|
||||
} catch (_: Exception) {
|
||||
// ignored because we don't want to show two error messages
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import org.fossify.commons.extensions.getAlertDialogBuilder
|
|||
import org.fossify.commons.extensions.getProperPrimaryColor
|
||||
import org.fossify.commons.extensions.setupDialogStuff
|
||||
import org.fossify.commons.extensions.toast
|
||||
import org.fossify.commons.helpers.MEDIUM_ALPHA
|
||||
import org.fossify.commons.helpers.ensureBackgroundThread
|
||||
import org.fossify.messages.R
|
||||
import org.fossify.messages.activities.SimpleActivity
|
||||
|
|
@ -65,7 +66,7 @@ class ImportMessagesDialog(
|
|||
negativeButton
|
||||
).forEach {
|
||||
it.isEnabled = false
|
||||
it.alpha = 0.6f
|
||||
it.alpha = MEDIUM_ALPHA
|
||||
}
|
||||
|
||||
ensureBackgroundThread {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ fun Activity.dialNumber(phoneNumber: String, callback: (() -> Unit)? = null) {
|
|||
try {
|
||||
startActivity(this)
|
||||
callback?.invoke()
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
toast(org.fossify.commons.R.string.no_app_found)
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
|
|
@ -46,7 +46,7 @@ fun Activity.launchViewIntent(uri: Uri, mimetype: String, filename: String) {
|
|||
try {
|
||||
hideKeyboard()
|
||||
startActivity(this)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
val newMimetype = filename.getMimeType()
|
||||
if (newMimetype.isNotEmpty() && mimetype != newMimetype) {
|
||||
launchViewIntent(uri, newMimetype, filename)
|
||||
|
|
@ -110,4 +110,4 @@ fun Activity.launchConversationDetails(threadId: Long) {
|
|||
putExtra(THREAD_ID, threadId)
|
||||
startActivity(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,32 +78,41 @@ import org.fossify.messages.models.RecycleBinMessage
|
|||
import org.xmlpull.v1.XmlPullParserException
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
val Context.config: Config get() = Config.newInstance(applicationContext)
|
||||
val Context.config: Config
|
||||
get() = Config.newInstance(applicationContext)
|
||||
|
||||
fun Context.getMessagesDB() = MessagesDatabase.getInstance(this)
|
||||
|
||||
val Context.conversationsDB: ConversationsDao get() = getMessagesDB().ConversationsDao()
|
||||
val Context.conversationsDB: ConversationsDao
|
||||
get() = getMessagesDB().ConversationsDao()
|
||||
|
||||
val Context.attachmentsDB: AttachmentsDao get() = getMessagesDB().AttachmentsDao()
|
||||
val Context.attachmentsDB: AttachmentsDao
|
||||
get() = getMessagesDB().AttachmentsDao()
|
||||
|
||||
val Context.messageAttachmentsDB: MessageAttachmentsDao get() = getMessagesDB().MessageAttachmentsDao()
|
||||
val Context.messageAttachmentsDB: MessageAttachmentsDao
|
||||
get() = getMessagesDB().MessageAttachmentsDao()
|
||||
|
||||
val Context.messagesDB: MessagesDao get() = getMessagesDB().MessagesDao()
|
||||
val Context.messagesDB: MessagesDao
|
||||
get() = getMessagesDB().MessagesDao()
|
||||
|
||||
val Context.draftsDB: DraftsDao get() = getMessagesDB().DraftsDao()
|
||||
val Context.draftsDB: DraftsDao
|
||||
get() = getMessagesDB().DraftsDao()
|
||||
|
||||
val Context.notificationHelper get() = NotificationHelper(this)
|
||||
val Context.notificationHelper
|
||||
get() = NotificationHelper(this)
|
||||
|
||||
val Context.messagingUtils get() = MessagingUtils(this)
|
||||
val Context.messagingUtils
|
||||
get() = MessagingUtils(this)
|
||||
|
||||
val Context.smsSender get() = SmsSender.getInstance(applicationContext as Application)
|
||||
val Context.smsSender
|
||||
get() = SmsSender.getInstance(applicationContext as Application)
|
||||
|
||||
fun Context.getMessages(
|
||||
threadId: Long,
|
||||
getImageResolutions: Boolean,
|
||||
dateFrom: Int = -1,
|
||||
includeScheduledMessages: Boolean = true,
|
||||
limit: Int = MESSAGES_LIMIT
|
||||
limit: Int = MESSAGES_LIMIT,
|
||||
): ArrayList<Message> {
|
||||
val uri = Sms.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
|
|
@ -212,7 +221,7 @@ fun Context.getMMS(
|
|||
threadId: Long? = null,
|
||||
getImageResolutions: Boolean = false,
|
||||
sortOrder: String? = null,
|
||||
dateFrom: Int = -1
|
||||
dateFrom: Int = -1,
|
||||
): ArrayList<Message> {
|
||||
val uri = Mms.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
|
|
@ -229,8 +238,8 @@ fun Context.getMMS(
|
|||
var selectionArgs: Array<String>? = null
|
||||
|
||||
if (threadId == null && dateFrom != -1) {
|
||||
selection =
|
||||
"${Sms.DATE} < ${dateFrom.toLong()}" //Should not multiply 1000 here, because date in mms's database is different from sms's.
|
||||
// Should not multiply 1000 here, because date in mms's database is different from sms's.
|
||||
selection = "${Sms.DATE} < ${dateFrom.toLong()}"
|
||||
} else if (threadId != null && dateFrom == -1) {
|
||||
selection = "${Sms.THREAD_ID} = ?"
|
||||
selectionArgs = arrayOf(threadId.toString())
|
||||
|
|
@ -315,18 +324,18 @@ fun Context.getMMSSender(msgId: Long): String {
|
|||
return it.getStringValue(Mms.Addr.ADDRESS)
|
||||
}
|
||||
}
|
||||
} catch (ignored: Exception) {
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
fun Context.getConversations(
|
||||
threadId: Long? = null,
|
||||
privateContacts: ArrayList<SimpleContact> = ArrayList()
|
||||
privateContacts: ArrayList<SimpleContact> = ArrayList(),
|
||||
): ArrayList<Conversation> {
|
||||
val archiveAvailable = config.isArchiveAvailable
|
||||
|
||||
val uri = Uri.parse("${Threads.CONTENT_URI}?simple=true")
|
||||
val uri = "${Threads.CONTENT_URI}?simple=true".toUri()
|
||||
val projection = mutableListOf(
|
||||
Threads._ID,
|
||||
Threads.SNIPPET,
|
||||
|
|
@ -413,7 +422,10 @@ fun Context.getConversations(
|
|||
conversations.add(conversation)
|
||||
}
|
||||
} catch (sqliteException: SQLiteException) {
|
||||
if (sqliteException.message?.contains("no such column: archived") == true && archiveAvailable) {
|
||||
if (
|
||||
sqliteException.message?.contains("no such column: archived") == true
|
||||
&& archiveAvailable
|
||||
) {
|
||||
config.isArchiveAvailable = false
|
||||
return getConversations(threadId, privateContacts)
|
||||
} else {
|
||||
|
|
@ -433,7 +445,7 @@ private fun Context.queryCursorUnsafe(
|
|||
selection: String? = null,
|
||||
selectionArgs: Array<String>? = null,
|
||||
sortOrder: String? = null,
|
||||
callback: (cursor: Cursor) -> Unit
|
||||
callback: (cursor: Cursor) -> Unit,
|
||||
) {
|
||||
val cursor = contentResolver.query(uri, projection, selection, selectionArgs, sortOrder)
|
||||
cursor?.use {
|
||||
|
|
@ -446,7 +458,7 @@ private fun Context.queryCursorUnsafe(
|
|||
}
|
||||
|
||||
fun Context.getConversationIds(): List<Long> {
|
||||
val uri = Uri.parse("${Threads.CONTENT_URI}?simple=true")
|
||||
val uri = "${Threads.CONTENT_URI}?simple=true".toUri()
|
||||
val projection = arrayOf(Threads._ID)
|
||||
val selection = "${Threads.MESSAGE_COUNT} > 0"
|
||||
val sortOrder = "${Threads.DATE} ASC"
|
||||
|
|
@ -464,7 +476,7 @@ fun Context.getMmsAttachment(id: Long, getImageResolutions: Boolean): MessageAtt
|
|||
val uri = if (isQPlus()) {
|
||||
Mms.Part.CONTENT_URI
|
||||
} else {
|
||||
Uri.parse("content://mms/part")
|
||||
"content://mms/part".toUri()
|
||||
}
|
||||
|
||||
val projection = arrayOf(
|
||||
|
|
@ -502,12 +514,21 @@ fun Context.getMmsAttachment(id: Long, getImageResolutions: Boolean): MessageAtt
|
|||
)
|
||||
width = options.outWidth
|
||||
height = options.outHeight
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
|
||||
val attachment = Attachment(partId, id, fileUri.toString(), mimetype, width, height, "")
|
||||
messageAttachment.attachments.add(attachment)
|
||||
messageAttachment.attachments.add(
|
||||
Attachment(
|
||||
id = partId,
|
||||
messageId = id,
|
||||
uriString = fileUri.toString(),
|
||||
mimetype = mimetype,
|
||||
width = width,
|
||||
height = height,
|
||||
filename = ""
|
||||
)
|
||||
)
|
||||
} else if (mimetype != "application/smil") {
|
||||
val attachmentName = attachmentNames?.getOrNull(attachmentCount) ?: ""
|
||||
val attachment = Attachment(
|
||||
|
|
@ -562,7 +583,7 @@ fun Context.getThreadSnippet(threadId: Long): String {
|
|||
snippet = cursor.getStringValue(Sms.BODY)
|
||||
}
|
||||
}
|
||||
} catch (ignored: Exception) {
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
return snippet
|
||||
}
|
||||
|
|
@ -583,7 +604,7 @@ fun Context.getMessageRecipientAddress(messageId: Long): String {
|
|||
return cursor.getStringValue(Sms.ADDRESS)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
|
||||
return ""
|
||||
|
|
@ -591,9 +612,9 @@ fun Context.getMessageRecipientAddress(messageId: Long): String {
|
|||
|
||||
fun Context.getThreadParticipants(
|
||||
threadId: Long,
|
||||
contactsMap: HashMap<Int, SimpleContact>?
|
||||
contactsMap: HashMap<Int, SimpleContact>?,
|
||||
): ArrayList<SimpleContact> {
|
||||
val uri = Uri.parse("${MmsSms.CONTENT_CONVERSATIONS_URI}?simple=true")
|
||||
val uri = "${MmsSms.CONTENT_CONVERSATIONS_URI}?simple=true".toUri()
|
||||
val projection = arrayOf(
|
||||
ThreadsColumns.RECIPIENT_IDS
|
||||
)
|
||||
|
|
@ -646,7 +667,7 @@ fun Context.getThreadPhoneNumbers(recipientIds: List<Int>): ArrayList<String> {
|
|||
|
||||
fun Context.getThreadContactNames(
|
||||
phoneNumbers: List<String>,
|
||||
privateContacts: ArrayList<SimpleContact>
|
||||
privateContacts: ArrayList<SimpleContact>,
|
||||
): ArrayList<String> {
|
||||
val names = ArrayList<String>()
|
||||
phoneNumbers.forEach { number ->
|
||||
|
|
@ -686,7 +707,9 @@ fun Context.getPhoneNumberFromAddressId(canonicalAddressId: Int): String {
|
|||
return ""
|
||||
}
|
||||
|
||||
fun Context.getSuggestedContacts(privateContacts: ArrayList<SimpleContact>): ArrayList<SimpleContact> {
|
||||
fun Context.getSuggestedContacts(
|
||||
privateContacts: ArrayList<SimpleContact>,
|
||||
): ArrayList<SimpleContact> {
|
||||
val contacts = ArrayList<SimpleContact>()
|
||||
val uri = Sms.CONTENT_URI
|
||||
val projection = arrayOf(
|
||||
|
|
@ -705,8 +728,9 @@ fun Context.getSuggestedContacts(privateContacts: ArrayList<SimpleContact>): Arr
|
|||
return@queryCursor
|
||||
} else if (namePhoto.name == senderNumber) {
|
||||
if (privateContacts.isNotEmpty()) {
|
||||
val privateContact =
|
||||
privateContacts.firstOrNull { it.phoneNumbers.first().normalizedNumber == senderNumber }
|
||||
val privateContact = privateContacts.firstOrNull {
|
||||
it.phoneNumbers.first().normalizedNumber == senderNumber
|
||||
}
|
||||
if (privateContact != null) {
|
||||
senderName = privateContact.name
|
||||
photoUri = privateContact.photoUri
|
||||
|
|
@ -757,7 +781,7 @@ fun Context.getNameAndPhotoFromPhoneNumber(number: String): NamePhoto {
|
|||
return NamePhoto(name, photoUri)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
|
||||
return NamePhoto(number, null)
|
||||
|
|
@ -771,7 +795,7 @@ fun Context.insertNewSMS(
|
|||
read: Int,
|
||||
threadId: Long,
|
||||
type: Int,
|
||||
subscriptionId: Int
|
||||
subscriptionId: Int,
|
||||
): Long {
|
||||
val uri = Sms.CONTENT_URI
|
||||
val contentValues = ContentValues().apply {
|
||||
|
|
@ -788,7 +812,7 @@ fun Context.insertNewSMS(
|
|||
return try {
|
||||
val newUri = contentResolver.insert(uri, contentValues)
|
||||
newUri?.lastPathSegment?.toLong() ?: 0L
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
0L
|
||||
}
|
||||
}
|
||||
|
|
@ -801,7 +825,7 @@ fun Context.removeAllArchivedConversations(callback: (() -> Unit)? = null) {
|
|||
}
|
||||
toast(R.string.archive_emptied_successfully)
|
||||
callback?.invoke()
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
toast(org.fossify.commons.R.string.unknown_error_occurred)
|
||||
}
|
||||
}
|
||||
|
|
@ -834,15 +858,20 @@ fun Context.deleteConversation(threadId: Long) {
|
|||
}
|
||||
|
||||
fun Context.checkAndDeleteOldRecycleBinMessages(callback: (() -> Unit)? = null) {
|
||||
if (config.useRecycleBin && config.lastRecycleBinCheck < System.currentTimeMillis() - DAY_SECONDS * 1000) {
|
||||
if (
|
||||
config.useRecycleBin
|
||||
&& config.lastRecycleBinCheck < System.currentTimeMillis() - DAY_SECONDS * 1000
|
||||
) {
|
||||
config.lastRecycleBinCheck = System.currentTimeMillis()
|
||||
ensureBackgroundThread {
|
||||
try {
|
||||
for (message in messagesDB.getOldRecycleBinMessages(System.currentTimeMillis() - MONTH_SECONDS * 1000L)) {
|
||||
messagesDB.getOldRecycleBinMessages(
|
||||
timestamp = System.currentTimeMillis() - MONTH_SECONDS * 1000L
|
||||
).forEach { message ->
|
||||
deleteMessage(message.id, message.isMMS)
|
||||
}
|
||||
callback?.invoke()
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -892,7 +921,10 @@ fun Context.updateConversationArchivedStatus(threadId: Long, archived: Boolean)
|
|||
try {
|
||||
contentResolver.update(uri, values, selection, selectionArgs)
|
||||
} catch (sqliteException: SQLiteException) {
|
||||
if (sqliteException.message?.contains("no such column: archived") == true && config.isArchiveAvailable) {
|
||||
if (
|
||||
sqliteException.message?.contains("no such column: archived") == true
|
||||
&& config.isArchiveAvailable
|
||||
) {
|
||||
config.isArchiveAvailable = false
|
||||
return
|
||||
} else {
|
||||
|
|
@ -976,7 +1008,7 @@ fun Context.updateUnreadCountBadge(conversations: List<Conversation>) {
|
|||
fun Context.getThreadId(address: String): Long {
|
||||
return try {
|
||||
Threads.getOrCreateThreadId(this, address)
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
0L
|
||||
}
|
||||
}
|
||||
|
|
@ -985,7 +1017,7 @@ fun Context.getThreadId(address: String): Long {
|
|||
fun Context.getThreadId(addresses: Set<String>): Long {
|
||||
return try {
|
||||
Threads.getOrCreateThreadId(this, addresses)
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
0L
|
||||
}
|
||||
}
|
||||
|
|
@ -995,7 +1027,7 @@ fun Context.showReceivedMessageNotification(
|
|||
address: String,
|
||||
body: String,
|
||||
threadId: Long,
|
||||
bitmap: Bitmap?
|
||||
bitmap: Bitmap?,
|
||||
) {
|
||||
val privateCursor = getMyContactsCursor(favoritesOnly = false, withPhoneNumbersOnly = true)
|
||||
ensureBackgroundThread {
|
||||
|
|
@ -1055,7 +1087,7 @@ fun Context.getNotificationBitmap(photoUri: String): Bitmap? {
|
|||
.apply(RequestOptions.circleCropTransform())
|
||||
.into(size, size)
|
||||
.get()
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
@ -1067,7 +1099,7 @@ fun Context.removeDiacriticsIfNeeded(text: String): String {
|
|||
fun Context.getSmsDraft(threadId: Long): String {
|
||||
val draft = try {
|
||||
draftsDB.getDraftById(threadId)
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
|
|
@ -1145,14 +1177,14 @@ fun Context.updateLastConversationMessage(threadIds: Iterable<Long>) {
|
|||
val newConversation = getConversations(threadId)[0]
|
||||
insertOrUpdateConversation(newConversation)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.getFileSizeFromUri(uri: Uri): Long {
|
||||
val assetFileDescriptor = try {
|
||||
contentResolver.openAssetFileDescriptor(uri, "r")
|
||||
} catch (e: FileNotFoundException) {
|
||||
} catch (_: FileNotFoundException) {
|
||||
null
|
||||
}
|
||||
|
||||
|
|
@ -1205,12 +1237,14 @@ fun Context.subscriptionManagerCompat(): SubscriptionManager {
|
|||
|
||||
fun Context.insertOrUpdateConversation(
|
||||
conversation: Conversation,
|
||||
cachedConv: Conversation? = conversationsDB.getConversationWithThreadId(conversation.threadId)
|
||||
cachedConv: Conversation? = conversationsDB.getConversationWithThreadId(conversation.threadId),
|
||||
) {
|
||||
var updatedConv = conversation
|
||||
if (cachedConv != null && cachedConv.usesCustomTitle) {
|
||||
updatedConv =
|
||||
updatedConv.copy(title = cachedConv.title, usesCustomTitle = cachedConv.usesCustomTitle)
|
||||
updatedConv = updatedConv.copy(
|
||||
title = cachedConv.title,
|
||||
usesCustomTitle = true
|
||||
)
|
||||
}
|
||||
conversationsDB.insertOrUpdate(updatedConv)
|
||||
}
|
||||
|
|
@ -1228,12 +1262,16 @@ fun Context.renameConversation(conversation: Conversation, newTitle: String): Co
|
|||
fun Context.createTemporaryThread(
|
||||
message: Message,
|
||||
threadId: Long = generateRandomId(),
|
||||
cachedConv: Conversation?
|
||||
cachedConv: Conversation?,
|
||||
) {
|
||||
val simpleContactHelper = SimpleContactsHelper(this)
|
||||
val addresses = message.participants.getAddresses()
|
||||
val photoUri =
|
||||
if (addresses.size == 1) simpleContactHelper.getPhotoUriFromPhoneNumber(addresses.first()) else ""
|
||||
val photoUri = if (addresses.size == 1) {
|
||||
simpleContactHelper.getPhotoUriFromPhoneNumber(addresses.first())
|
||||
} else {
|
||||
""
|
||||
}
|
||||
|
||||
val title = if (cachedConv != null && cachedConv.usesCustomTitle) {
|
||||
cachedConv.title
|
||||
} else {
|
||||
|
|
@ -1286,5 +1324,6 @@ fun Context.clearExpiredScheduledMessages(threadId: Long, messagesToDelete: List
|
|||
}
|
||||
}
|
||||
|
||||
fun Context.getDefaultKeyboardHeight() =
|
||||
resources.getDimensionPixelSize(R.dimen.default_keyboard_height)
|
||||
fun Context.getDefaultKeyboardHeight(): Int {
|
||||
return resources.getDimensionPixelSize(R.dimen.default_keyboard_height)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,8 @@ class MessagesImporter(private val activity: SimpleActivity) {
|
|||
|
||||
val messages = if (isUpsideDownCakePlus()) {
|
||||
deserializedList.map { message ->
|
||||
// workaround for messages not being imported on Android 14 when the device has a different subscriptionId (see #191)
|
||||
// workaround for messages not being imported on Android 14 when the device
|
||||
// has a different subscriptionId (see #191)
|
||||
when (message) {
|
||||
is SmsBackup -> message.copy(subscriptionId = -1)
|
||||
is MmsBackup -> message.copy(subscriptionId = -1)
|
||||
|
|
@ -70,9 +71,9 @@ class MessagesImporter(private val activity: SimpleActivity) {
|
|||
}
|
||||
|
||||
ImportMessagesDialog(activity, messages)
|
||||
} catch (e: SerializationException) {
|
||||
} catch (_: SerializationException) {
|
||||
activity.toast(org.fossify.commons.R.string.invalid_file_format)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
} catch (_: IllegalArgumentException) {
|
||||
activity.toast(org.fossify.commons.R.string.invalid_file_format)
|
||||
} catch (e: Exception) {
|
||||
activity.showErrorToast(e)
|
||||
|
|
@ -201,7 +202,7 @@ class MessagesImporter(private val activity: SimpleActivity) {
|
|||
private fun getInputStreamFromUri(uri: Uri): InputStream? {
|
||||
return try {
|
||||
activity.contentResolver.openInputStream(uri)
|
||||
} catch (e: Exception) {
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
@ -209,14 +210,12 @@ class MessagesImporter(private val activity: SimpleActivity) {
|
|||
private fun isFileXml(uri: Uri): Boolean {
|
||||
val inputStream = getInputStreamFromUri(uri)
|
||||
return inputStream?.bufferedReader()?.use { reader ->
|
||||
reader.readLine()?.startsWith("<?xml") ?: false
|
||||
} ?: false
|
||||
reader.readLine()?.startsWith("<?xml") == true
|
||||
} == true
|
||||
}
|
||||
|
||||
private fun isXmlMimeType(mimeType: String): Boolean {
|
||||
return mimeType.equals("application/xml", ignoreCase = true) || mimeType.equals(
|
||||
other = "text/xml",
|
||||
ignoreCase = true
|
||||
)
|
||||
return mimeType.equals("application/xml", ignoreCase = true)
|
||||
|| mimeType.equals(other = "text/xml", ignoreCase = true)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,4 +9,4 @@ data class Draft(
|
|||
@ColumnInfo(name = "thread_id") @PrimaryKey val threadId: Long,
|
||||
@ColumnInfo(name = "body") val body: String,
|
||||
@ColumnInfo(name = "date") val date: Long,
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ eventbus = "3.3.1"
|
|||
#Room
|
||||
room = "2.6.1"
|
||||
#Fossify
|
||||
commons = "29e9b0d13e"
|
||||
commons = "79b1077e39"
|
||||
android-smsmms = "c3e678befd"
|
||||
indicator-fast-scroll = "4524cd0b61"
|
||||
#Gradle
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue