Skip to content

Commit 8818aab

Browse files
authored
fix: respect default SIM preference on dialpad screen (#344)
* Remove extra dialer button and use preferred sim The app should just use the default SIM handle or let the user decide. Context: - #50 - #155 - #324 * docs: update changelog * fix: add option to force SIM selector in call intents * style: format code * style: suppress long method error * fix: only show sim selector when multiple sims are available * refactor: initialize russian chars lazily * refactor: use coroutines for clearing dialpad input
1 parent 6e302fe commit 8818aab

4 files changed

Lines changed: 195 additions & 98 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
12+
- Dialpad screen now respects the default SIM preference ([#50])
13+
1014
## [1.5.1] - 2025-06-17
1115

1216
### Changed
@@ -15,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1519

1620
### Fixed
1721

18-
- Fixes crash when searching in call history ([#378])
22+
- Fixed crash when searching in call history ([#378])
1923

2024
## [1.5.0] - 2025-06-06
2125

@@ -168,6 +172,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
168172
[#15]: https://github.com/FossifyOrg/Contacts/issues/15
169173
[#35]: https://github.com/FossifyOrg/Phone/issues/35
170174
[#39]: https://github.com/FossifyOrg/Phone/issues/39
175+
[#50]: https://github.com/FossifyOrg/Phone/issues/50
171176
[#60]: https://github.com/FossifyOrg/Phone/issues/60
172177
[#81]: https://github.com/FossifyOrg/Phone/issues/81
173178
[#88]: https://github.com/FossifyOrg/Phone/issues/88

app/src/main/kotlin/org/fossify/phone/activities/DialpadActivity.kt

Lines changed: 111 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,50 @@ import android.view.MotionEvent
1515
import android.view.View
1616
import android.view.ViewConfiguration
1717
import androidx.core.content.res.ResourcesCompat
18+
import androidx.core.net.toUri
1819
import androidx.core.view.isVisible
19-
import org.fossify.commons.extensions.*
20-
import org.fossify.commons.helpers.*
20+
import androidx.lifecycle.lifecycleScope
21+
import kotlinx.coroutines.delay
22+
import kotlinx.coroutines.launch
23+
import org.fossify.commons.extensions.applyColorFilter
24+
import org.fossify.commons.extensions.beVisibleIf
25+
import org.fossify.commons.extensions.checkAppSideloading
26+
import org.fossify.commons.extensions.getColorStateList
27+
import org.fossify.commons.extensions.getColoredDrawableWithColor
28+
import org.fossify.commons.extensions.getContrastColor
29+
import org.fossify.commons.extensions.getMyContactsCursor
30+
import org.fossify.commons.extensions.getProperBackgroundColor
31+
import org.fossify.commons.extensions.getProperPrimaryColor
32+
import org.fossify.commons.extensions.getProperTextColor
33+
import org.fossify.commons.extensions.isDefaultDialer
34+
import org.fossify.commons.extensions.launchActivityIntent
35+
import org.fossify.commons.extensions.normalizeString
36+
import org.fossify.commons.extensions.onTextChangeListener
37+
import org.fossify.commons.extensions.performHapticFeedback
38+
import org.fossify.commons.extensions.updateTextColors
39+
import org.fossify.commons.extensions.value
40+
import org.fossify.commons.extensions.viewBinding
41+
import org.fossify.commons.helpers.ContactsHelper
42+
import org.fossify.commons.helpers.KEY_PHONE
43+
import org.fossify.commons.helpers.KeypadHelper
44+
import org.fossify.commons.helpers.LOWER_ALPHA_INT
45+
import org.fossify.commons.helpers.MyContactsContentProvider
46+
import org.fossify.commons.helpers.NavigationIcon
47+
import org.fossify.commons.helpers.REQUEST_CODE_SET_DEFAULT_DIALER
48+
import org.fossify.commons.helpers.isOreoPlus
2149
import org.fossify.commons.models.contacts.Contact
2250
import org.fossify.phone.R
2351
import org.fossify.phone.adapters.ContactsAdapter
2452
import org.fossify.phone.databinding.ActivityDialpadBinding
25-
import org.fossify.phone.extensions.*
53+
import org.fossify.phone.extensions.addCharacter
54+
import org.fossify.phone.extensions.areMultipleSIMsAvailable
55+
import org.fossify.phone.extensions.boundingBox
56+
import org.fossify.phone.extensions.config
57+
import org.fossify.phone.extensions.disableKeyboard
58+
import org.fossify.phone.extensions.getKeyEvent
59+
import org.fossify.phone.extensions.setupWithContacts
60+
import org.fossify.phone.extensions.startCallWithConfirmationCheck
61+
import org.fossify.phone.extensions.startContactDetailsIntent
2662
import org.fossify.phone.helpers.DIALPAD_TONE_LENGTH_MS
2763
import org.fossify.phone.helpers.RecentsHelper
2864
import org.fossify.phone.helpers.ToneGeneratorHelper
@@ -35,21 +71,39 @@ class DialpadActivity : SimpleActivity() {
3571

3672
private var allContacts = ArrayList<Contact>()
3773
private var speedDialValues = ArrayList<SpeedDial>()
38-
private val russianCharsMap = HashMap<Char, Int>()
39-
private var hasRussianLocale = false
4074
private var privateCursor: Cursor? = null
4175
private var toneGeneratorHelper: ToneGeneratorHelper? = null
4276
private val longPressTimeout = ViewConfiguration.getLongPressTimeout().toLong()
4377
private val longPressHandler = Handler(Looper.getMainLooper())
4478
private val pressedKeys = mutableSetOf<Char>()
4579

80+
private var hasRussianLocale = false
81+
private val russianCharsMap by lazy {
82+
hashMapOf(
83+
'а' to 2, 'б' to 2, 'в' to 2, 'г' to 2,
84+
'д' to 3, 'е' to 3, 'ё' to 3, 'ж' to 3, 'з' to 3,
85+
'и' to 4, 'й' to 4, 'к' to 4, 'л' to 4,
86+
'м' to 5, 'н' to 5, 'о' to 5, 'п' to 5,
87+
'р' to 6, 'с' to 6, 'т' to 6, 'у' to 6,
88+
'ф' to 7, 'х' to 7, 'ц' to 7, 'ч' to 7,
89+
'ш' to 8, 'щ' to 8, 'ъ' to 8, 'ы' to 8,
90+
'ь' to 9, 'э' to 9, 'ю' to 9, 'я' to 9
91+
)
92+
}
93+
94+
@Suppress("LongMethod")
4695
override fun onCreate(savedInstanceState: Bundle?) {
4796
super.onCreate(savedInstanceState)
4897
setContentView(binding.root)
4998
hasRussianLocale = Locale.getDefault().language == "ru"
5099

51100
binding.apply {
52-
updateMaterialActivityViews(dialpadCoordinator, dialpadHolder, useTransparentNavigation = true, useTopSearchMenu = false)
101+
updateMaterialActivityViews(
102+
mainCoordinatorLayout = dialpadCoordinator,
103+
nestedView = dialpadHolder,
104+
useTransparentNavigation = true,
105+
useTopSearchMenu = false
106+
)
53107
setupMaterialScrollListener(dialpadList, dialpadToolbar)
54108
}
55109

@@ -89,7 +143,8 @@ class DialpadActivity : SimpleActivity() {
89143
dialpadAsteriskHolder,
90144
dialpadHashtagHolder
91145
).forEach {
92-
it.background = ResourcesCompat.getDrawable(resources, R.drawable.pill_background, theme)
146+
it.background =
147+
ResourcesCompat.getDrawable(resources, R.drawable.pill_background, theme)
93148
it.background?.alpha = LOWER_ALPHA_INT
94149
}
95150
}
@@ -102,7 +157,6 @@ class DialpadActivity : SimpleActivity() {
102157

103158
binding.dialpadWrapper.apply {
104159
if (hasRussianLocale) {
105-
initRussianChars()
106160
dialpad2Letters.append("\nАБВГ")
107161
dialpad3Letters.append("\nДЕЁЖЗ")
108162
dialpad4Letters.append("\nИЙКЛ")
@@ -114,7 +168,13 @@ class DialpadActivity : SimpleActivity() {
114168

115169
val fontSize = resources.getDimension(R.dimen.small_text_size)
116170
arrayOf(
117-
dialpad2Letters, dialpad3Letters, dialpad4Letters, dialpad5Letters, dialpad6Letters, dialpad7Letters, dialpad8Letters,
171+
dialpad2Letters,
172+
dialpad3Letters,
173+
dialpad4Letters,
174+
dialpad5Letters,
175+
dialpad6Letters,
176+
dialpad7Letters,
177+
dialpad8Letters,
118178
dialpad9Letters
119179
).forEach {
120180
it.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize)
@@ -139,7 +199,8 @@ class DialpadActivity : SimpleActivity() {
139199
binding.apply {
140200
dialpadClearChar.setOnClickListener { clearChar(it) }
141201
dialpadClearChar.setOnLongClickListener { clearInput(); true }
142-
dialpadCallButton.setOnClickListener { initCall(dialpadInput.value, 0) }
202+
dialpadCallButton.setOnClickListener { initCall(dialpadInput.value) }
203+
dialpadCallButton.setOnLongClickListener { initCallWithSimSelector() }
143204
dialpadInput.onTextChangeListener { dialpadValueChanged(it) }
144205
dialpadInput.requestFocus()
145206
dialpadInput.disableKeyboard()
@@ -150,24 +211,11 @@ class DialpadActivity : SimpleActivity() {
150211
}
151212

152213
val properPrimaryColor = getProperPrimaryColor()
153-
val callIconId = if (areMultipleSIMsAvailable()) {
154-
val callIcon = resources.getColoredDrawableWithColor(R.drawable.ic_phone_two_vector, properPrimaryColor.getContrastColor())
155-
binding.apply {
156-
dialpadCallTwoButton.setImageDrawable(callIcon)
157-
dialpadCallTwoButton.background.applyColorFilter(properPrimaryColor)
158-
dialpadCallTwoButton.beVisible()
159-
dialpadCallTwoButton.setOnClickListener {
160-
initCall(dialpadInput.value, 1)
161-
}
162-
}
163-
164-
R.drawable.ic_phone_one_vector
165-
} else {
166-
R.drawable.ic_phone_vector
167-
}
168-
169214
binding.apply {
170-
val callIcon = resources.getColoredDrawableWithColor(callIconId, properPrimaryColor.getContrastColor())
215+
val callIcon = resources.getColoredDrawableWithColor(
216+
drawableId = R.drawable.ic_phone_vector,
217+
color = properPrimaryColor.getContrastColor()
218+
)
171219
dialpadCallButton.setImageDrawable(callIcon)
172220
dialpadCallButton.background.applyColorFilter(properPrimaryColor)
173221

@@ -198,7 +246,10 @@ class DialpadActivity : SimpleActivity() {
198246
}
199247

200248
private fun checkDialIntent(): Boolean {
201-
return if ((intent.action == Intent.ACTION_DIAL || intent.action == Intent.ACTION_VIEW) && intent.data != null && intent.dataString?.contains("tel:") == true) {
249+
return if (
250+
(intent.action == Intent.ACTION_DIAL || intent.action == Intent.ACTION_VIEW)
251+
&& intent.data != null && intent.dataString?.contains("tel:") == true
252+
) {
202253
val number = Uri.decode(intent.dataString).substringAfter("tel:")
203254
binding.dialpadInput.setText(number)
204255
binding.dialpadInput.setSelection(number.length)
@@ -231,6 +282,13 @@ class DialpadActivity : SimpleActivity() {
231282
binding.dialpadInput.setText("")
232283
}
233284

285+
private fun clearInputWithDelay() {
286+
lifecycleScope.launch {
287+
delay(1000)
288+
clearInput()
289+
}
290+
}
291+
234292
private fun gotContacts(newContacts: ArrayList<Contact>) {
235293
allContacts = newContacts
236294

@@ -258,7 +316,8 @@ class DialpadActivity : SimpleActivity() {
258316
launchSetDefaultDialerIntent()
259317
}
260318
} else {
261-
val intent = Intent(SECRET_CODE_ACTION, Uri.parse("android_secret_code://$secretCode"))
319+
val intent =
320+
Intent(SECRET_CODE_ACTION, "android_secret_code://$secretCode".toUri())
262321
sendBroadcast(intent)
263322
}
264323
return
@@ -294,10 +353,11 @@ class DialpadActivity : SimpleActivity() {
294353
highlightText = text,
295354
itemClick = {
296355
val contact = it as Contact
297-
startCallWithConfirmationCheck(contact.getPrimaryNumber() ?: return@ContactsAdapter, contact.getNameToDisplay())
298-
Handler().postDelayed({
299-
binding.dialpadInput.setText("")
300-
}, 1000)
356+
startCallWithConfirmationCheck(
357+
recipient = contact.getPrimaryNumber() ?: return@ContactsAdapter,
358+
name = contact.getNameToDisplay()
359+
)
360+
clearInputWithDelay()
301361
},
302362
profileIconClick = {
303363
startContactDetailsIntent(it as Contact)
@@ -316,17 +376,10 @@ class DialpadActivity : SimpleActivity() {
316376
}
317377
}
318378

319-
private fun initCall(number: String = binding.dialpadInput.value, handleIndex: Int) {
379+
private fun initCall(number: String = binding.dialpadInput.value) {
320380
if (number.isNotEmpty()) {
321-
if (handleIndex != -1 && areMultipleSIMsAvailable()) {
322-
callContactWithSimWithConfirmationCheck(number, number, handleIndex == 0)
323-
} else {
324-
startCallWithConfirmationCheck(number, number)
325-
}
326-
327-
Handler().postDelayed({
328-
binding.dialpadInput.setText("")
329-
}, 1000)
381+
startCallWithConfirmationCheck(number, number)
382+
clearInputWithDelay()
330383
} else {
331384
RecentsHelper(this).getRecentCalls(queryLimit = 1) {
332385
val mostRecentNumber = it.firstOrNull()?.phoneNumber
@@ -339,28 +392,31 @@ class DialpadActivity : SimpleActivity() {
339392
}
340393
}
341394

395+
private fun initCallWithSimSelector(): Boolean {
396+
val number = binding.dialpadInput.value
397+
return if (areMultipleSIMsAvailable() && number.isNotEmpty()) {
398+
startCallWithConfirmationCheck(
399+
recipient = number,
400+
name = number,
401+
forceSimSelector = true
402+
)
403+
true
404+
} else {
405+
false
406+
}
407+
}
408+
342409
private fun speedDial(id: Int): Boolean {
343410
if (binding.dialpadInput.value.length == 1) {
344411
val speedDial = speedDialValues.firstOrNull { it.id == id }
345412
if (speedDial?.isValid() == true) {
346-
initCall(speedDial.number, -1)
413+
initCall(speedDial.number)
347414
return true
348415
}
349416
}
350417
return false
351418
}
352419

353-
private fun initRussianChars() {
354-
russianCharsMap['а'] = 2; russianCharsMap['б'] = 2; russianCharsMap['в'] = 2; russianCharsMap['г'] = 2
355-
russianCharsMap['д'] = 3; russianCharsMap['е'] = 3; russianCharsMap['ё'] = 3; russianCharsMap['ж'] = 3; russianCharsMap['з'] = 3
356-
russianCharsMap['и'] = 4; russianCharsMap['й'] = 4; russianCharsMap['к'] = 4; russianCharsMap['л'] = 4
357-
russianCharsMap['м'] = 5; russianCharsMap['н'] = 5; russianCharsMap['о'] = 5; russianCharsMap['п'] = 5
358-
russianCharsMap['р'] = 6; russianCharsMap['с'] = 6; russianCharsMap['т'] = 6; russianCharsMap['у'] = 6
359-
russianCharsMap['ф'] = 7; russianCharsMap['х'] = 7; russianCharsMap['ц'] = 7; russianCharsMap['ч'] = 7
360-
russianCharsMap['ш'] = 8; russianCharsMap['щ'] = 8; russianCharsMap['ъ'] = 8; russianCharsMap['ы'] = 8
361-
russianCharsMap['ь'] = 9; russianCharsMap['э'] = 9; russianCharsMap['ю'] = 9; russianCharsMap['я'] = 9
362-
}
363-
364420
private fun startDialpadTone(char: Char) {
365421
if (config.dialpadBeeps) {
366422
pressedKeys.add(char)

0 commit comments

Comments
 (0)