Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1 +1 @@
* @naveensingh
* @adarshkannada
5 changes: 4 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ base {
}

android {
compileSdk = project.libs.versions.app.build.compileSDKVersion.get().toInt()

compileSdkVersion(project.libs.versions.app.build.compileSDKVersion.get().toInt())
defaultConfig {
applicationId = project.property("APP_ID").toString()
minSdk = project.libs.versions.app.build.minimumSDK.get().toInt()
Expand Down Expand Up @@ -131,6 +131,9 @@ android {
enableSplit = false
}
}
kotlin {
jvmToolchain(21)
}
}

detekt {
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.RECEIVE_MMS" />
<uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" />
<uses-permission android:name="android.permission.READ_CELL_BROADCASTS" />
<uses-permission android:name="android.provider.Telephony.SMS_RECEIVED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
Expand Down
183 changes: 162 additions & 21 deletions app/src/main/kotlin/org/fossify/messages/activities/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import android.os.Bundle
import android.provider.Telephony
import android.text.TextUtils
import androidx.appcompat.content.res.AppCompatResources
import androidx.appcompat.widget.PopupMenu
import androidx.appcompat.view.ContextThemeWrapper
import org.fossify.commons.extensions.getPopupMenuTheme
import org.fossify.commons.dialogs.PermissionRequiredDialog
import org.fossify.commons.extensions.adjustAlpha
import org.fossify.commons.extensions.appLaunched
Expand Down Expand Up @@ -79,6 +82,13 @@ class MainActivity : SimpleActivity() {

private val MAKE_DEFAULT_APP_REQUEST = 1

private enum class Tab {
PERSONAL, FINANCIAL, OTHERS
}

private var currentTab = Tab.PERSONAL
private var allLoadedConversations = ArrayList<Conversation>()

private var storedTextColor = 0
private var storedFontSize = 0
private var lastSearchedText = ""
Expand All @@ -92,9 +102,14 @@ class MainActivity : SimpleActivity() {
setContentView(binding.root)
appLaunched(BuildConfig.APPLICATION_ID)
setupOptionsMenu()
refreshMenuItems()

setupEdgeToEdge(padBottomImeAndSystem = listOf(binding.conversationsList))
if (savedInstanceState != null) {
val tabOrdinal = savedInstanceState.getInt("current_tab", Tab.PERSONAL.ordinal)
currentTab = Tab.values()[tabOrdinal]
}

setupEdgeToEdge(padBottomImeAndSystem = listOf(binding.conversationsList, binding.bottomNavigationBar))
setupBottomNavigation()

checkAndDeleteOldRecycleBinMessages()
clearAllMessagesIfNeeded {
Expand All @@ -106,10 +121,15 @@ class MainActivity : SimpleActivity() {
}
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("current_tab", currentTab.ordinal)
}

override fun onResume() {
super.onResume()
updateMenuColors()
refreshMenuItems()
updateBottomBar()

getOrCreateConversationsAdapter().apply {
if (storedTextColor != getProperTextColor()) {
Expand Down Expand Up @@ -156,7 +176,6 @@ class MainActivity : SimpleActivity() {
}

private fun setupOptionsMenu() {
binding.mainMenu.requireToolbar().inflateMenu(R.menu.menu_main)
binding.mainMenu.toggleHideOnScroll(true)
binding.mainMenu.setupMenu()

Expand All @@ -174,24 +193,17 @@ class MainActivity : SimpleActivity() {
}
searchTextChanged(text)
}

binding.mainMenu.requireToolbar().setOnMenuItemClickListener { menuItem ->
when (menuItem.itemId) {
R.id.show_recycle_bin -> launchRecycleBin()
R.id.show_archived -> launchArchivedConversations()
R.id.settings -> launchSettings()
R.id.about -> launchAbout()
else -> return@setOnMenuItemClickListener false
}
return@setOnMenuItemClickListener true
}
}

private fun refreshMenuItems() {
binding.mainMenu.requireToolbar().menu.apply {
findItem(R.id.show_recycle_bin).isVisible = config.useRecycleBin
findItem(R.id.show_archived).isVisible = config.isArchiveAvailable
private fun handleMenuItemClick(itemId: Int): Boolean {
when (itemId) {
R.id.show_recycle_bin -> launchRecycleBin()
R.id.show_archived -> launchArchivedConversations()
R.id.settings -> launchSettings()
R.id.about -> launchAbout()
else -> return false
}
return true
}

override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
Expand Down Expand Up @@ -403,25 +415,33 @@ class MainActivity : SimpleActivity() {
conversations: ArrayList<Conversation>,
cached: Boolean = false,
) {
allLoadedConversations = conversations

val sortedConversations = conversations
.sortedWith(
compareByDescending<Conversation> {
config.pinnedConversations.contains(it.threadId.toString())
}.thenByDescending { it.date }
).toMutableList() as ArrayList<Conversation>

val filteredConversations = when (currentTab) {
Tab.PERSONAL -> sortedConversations.filter { isPersonal(it) }
Tab.FINANCIAL -> sortedConversations.filter { isFinancial(it) }
Tab.OTHERS -> sortedConversations.filter { !isPersonal(it) && !isFinancial(it) }
}.toMutableList() as ArrayList<Conversation>

if (cached && config.appRunCount == 1) {
// there are no cached conversations on the first run so we show the
// loading placeholder and progress until we are done loading from telephony
showOrHideProgress(conversations.isEmpty())
} else {
showOrHideProgress(false)
showOrHidePlaceholder(conversations.isEmpty())
showOrHidePlaceholder(filteredConversations.isEmpty())
}

try {
getOrCreateConversationsAdapter().apply {
updateConversations(sortedConversations) {
updateConversations(filteredConversations) {
if (!cached) {
showOrHidePlaceholder(currentList.isEmpty())
}
Expand Down Expand Up @@ -682,4 +702,125 @@ class MainActivity : SimpleActivity() {
checkWhatsNew(this, BuildConfig.VERSION_CODE)
}
}

private fun isFinancial(conversation: Conversation): Boolean {
val text = (conversation.snippet + " " + conversation.title).lowercase()
val financialKeywords = listOf(
"bank", "account", "txn", "transaction", "debit", "credit", "otp",
"spent", "withdraw", "rs.", "inr", "paytm", "gpay", "card", "payment"
)
return financialKeywords.any { text.contains(it) }
}

private fun isPersonal(conversation: Conversation): Boolean {
if (isFinancial(conversation)) return false

val number = conversation.phoneNumber.trim()
if (number.isEmpty()) return false

if (conversation.title != conversation.phoneNumber) {
return true
}

val digitsAndSymbols = number.filter { it.isDigit() || it == '+' || it == '-' || it == '(' || it == ')' || it.isWhitespace() }
if (digitsAndSymbols.length != number.length) {
return false
}

val digitCount = number.filter { it.isDigit() }.length
if (digitCount < 7) {
return false
}

return true
}

private fun setupBottomNavigation() {
binding.tabPersonalBtn.setOnClickListener {
if (currentTab != Tab.PERSONAL) {
currentTab = Tab.PERSONAL
updateBottomBar()
setupConversations(allLoadedConversations)
}
}

binding.tabFinancialBtn.setOnClickListener {
if (currentTab != Tab.FINANCIAL) {
currentTab = Tab.FINANCIAL
updateBottomBar()
setupConversations(allLoadedConversations)
}
}

binding.tabOthersBtn.setOnClickListener {
if (currentTab != Tab.OTHERS) {
currentTab = Tab.OTHERS
updateBottomBar()
setupConversations(allLoadedConversations)
}
}

binding.tabMenuBtn.setOnClickListener {
showPopupMenu()
}
}

private fun showPopupMenu() {
val theme = getPopupMenuTheme()
val contextTheme = ContextThemeWrapper(this, theme)
val popup = PopupMenu(contextTheme, binding.tabMenuBtn)
popup.menuInflater.inflate(R.menu.menu_main, popup.menu)
popup.menu.apply {
findItem(R.id.show_recycle_bin).isVisible = config.useRecycleBin
findItem(R.id.show_archived).isVisible = config.isArchiveAvailable
}
popup.setOnMenuItemClickListener { menuItem ->
handleMenuItemClick(menuItem.itemId)
}
popup.show()
}

private fun updateBottomBar() {
val primaryColor = getProperPrimaryColor()
val textColor = getProperTextColor()
val inactiveColor = textColor.adjustAlpha(0.6f)

binding.bottomNavigationBar.setBackgroundColor(getProperBackgroundColor())

if (currentTab == Tab.PERSONAL) {
binding.tabPersonalIcon.applyColorFilter(primaryColor)
binding.tabPersonalLabel.setTextColor(primaryColor)
binding.tabPersonalLabel.paint.isFakeBoldText = true
} else {
binding.tabPersonalIcon.applyColorFilter(inactiveColor)
binding.tabPersonalLabel.setTextColor(inactiveColor)
binding.tabPersonalLabel.paint.isFakeBoldText = false
}

if (currentTab == Tab.FINANCIAL) {
binding.tabFinancialIcon.applyColorFilter(primaryColor)
binding.tabFinancialLabel.setTextColor(primaryColor)
binding.tabFinancialLabel.paint.isFakeBoldText = true
} else {
binding.tabFinancialIcon.applyColorFilter(inactiveColor)
binding.tabFinancialLabel.setTextColor(inactiveColor)
binding.tabFinancialLabel.paint.isFakeBoldText = false
}

if (currentTab == Tab.OTHERS) {
binding.tabOthersIcon.applyColorFilter(primaryColor)
binding.tabOthersLabel.setTextColor(primaryColor)
binding.tabOthersLabel.paint.isFakeBoldText = true
} else {
binding.tabOthersIcon.applyColorFilter(inactiveColor)
binding.tabOthersLabel.setTextColor(inactiveColor)
binding.tabOthersLabel.paint.isFakeBoldText = false
}

// Menu item is not a tab, so it always uses the inactive color
binding.tabMenuIcon.applyColorFilter(inactiveColor)
binding.tabMenuLabel.setTextColor(inactiveColor)
binding.tabMenuLabel.paint.isFakeBoldText = false
}
}

9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_tab_financial.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M21,18v1c0,1.1 -0.9,2 -2,2H5c-1.11,0 -2,-0.9 -2,-2V5c0,-1.1 0.89,-2 2,-2h14c1.1,0 2,0.9 2,2v1h-9c-1.11,0 -2,0.9 -2,2v8c0,1.1 0.89,2 2,2h9zm-9,-2h10V8H12v8zm4,-2.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
</vector>
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_tab_menu.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M3,6h18v2H3V6z M3,11h18v2H3v-2z M3,16h18v2H3v-2z"/>
</vector>
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_tab_others.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M4,4h6v6H4V4z M14,4h6v6H14V4z M4,14h6v6H4v-6z M14,14h6v6H14v-6z"/>
</vector>
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_tab_personal.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
</vector>
Loading
Loading