Setup full screen app lock

- Updated commons
This commit is contained in:
Naveen Singh 2024-12-26 13:16:31 +05:30
parent 0df10f18f0
commit 3f17c2d94c
No known key found for this signature in database
GPG key ID: AF5D43C216778C0B
10 changed files with 553 additions and 227 deletions

View file

@ -1,11 +1,7 @@
package org.fossify.messages package org.fossify.messages
import android.app.Application import org.fossify.commons.FossifyApp
import org.fossify.commons.extensions.checkUseEnglish
class App : Application() { class App : FossifyApp() {
override fun onCreate() { override val isAppLockFeatureAvailable = true
super.onCreate()
checkUseEnglish()
}
} }

View file

@ -6,7 +6,6 @@ import android.os.Bundle
import org.fossify.commons.dialogs.ConfirmationDialog import org.fossify.commons.dialogs.ConfirmationDialog
import org.fossify.commons.extensions.* import org.fossify.commons.extensions.*
import org.fossify.commons.helpers.NavigationIcon import org.fossify.commons.helpers.NavigationIcon
import org.fossify.commons.helpers.WAS_PROTECTION_HANDLED
import org.fossify.commons.helpers.ensureBackgroundThread import org.fossify.commons.helpers.ensureBackgroundThread
import org.fossify.messages.R import org.fossify.messages.R
import org.fossify.messages.adapters.ArchivedConversationsAdapter import org.fossify.messages.adapters.ArchivedConversationsAdapter
@ -165,7 +164,6 @@ class ArchivedConversationsActivity : SimpleActivity() {
val conversation = any as Conversation val conversation = any as Conversation
putExtra(THREAD_ID, conversation.threadId) putExtra(THREAD_ID, conversation.threadId)
putExtra(THREAD_TITLE, conversation.title) putExtra(THREAD_TITLE, conversation.title)
putExtra(WAS_PROTECTION_HANDLED, true)
startActivity(this) startActivity(this)
} }
} }

View file

