Remove support for Android and older versions

See https://github.com/orgs/FossifyOrg/discussions/241
This commit is contained in:
Naveen Singh 2024-12-26 14:00:24 +05:30
parent 3f17c2d94c
commit 7d2b87adc1
No known key found for this signature in database
GPG key ID: AF5D43C216778C0B
108 changed files with 347 additions and 137 deletions

View file

@ -6,20 +6,31 @@ import android.content.Intent
import android.media.AudioAttributes
import android.media.AudioManager
import android.media.RingtoneManager
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import androidx.annotation.RequiresApi
import androidx.core.content.res.ResourcesCompat
import org.fossify.commons.extensions.*
import org.fossify.commons.extensions.applyColorFilter
import org.fossify.commons.extensions.beGone
import org.fossify.commons.extensions.beVisible
import org.fossify.commons.extensions.beVisibleIf
import org.fossify.commons.extensions.getProperPrimaryColor
import org.fossify.commons.extensions.getProperTextColor
import org.fossify.commons.extensions.notificationManager
import org.fossify.commons.extensions.updateTextColors
import org.fossify.commons.extensions.viewBinding
import org.fossify.commons.helpers.NavigationIcon
import org.fossify.commons.helpers.ensureBackgroundThread
import org.fossify.commons.helpers.isOreoPlus
import org.fossify.commons.models.SimpleContact
import org.fossify.messages.adapters.ContactsAdapter
import org.fossify.messages.databinding.ActivityConversationDetailsBinding
import org.fossify.messages.dialogs.RenameConversationDialog
import org.fossify.messages.extensions.*
import org.fossify.messages.extensions.config
import org.fossify.messages.extensions.conversationsDB
import org.fossify.messages.extensions.getContactFromAddress
import org.fossify.messages.extensions.getThreadParticipants
import org.fossify.messages.extensions.messagesDB
import org.fossify.messages.extensions.renameConversation
import org.fossify.messages.extensions.startContactDetailsIntent
import org.fossify.messages.helpers.THREAD_ID
import org.fossify.messages.models.Conversation
@ -42,7 +53,10 @@ class ConversationDetailsActivity : SimpleActivity() {
useTransparentNavigation = true,
useTopSearchMenu = false
)
setupMaterialScrollListener(scrollingView = binding.participantsRecyclerview, toolbar = binding.conversationDetailsToolbar)
setupMaterialScrollListener(
scrollingView = binding.participantsRecyclerview,
toolbar = binding.conversationDetailsToolbar
)
threadId = intent.getLongExtra(THREAD_ID, 0L)
ensureBackgroundThread {
@ -56,9 +70,7 @@ class ConversationDetailsActivity : SimpleActivity() {
runOnUiThread {
setupTextViews()
setupParticipants()
if (isOreoPlus()) {
setupCustomNotifications()
}
setupCustomNotifications()
}
}
}
@ -73,7 +85,6 @@ class ConversationDetailsActivity : SimpleActivity() {
binding.membersHeading.setTextColor(primaryColor)
}
@RequiresApi(Build.VERSION_CODES.O)
private fun setupCustomNotifications() {
binding.apply {
notificationsHeading.beVisible()
@ -104,7 +115,6 @@ class ConversationDetailsActivity : SimpleActivity() {
}
}
@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel() {
val name = conversation?.title
val audioAttributes = AudioAttributes.Builder()
@ -116,27 +126,36 @@ class ConversationDetailsActivity : SimpleActivity() {
NotificationChannel(threadId.toString(), name, NotificationManager.IMPORTANCE_HIGH).apply {
setBypassDnd(false)
enableLights(true)
setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), audioAttributes)
setSound(
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION),
audioAttributes
)
enableVibration(true)
notificationManager.createNotificationChannel(this)
}
}
@RequiresApi(Build.VERSION_CODES.O)
private fun removeNotificationChannel() {
notificationManager.deleteNotificationChannel(threadId.toString())
}
private fun setupTextViews() {
binding.conversationName.apply {
ResourcesCompat.getDrawable(resources, org.fossify.commons.R.drawable.ic_edit_vector, theme)?.apply {
ResourcesCompat.getDrawable(
resources,
org.fossify.commons.R.drawable.ic_edit_vector,
theme
)?.apply {
applyColorFilter(getProperTextColor())
setCompoundDrawablesWithIntrinsicBounds(null, null, this, null)
}
text = conversation?.title
setOnClickListener {
RenameConversationDialog(this@ConversationDetailsActivity, conversation!!) { title ->
RenameConversationDialog(
this@ConversationDetailsActivity,
conversation!!
) { title ->
text = title
ensureBackgroundThread {
conversation = renameConversation(conversation!!, newTitle = title)

View file

@ -49,7 +49,6 @@ import org.fossify.commons.helpers.PERMISSION_READ_SMS
import org.fossify.commons.helpers.PERMISSION_SEND_SMS
import org.fossify.commons.helpers.SHORT_ANIMATION_DURATION
import org.fossify.commons.helpers.ensureBackgroundThread
import org.fossify.commons.helpers.isNougatMR1Plus
import org.fossify.commons.helpers.isQPlus
import org.fossify.commons.models.FAQItem
import org.fossify.commons.models.Release
@ -494,7 +493,7 @@ class MainActivity : SimpleActivity() {
@SuppressLint("NewApi")
private fun checkShortcut() {
val appIconColor = config.appIconColor
if (isNougatMR1Plus() && config.lastHandledShortcutColor != appIconColor) {
if (config.lastHandledShortcutColor != appIconColor) {
val newConversation = getCreateNewContactShortcut(appIconColor)
val manager = getSystemService(ShortcutManager::class.java)

View file

@ -1,9 +1,7 @@
package org.fossify.messages.activities
import android.annotation.TargetApi
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.DocumentsContract
import androidx.activity.result.contract.ActivityResultContracts
@ -16,7 +14,9 @@ import org.fossify.commons.dialogs.FeatureLockedDialog
import org.fossify.commons.dialogs.RadioGroupDialog
import org.fossify.commons.dialogs.SecurityDialog
import org.fossify.commons.extensions.addLockedLabelIfNeeded
import org.fossify.commons.extensions.beGone
import org.fossify.commons.extensions.beGoneIf
import org.fossify.commons.extensions.beVisible
import org.fossify.commons.extensions.beVisibleIf
import org.fossify.commons.extensions.getBlockedNumbers
import org.fossify.commons.extensions.getCustomizeColorsString
@ -36,8 +36,6 @@ import org.fossify.commons.helpers.NavigationIcon
import org.fossify.commons.helpers.PROTECTION_FINGERPRINT
import org.fossify.commons.helpers.SHOW_ALL_TABS
import org.fossify.commons.helpers.ensureBackgroundThread
import org.fossify.commons.helpers.isNougatPlus
import org.fossify.commons.helpers.isOreoPlus
import org.fossify.commons.helpers.isPiePlus
import org.fossify.commons.helpers.isTiramisuPlus
import org.fossify.commons.models.RadioItem
@ -226,7 +224,6 @@ class SettingsActivity : SimpleActivity() {
}
private fun setupCustomizeNotifications() = binding.apply {
settingsCustomizeNotificationsHolder.beVisibleIf(isOreoPlus())
settingsCustomizeNotificationsHolder.setOnClickListener {
launchCustomizeNotificationsIntent()
}
@ -247,19 +244,20 @@ class SettingsActivity : SimpleActivity() {
private fun setupLanguage() = binding.apply {
settingsLanguage.text = Locale.getDefault().displayLanguage
settingsLanguageHolder.beVisibleIf(isTiramisuPlus())
settingsLanguageHolder.setOnClickListener {
launchChangeAppLanguageIntent()
if (isTiramisuPlus()) {
settingsLanguageHolder.beVisible()
settingsLanguageHolder.setOnClickListener {
launchChangeAppLanguageIntent()
}
} else {
settingsLanguageHolder.beGone()
}
}
// support for device-wise blocking came on Android 7, rely only on that
@TargetApi(Build.VERSION_CODES.N)
private fun setupManageBlockedNumbers() = binding.apply {
settingsManageBlockedNumbers.text =
addLockedLabelIfNeeded(org.fossify.commons.R.string.manage_blocked_numbers)
settingsManageBlockedNumbersHolder.beVisibleIf(isNougatPlus())
settingsManageBlockedNumbersHolder.beVisible()
settingsManageBlockedNumbersHolder.setOnClickListener {
if (isOrWasThankYouInstalled()) {
Intent(this@SettingsActivity, ManageBlockedNumbersActivity::class.java).apply {

View file

@ -95,8 +95,6 @@ import org.fossify.commons.helpers.PERMISSION_READ_PHONE_STATE
import org.fossify.commons.helpers.SimpleContactsHelper
import org.fossify.commons.helpers.VcfExporter
import org.fossify.commons.helpers.ensureBackgroundThread
import org.fossify.commons.helpers.isNougatPlus
import org.fossify.commons.helpers.isOreoPlus
import org.fossify.commons.helpers.isSPlus
import org.fossify.commons.models.PhoneNumber
import org.fossify.commons.models.RadioItem
@ -349,7 +347,7 @@ class ThreadActivity : SimpleActivity() {
findItem(R.id.conversation_details).isVisible = conversation != null && !isRecycleBin
findItem(R.id.block_number).title =
addLockedLabelIfNeeded(org.fossify.commons.R.string.block_number)
findItem(R.id.block_number).isVisible = isNougatPlus() && !isRecycleBin
findItem(R.id.block_number).isVisible = !isRecycleBin
findItem(R.id.dial_number).isVisible =
participants.size == 1 && !isSpecialNumber() && !isRecycleBin
findItem(R.id.manage_people).isVisible = !isSpecialNumber() && !isRecycleBin
@ -983,9 +981,7 @@ class ThreadActivity : SimpleActivity() {
text = getString(R.string.invalid_short_code_desc)
)
}
if (isOreoPlus()) {
tooltipText = getString(org.fossify.commons.R.string.more_info)
}
tooltipText = getString(org.fossify.commons.R.string.more_info)
}
}
}

View file

@ -5,21 +5,34 @@ import android.text.TextUtils
import android.view.Menu
import org.fossify.commons.dialogs.ConfirmationDialog
import org.fossify.commons.dialogs.FeatureLockedDialog
import org.fossify.commons.extensions.*
import org.fossify.commons.extensions.addBlockedNumber
import org.fossify.commons.extensions.addLockedLabelIfNeeded
import org.fossify.commons.extensions.copyToClipboard
import org.fossify.commons.extensions.isOrWasThankYouInstalled
import org.fossify.commons.extensions.launchActivityIntent
import org.fossify.commons.extensions.notificationManager
import org.fossify.commons.helpers.KEY_PHONE
import org.fossify.commons.helpers.ensureBackgroundThread
import org.fossify.commons.helpers.isNougatPlus
import org.fossify.commons.views.MyRecyclerView
import org.fossify.messages.R
import org.fossify.messages.activities.SimpleActivity
import org.fossify.messages.dialogs.RenameConversationDialog
import org.fossify.messages.extensions.*
import org.fossify.messages.extensions.config
import org.fossify.messages.extensions.deleteConversation
import org.fossify.messages.extensions.dialNumber
import org.fossify.messages.extensions.markThreadMessagesRead
import org.fossify.messages.extensions.markThreadMessagesUnread
import org.fossify.messages.extensions.renameConversation
import org.fossify.messages.extensions.updateConversationArchivedStatus
import org.fossify.messages.helpers.refreshMessages
import org.fossify.messages.messaging.isShortCodeWithLetters
import org.fossify.messages.models.Conversation
class ConversationsAdapter(
activity: SimpleActivity, recyclerView: MyRecyclerView, onRefresh: () -> Unit, itemClick: (Any) -> Unit
activity: SimpleActivity,
recyclerView: MyRecyclerView,
onRefresh: () -> Unit,
itemClick: (Any) -> Unit
) : BaseConversationsAdapter(activity, recyclerView, onRefresh, itemClick) {
override fun getActionMenuId() = R.menu.cab_conversations
@ -31,12 +44,16 @@ class ConversationsAdapter(
val archiveAvailable = activity.config.isArchiveAvailable
menu.apply {
findItem(R.id.cab_block_number).title = activity.addLockedLabelIfNeeded(org.fossify.commons.R.string.block_number)
findItem(R.id.cab_block_number).isVisible = isNougatPlus()
findItem(R.id.cab_add_number_to_contact).isVisible = isSingleSelection && !isGroupConversation
findItem(R.id.cab_dial_number).isVisible = isSingleSelection && !isGroupConversation && !isShortCodeWithLetters(selectedConversation.phoneNumber)
findItem(R.id.cab_block_number).title =
activity.addLockedLabelIfNeeded(org.fossify.commons.R.string.block_number)
findItem(R.id.cab_add_number_to_contact).isVisible =
isSingleSelection && !isGroupConversation
findItem(R.id.cab_dial_number).isVisible =
isSingleSelection && !isGroupConversation &&
!isShortCodeWithLetters(selectedConversation.phoneNumber)
findItem(R.id.cab_copy_number).isVisible = isSingleSelection && !isGroupConversation
findItem(R.id.cab_rename_conversation).isVisible = isSingleSelection && isGroupConversation
findItem(R.id.cab_rename_conversation).isVisible =
isSingleSelection && isGroupConversation
findItem(R.id.cab_mark_as_read).isVisible = selectedItems.any { !it.read }
findItem(R.id.cab_mark_as_unread).isVisible = selectedItems.any { it.read }
findItem(R.id.cab_archive).isVisible = archiveAvailable
@ -76,7 +93,10 @@ class ConversationsAdapter(
private fun askConfirmBlock() {
val numbers = getSelectedItems().distinctBy { it.phoneNumber }.map { it.phoneNumber }
val numbersString = TextUtils.join(", ", numbers)
val question = String.format(resources.getString(org.fossify.commons.R.string.block_confirmation), numbersString)
val question = String.format(
resources.getString(org.fossify.commons.R.string.block_confirmation),
numbersString
)
ConfirmationDialog(activity, question) {
blockNumbers()
@ -149,7 +169,8 @@ class ConversationsAdapter(
return
}
val conversationsToRemove = currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
val conversationsToRemove =
currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
conversationsToRemove.forEach {
activity.updateConversationArchivedStatus(it.threadId, true)
activity.notificationManager.cancel(it.threadId.hashCode())
@ -179,7 +200,8 @@ class ConversationsAdapter(
return
}
val conversationsToRemove = currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
val conversationsToRemove =
currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
conversationsToRemove.forEach {
activity.deleteConversation(it.threadId)
activity.notificationManager.cancel(it.threadId.hashCode())
@ -224,7 +246,8 @@ class ConversationsAdapter(
return
}
val conversationsMarkedAsRead = currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
val conversationsMarkedAsRead =
currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
ensureBackgroundThread {
conversationsMarkedAsRead.filter { conversation -> !conversation.read }.forEach {
activity.markThreadMessagesRead(it.threadId)
@ -239,7 +262,8 @@ class ConversationsAdapter(
return
}
val conversationsMarkedAsUnread = currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
val conversationsMarkedAsUnread =
currentList.filter { selectedKeys.contains(it.hashCode()) } as ArrayList<Conversation>
ensureBackgroundThread {
conversationsMarkedAsUnread.filter { conversation -> conversation.read }.forEach {
activity.markThreadMessagesUnread(it.threadId)
@ -280,8 +304,10 @@ class ConversationsAdapter(
private fun checkPinBtnVisibility(menu: Menu) {
val pinnedConversations = activity.config.pinnedConversations
val selectedConversations = getSelectedItems()
menu.findItem(R.id.cab_pin_conversation).isVisible = selectedConversations.any { !pinnedConversations.contains(it.threadId.toString()) }
menu.findItem(R.id.cab_unpin_conversation).isVisible = selectedConversations.any { pinnedConversations.contains(it.threadId.toString()) }
menu.findItem(R.id.cab_pin_conversation).isVisible =
selectedConversations.any { !pinnedConversations.contains(it.threadId.toString()) }
menu.findItem(R.id.cab_unpin_conversation).isVisible =
selectedConversations.any { pinnedConversations.contains(it.threadId.toString()) }
}
private fun refreshConversations() {

View file

@ -14,21 +14,49 @@ import android.os.Handler
import android.os.Looper
import android.provider.ContactsContract.PhoneLookup
import android.provider.OpenableColumns
import android.provider.Telephony.*
import android.provider.Telephony.Mms
import android.provider.Telephony.MmsSms
import android.provider.Telephony.Sms
import android.provider.Telephony.Threads
import android.provider.Telephony.ThreadsColumns
import android.telephony.SubscriptionManager
import android.text.TextUtils
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.RequestOptions
import me.leolin.shortcutbadger.ShortcutBadger
import org.fossify.commons.extensions.*
import org.fossify.commons.helpers.*
import org.fossify.commons.extensions.areDigitsOnly
import org.fossify.commons.extensions.getBlockedNumbers
import org.fossify.commons.extensions.getIntValue
import org.fossify.commons.extensions.getLongValue
import org.fossify.commons.extensions.getMyContactsCursor
import org.fossify.commons.extensions.getStringValue
import org.fossify.commons.extensions.hasPermission
import org.fossify.commons.extensions.isNumberBlocked
import org.fossify.commons.extensions.normalizeString
import org.fossify.commons.extensions.notificationManager
import org.fossify.commons.extensions.queryCursor
import org.fossify.commons.extensions.showErrorToast
import org.fossify.commons.extensions.toInt
import org.fossify.commons.extensions.toast
import org.fossify.commons.extensions.trimToComparableNumber
import org.fossify.commons.helpers.DAY_SECONDS
import org.fossify.commons.helpers.MONTH_SECONDS
import org.fossify.commons.helpers.MyContactsContentProvider
import org.fossify.commons.helpers.PERMISSION_READ_CONTACTS
import org.fossify.commons.helpers.SimpleContactsHelper
import org.fossify.commons.helpers.ensureBackgroundThread
import org.fossify.commons.helpers.isQPlus
import org.fossify.commons.models.PhoneNumber
import org.fossify.commons.models.SimpleContact
import org.fossify.messages.R
import org.fossify.messages.databases.MessagesDatabase
import org.fossify.messages.helpers.*
import org.fossify.messages.helpers.AttachmentUtils.parseAttachmentNames
import org.fossify.messages.helpers.Config
import org.fossify.messages.helpers.FILE_SIZE_NONE
import org.fossify.messages.helpers.MESSAGES_LIMIT
import org.fossify.messages.helpers.NotificationHelper
import org.fossify.messages.helpers.generateRandomId
import org.fossify.messages.interfaces.AttachmentsDao
import org.fossify.messages.interfaces.ConversationsDao
import org.fossify.messages.interfaces.MessageAttachmentsDao
@ -36,7 +64,12 @@ import org.fossify.messages.interfaces.MessagesDao
import org.fossify.messages.messaging.MessagingUtils
import org.fossify.messages.messaging.MessagingUtils.Companion.ADDRESS_SEPARATOR
import org.fossify.messages.messaging.SmsSender
import org.fossify.messages.models.*
import org.fossify.messages.models.Attachment
import org.fossify.messages.models.Conversation
import org.fossify.messages.models.Message
import org.fossify.messages.models.MessageAttachment
import org.fossify.messages.models.NamePhoto
import org.fossify.messages.models.RecycleBinMessage
import java.io.FileNotFoundException
val Context.config: Config get() = Config.newInstance(applicationContext)
@ -114,7 +147,15 @@ fun Context.getMessages(
val participants = senderNumber.split(ADDRESS_SEPARATOR).map { number ->
val phoneNumber = PhoneNumber(number, 0, "", number)
val participantPhoto = getNameAndPhotoFromPhoneNumber(number)
SimpleContact(0, 0, participantPhoto.name, photoUri, arrayListOf(phoneNumber), ArrayList(), ArrayList())
SimpleContact(
0,
0,
participantPhoto.name,
photoUri,
arrayListOf(phoneNumber),
ArrayList(),
ArrayList()
)
}
val isMMS = false
val message =
@ -159,7 +200,12 @@ fun Context.getMessages(
}
// as soon as a message contains multiple recipients it counts as an MMS instead of SMS
fun Context.getMMS(threadId: Long? = null, getImageResolutions: Boolean = false, sortOrder: String? = null, dateFrom: Int = -1): ArrayList<Message> {
fun Context.getMMS(
threadId: Long? = null,
getImageResolutions: Boolean = false,
sortOrder: String? = null,
dateFrom: Int = -1
): ArrayList<Message> {
val uri = Mms.CONTENT_URI
val projection = arrayOf(
Mms._ID,
@ -175,7 +221,8 @@ fun Context.getMMS(threadId: Long? = null, getImageResolutions: Boolean = false,
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.
selection =
"${Sms.DATE} < ${dateFrom.toLong()}" //Should not multiply 1000 here, because date in mms's database is different from sms's.
} else if (threadId != null && dateFrom == -1) {
selection = "${Sms.THREAD_ID} = ?"
selectionArgs = arrayOf(threadId.toString())
@ -262,7 +309,10 @@ fun Context.getMMSSender(msgId: Long): String {
return ""
}
fun Context.getConversations(threadId: Long? = null, privateContacts: ArrayList<SimpleContact> = ArrayList()): ArrayList<Conversation> {
fun Context.getConversations(
threadId: Long? = null,
privateContacts: ArrayList<SimpleContact> = ArrayList()
): ArrayList<Conversation> {
val archiveAvailable = config.isArchiveAvailable
val uri = Uri.parse("${Threads.CONTENT_URI}?simple=true")
@ -291,7 +341,13 @@ fun Context.getConversations(threadId: Long? = null, privateContacts: ArrayList<
val simpleContactHelper = SimpleContactsHelper(this)
val blockedNumbers = getBlockedNumbers()
try {
queryCursorUnsafe(uri, projection.toTypedArray(), selection, selectionArgs, sortOrder) { cursor ->
queryCursorUnsafe(
uri,
projection.toTypedArray(),
selection,
selectionArgs,
sortOrder
) { cursor ->
val id = cursor.getLongValue(Threads._ID)
var snippet = cursor.getStringValue(Threads.SNIPPET) ?: ""
if (snippet.isEmpty()) {
@ -304,19 +360,39 @@ fun Context.getConversations(threadId: Long? = null, privateContacts: ArrayList<
}
val rawIds = cursor.getStringValue(Threads.RECIPIENT_IDS)
val recipientIds = rawIds.split(" ").filter { it.areDigitsOnly() }.map { it.toInt() }.toMutableList()
val recipientIds =
rawIds.split(" ").filter { it.areDigitsOnly() }.map { it.toInt() }.toMutableList()
val phoneNumbers = getThreadPhoneNumbers(recipientIds)
if (phoneNumbers.isEmpty() || phoneNumbers.any { isNumberBlocked(it, blockedNumbers) }) {
if (phoneNumbers.isEmpty() || phoneNumbers.any {
isNumberBlocked(
it,
blockedNumbers
)
}) {
return@queryCursorUnsafe
}
val names = getThreadContactNames(phoneNumbers, privateContacts)
val title = TextUtils.join(", ", names.toTypedArray())
val photoUri = if (phoneNumbers.size == 1) simpleContactHelper.getPhotoUriFromPhoneNumber(phoneNumbers.first()) else ""
val photoUri =
if (phoneNumbers.size == 1) simpleContactHelper.getPhotoUriFromPhoneNumber(
phoneNumbers.first()
) else ""
val isGroupConversation = phoneNumbers.size > 1
val read = cursor.getIntValue(Threads.READ) == 1
val archived = if (archiveAvailable) cursor.getIntValue(Threads.ARCHIVED) == 1 else false
val conversation = Conversation(id, snippet, date.toInt(), read, title, photoUri, isGroupConversation, phoneNumbers.first(), isArchived = archived)
val archived =
if (archiveAvailable) cursor.getIntValue(Threads.ARCHIVED) == 1 else false
val conversation = Conversation(
id,
snippet,
date.toInt(),
read,
title,
photoUri,
isGroupConversation,
phoneNumbers.first(),
isArchived = archived
)
conversations.add(conversation)
}
} catch (sqliteException: SQLiteException) {
@ -399,7 +475,11 @@ fun Context.getMmsAttachment(id: Long, getImageResolutions: Boolean): MessageAtt
try {
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeStream(contentResolver.openInputStream(fileUri), null, options)
BitmapFactory.decodeStream(
contentResolver.openInputStream(fileUri),
null,
options
)
width = options.outWidth
height = options.outHeight
} catch (e: Exception) {
@ -410,7 +490,15 @@ fun Context.getMmsAttachment(id: Long, getImageResolutions: Boolean): MessageAtt
messageAttachment.attachments.add(attachment)
} else if (mimetype != "application/smil") {
val attachmentName = attachmentNames?.getOrNull(attachmentCount) ?: ""
val attachment = Attachment(partId, id, Uri.withAppendedPath(uri, partId.toString()).toString(), mimetype, 0, 0, attachmentName)
val attachment = Attachment(
partId,
id,
Uri.withAppendedPath(uri, partId.toString()).toString(),
mimetype,
0,
0,
attachmentName
)
messageAttachment.attachments.add(attachment)
attachmentCount++
} else {
@ -476,7 +564,10 @@ fun Context.getMessageRecipientAddress(messageId: Long): String {
return ""
}
fun Context.getThreadParticipants(threadId: Long, contactsMap: HashMap<Int, SimpleContact>?): ArrayList<SimpleContact> {
fun Context.getThreadParticipants(
threadId: Long,
contactsMap: HashMap<Int, SimpleContact>?
): ArrayList<SimpleContact> {
val uri = Uri.parse("${MmsSms.CONTENT_CONVERSATIONS_URI}?simple=true")
val projection = arrayOf(
ThreadsColumns.RECIPIENT_IDS
@ -501,7 +592,15 @@ fun Context.getThreadParticipants(threadId: Long, contactsMap: HashMap<Int, Simp
val name = namePhoto.name
val photoUri = namePhoto.photoUri ?: ""
val phoneNumber = PhoneNumber(number, 0, "", number)
val contact = SimpleContact(addressId, addressId, name, photoUri, arrayListOf(phoneNumber), ArrayList(), ArrayList())
val contact = SimpleContact(
addressId,
addressId,
name,
photoUri,
arrayListOf(phoneNumber),
ArrayList(),
ArrayList()
)
participants.add(contact)
}
}
@ -520,7 +619,10 @@ fun Context.getThreadPhoneNumbers(recipientIds: List<Int>): ArrayList<String> {
return numbers
}
fun Context.getThreadContactNames(phoneNumbers: List<String>, privateContacts: ArrayList<SimpleContact>): ArrayList<String> {
fun Context.getThreadContactNames(
phoneNumbers: List<String>,
privateContacts: ArrayList<SimpleContact>
): ArrayList<String> {
val names = ArrayList<String>()
phoneNumbers.forEach { number ->
val name = SimpleContactsHelper(this).getNameFromPhoneNumber(number)
@ -578,7 +680,8 @@ 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
@ -591,8 +694,17 @@ fun Context.getSuggestedContacts(privateContacts: ArrayList<SimpleContact>): Arr
}
val phoneNumber = PhoneNumber(senderNumber, 0, "", senderNumber)
val contact = SimpleContact(0, 0, senderName, photoUri, arrayListOf(phoneNumber), ArrayList(), ArrayList())
if (!contacts.map { it.phoneNumbers.first().normalizedNumber.trimToComparableNumber() }.contains(senderNumber.trimToComparableNumber())) {
val contact = SimpleContact(
0,
0,
senderName,
photoUri,
arrayListOf(phoneNumber),
ArrayList(),
ArrayList()
)
if (!contacts.map { it.phoneNumbers.first().normalizedNumber.trimToComparableNumber() }
.contains(senderNumber.trimToComparableNumber())) {
contacts.add(contact)
}
}
@ -690,7 +802,7 @@ fun Context.deleteConversation(threadId: Long) {
conversationsDB.deleteThreadId(threadId)
messagesDB.deleteThreadMessages(threadId)
if (config.customNotifications.contains(threadId.toString()) && isOreoPlus()) {
if (config.customNotifications.contains(threadId.toString())) {
config.removeCustomNotificationsByThreadId(threadId)
notificationManager.deleteNotificationChannel(threadId.toString())
}
@ -853,13 +965,26 @@ fun Context.getThreadId(addresses: Set<String>): Long {
}
}
fun Context.showReceivedMessageNotification(messageId: Long, address: String, body: String, threadId: Long, bitmap: Bitmap?) {
fun Context.showReceivedMessageNotification(
messageId: Long,
address: String,
body: String,
threadId: Long,
bitmap: Bitmap?
) {
val privateCursor = getMyContactsCursor(favoritesOnly = false, withPhoneNumbersOnly = true)
ensureBackgroundThread {
val senderName = getNameFromAddress(address, privateCursor)
Handler(Looper.getMainLooper()).post {
notificationHelper.showMessageNotification(messageId, address, body, threadId, bitmap, senderName)
notificationHelper.showMessageNotification(
messageId,
address,
body,
threadId,
bitmap,
senderName
)
}
}
}
@ -993,7 +1118,8 @@ fun Context.updateLastConversationMessage(threadId: Long) {
// following Android code (which runs even if no messages are deleted):
// https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/android14-release/src/com/android/providers/telephony/MmsSmsProvider.java#1409
val uri = Threads.CONTENT_URI
val selection = "1 = 0" // always-false condition, because we don't actually want to delete any messages
val selection =
"1 = 0" // always-false condition, because we don't actually want to delete any messages
try {
contentResolver.delete(uri, selection, null)
val newConversation = getConversations(threadId)[0]
@ -1017,20 +1143,21 @@ fun Context.getFileSizeFromUri(uri: Uri): Long {
// if "content://" uri scheme, try contentResolver table
if (uri.scheme.equals(ContentResolver.SCHEME_CONTENT)) {
return contentResolver.query(uri, arrayOf(OpenableColumns.SIZE), null, null, null)?.use { cursor ->
// maybe shouldn't trust ContentResolver for size:
// https://stackoverflow.com/questions/48302972/content-resolver-returns-wrong-size
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
if (sizeIndex == -1) {
return@use FILE_SIZE_NONE
}
cursor.moveToFirst()
return try {
cursor.getLong(sizeIndex)
} catch (_: Throwable) {
FILE_SIZE_NONE
}
} ?: FILE_SIZE_NONE
return contentResolver.query(uri, arrayOf(OpenableColumns.SIZE), null, null, null)
?.use { cursor ->
// maybe shouldn't trust ContentResolver for size:
// https://stackoverflow.com/questions/48302972/content-resolver-returns-wrong-size
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
if (sizeIndex == -1) {
return@use FILE_SIZE_NONE
}
cursor.moveToFirst()
return try {
cursor.getLong(sizeIndex)
} catch (_: Throwable) {
FILE_SIZE_NONE
}
} ?: FILE_SIZE_NONE
} else {
return FILE_SIZE_NONE
}
@ -1061,7 +1188,8 @@ fun Context.insertOrUpdateConversation(
) {
var updatedConv = conversation
if (cachedConv != null && cachedConv.usesCustomTitle) {
updatedConv = updatedConv.copy(title = cachedConv.title, usesCustomTitle = cachedConv.usesCustomTitle)
updatedConv =
updatedConv.copy(title = cachedConv.title, usesCustomTitle = cachedConv.usesCustomTitle)
}
conversationsDB.insertOrUpdate(updatedConv)
}
@ -1076,10 +1204,15 @@ fun Context.renameConversation(conversation: Conversation, newTitle: String): Co
return updatedConv
}
fun Context.createTemporaryThread(message: Message, threadId: Long = generateRandomId(), cachedConv: Conversation?) {
fun Context.createTemporaryThread(
message: Message,
threadId: Long = generateRandomId(),
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 {
@ -1132,4 +1265,5 @@ fun Context.clearExpiredScheduledMessages(threadId: Long, messagesToDelete: List
}
}
fun Context.getDefaultKeyboardHeight() = resources.getDimensionPixelSize(R.dimen.default_keyboard_height)
fun Context.getDefaultKeyboardHeight() =
resources.getDimensionPixelSize(R.dimen.default_keyboard_height)

View file

@ -17,8 +17,6 @@ import androidx.core.app.RemoteInput
import org.fossify.commons.extensions.getProperPrimaryColor
import org.fossify.commons.extensions.notificationManager
import org.fossify.commons.helpers.SimpleContactsHelper
import org.fossify.commons.helpers.isNougatPlus
import org.fossify.commons.helpers.isOreoPlus
import org.fossify.messages.R
import org.fossify.messages.activities.ThreadActivity
import org.fossify.messages.extensions.config
@ -45,10 +43,12 @@ class NotificationHelper(private val context: Context) {
sender: String?,
alertOnlyOnce: Boolean = false
) {
val hasCustomNotifications = context.config.customNotifications.contains(threadId.toString())
val notificationChannelId = if (hasCustomNotifications) threadId.toString() else NOTIFICATION_CHANNEL_ID
val hasCustomNotifications =
context.config.customNotifications.contains(threadId.toString())
val notificationChannelId =
if (hasCustomNotifications) threadId.toString() else NOTIFICATION_CHANNEL_ID
if (!hasCustomNotifications) {
maybeCreateChannel(notificationChannelId, context.getString(R.string.channel_received_sms))
createChannel(notificationChannelId, context.getString(R.string.channel_received_sms))
}
val notificationId = threadId.hashCode()
@ -56,25 +56,40 @@ class NotificationHelper(private val context: Context) {
putExtra(THREAD_ID, threadId)
}
val contentPendingIntent =
PendingIntent.getActivity(context, notificationId, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
PendingIntent.getActivity(
context,
notificationId,
contentIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
)
val markAsReadIntent = Intent(context, MarkAsReadReceiver::class.java).apply {
action = MARK_AS_READ
putExtra(THREAD_ID, threadId)
}
val markAsReadPendingIntent =
PendingIntent.getBroadcast(context, notificationId, markAsReadIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
PendingIntent.getBroadcast(
context,
notificationId,
markAsReadIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
)
val deleteSmsIntent = Intent(context, DeleteSmsReceiver::class.java).apply {
putExtra(THREAD_ID, threadId)
putExtra(MESSAGE_ID, messageId)
}
val deleteSmsPendingIntent =
PendingIntent.getBroadcast(context, notificationId, deleteSmsIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
PendingIntent.getBroadcast(
context,
notificationId,
deleteSmsIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
)
var replyAction: NotificationCompat.Action? = null
val isNoReplySms = isShortCodeWithLetters(address)
if (isNougatPlus() && !isNoReplySms) {
if (!isNoReplySms) {
val replyLabel = context.getString(R.string.reply)
val remoteInput = RemoteInput.Builder(REPLY)
.setLabel(replyLabel)
@ -92,7 +107,11 @@ class NotificationHelper(private val context: Context) {
replyIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
)
replyAction = NotificationCompat.Action.Builder(R.drawable.ic_send_vector, replyLabel, replyPendingIntent)
replyAction = NotificationCompat.Action.Builder(
R.drawable.ic_send_vector,
replyLabel,
replyPendingIntent
)
.addRemoteInput(remoteInput)
.build()
}
@ -113,7 +132,9 @@ class NotificationHelper(private val context: Context) {
setContentTitle(sender)
setLargeIcon(largeIcon)
val summaryText = context.getString(R.string.new_message)
setStyle(NotificationCompat.BigTextStyle().setSummaryText(summaryText).bigText(body))
setStyle(
NotificationCompat.BigTextStyle().setSummaryText(summaryText).bigText(body)
)
}
}
@ -132,7 +153,11 @@ class NotificationHelper(private val context: Context) {
builder.addAction(replyAction)
}
builder.addAction(org.fossify.commons.R.drawable.ic_check_vector, context.getString(R.string.mark_as_read), markAsReadPendingIntent)
builder.addAction(
org.fossify.commons.R.drawable.ic_check_vector,
context.getString(R.string.mark_as_read),
markAsReadPendingIntent
)
.setChannelId(notificationChannelId)
if (isNoReplySms) {
builder.addAction(
@ -146,19 +171,27 @@ class NotificationHelper(private val context: Context) {
@SuppressLint("NewApi")
fun showSendingFailedNotification(recipientName: String, threadId: Long) {
val hasCustomNotifications = context.config.customNotifications.contains(threadId.toString())
val notificationChannelId = if (hasCustomNotifications) threadId.toString() else NOTIFICATION_CHANNEL_ID
val hasCustomNotifications =
context.config.customNotifications.contains(threadId.toString())
val notificationChannelId =
if (hasCustomNotifications) threadId.toString() else NOTIFICATION_CHANNEL_ID
if (!hasCustomNotifications) {
maybeCreateChannel(notificationChannelId, context.getString(R.string.message_not_sent_short))
createChannel(notificationChannelId, context.getString(R.string.message_not_sent_short))
}
val notificationId = generateRandomId().hashCode()
val intent = Intent(context, ThreadActivity::class.java).apply {
putExtra(THREAD_ID, threadId)
}
val contentPendingIntent = PendingIntent.getActivity(context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
val contentPendingIntent = PendingIntent.getActivity(
context,
notificationId,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
)
val summaryText = String.format(context.getString(R.string.message_sending_error), recipientName)
val summaryText =
String.format(context.getString(R.string.message_sending_error), recipientName)
val largeIcon = SimpleContactsHelper(context).getContactLetterIcon(recipientName)
val builder = NotificationCompat.Builder(context, notificationChannelId)
.setContentTitle(context.getString(R.string.message_not_sent_short))
@ -177,26 +210,29 @@ class NotificationHelper(private val context: Context) {
notificationManager.notify(notificationId, builder.build())
}
private fun maybeCreateChannel(id: String, name: String) {
if (isOreoPlus()) {
val audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setLegacyStreamType(AudioManager.STREAM_NOTIFICATION)
.build()
private fun createChannel(id: String, name: String) {
val audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setLegacyStreamType(AudioManager.STREAM_NOTIFICATION)
.build()
val importance = IMPORTANCE_HIGH
NotificationChannel(id, name, importance).apply {
setBypassDnd(false)
enableLights(true)
setSound(soundUri, audioAttributes)
enableVibration(true)
notificationManager.createNotificationChannel(this)
}
val importance = IMPORTANCE_HIGH
NotificationChannel(id, name, importance).apply {
setBypassDnd(false)
enableLights(true)
setSound(soundUri, audioAttributes)
enableVibration(true)
notificationManager.createNotificationChannel(this)
}
}
private fun getMessagesStyle(address: String, body: String, notificationId: Int, name: String?): NotificationCompat.MessagingStyle {
private fun getMessagesStyle(
address: String,
body: String,
notificationId: Int,
name: String?
): NotificationCompat.MessagingStyle {
val sender = if (name != null) {
Person.Builder()
.setName(name)
@ -210,18 +246,20 @@ class NotificationHelper(private val context: Context) {
getOldMessages(notificationId).forEach {
style.addMessage(it)
}
val newMessage = NotificationCompat.MessagingStyle.Message(body, System.currentTimeMillis(), sender)
val newMessage =
NotificationCompat.MessagingStyle.Message(body, System.currentTimeMillis(), sender)
style.addMessage(newMessage)
}
}
private fun getOldMessages(notificationId: Int): List<NotificationCompat.MessagingStyle.Message> {
if (!isNougatPlus()) {
return emptyList()
}
val currentNotification = notificationManager.activeNotifications.find { it.id == notificationId }
val currentNotification =
notificationManager.activeNotifications.find { it.id == notificationId }
return if (currentNotification != null) {
val activeStyle = NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification(currentNotification.notification)
val activeStyle =
NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification(
currentNotification.notification
)
activeStyle?.messages.orEmpty()
} else {
emptyList()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 890 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Some files were not shown because too many files have changed in this diff Show more