@ -11,10 +11,46 @@ import android.graphics.drawable.LayerDrawable
import android.os.Bundle import android.os.Bundle
import android.provider.Telephony import android.provider.Telephony
import android.text.TextUtils import android.text.TextUtils
import androidx.appcompat.content.res.AppCompatResources
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import org.fossify.commons.dialogs.PermissionRequiredDialog import org.fossify.commons.dialogs.PermissionRequiredDialog
import org.fossify.commons.extensions.* import org.fossify.commons.extensions.adjustAlpha
import org.fossify.commons.helpers.* import org.fossify.commons.extensions.appLaunched
import org.fossify.commons.extensions.appLockManager
import org.fossify.commons.extensions.applyColorFilter
import org.fossify.commons.extensions.areSystemAnimationsEnabled
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.checkAppSideloading
import org.fossify.commons.extensions.checkWhatsNew
import org.fossify.commons.extensions.convertToBitmap
import org.fossify.commons.extensions.fadeIn
import org.fossify.commons.extensions.formatDateOrTime
import org.fossify.commons.extensions.getMyContactsCursor
import org.fossify.commons.extensions.getProperBackgroundColor
import org.fossify.commons.extensions.getProperPrimaryColor
import org.fossify.commons.extensions.getProperTextColor
import org.fossify.commons.extensions.hideKeyboard
import org.fossify.commons.extensions.navigationBarHeight
import org.fossify.commons.extensions.openNotificationSettings
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.LICENSE_EVENT_BUS
import org.fossify.commons.helpers.LICENSE_INDICATOR_FAST_SCROLL
import org.fossify.commons.helpers.LICENSE_SMS_MMS
import org.fossify.commons.helpers.LOWER_ALPHA
import org.fossify.commons.helpers.MyContactsContentProvider
import org.fossify.commons.helpers.PERMISSION_READ_CONTACTS
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.FAQItem
import org.fossify.commons.models.Release import org.fossify.commons.models.Release
import org.fossify.messages.BuildConfig import org.fossify.messages.BuildConfig
@ -22,7 +58,16 @@ import org.fossify.messages.R
import org.fossify.messages.adapters.ConversationsAdapter import org.fossify.messages.adapters.ConversationsAdapter
import org.fossify.messages.adapters.SearchResultsAdapter import org.fossify.messages.adapters.SearchResultsAdapter
import org.fossify.messages.databinding.ActivityMainBinding import org.fossify.messages.databinding.ActivityMainBinding
import org.fossify.messages.extensions.* import org.fossify.messages.extensions.checkAndDeleteOldRecycleBinMessages
import org.fossify.messages.extensions.clearAllMessagesIfNeeded
import org.fossify.messages.extensions.clearExpiredScheduledMessages
import org.fossify.messages.extensions.config
import org.fossify.messages.extensions.conversationsDB
import org.fossify.messages.extensions.getConversations
import org.fossify.messages.extensions.getMessages
import org.fossify.messages.extensions.insertOrUpdateConversation
import org.fossify.messages.extensions.messagesDB
import org.fossify.messages.extensions.updateUnreadCountBadge
import org.fossify.messages.helpers.SEARCHED_MESSAGE_ID import org.fossify.messages.helpers.SEARCHED_MESSAGE_ID
import org.fossify.messages.helpers.THREAD_ID import org.fossify.messages.helpers.THREAD_ID
import org.fossify.messages.helpers.THREAD_TITLE import org.fossify.messages.helpers.THREAD_TITLE
@ -41,7 +86,6 @@ class MainActivity : SimpleActivity() {
private var storedFontSize = 0 private var storedFontSize = 0
private var lastSearchedText = "" private var lastSearchedText = ""
private var bus: EventBus? = null private var bus: EventBus? = null
private var wasProtectionHandled = false
private val binding by viewBinding(ActivityMainBinding::inflate) private val binding by viewBinding(ActivityMainBinding::inflate)
@ -63,16 +107,9 @@ class MainActivity : SimpleActivity() {
if (savedInstanceState == null) { if (savedInstanceState == null) {
checkAndDeleteOldRecycleBinMessages() checkAndDeleteOldRecycleBinMessages()
handleAppPasswordProtection {
wasProtectionHandled = it
if (it) {
clearAllMessagesIfNeeded { clearAllMessagesIfNeeded {
loadMessages() loadMessages()
} }
} else {
finish()
}
}
} }
if (checkAppSideloading()) { if (checkAppSideloading()) {
@ -108,7 +145,8 @@ class MainActivity : SimpleActivity() {
binding.conversationsProgressBar.trackColor = properPrimaryColor.adjustAlpha(LOWER_ALPHA) binding.conversationsProgressBar.trackColor = properPrimaryColor.adjustAlpha(LOWER_ALPHA)
checkShortcut() checkShortcut()
(binding.conversationsFab.layoutParams as? CoordinatorLayout.LayoutParams)?.bottomMargin = (binding.conversationsFab.layoutParams as? CoordinatorLayout.LayoutParams)?.bottomMargin =
navigationBarHeight + resources.getDimension(org.fossify.commons.R.dimen.activity_margin).toInt() navigationBarHeight + resources.getDimension(org.fossify.commons.R.dimen.activity_margin)
.toInt()
} }
override fun onPause() { override fun onPause() {
@ -125,33 +163,11 @@ class MainActivity : SimpleActivity() {
if (binding.mainMenu.isSearchOpen) { if (binding.mainMenu.isSearchOpen) {
binding.mainMenu.closeSearch() binding.mainMenu.closeSearch()
} else { } else {
appLockManager.lock()
super.onBackPressed() super.onBackPressed()
} }
} }
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(WAS_PROTECTION_HANDLED, wasProtectionHandled)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
wasProtectionHandled = savedInstanceState.getBoolean(WAS_PROTECTION_HANDLED, false)
if (!wasProtectionHandled) {
handleAppPasswordProtection {
wasProtectionHandled = it
if (it) {
loadMessages()
} else {
finish()
}
}
} else {
loadMessages()
}
}
private fun setupOptionsMenu() { private fun setupOptionsMenu() {
binding.mainMenu.getToolbar().inflateMenu(R.menu.menu_main) binding.mainMenu.getToolbar().inflateMenu(R.menu.menu_main)
binding.mainMenu.toggleHideOnScroll(true) binding.mainMenu.toggleHideOnScroll(true)
@ -332,7 +348,8 @@ class MainActivity : SimpleActivity() {
conversationsDB.deleteThreadId(threadId) conversationsDB.deleteThreadId(threadId)
} }
val newConversation = conversations.find { it.phoneNumber == cachedConversation.phoneNumber } val newConversation =
conversations.find { it.phoneNumber == cachedConversation.phoneNumber }
if (isTemporaryThread && newConversation != null) { 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) conversationsDB.deleteThreadId(threadId)
@ -346,7 +363,9 @@ class MainActivity : SimpleActivity() {
cachedConversations.forEach { cachedConv -> cachedConversations.forEach { cachedConv ->
val conv = conversations.find { val conv = conversations.find {
it.threadId == cachedConv.threadId && !Conversation.areContentsTheSame(cachedConv, it) it.threadId == cachedConv.threadId && !Conversation.areContentsTheSame(
old = cachedConv, new = it
)
} }
if (conv != null) { if (conv != null) {
val lastModified = maxOf(cachedConv.date, conv.date) val lastModified = maxOf(cachedConv.date, conv.date)
@ -362,7 +381,11 @@ class MainActivity : SimpleActivity() {
if (config.appRunCount == 1) { if (config.appRunCount == 1) {
conversations.map { it.threadId }.forEach { threadId -> conversations.map { it.threadId }.forEach { threadId ->
val messages = getMessages(threadId, getImageResolutions = false, includeScheduledMessages = false) val messages = getMessages(
threadId = threadId,
getImageResolutions = false,
includeScheduledMessages = false
)
messages.chunked(30).forEach { currentMessages -> messages.chunked(30).forEach { currentMessages ->
messagesDB.insertMessages(*currentMessages.toTypedArray()) messagesDB.insertMessages(*currentMessages.toTypedArray())
} }
@ -390,7 +413,10 @@ class MainActivity : SimpleActivity() {
return currAdapter as ConversationsAdapter return currAdapter as ConversationsAdapter
} }
private fun setupConversations(conversations: ArrayList<Conversation>, cached: Boolean = false) { private fun setupConversations(
conversations: ArrayList<Conversation>,
cached: Boolean = false
) {
val sortedConversations = conversations.sortedWith( val sortedConversations = conversations.sortedWith(
compareByDescending<Conversation> { config.pinnedConversations.contains(it.threadId.toString()) } compareByDescending<Conversation> { config.pinnedConversations.contains(it.threadId.toString()) }
.thenByDescending { it.date } .thenByDescending { it.date }
@ -435,7 +461,10 @@ class MainActivity : SimpleActivity() {
} }
private fun fadeOutSearch() { private fun fadeOutSearch() {
binding.searchHolder.animate().alpha(0f).setDuration(SHORT_ANIMATION_DURATION).withEndAction { binding.searchHolder.animate()
.alpha(0f)
.setDuration(SHORT_ANIMATION_DURATION)
.withEndAction {
binding.searchHolder.beGone() binding.searchHolder.beGone()
searchTextChanged("", true) searchTextChanged("", true)
}.start() }.start()
@ -451,7 +480,6 @@ class MainActivity : SimpleActivity() {
val conversation = any as Conversation val conversation = any as Conversation
putExtra(THREAD_ID, conversation.threadId) putExtra(THREAD_ID, conversation.threadId)
putExtra(THREAD_TITLE, conversation.title) putExtra(THREAD_TITLE, conversation.title)
putExtra(WAS_PROTECTION_HANDLED, wasProtectionHandled)
startActivity(this) startActivity(this)
} }
} }
@ -481,8 +509,13 @@ class MainActivity : SimpleActivity() {
@SuppressLint("NewApi") @SuppressLint("NewApi")
private fun getCreateNewContactShortcut(appIconColor: Int): ShortcutInfo { private fun getCreateNewContactShortcut(appIconColor: Int): ShortcutInfo {
val newEvent = getString(R.string.new_conversation) val newEvent = getString(R.string.new_conversation)
val drawable = resources.getDrawable(org.fossify.commons.R.drawable.shortcut_plus) val drawable =
(drawable as LayerDrawable).findDrawableByLayerId(org.fossify.commons.R.id.shortcut_plus_background).applyColorFilter(appIconColor) AppCompatResources.getDrawable(this, org.fossify.commons.R.drawable.shortcut_plus)
(drawable as LayerDrawable).findDrawableByLayerId(
org.fossify.commons.R.id.shortcut_plus_background
).applyColorFilter(appIconColor)
val bmp = drawable.convertToBitmap() val bmp = drawable.convertToBitmap()
val intent = Intent(this, NewConversationActivity::class.java) val intent = Intent(this, NewConversationActivity::class.java)
@ -517,11 +550,27 @@ class MainActivity : SimpleActivity() {
} }
} }
private fun showSearchResults(messages: List<Message>, conversations: List<Conversation>, searchedText: String) { private fun showSearchResults(
messages: List<Message>,
conversations: List<Conversation>,
searchedText: String
) {
val searchResults = ArrayList<SearchResult>() val searchResults = ArrayList<SearchResult>()
conversations.forEach { conversation -> conversations.forEach { conversation ->
val date = conversation.date.formatDateOrTime(this, true, true) val date = (conversation.date * 1000L).formatDateOrTime(
val searchResult = SearchResult(-1, conversation.title, conversation.phoneNumber, date, conversation.threadId, conversation.photoUri) context = this,
hideTimeOnOtherDays = true,
showCurrentYear = true
)
val searchResult = SearchResult(
messageId = -1,
title = conversation.title,
snippet = conversation.phoneNumber,
date = date,
threadId = conversation.threadId,
photoUri = conversation.photoUri
)
searchResults.add(searchResult) searchResults.add(searchResult)
} }
@ -532,8 +581,20 @@ class MainActivity : SimpleActivity() {
recipient = TextUtils.join(", ", participantNames) recipient = TextUtils.join(", ", participantNames)
} }
val date = message.date.formatDateOrTime(this, true, true) val date = (message.date * 1000L).formatDateOrTime(
val searchResult = SearchResult(message.id, recipient, message.body, date, message.threadId, message.senderPhotoUri) context = this,
hideTimeOnOtherDays = true,
showCurrentYear = true
)
val searchResult = SearchResult(
messageId = message.id,
title = recipient,
snippet = message.body,
date = date,
threadId = message.threadId,
photoUri = message.senderPhotoUri
)
searchResults.add(searchResult) searchResults.add(searchResult)
} }
@ -579,17 +640,36 @@ class MainActivity : SimpleActivity() {
val licenses = LICENSE_EVENT_BUS or LICENSE_SMS_MMS or LICENSE_INDICATOR_FAST_SCROLL val licenses = LICENSE_EVENT_BUS or LICENSE_SMS_MMS or LICENSE_INDICATOR_FAST_SCROLL
val faqItems = arrayListOf( val faqItems = arrayListOf(
FAQItem(R.string.faq_2_title, R.string.faq_2_text), FAQItem(title = R.string.faq_2_title, text = R.string.faq_2_text),
FAQItem(R.string.faq_3_title, R.string.faq_3_text), FAQItem(title = R.string.faq_3_title, text = R.string.faq_3_text),
FAQItem(org.fossify.commons.R.string.faq_9_title_commons, org.fossify.commons.R.string.faq_9_text_commons) FAQItem(
title = org.fossify.commons.R.string.faq_9_title_commons,
text = org.fossify.commons.R.string.faq_9_text_commons
)
) )
if (!resources.getBoolean(org.fossify.commons.R.bool.hide_google_relations)) { if (!resources.getBoolean(org.fossify.commons.R.bool.hide_google_relations)) {
faqItems.add(FAQItem(org.fossify.commons.R.string.faq_2_title_commons, org.fossify.commons.R.string.faq_2_text_commons)) faqItems.add(
faqItems.add(FAQItem(org.fossify.commons.R.string.faq_6_title_commons, org.fossify.commons.R.string.faq_6_text_commons)) FAQItem(
title = org.fossify.commons.R.string.faq_2_title_commons,
text = org.fossify.commons.R.string.faq_2_text_commons
)
)
faqItems.add(
FAQItem(
title = org.fossify.commons.R.string.faq_6_title_commons,
text = org.fossify.commons.R.string.faq_6_text_commons
)
)
} }
startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true) startAboutActivity(
appNameId = R.string.app_name,
licenseMask = licenses,
versionName = BuildConfig.VERSION_NAME,
faqItems = faqItems,
showFAQBeforeMail = true
)
} }
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)

View file

@ -6,7 +6,6 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import org.fossify.commons.activities.BaseSimpleActivity
import org.fossify.commons.dialogs.ExportBlockedNumbersDialog import org.fossify.commons.dialogs.ExportBlockedNumbersDialog
import org.fossify.commons.dialogs.FilePickerDialog import org.fossify.commons.dialogs.FilePickerDialog
import org.fossify.commons.extensions.beVisibleIf import org.fossify.commons.extensions.beVisibleIf
@ -19,8 +18,6 @@ import org.fossify.commons.extensions.toast
import org.fossify.commons.extensions.underlineText import org.fossify.commons.extensions.underlineText
import org.fossify.commons.extensions.updateTextColors import org.fossify.commons.extensions.updateTextColors
import org.fossify.commons.extensions.viewBinding import org.fossify.commons.extensions.viewBinding
import org.fossify.commons.helpers.APP_ICON_IDS
import org.fossify.commons.helpers.APP_LAUNCHER_NAME
import org.fossify.commons.helpers.ExportResult import org.fossify.commons.helpers.ExportResult
import org.fossify.commons.helpers.NavigationIcon import org.fossify.commons.helpers.NavigationIcon
import org.fossify.commons.helpers.PERMISSION_READ_STORAGE import org.fossify.commons.helpers.PERMISSION_READ_STORAGE
@ -40,11 +37,7 @@ import org.fossify.messages.helpers.BlockedKeywordsImporter
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.OutputStream import java.io.OutputStream
class ManageBlockedKeywordsActivity : BaseSimpleActivity(), RefreshRecyclerViewListener { class ManageBlockedKeywordsActivity : SimpleActivity(), RefreshRecyclerViewListener {
override fun getAppIconIDs() = intent.getIntegerArrayListExtra(APP_ICON_IDS) ?: ArrayList()
override fun getAppLauncherName() = intent.getStringExtra(APP_LAUNCHER_NAME) ?: ""
private val binding by viewBinding(ActivityManageBlockedKeywordsBinding::inflate) private val binding by viewBinding(ActivityManageBlockedKeywordsBinding::inflate)
@ -131,10 +124,11 @@ class ManageBlockedKeywordsActivity : BaseSimpleActivity(), RefreshRecyclerViewL
if (isQPlus()) { if (isQPlus()) {
Intent(Intent.ACTION_GET_CONTENT).apply { Intent(Intent.ACTION_GET_CONTENT).apply {
addCategory(Intent.CATEGORY_OPENABLE) addCategory(Intent.CATEGORY_OPENABLE)
type = "text/plain" val mimeType = "text/plain"
type = mimeType
try { try {
importActivityResultLauncher.launch(type) importActivityResultLauncher.launch(mimeType)
} catch (e: ActivityNotFoundException) { } catch (e: ActivityNotFoundException) {
toast(org.fossify.commons.R.string.system_service_disabled, Toast.LENGTH_LONG) toast(org.fossify.commons.R.string.system_service_disabled, Toast.LENGTH_LONG)
} catch (e: Exception) { } catch (e: Exception) {

View file

@ -4,9 +4,13 @@ import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import org.fossify.commons.dialogs.ConfirmationDialog import org.fossify.commons.dialogs.ConfirmationDialog
import org.fossify.commons.extensions.* import org.fossify.commons.extensions.areSystemAnimationsEnabled
import org.fossify.commons.extensions.beGoneIf
import org.fossify.commons.extensions.beVisibleIf
import org.fossify.commons.extensions.getProperBackgroundColor
import org.fossify.commons.extensions.hideKeyboard
import org.fossify.commons.extensions.viewBinding
import org.fossify.commons.helpers.NavigationIcon import org.fossify.commons.helpers.NavigationIcon
import org.fossify.commons.helpers.WAS_PROTECTION_HANDLED
import org.fossify.commons.helpers.ensureBackgroundThread import org.fossify.commons.helpers.ensureBackgroundThread
import org.fossify.messages.R import org.fossify.messages.R
import org.fossify.messages.adapters.RecycleBinConversationsAdapter import org.fossify.messages.adapters.RecycleBinConversationsAdapter
@ -40,7 +44,10 @@ class RecycleBinConversationsActivity : SimpleActivity() {
useTransparentNavigation = true, useTransparentNavigation = true,
useTopSearchMenu = false useTopSearchMenu = false
) )
setupMaterialScrollListener(scrollingView = binding.conversationsList, toolbar = binding.recycleBinToolbar) setupMaterialScrollListener(
scrollingView = binding.conversationsList,
toolbar = binding.recycleBinToolbar
)
loadRecycleBinConversations() loadRecycleBinConversations()
} }
@ -82,7 +89,8 @@ class RecycleBinConversationsActivity : SimpleActivity() {
private fun loadRecycleBinConversations() { private fun loadRecycleBinConversations() {
ensureBackgroundThread { ensureBackgroundThread {
val conversations = try { val conversations = try {
conversationsDB.getAllWithMessagesInRecycleBin().toMutableList() as ArrayList<Conversation> conversationsDB.getAllWithMessagesInRecycleBin()
.toMutableList() as ArrayList<Conversation>
} catch (e: Exception) { } catch (e: Exception) {
ArrayList() ArrayList()
} }
@ -166,7 +174,6 @@ class RecycleBinConversationsActivity : SimpleActivity() {
val conversation = any as Conversation val conversation = any as Conversation
putExtra(THREAD_ID, conversation.threadId) putExtra(THREAD_ID, conversation.threadId)
putExtra(THREAD_TITLE, conversation.title) putExtra(THREAD_TITLE, conversation.title)
putExtra(WAS_PROTECTION_HANDLED, true)
putExtra(IS_RECYCLE_BIN, true) putExtra(IS_RECYCLE_BIN, true)
startActivity(this) startActivity(this)
} }

View file

@ -27,4 +27,6 @@ open class SimpleActivity : BaseSimpleActivity() {
) )
override fun getAppLauncherName() = getString(R.string.app_launcher_name) override fun getAppLauncherName() = getString(R.string.app_launcher_name)
override fun getRepositoryName() = "Messages"
} }

View file

@ -36,9 +36,14 @@ import android.widget.LinearLayout.LayoutParams
import android.widget.RelativeLayout import android.widget.RelativeLayout
import android.widget.Toast import android.widget.Toast
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.appcompat.content.res.AppCompatResources
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.view.* import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsAnimationCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.gson.Gson import com.google.gson.Gson
@ -47,8 +52,52 @@ import org.fossify.commons.dialogs.ConfirmationDialog
import org.fossify.commons.dialogs.FeatureLockedDialog import org.fossify.commons.dialogs.FeatureLockedDialog
import org.fossify.commons.dialogs.PermissionRequiredDialog import org.fossify.commons.dialogs.PermissionRequiredDialog
import org.fossify.commons.dialogs.RadioGroupDialog import org.fossify.commons.dialogs.RadioGroupDialog
import org.fossify.commons.extensions.* import org.fossify.commons.extensions.addBlockedNumber
import org.fossify.commons.helpers.* import org.fossify.commons.extensions.addLockedLabelIfNeeded
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.darkenColor
import org.fossify.commons.extensions.formatDate
import org.fossify.commons.extensions.getBottomNavigationBackgroundColor
import org.fossify.commons.extensions.getContrastColor
import org.fossify.commons.extensions.getFilenameFromUri
import org.fossify.commons.extensions.getMyContactsCursor
import org.fossify.commons.extensions.getMyFileUri
import org.fossify.commons.extensions.getProperBackgroundColor
import org.fossify.commons.extensions.getProperPrimaryColor
import org.fossify.commons.extensions.getProperTextColor
import org.fossify.commons.extensions.getTextSize
import org.fossify.commons.extensions.hideKeyboard
import org.fossify.commons.extensions.isDynamicTheme
import org.fossify.commons.extensions.isOrWasThankYouInstalled
import org.fossify.commons.extensions.isVisible
import org.fossify.commons.extensions.launchActivityIntent
import org.fossify.commons.extensions.normalizeString
import org.fossify.commons.extensions.notificationManager
import org.fossify.commons.extensions.onTextChangeListener
import org.fossify.commons.extensions.openRequestExactAlarmSettings
import org.fossify.commons.extensions.realScreenSize
import org.fossify.commons.extensions.showErrorToast
import org.fossify.commons.extensions.showKeyboard
import org.fossify.commons.extensions.toInt
import org.fossify.commons.extensions.toast
import org.fossify.commons.extensions.updateTextColors
import org.fossify.commons.extensions.value
import org.fossify.commons.extensions.viewBinding
import org.fossify.commons.helpers.ContactsHelper
import org.fossify.commons.helpers.ExportResult
import org.fossify.commons.helpers.KEY_PHONE
import org.fossify.commons.helpers.MyContactsContentProvider
import org.fossify.commons.helpers.NavigationIcon
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.PhoneNumber
import org.fossify.commons.models.RadioItem import org.fossify.commons.models.RadioItem
import org.fossify.commons.models.SimpleContact import org.fossify.commons.models.SimpleContact
@ -63,11 +112,80 @@ import org.fossify.messages.databinding.ItemSelectedContactBinding
import org.fossify.messages.dialogs.InvalidNumberDialog import org.fossify.messages.dialogs.InvalidNumberDialog
import org.fossify.messages.dialogs.RenameConversationDialog import org.fossify.messages.dialogs.RenameConversationDialog
import org.fossify.messages.dialogs.ScheduleMessageDialog import org.fossify.messages.dialogs.ScheduleMessageDialog
import org.fossify.messages.extensions.* import org.fossify.messages.extensions.clearExpiredScheduledMessages
import org.fossify.messages.helpers.* import org.fossify.messages.extensions.config
import org.fossify.messages.messaging.* import org.fossify.messages.extensions.conversationsDB
import org.fossify.messages.models.* import org.fossify.messages.extensions.createTemporaryThread
import org.fossify.messages.models.ThreadItem.* import org.fossify.messages.extensions.deleteConversation
import org.fossify.messages.extensions.deleteMessage
import org.fossify.messages.extensions.deleteScheduledMessage
import org.fossify.messages.extensions.deleteSmsDraft
import org.fossify.messages.extensions.dialNumber
import org.fossify.messages.extensions.emptyMessagesRecycleBinForConversation
import org.fossify.messages.extensions.getAddresses
import org.fossify.messages.extensions.getDefaultKeyboardHeight
import org.fossify.messages.extensions.getFileSizeFromUri
import org.fossify.messages.extensions.getMessages
import org.fossify.messages.extensions.getSmsDraft
import org.fossify.messages.extensions.getThreadId
import org.fossify.messages.extensions.getThreadParticipants
import org.fossify.messages.extensions.getThreadTitle
import org.fossify.messages.extensions.indexOfFirstOrNull
import org.fossify.messages.extensions.isGifMimeType
import org.fossify.messages.extensions.isImageMimeType
import org.fossify.messages.extensions.markMessageRead
import org.fossify.messages.extensions.markThreadMessagesUnread
import org.fossify.messages.extensions.messagesDB
import org.fossify.messages.extensions.moveMessageToRecycleBin
import org.fossify.messages.extensions.removeDiacriticsIfNeeded
import org.fossify.messages.extensions.renameConversation
import org.fossify.messages.extensions.restoreAllMessagesFromRecycleBinForConversation
import org.fossify.messages.extensions.restoreMessageFromRecycleBin
import org.fossify.messages.extensions.saveSmsDraft
import org.fossify.messages.extensions.showWithAnimation
import org.fossify.messages.extensions.subscriptionManagerCompat
import org.fossify.messages.extensions.toArrayList
import org.fossify.messages.extensions.updateConversationArchivedStatus
import org.fossify.messages.extensions.updateLastConversationMessage
import org.fossify.messages.extensions.updateScheduledMessagesThreadId
import org.fossify.messages.helpers.CAPTURE_AUDIO_INTENT
import org.fossify.messages.helpers.CAPTURE_PHOTO_INTENT
import org.fossify.messages.helpers.CAPTURE_VIDEO_INTENT
import org.fossify.messages.helpers.FILE_SIZE_NONE
import org.fossify.messages.helpers.IS_RECYCLE_BIN
import org.fossify.messages.helpers.MESSAGES_LIMIT
import org.fossify.messages.helpers.PICK_CONTACT_INTENT
import org.fossify.messages.helpers.PICK_DOCUMENT_INTENT
import org.fossify.messages.helpers.PICK_PHOTO_INTENT
import org.fossify.messages.helpers.PICK_SAVE_FILE_INTENT
import org.fossify.messages.helpers.PICK_VIDEO_INTENT
import org.fossify.messages.helpers.SEARCHED_MESSAGE_ID
import org.fossify.messages.helpers.THREAD_ATTACHMENT_URI
import org.fossify.messages.helpers.THREAD_ATTACHMENT_URIS
import org.fossify.messages.helpers.THREAD_ID
import org.fossify.messages.helpers.THREAD_NUMBER
import org.fossify.messages.helpers.THREAD_TEXT
import org.fossify.messages.helpers.THREAD_TITLE
import org.fossify.messages.helpers.generateRandomId
import org.fossify.messages.helpers.refreshMessages
import org.fossify.messages.messaging.cancelScheduleSendPendingIntent
import org.fossify.messages.messaging.isLongMmsMessage
import org.fossify.messages.messaging.isShortCodeWithLetters
import org.fossify.messages.messaging.scheduleMessage
import org.fossify.messages.messaging.sendMessageCompat
import org.fossify.messages.models.Attachment
import org.fossify.messages.models.AttachmentSelection
import org.fossify.messages.models.Conversation
import org.fossify.messages.models.Events
import org.fossify.messages.models.Message
import org.fossify.messages.models.MessageAttachment
import org.fossify.messages.models.SIMCard
import org.fossify.messages.models.ThreadItem
import org.fossify.messages.models.ThreadItem.ThreadDateTime
import org.fossify.messages.models.ThreadItem.ThreadError
import org.fossify.messages.models.ThreadItem.ThreadLoading
import org.fossify.messages.models.ThreadItem.ThreadSending
import org.fossify.messages.models.ThreadItem.ThreadSent
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
@ -75,6 +193,7 @@ import org.joda.time.DateTime
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import kotlin.collections.set
class ThreadActivity : SimpleActivity() { class ThreadActivity : SimpleActivity() {
private val MIN_DATE_TIME_DIFF_SECS = 300 private val MIN_DATE_TIME_DIFF_SECS = 300
@ -101,7 +220,6 @@ class ThreadActivity : SimpleActivity() {
private var loadingOlderMessages = false private var loadingOlderMessages = false
private var allMessagesFetched = false private var allMessagesFetched = false
private var oldestMessageDate = -1 private var oldestMessageDate = -1
private var wasProtectionHandled = false
private var isRecycleBin = false private var isRecycleBin = false
private var isScheduledMessage: Boolean = false private var isScheduledMessage: Boolean = false
@ -113,7 +231,7 @@ class ThreadActivity : SimpleActivity() {
private val binding by viewBinding(ActivityThreadBinding::inflate) private val binding by viewBinding(ActivityThreadBinding::inflate)
override fun onNewIntent(intent: Intent?) { override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent) super.onNewIntent(intent)
finish() finish()
startActivity(intent) startActivity(intent)
@ -126,7 +244,12 @@ class ThreadActivity : SimpleActivity() {
setupOptionsMenu() setupOptionsMenu()
refreshMenuItems() refreshMenuItems()
updateMaterialActivityViews(binding.threadCoordinator, null, useTransparentNavigation = false, useTopSearchMenu = false) updateMaterialActivityViews(
mainCoordinatorLayout = binding.threadCoordinator,
nestedView = null,
useTransparentNavigation = false,
useTopSearchMenu = false
)
setupMaterialScrollListener(null, binding.threadToolbar) setupMaterialScrollListener(null, binding.threadToolbar)
val extras = intent.extras val extras = intent.extras
@ -141,28 +264,11 @@ class ThreadActivity : SimpleActivity() {
binding.threadToolbar.title = it binding.threadToolbar.title = it
} }
isRecycleBin = intent.getBooleanExtra(IS_RECYCLE_BIN, false) isRecycleBin = intent.getBooleanExtra(IS_RECYCLE_BIN, false)
wasProtectionHandled = intent.getBooleanExtra(WAS_PROTECTION_HANDLED, false)
bus = EventBus.getDefault() bus = EventBus.getDefault()
bus!!.register(this) bus!!.register(this)
if (savedInstanceState == null) {
if (!wasProtectionHandled) {
handleAppPasswordProtection {
wasProtectionHandled = it
if (it) {
clearAllMessagesIfNeeded {
loadConversation() loadConversation()
}
} else {
finish()
}
}
} else {
loadConversation()
}
}
setupAttachmentPickerView() setupAttachmentPickerView()
setupKeyboardListener() setupKeyboardListener()
hideAttachmentPicker() hideAttachmentPicker()
@ -171,7 +277,11 @@ class ThreadActivity : SimpleActivity() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
setupToolbar(binding.threadToolbar, NavigationIcon.Arrow, statusBarColor = getProperBackgroundColor()) setupToolbar(
toolbar = binding.threadToolbar,
toolbarNavigationIcon = NavigationIcon.Arrow,
statusBarColor = getProperBackgroundColor()
)
val smsDraft = getSmsDraft(threadId) val smsDraft = getSmsDraft(threadId)
if (smsDraft != null) { if (smsDraft != null) {
@ -224,47 +334,30 @@ class ThreadActivity : SimpleActivity() {
bus?.unregister(this) bus?.unregister(this)
} }
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(WAS_PROTECTION_HANDLED, wasProtectionHandled)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
wasProtectionHandled = savedInstanceState.getBoolean(WAS_PROTECTION_HANDLED, false)
if (!wasProtectionHandled) {
handleAppPasswordProtection {
wasProtectionHandled = it
if (it) {
loadConversation()
} else {
finish()
}
}
} else {
loadConversation()
}
}
private fun refreshMenuItems() { private fun refreshMenuItems() {
val firstPhoneNumber = participants.firstOrNull()?.phoneNumbers?.firstOrNull()?.value val firstPhoneNumber = participants.firstOrNull()?.phoneNumbers?.firstOrNull()?.value
val archiveAvailable = config.isArchiveAvailable val archiveAvailable = config.isArchiveAvailable
binding.threadToolbar.menu.apply { binding.threadToolbar.menu.apply {
findItem(R.id.delete).isVisible = threadItems.isNotEmpty() findItem(R.id.delete).isVisible = threadItems.isNotEmpty()
findItem(R.id.restore).isVisible = threadItems.isNotEmpty() && isRecycleBin findItem(R.id.restore).isVisible = threadItems.isNotEmpty() && isRecycleBin
findItem(R.id.archive).isVisible = threadItems.isNotEmpty() && conversation?.isArchived == false && !isRecycleBin && archiveAvailable findItem(R.id.archive).isVisible =
findItem(R.id.unarchive).isVisible = threadItems.isNotEmpty() && conversation?.isArchived == true && !isRecycleBin && archiveAvailable threadItems.isNotEmpty() && conversation?.isArchived == false && !isRecycleBin && archiveAvailable
findItem(R.id.rename_conversation).isVisible = participants.size > 1 && conversation != null && !isRecycleBin findItem(R.id.unarchive).isVisible =
threadItems.isNotEmpty() && conversation?.isArchived == true && !isRecycleBin && archiveAvailable
findItem(R.id.rename_conversation).isVisible =
participants.size > 1 && conversation != null && !isRecycleBin
findItem(R.id.conversation_details).isVisible = conversation != null && !isRecycleBin 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).title =
addLockedLabelIfNeeded(org.fossify.commons.R.string.block_number)
findItem(R.id.block_number).isVisible = isNougatPlus() && !isRecycleBin findItem(R.id.block_number).isVisible = isNougatPlus() && !isRecycleBin
findItem(R.id.dial_number).isVisible = participants.size == 1 && !isSpecialNumber() && !isRecycleBin findItem(R.id.dial_number).isVisible =
participants.size == 1 && !isSpecialNumber() && !isRecycleBin
findItem(R.id.manage_people).isVisible = !isSpecialNumber() && !isRecycleBin findItem(R.id.manage_people).isVisible = !isSpecialNumber() && !isRecycleBin
findItem(R.id.mark_as_unread).isVisible = threadItems.isNotEmpty() && !isRecycleBin findItem(R.id.mark_as_unread).isVisible = threadItems.isNotEmpty() && !isRecycleBin
// allow saving number in cases when we dont have it stored yet and it is a casual readable number // allow saving number in cases when we don't have it stored yet and it is a casual readable number
findItem(R.id.add_number_to_contact).isVisible = participants.size == 1 && participants.first().name == firstPhoneNumber && firstPhoneNumber.any { findItem(R.id.add_number_to_contact).isVisible =
participants.size == 1 && participants.first().name == firstPhoneNumber && firstPhoneNumber.any {
it.isDigit() it.isDigit()
} && !isRecycleBin } && !isRecycleBin
} }
@ -304,7 +397,12 @@ class ThreadActivity : SimpleActivity() {
addAttachment(capturedImageUri!!) addAttachment(capturedImageUri!!)
} else if (data != null) { } else if (data != null) {
when (requestCode) { when (requestCode) {
CAPTURE_VIDEO_INTENT, PICK_DOCUMENT_INTENT, CAPTURE_AUDIO_INTENT, PICK_PHOTO_INTENT, PICK_VIDEO_INTENT -> addAttachment(data) CAPTURE_VIDEO_INTENT,
PICK_DOCUMENT_INTENT,
CAPTURE_AUDIO_INTENT,
PICK_PHOTO_INTENT,
PICK_VIDEO_INTENT -> addAttachment(data)
PICK_CONTACT_INTENT -> addContactAttachment(data) PICK_CONTACT_INTENT -> addContactAttachment(data)
PICK_SAVE_FILE_INTENT -> saveAttachment(resultData) PICK_SAVE_FILE_INTENT -> saveAttachment(resultData)
} }
@ -360,8 +458,10 @@ class ThreadActivity : SimpleActivity() {
if (!isRecycleBin) { if (!isRecycleBin) {
messages = getMessages(threadId, true) messages = getMessages(threadId, true)
if (config.useRecycleBin) { if (config.useRecycleBin) {
val recycledMessages = messagesDB.getThreadMessagesFromRecycleBin(threadId).map { it.id } val recycledMessages =
messages = messages.filter { !recycledMessages.contains(it.id) }.toMutableList() as ArrayList<Message> messagesDB.getThreadMessagesFromRecycleBin(threadId).map { it.id }
messages = messages.filter { !recycledMessages.contains(it.id) }
.toMutableList() as ArrayList<Message>
} }
} }
@ -383,8 +483,10 @@ class ThreadActivity : SimpleActivity() {
if (privateContacts.isNotEmpty()) { if (privateContacts.isNotEmpty()) {
val senderNumbersToReplace = HashMap<String, String>() val senderNumbersToReplace = HashMap<String, String>()
participants.filter { it.doesHavePhoneNumber(it.name) }.forEach { participant -> participants.filter { it.doesHavePhoneNumber(it.name) }.forEach { participant ->
privateContacts.firstOrNull { it.doesHavePhoneNumber(participant.phoneNumbers.first().normalizedNumber) }?.apply { privateContacts.firstOrNull { it.doesHavePhoneNumber(participant.phoneNumbers.first().normalizedNumber) }
senderNumbersToReplace[participant.phoneNumbers.first().normalizedNumber] = name ?.apply {
senderNumbersToReplace[participant.phoneNumbers.first().normalizedNumber] =
name
participant.name = name participant.name = name
participant.photoUri = photoUri participant.photoUri = photoUri
} }
@ -407,7 +509,15 @@ class ThreadActivity : SimpleActivity() {
} }
val phoneNumber = PhoneNumber(number, 0, "", number) val phoneNumber = PhoneNumber(number, 0, "", number)
val contact = SimpleContact(0, 0, name, "", arrayListOf(phoneNumber), ArrayList(), ArrayList()) val contact = SimpleContact(
rawId = 0,
contactId = 0,
name = name,
photoUri = "",
phoneNumbers = arrayListOf(phoneNumber),
birthdays = ArrayList(),
anniversaries = ArrayList()
)
participants.add(contact) participants.add(contact)
} }
@ -434,11 +544,18 @@ class ThreadActivity : SimpleActivity() {
recyclerView = binding.threadMessagesList, recyclerView = binding.threadMessagesList,
itemClick = { handleItemClick(it) }, itemClick = { handleItemClick(it) },
isRecycleBin = isRecycleBin, isRecycleBin = isRecycleBin,
deleteMessages = { messages, toRecycleBin, fromRecycleBin -> deleteMessages(messages, toRecycleBin, fromRecycleBin) } deleteMessages = { messages, toRecycleBin, fromRecycleBin ->
deleteMessages(
messages,
toRecycleBin,
fromRecycleBin
)
}
) )
binding.threadMessagesList.adapter = currAdapter binding.threadMessagesList.adapter = currAdapter
binding.threadMessagesList.endlessScrollListener = object : MyRecyclerView.EndlessScrollListener { binding.threadMessagesList.endlessScrollListener =
object : MyRecyclerView.EndlessScrollListener {
override fun updateBottom() {} override fun updateBottom() {}
override fun updateTop() { override fun updateTop() {
@ -458,7 +575,8 @@ class ThreadActivity : SimpleActivity() {
val layoutManager = binding.threadMessagesList.layoutManager as LinearLayoutManager val layoutManager = binding.threadMessagesList.layoutManager as LinearLayoutManager
val lastPosition = itemCount - 1 val lastPosition = itemCount - 1
val lastVisiblePosition = layoutManager.findLastVisibleItemPosition() val lastVisiblePosition = layoutManager.findLastVisibleItemPosition()
val shouldScrollToBottom = currentList.lastOrNull() != threadItems.lastOrNull() && lastPosition - lastVisiblePosition == 1 val shouldScrollToBottom =
currentList.lastOrNull() != threadItems.lastOrNull() && lastPosition - lastVisiblePosition == 1
updateMessages(threadItems, if (shouldScrollToBottom) lastPosition else -1) updateMessages(threadItems, if (shouldScrollToBottom) lastPosition else -1)
} }
} }
@ -470,7 +588,8 @@ class ThreadActivity : SimpleActivity() {
binding.addContactOrNumber.setAdapter(adapter) binding.addContactOrNumber.setAdapter(adapter)
binding.addContactOrNumber.imeOptions = EditorInfo.IME_ACTION_NEXT binding.addContactOrNumber.imeOptions = EditorInfo.IME_ACTION_NEXT
binding.addContactOrNumber.setOnItemClickListener { _, _, position, _ -> binding.addContactOrNumber.setOnItemClickListener { _, _, position, _ ->
val currContacts = (binding.addContactOrNumber.adapter as AutoCompleteTextViewAdapter).resultList val currContacts =
(binding.addContactOrNumber.adapter as AutoCompleteTextViewAdapter).resultList
val selectedContact = currContacts[position] val selectedContact = currContacts[position]
addSelectedContact(selectedContact) addSelectedContact(selectedContact)
} }
@ -485,7 +604,15 @@ class ThreadActivity : SimpleActivity() {
binding.confirmInsertedNumber.setOnClickListener { binding.confirmInsertedNumber.setOnClickListener {
val number = binding.addContactOrNumber.value val number = binding.addContactOrNumber.value
val phoneNumber = PhoneNumber(number, 0, "", number) val phoneNumber = PhoneNumber(number, 0, "", number)
val contact = SimpleContact(number.hashCode(), number.hashCode(), number, "", arrayListOf(phoneNumber), ArrayList(), ArrayList()) val contact = SimpleContact(
rawId = number.hashCode(),
contactId = number.hashCode(),
name = number,
photoUri = "",
phoneNumbers = arrayListOf(phoneNumber),
birthdays = ArrayList(),
anniversaries = ArrayList()
)
addSelectedContact(contact) addSelectedContact(contact)
} }
} }
@ -504,7 +631,8 @@ class ThreadActivity : SimpleActivity() {
super.onScrolled(recyclerView, dx, dy) super.onScrolled(recyclerView, dx, dy)
val layoutManager = binding.threadMessagesList.layoutManager as LinearLayoutManager val layoutManager = binding.threadMessagesList.layoutManager as LinearLayoutManager
val lastVisibleItemPosition = layoutManager.findLastCompletelyVisibleItemPosition() val lastVisibleItemPosition = layoutManager.findLastCompletelyVisibleItemPosition()
val isCloseToBottom = lastVisibleItemPosition >= getOrCreateThreadAdapter().itemCount - SCROLL_TO_BOTTOM_FAB_LIMIT val isCloseToBottom =
lastVisibleItemPosition >= getOrCreateThreadAdapter().itemCount - SCROLL_TO_BOTTOM_FAB_LIMIT
if (isCloseToBottom) { if (isCloseToBottom) {
binding.scrollToBottomFab.hide() binding.scrollToBottomFab.hide()
} else { } else {
@ -524,7 +652,11 @@ class ThreadActivity : SimpleActivity() {
} }
} }
private fun deleteMessages(messagesToRemove: List<Message>, toRecycleBin: Boolean, fromRecycleBin: Boolean) { private fun deleteMessages(
messagesToRemove: List<Message>,
toRecycleBin: Boolean,
fromRecycleBin: Boolean
) {
val deletePosition = threadItems.indexOf(messagesToRemove.first()) val deletePosition = threadItems.indexOf(messagesToRemove.first())
messages.removeAll(messagesToRemove.toSet()) messages.removeAll(messagesToRemove.toSet())
threadItems = getThreadItems() threadItems = getThreadItems()
@ -574,7 +706,10 @@ class ThreadActivity : SimpleActivity() {
val newList = currentList.toMutableList().apply { val newList = currentList.toMutableList().apply {
removeAll { it is ThreadLoading } removeAll { it is ThreadLoading }
} }
updateMessages(newMessages = newList as ArrayList<ThreadItem>, scrollPosition = 0) updateMessages(
newMessages = newList as ArrayList<ThreadItem>,
scrollPosition = 0
)
} }
} }
return return
@ -615,7 +750,8 @@ class ThreadActivity : SimpleActivity() {
val searchedMessageId = intent.getLongExtra(SEARCHED_MESSAGE_ID, -1L) val searchedMessageId = intent.getLongExtra(SEARCHED_MESSAGE_ID, -1L)
intent.removeExtra(SEARCHED_MESSAGE_ID) intent.removeExtra(SEARCHED_MESSAGE_ID)
if (searchedMessageId != -1L) { if (searchedMessageId != -1L) {
val index = threadItems.indexOfFirst { (it as? Message)?.id == searchedMessageId } val index =
threadItems.indexOfFirst { (it as? Message)?.id == searchedMessageId }
if (index != -1) { if (index != -1) {
binding.threadMessagesList.smoothScrollToPosition(index) binding.threadMessagesList.smoothScrollToPosition(index)
} }
@ -677,6 +813,7 @@ class ThreadActivity : SimpleActivity() {
it it
} }
val messageLength = SmsMessage.calculateLength(messageString, false) val messageLength = SmsMessage.calculateLength(messageString, false)
@SuppressLint("SetTextI18n")
threadCharacterCounter.text = "${messageLength[2]}/${messageLength[0]}" threadCharacterCounter.text = "${messageLength[2]}/${messageLength[0]}"
} }
@ -726,11 +863,13 @@ class ThreadActivity : SimpleActivity() {
threadAddAttachment.setOnClickListener { threadAddAttachment.setOnClickListener {
if (attachmentPickerHolder.isVisible()) { if (attachmentPickerHolder.isVisible()) {
isAttachmentPickerVisible = false isAttachmentPickerVisible = false
WindowCompat.getInsetsController(window, threadTypeMessage).show(WindowInsetsCompat.Type.ime()) WindowCompat.getInsetsController(window, threadTypeMessage)
.show(WindowInsetsCompat.Type.ime())
} else { } else {
isAttachmentPickerVisible = true isAttachmentPickerVisible = true
showOrHideAttachmentPicker() showOrHideAttachmentPicker()
WindowCompat.getInsetsController(window, threadTypeMessage).hide(WindowInsetsCompat.Type.ime()) WindowCompat.getInsetsController(window, threadTypeMessage)
.hide(WindowInsetsCompat.Type.ime())
} }
window.decorView.requestApplyInsets() window.decorView.requestApplyInsets()
} }
@ -779,14 +918,22 @@ class ThreadActivity : SimpleActivity() {
if (it.mimetype.startsWith("image/")) { if (it.mimetype.startsWith("image/")) {
val fileOptions = BitmapFactory.Options() val fileOptions = BitmapFactory.Options()
fileOptions.inJustDecodeBounds = true fileOptions.inJustDecodeBounds = true
BitmapFactory.decodeStream(contentResolver.openInputStream(it.getUri()), null, fileOptions) BitmapFactory.decodeStream(
contentResolver.openInputStream(it.getUri()),
null,
fileOptions
)
it.width = fileOptions.outWidth it.width = fileOptions.outWidth
it.height = fileOptions.outHeight it.height = fileOptions.outHeight
} else if (it.mimetype.startsWith("video/")) { } else if (it.mimetype.startsWith("video/")) {
val metaRetriever = MediaMetadataRetriever() val metaRetriever = MediaMetadataRetriever()
metaRetriever.setDataSource(this, it.getUri()) metaRetriever.setDataSource(this, it.getUri())
it.width = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)!!.toInt() it.width = metaRetriever.extractMetadata(
it.height = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)!!.toInt() MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH
)!!.toInt()
it.height = metaRetriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT
)!!.toInt()
} }
if (it.width < 0) { if (it.width < 0) {
@ -885,6 +1032,7 @@ class ThreadActivity : SimpleActivity() {
binding.messageHolder.threadSelectSimIcon.setOnClickListener { binding.messageHolder.threadSelectSimIcon.setOnClickListener {
currentSIMCardIndex = (currentSIMCardIndex + 1) % availableSIMCards.size currentSIMCardIndex = (currentSIMCardIndex + 1) % availableSIMCards.size
val currentSIMCard = availableSIMCards[currentSIMCardIndex] val currentSIMCard = availableSIMCards[currentSIMCardIndex]
@SuppressLint("SetTextI18n")
binding.messageHolder.threadSelectSimNumber.text = currentSIMCard.id.toString() binding.messageHolder.threadSelectSimNumber.text = currentSIMCard.id.toString()
val currentSubscriptionId = currentSIMCard.subscriptionId val currentSubscriptionId = currentSIMCard.subscriptionId
numbers.forEach { numbers.forEach {
@ -894,9 +1042,13 @@ class ThreadActivity : SimpleActivity() {
} }
} }
binding.messageHolder.threadSelectSimNumber.setTextColor(getProperTextColor().getContrastColor()) binding.messageHolder.threadSelectSimNumber.setTextColor(
getProperTextColor().getContrastColor()
)
try { try {
binding.messageHolder.threadSelectSimNumber.text = (availableSIMCards[currentSIMCardIndex].id).toString() @SuppressLint("SetTextI18n")
binding.messageHolder.threadSelectSimNumber.text =
(availableSIMCards[currentSIMCardIndex].id).toString()
} catch (e: Exception) { } catch (e: Exception) {
showErrorToast(e) showErrorToast(e)
} }
@ -904,9 +1056,13 @@ class ThreadActivity : SimpleActivity() {
} }
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
private fun getProperSimIndex(availableSIMs: MutableList<SubscriptionInfo>, numbers: List<String>): Int { private fun getProperSimIndex(
availableSIMs: MutableList<SubscriptionInfo>,
numbers: List<String>
): Int {
val userPreferredSimId = config.getUseSIMIdAtNumber(numbers.first()) val userPreferredSimId = config.getUseSIMIdAtNumber(numbers.first())
val userPreferredSimIdx = availableSIMs.indexOfFirstOrNull { it.subscriptionId == userPreferredSimId } val userPreferredSimIdx =
availableSIMs.indexOfFirstOrNull { it.subscriptionId == userPreferredSimId }
val lastMessage = messages.lastOrNull() val lastMessage = messages.lastOrNull()
val senderPreferredSimIdx = if (lastMessage?.isReceivedMessage() == true) { val senderPreferredSimIdx = if (lastMessage?.isReceivedMessage() == true) {
@ -936,7 +1092,10 @@ class ThreadActivity : SimpleActivity() {
private fun blockNumber() { private fun blockNumber() {
val numbers = participants.getAddresses() val numbers = participants.getAddresses()
val numbersString = TextUtils.join(", ", numbers) 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(this, question) { ConfirmationDialog(this, question) {
ensureBackgroundThread { ensureBackgroundThread {
@ -1021,8 +1180,13 @@ class ThreadActivity : SimpleActivity() {
val views = ArrayList<View>() val views = ArrayList<View>()
participants.forEach { contact -> participants.forEach { contact ->
ItemSelectedContactBinding.inflate(layoutInflater).apply { ItemSelectedContactBinding.inflate(layoutInflater).apply {
val selectedContactBg = resources.getDrawable(R.drawable.item_selected_contact_background) val selectedContactBg =
(selectedContactBg as LayerDrawable).findDrawableByLayerId(R.id.selected_contact_bg).applyColorFilter(properPrimaryColor) AppCompatResources.getDrawable(
this@ThreadActivity,
R.drawable.item_selected_contact_background
)
(selectedContactBg as LayerDrawable).findDrawableByLayerId(R.id.selected_contact_bg)
.applyColorFilter(properPrimaryColor)
selectedContactHolder.background = selectedContactBg selectedContactHolder.background = selectedContactBg
selectedContactName.text = contact.name selectedContactName.text = contact.name
@ -1063,7 +1227,8 @@ class ThreadActivity : SimpleActivity() {
} }
private fun addNumberToContact() { private fun addNumberToContact() {
val phoneNumber = participants.firstOrNull()?.phoneNumbers?.firstOrNull()?.normalizedNumber ?: return val phoneNumber =
participants.firstOrNull()?.phoneNumbers?.firstOrNull()?.normalizedNumber ?: return
Intent().apply { Intent().apply {
action = Intent.ACTION_INSERT_OR_EDIT action = Intent.ACTION_INSERT_OR_EDIT
type = "vnd.android.cursor.item/contact" type = "vnd.android.cursor.item/contact"
@ -1113,7 +1278,8 @@ class ThreadActivity : SimpleActivity() {
val message = messages.getOrNull(i) ?: continue val message = messages.getOrNull(i) ?: continue
// do not show the date/time above every message, only if the difference between the 2 messages is at least MIN_DATE_TIME_DIFF_SECS, // do not show the date/time above every message, only if the difference between the 2 messages is at least MIN_DATE_TIME_DIFF_SECS,
// or if the message is sent from a different SIM // or if the message is sent from a different SIM
val isSentFromDifferentKnownSIM = prevSIMId != -1 && message.subscriptionId != -1 && prevSIMId != message.subscriptionId val isSentFromDifferentKnownSIM =
prevSIMId != -1 && message.subscriptionId != -1 && prevSIMId != message.subscriptionId
if (message.date - prevDateTime > MIN_DATE_TIME_DIFF_SECS || isSentFromDifferentKnownSIM) { if (message.date - prevDateTime > MIN_DATE_TIME_DIFF_SECS || isSentFromDifferentKnownSIM) {
val simCardID = subscriptionIdToSimId[message.subscriptionId] ?: "?" val simCardID = subscriptionIdToSimId[message.subscriptionId] ?: "?"
items.add(ThreadDateTime(message.date, simCardID)) items.add(ThreadDateTime(message.date, simCardID))
@ -1136,7 +1302,12 @@ class ThreadActivity : SimpleActivity() {
} }
if (i == cnt - 1 && (message.type == Telephony.Sms.MESSAGE_TYPE_SENT)) { if (i == cnt - 1 && (message.type == Telephony.Sms.MESSAGE_TYPE_SENT)) {
items.add(ThreadSent(message.id, delivered = message.status == Telephony.Sms.STATUS_COMPLETE)) items.add(
ThreadSent(
messageId = message.id,
delivered = message.status == Telephony.Sms.STATUS_COMPLETE
)
)
} }
prevSIMId = message.subscriptionId prevSIMId = message.subscriptionId
} }
@ -1153,7 +1324,11 @@ class ThreadActivity : SimpleActivity() {
return items return items
} }
private fun launchActivityForResult(intent: Intent, requestCode: Int, @StringRes error: Int = org.fossify.commons.R.string.no_app_found) { private fun launchActivityForResult(
intent: Intent,
requestCode: Int,
@StringRes error: Int = org.fossify.commons.R.string.no_app_found
) {
hideKeyboard() hideKeyboard()
try { try {
startActivityForResult(intent, requestCode) startActivityForResult(intent, requestCode)
@ -1293,13 +1468,18 @@ class ThreadActivity : SimpleActivity() {
} }
private fun saveAttachment(resultData: Intent) { private fun saveAttachment(resultData: Intent) {
val takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION val takeFlags =
applicationContext.contentResolver.takePersistableUriPermission(resultData.data!!, takeFlags) Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
applicationContext.contentResolver.takePersistableUriPermission(
resultData.data!!,
takeFlags
)
var inputStream: InputStream? = null var inputStream: InputStream? = null
var outputStream: OutputStream? = null var outputStream: OutputStream? = null
try { try {
inputStream = contentResolver.openInputStream(Uri.parse(lastAttachmentUri)) inputStream = contentResolver.openInputStream(Uri.parse(lastAttachmentUri))
outputStream = contentResolver.openOutputStream(Uri.parse(resultData.dataString!!), "rwt") outputStream =
contentResolver.openOutputStream(Uri.parse(resultData.dataString!!), "rwt")
inputStream!!.copyTo(outputStream!!) inputStream!!.copyTo(outputStream!!)
outputStream.flush() outputStream.flush()
toast(org.fossify.commons.R.string.file_saved) toast(org.fossify.commons.R.string.file_saved)
@ -1338,7 +1518,8 @@ class ThreadActivity : SimpleActivity() {
text = removeDiacriticsIfNeeded(text) text = removeDiacriticsIfNeeded(text)
val subscriptionId = availableSIMCards.getOrNull(currentSIMCardIndex)?.subscriptionId ?: SmsManager.getDefaultSmsSubscriptionId() val subscriptionId = availableSIMCards.getOrNull(currentSIMCardIndex)?.subscriptionId
?: SmsManager.getDefaultSmsSubscriptionId()
if (isScheduledMessage) { if (isScheduledMessage) {
sendScheduledMessage(text, subscriptionId) sendScheduledMessage(text, subscriptionId)
@ -1367,7 +1548,12 @@ class ThreadActivity : SimpleActivity() {
val conversation = conversationsDB.getConversationWithThreadId(threadId) val conversation = conversationsDB.getConversationWithThreadId(threadId)
if (conversation != null) { if (conversation != null) {
val nowSeconds = (System.currentTimeMillis() / 1000).toInt() val nowSeconds = (System.currentTimeMillis() / 1000).toInt()
conversationsDB.insertOrUpdate(conversation.copy(date = nowSeconds, snippet = message.body)) conversationsDB.insertOrUpdate(
conversation.copy(
date = nowSeconds,
snippet = message.body
)
)
} }
scheduleMessage(message) scheduleMessage(message)
insertOrUpdateMessage(message) insertOrUpdateMessage(message)
@ -1379,7 +1565,9 @@ class ThreadActivity : SimpleActivity() {
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
showErrorToast(e.localizedMessage ?: getString(org.fossify.commons.R.string.unknown_error_occurred)) showErrorToast(
e.localizedMessage ?: getString(org.fossify.commons.R.string.unknown_error_occurred)
)
} }
} }
@ -1392,7 +1580,11 @@ class ThreadActivity : SimpleActivity() {
sendMessageCompat(text, addresses, subscriptionId, attachments, messageToResend) sendMessageCompat(text, addresses, subscriptionId, attachments, messageToResend)
ensureBackgroundThread { ensureBackgroundThread {
val messageIds = messages.map { it.id } val messageIds = messages.map { it.id }
val messages = getMessages(threadId, getImageResolutions = true, limit = maxOf(1, attachments.size)) val messages = getMessages(
threadId = threadId,
getImageResolutions = true,
limit = maxOf(1, attachments.size)
)
.filter { it.id !in messageIds } .filter { it.id !in messageIds }
for (message in messages) { for (message in messages) {
insertOrUpdateMessage(message) insertOrUpdateMessage(message)
@ -1403,7 +1595,9 @@ class ThreadActivity : SimpleActivity() {
} catch (e: Exception) { } catch (e: Exception) {
showErrorToast(e) showErrorToast(e)
} catch (e: Error) { } catch (e: Error) {
showErrorToast(e.localizedMessage ?: getString(org.fossify.commons.R.string.unknown_error_occurred)) showErrorToast(
e.localizedMessage ?: getString(org.fossify.commons.R.string.unknown_error_occurred)
)
} }
} }
@ -1437,13 +1631,17 @@ class ThreadActivity : SimpleActivity() {
private fun showSelectedContact(views: ArrayList<View>) { private fun showSelectedContact(views: ArrayList<View>) {
binding.selectedContacts.removeAllViews() binding.selectedContacts.removeAllViews()
var newLinearLayout = LinearLayout(this) var newLinearLayout = LinearLayout(this)
newLinearLayout.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) newLinearLayout.layoutParams =
LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
newLinearLayout.orientation = LinearLayout.HORIZONTAL newLinearLayout.orientation = LinearLayout.HORIZONTAL
val sideMargin = (binding.selectedContacts.layoutParams as RelativeLayout.LayoutParams).leftMargin val sideMargin =
(binding.selectedContacts.layoutParams as RelativeLayout.LayoutParams).leftMargin
val mediumMargin = resources.getDimension(org.fossify.commons.R.dimen.medium_margin).toInt() val mediumMargin = resources.getDimension(org.fossify.commons.R.dimen.medium_margin).toInt()
val parentWidth = realScreenSize.x - sideMargin * 2 val parentWidth = realScreenSize.x - sideMargin * 2
val firstRowWidth = parentWidth - resources.getDimension(org.fossify.commons.R.dimen.normal_icon_size).toInt() + sideMargin / 2 val firstRowWidth =
parentWidth - resources.getDimension(org.fossify.commons.R.dimen.normal_icon_size)
.toInt() + sideMargin / 2
var widthSoFar = 0 var widthSoFar = 0
var isFirstRow = true var isFirstRow = true
@ -1465,7 +1663,8 @@ class ThreadActivity : SimpleActivity() {
isFirstRow = false isFirstRow = false
binding.selectedContacts.addView(newLinearLayout) binding.selectedContacts.addView(newLinearLayout)
newLinearLayout = LinearLayout(this) newLinearLayout = LinearLayout(this)
newLinearLayout.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) newLinearLayout.layoutParams =
LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
newLinearLayout.orientation = LinearLayout.HORIZONTAL newLinearLayout.orientation = LinearLayout.HORIZONTAL
params = LayoutParams(layout.measuredWidth, layout.measuredHeight) params = LayoutParams(layout.measuredWidth, layout.measuredHeight)
params.topMargin = mediumMargin params.topMargin = mediumMargin
@ -1482,7 +1681,8 @@ class ThreadActivity : SimpleActivity() {
} }
private fun removeSelectedContact(id: Int) { private fun removeSelectedContact(id: Int) {
participants = participants.filter { it.rawId != id }.toMutableList() as ArrayList<SimpleContact> participants =
participants.filter { it.rawId != id }.toMutableList() as ArrayList<SimpleContact>
showSelectedContacts() showSelectedContacts()
updateMessageType() updateMessageType()
} }
@ -1502,7 +1702,10 @@ class ThreadActivity : SimpleActivity() {
return numbers return numbers
} }
private fun fixParticipantNumbers(participants: ArrayList<SimpleContact>, properNumbers: ArrayList<String>): ArrayList<SimpleContact> { private fun fixParticipantNumbers(
participants: ArrayList<SimpleContact>,
properNumbers: ArrayList<String>
): ArrayList<SimpleContact> {
for (number in properNumbers) { for (number in properNumbers) {
for (participant in participants) { for (participant in participants) {
participant.phoneNumbers = participant.phoneNumbers.map { participant.phoneNumbers = participant.phoneNumbers.map {
@ -1529,7 +1732,11 @@ class ThreadActivity : SimpleActivity() {
type = mimeType type = mimeType
addCategory(Intent.CATEGORY_OPENABLE) addCategory(Intent.CATEGORY_OPENABLE)
putExtra(Intent.EXTRA_TITLE, path.split("/").last()) putExtra(Intent.EXTRA_TITLE, path.split("/").last())
launchActivityForResult(this, PICK_SAVE_FILE_INTENT, error = org.fossify.commons.R.string.system_service_disabled) launchActivityForResult(
intent = this,
requestCode = PICK_SAVE_FILE_INTENT,
error = org.fossify.commons.R.string.system_service_disabled
)
} }
} }
@ -1549,12 +1756,16 @@ class ThreadActivity : SimpleActivity() {
val lastMaxId = messages.filterNot { it.isScheduled }.maxByOrNull { it.id }?.id ?: 0L val lastMaxId = messages.filterNot { it.isScheduled }.maxByOrNull { it.id }?.id ?: 0L
val newThreadId = getThreadId(participants.getAddresses().toSet()) val newThreadId = getThreadId(participants.getAddresses().toSet())
val newMessages = getMessages(newThreadId, getImageResolutions = true, includeScheduledMessages = false) val newMessages =
getMessages(newThreadId, getImageResolutions = true, includeScheduledMessages = false)
if (messages.isNotEmpty() && messages.all { it.isScheduled } && newMessages.isNotEmpty()) { if (messages.isNotEmpty() && messages.all { it.isScheduled } && newMessages.isNotEmpty()) {
// update scheduled messages with real thread id // update scheduled messages with real thread id
threadId = newThreadId threadId = newThreadId
updateScheduledMessagesThreadId(messages = messages.filter { it.threadId != threadId }, threadId) updateScheduledMessagesThreadId(
messages = messages.filter { it.threadId != threadId },
newThreadId = threadId
)
} }
messages = newMessages.apply { messages = newMessages.apply {
@ -1567,7 +1778,8 @@ class ThreadActivity : SimpleActivity() {
} }
} }
messages.filter { !it.isScheduled && !it.isReceivedMessage() && it.id > lastMaxId }.forEach { latestMessage -> messages.filter { !it.isScheduled && !it.isReceivedMessage() && it.id > lastMaxId }
.forEach { latestMessage ->
messagesDB.insertOrIgnore(latestMessage) messagesDB.insertOrIgnore(latestMessage)
} }
@ -1599,7 +1811,11 @@ class ThreadActivity : SimpleActivity() {
RadioItem(TYPE_SEND, getString(R.string.send_now)), RadioItem(TYPE_SEND, getString(R.string.send_now)),
RadioItem(TYPE_DELETE, getString(org.fossify.commons.R.string.delete)) RadioItem(TYPE_DELETE, getString(org.fossify.commons.R.string.delete))
) )
RadioGroupDialog(activity = this, items = items, titleId = R.string.scheduled_message) { any -> RadioGroupDialog(
activity = this,
items = items,
titleId = R.string.scheduled_message
) { any ->
when (any as Int) { when (any as Int) {
TYPE_DELETE -> cancelScheduledMessageAndRefresh(message.id) TYPE_DELETE -> cancelScheduledMessageAndRefresh(message.id)
TYPE_EDIT -> editScheduledMessage(message) TYPE_EDIT -> editScheduledMessage(message)
@ -1680,7 +1896,8 @@ class ThreadActivity : SimpleActivity() {
val dateTime = scheduledDateTime val dateTime = scheduledDateTime
val millis = dateTime.millis val millis = dateTime.millis
binding.messageHolder.scheduledMessageButton.text = if (dateTime.yearOfCentury().get() > DateTime.now().yearOfCentury().get()) { binding.messageHolder.scheduledMessageButton.text =
if (dateTime.yearOfCentury().get() > DateTime.now().yearOfCentury().get()) {
millis.formatDate(this) millis.formatDate(this)
} else { } else {
val flags = FORMAT_SHOW_TIME or FORMAT_SHOW_DATE or FORMAT_NO_YEAR val flags = FORMAT_SHOW_TIME or FORMAT_SHOW_DATE or FORMAT_NO_YEAR
@ -1702,7 +1919,9 @@ class ThreadActivity : SimpleActivity() {
} }
ResourcesCompat.getDrawable(resources, drawableResId, theme)?.apply { ResourcesCompat.getDrawable(resources, drawableResId, theme)?.apply {
applyColorFilter(getProperTextColor()) applyColorFilter(getProperTextColor())
binding.messageHolder.threadSendMessage.setCompoundDrawablesWithIntrinsicBounds(null, this, null, null) binding.messageHolder.threadSendMessage.setCompoundDrawablesWithIntrinsicBounds(
null, this, null, null
)
} }
} }
@ -1836,13 +2055,17 @@ class ThreadActivity : SimpleActivity() {
view.onApplyWindowInsets(insets) view.onApplyWindowInsets(insets)
} }
val callback = object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) { val callback =
object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
override fun onPrepare(animation: WindowInsetsAnimationCompat) { override fun onPrepare(animation: WindowInsetsAnimationCompat) {
super.onPrepare(animation) super.onPrepare(animation)
showOrHideAttachmentPicker() showOrHideAttachmentPicker()
} }
override fun onProgress(insets: WindowInsetsCompat, runningAnimations: MutableList<WindowInsetsAnimationCompat>) = insets override fun onProgress(
insets: WindowInsetsCompat,
runningAnimations: MutableList<WindowInsetsAnimationCompat>
) = insets
} }
ViewCompat.setWindowInsetsAnimationCallback(window.decorView, callback) ViewCompat.setWindowInsetsAnimationCallback(window.decorView, callback)
} }
@ -1868,7 +2091,7 @@ class ThreadActivity : SimpleActivity() {
} }
} }
private fun getBottomBarColor() = if (baseConfig.isUsingSystemTheme) { private fun getBottomBarColor() = if (isDynamicTheme()) {
resources.getColor(org.fossify.commons.R.color.you_bottom_bar_color) resources.getColor(org.fossify.commons.R.color.you_bottom_bar_color)
} else { } else {
getBottomNavigationBackgroundColor() getBottomNavigationBackgroundColor()

View file

@ -9,7 +9,14 @@ import androidx.appcompat.app.AlertDialog
import com.google.android.material.timepicker.MaterialTimePicker import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat import com.google.android.material.timepicker.TimeFormat
import org.fossify.commons.activities.BaseSimpleActivity import org.fossify.commons.activities.BaseSimpleActivity
import org.fossify.commons.extensions.* import org.fossify.commons.extensions.applyColorFilter
import org.fossify.commons.extensions.getAlertDialogBuilder
import org.fossify.commons.extensions.getDatePickerDialogTheme
import org.fossify.commons.extensions.getProperTextColor
import org.fossify.commons.extensions.getTimeFormat
import org.fossify.commons.extensions.isDynamicTheme
import org.fossify.commons.extensions.setupDialogStuff
import org.fossify.commons.extensions.toast
import org.fossify.messages.R import org.fossify.messages.R
import org.fossify.messages.databinding.ScheduleMessageDialogBinding import org.fossify.messages.databinding.ScheduleMessageDialogBinding
import org.fossify.messages.extensions.config import org.fossify.messages.extensions.config
@ -94,7 +101,12 @@ class ScheduleMessageDialog(
val dateSetListener = OnDateSetListener { _, y, m, d -> dateSet(y, m, d) } val dateSetListener = OnDateSetListener { _, y, m, d -> dateSet(y, m, d) }
DatePickerDialog( DatePickerDialog(
activity, activity.getDatePickerDialogTheme(), dateSetListener, year, monthOfYear, dayOfMonth activity,
activity.getDatePickerDialogTheme(),
dateSetListener,
year,
monthOfYear,
dayOfMonth
).apply { ).apply {
datePicker.minDate = System.currentTimeMillis() datePicker.minDate = System.currentTimeMillis()
show() show()
@ -111,7 +123,7 @@ class ScheduleMessageDialog(
val hourOfDay = dateTime?.hourOfDay ?: getNextHour() val hourOfDay = dateTime?.hourOfDay ?: getNextHour()
val minute = dateTime?.minuteOfHour ?: getNextMinute() val minute = dateTime?.minuteOfHour ?: getNextMinute()
if (activity.config.isUsingSystemTheme) { if (activity.isDynamicTheme()) {
val timeFormat = if (DateFormat.is24HourFormat(activity)) { val timeFormat = if (DateFormat.is24HourFormat(activity)) {
TimeFormat.CLOCK_24H TimeFormat.CLOCK_24H
} else { } else {
@ -132,7 +144,12 @@ class ScheduleMessageDialog(
} else { } else {
val timeSetListener = OnTimeSetListener { _, hours, minutes -> timeSet(hours, minutes) } val timeSetListener = OnTimeSetListener { _, hours, minutes -> timeSet(hours, minutes) }
TimePickerDialog( TimePickerDialog(
activity, activity.getDatePickerDialogTheme(), timeSetListener, hourOfDay, minute, DateFormat.is24HourFormat(activity) activity,
activity.getDatePickerDialogTheme(),
timeSetListener,
hourOfDay,
minute,
DateFormat.is24HourFormat(activity)
).apply { ).apply {
show() show()
getButton(AlertDialog.BUTTON_NEGATIVE).apply { getButton(AlertDialog.BUTTON_NEGATIVE).apply {
@ -187,7 +204,14 @@ class ScheduleMessageDialog(
} }
} }
private fun getNextHour() = (calendar.get(Calendar.HOUR_OF_DAY) + 1).coerceIn(0, 23) private fun getNextHour(): Int {
return (calendar.get(Calendar.HOUR_OF_DAY) + 1)
.coerceIn(0, 23)
}
private fun getNextMinute() = (calendar.get(Calendar.MINUTE) + 5).roundToClosestMultipleOf(5).coerceIn(0, 59) private fun getNextMinute(): Int {
return (calendar.get(Calendar.MINUTE) + 5)
.roundToClosestMultipleOf(5)
.coerceIn(0, 59)
}
} }

View file

@ -5,4 +5,6 @@ import kotlin.math.roundToInt
/** /**
* Returns the closest number divisible by [multipleOf]. * Returns the closest number divisible by [multipleOf].
*/ */
fun Int.roundToClosestMultipleOf(multipleOf: Int = 1) = (toDouble() / multipleOf).roundToInt() * multipleOf fun Int.roundToClosestMultipleOf(multipleOf: Int = 1): Int {
return (toDouble() / multipleOf).roundToInt() * multipleOf
}

View file

@ -15,7 +15,7 @@ eventbus = "3.3.1"
#Room #Room
room = "2.6.1" room = "2.6.1"
#Fossify #Fossify
commons = "8685963657" commons = "8038e3277e"
android-smsmms = "c3e678befd" android-smsmms = "c3e678befd"
indicator-fast-scroll = "4524cd0b61" indicator-fast-scroll = "4524cd0b61"
#Gradle #Gradle