Merge pull request #3516 from vector-im/feature/fga/dial_pad_improvements
Feature/fga/dial pad improvements
This commit is contained in:
commit
da19992f3f
|
@ -0,0 +1 @@
|
|||
Some improvements on DialPad (cursor edition, paste number, small fixes).
|
|
@ -61,6 +61,7 @@ class CallDialPadBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetCa
|
|||
arguments = Bundle().apply {
|
||||
putBoolean(DialPadFragment.EXTRA_ENABLE_DELETE, showActions)
|
||||
putBoolean(DialPadFragment.EXTRA_ENABLE_OK, showActions)
|
||||
putBoolean(DialPadFragment.EXTRA_CURSOR_VISIBLE, false)
|
||||
putString(DialPadFragment.EXTRA_REGION_CODE, VectorLocale.applicationLocale.country)
|
||||
}
|
||||
callback = DialPadFragmentCallbackWrapper(this@CallDialPadBottomSheet.callback)
|
||||
|
@ -88,10 +89,6 @@ class CallDialPadBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetCa
|
|||
|
||||
private inner class DialPadFragmentCallbackWrapper(val callback: DialPadFragment.Callback?): DialPadFragment.Callback {
|
||||
|
||||
override fun onDigitAppended(digit: String) {
|
||||
callback?.onDigitAppended(digit)
|
||||
}
|
||||
|
||||
override fun onOkClicked(formatted: String?, raw: String?) {
|
||||
callback?.onOkClicked(formatted, raw)
|
||||
dismiss()
|
||||
|
|
|
@ -16,34 +16,40 @@
|
|||
|
||||
package im.vector.app.features.call.dialpad
|
||||
|
||||
import android.content.ClipboardManager
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.telephony.PhoneNumberFormattingTextWatcher
|
||||
import android.telephony.PhoneNumberUtils
|
||||
import android.text.Editable
|
||||
import android.text.InputType
|
||||
import android.text.TextUtils
|
||||
import android.text.TextWatcher
|
||||
import android.text.method.DialerKeyListener
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.ImageViewCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.android.dialer.dialpadview.DialpadView
|
||||
import com.android.dialer.dialpadview.DigitsEditText
|
||||
import com.google.i18n.phonenumbers.AsYouTypeFormatter
|
||||
import com.google.i18n.phonenumbers.PhoneNumberUtil
|
||||
import im.vector.app.R
|
||||
import im.vector.app.features.themes.ThemeUtils
|
||||
|
||||
class DialPadFragment : Fragment() {
|
||||
class DialPadFragment : Fragment(), TextWatcher {
|
||||
|
||||
var callback: Callback? = null
|
||||
|
||||
private var digits: DigitsEditText? = null
|
||||
private var formatter: AsYouTypeFormatter? = null
|
||||
private var input = ""
|
||||
private lateinit var digits: DigitsEditText
|
||||
private var regionCode: String = DEFAULT_REGION_CODE
|
||||
private var formatAsYouType = true
|
||||
private var enableStar = true
|
||||
private var enablePound = true
|
||||
private var enablePlus = true
|
||||
private var cursorVisible = false
|
||||
private var cursorVisible = true
|
||||
private var enableDelete = true
|
||||
private var enableFabOk = true
|
||||
|
||||
|
@ -53,72 +59,80 @@ class DialPadFragment : Fragment() {
|
|||
savedInstanceState: Bundle?): View {
|
||||
initArgs(savedInstanceState)
|
||||
val view = inflater.inflate(R.layout.dialpad_fragment, container, false)
|
||||
view.setBackgroundColor(ThemeUtils.getColor(requireContext(), R.attr.backgroundColor))
|
||||
val dialpadView = view.findViewById<View>(R.id.dialpad_view) as DialpadView
|
||||
dialpadView.findViewById<View>(R.id.dialpad_key_voicemail).isVisible = false
|
||||
digits = dialpadView.digits as? DigitsEditText
|
||||
digits?.isCursorVisible = cursorVisible
|
||||
digits?.setTextColor(ThemeUtils.getColor(requireContext(), R.attr.vctr_content_primary))
|
||||
dialpadView.findViewById<View>(R.id.zero).setOnClickListener { append('0') }
|
||||
if (enablePlus) {
|
||||
dialpadView.findViewById<View>(R.id.zero).setOnLongClickListener {
|
||||
append('+')
|
||||
true
|
||||
}
|
||||
}
|
||||
dialpadView.findViewById<View>(R.id.one).setOnClickListener { append('1') }
|
||||
dialpadView.findViewById<View>(R.id.two).setOnClickListener { append('2') }
|
||||
dialpadView.findViewById<View>(R.id.three).setOnClickListener { append('3') }
|
||||
dialpadView.findViewById<View>(R.id.four).setOnClickListener { append('4') }
|
||||
dialpadView.findViewById<View>(R.id.four).setOnClickListener { append('4') }
|
||||
dialpadView.findViewById<View>(R.id.five).setOnClickListener { append('5') }
|
||||
dialpadView.findViewById<View>(R.id.six).setOnClickListener { append('6') }
|
||||
dialpadView.findViewById<View>(R.id.seven).setOnClickListener { append('7') }
|
||||
dialpadView.findViewById<View>(R.id.eight).setOnClickListener { append('8') }
|
||||
dialpadView.findViewById<View>(R.id.nine).setOnClickListener { append('9') }
|
||||
digits = dialpadView.digits as DigitsEditText
|
||||
digits.isCursorVisible = cursorVisible
|
||||
digits.inputType = InputType.TYPE_CLASS_PHONE
|
||||
digits.keyListener = DialerKeyListener.getInstance()
|
||||
digits.setTextColor(ThemeUtils.getColor(requireContext(), R.attr.vctr_content_primary))
|
||||
digits.addTextChangedListener(PhoneNumberFormattingTextWatcher(if (formatAsYouType) regionCode else ""))
|
||||
digits.addTextChangedListener(this)
|
||||
dialpadView.findViewById<View>(R.id.zero).setOnClickListener { keyPressed(KeyEvent.KEYCODE_0, "0") }
|
||||
dialpadView.findViewById<View>(R.id.one).setOnClickListener { keyPressed(KeyEvent.KEYCODE_1, "1") }
|
||||
dialpadView.findViewById<View>(R.id.two).setOnClickListener { keyPressed(KeyEvent.KEYCODE_2, "2") }
|
||||
dialpadView.findViewById<View>(R.id.three).setOnClickListener { keyPressed(KeyEvent.KEYCODE_3, "3") }
|
||||
dialpadView.findViewById<View>(R.id.four).setOnClickListener { keyPressed(KeyEvent.KEYCODE_4, "4") }
|
||||
dialpadView.findViewById<View>(R.id.five).setOnClickListener { keyPressed(KeyEvent.KEYCODE_5, "5") }
|
||||
dialpadView.findViewById<View>(R.id.six).setOnClickListener { keyPressed(KeyEvent.KEYCODE_6, "6") }
|
||||
dialpadView.findViewById<View>(R.id.seven).setOnClickListener { keyPressed(KeyEvent.KEYCODE_7, "7") }
|
||||
dialpadView.findViewById<View>(R.id.eight).setOnClickListener { keyPressed(KeyEvent.KEYCODE_8, "8") }
|
||||
dialpadView.findViewById<View>(R.id.nine).setOnClickListener { keyPressed(KeyEvent.KEYCODE_9, "9") }
|
||||
if (enableStar) {
|
||||
dialpadView.findViewById<View>(R.id.star).setOnClickListener { append('*') }
|
||||
dialpadView.findViewById<View>(R.id.star).setOnClickListener { keyPressed(KeyEvent.KEYCODE_STAR, "*") }
|
||||
} else {
|
||||
dialpadView.findViewById<View>(R.id.star).isVisible = false
|
||||
}
|
||||
if (enablePound) {
|
||||
dialpadView.findViewById<View>(R.id.pound).setOnClickListener { append('#') }
|
||||
dialpadView.findViewById<View>(R.id.pound).setOnClickListener { keyPressed(KeyEvent.KEYCODE_POUND, "#") }
|
||||
} else {
|
||||
dialpadView.findViewById<View>(R.id.pound).isVisible = false
|
||||
}
|
||||
if (enablePlus) {
|
||||
dialpadView.findViewById<View>(R.id.zero).setOnLongClickListener {
|
||||
keyPressed(KeyEvent.KEYCODE_PLUS, "+")
|
||||
true
|
||||
}
|
||||
}
|
||||
if (enableDelete) {
|
||||
dialpadView.deleteButton.setOnClickListener { poll() }
|
||||
dialpadView.deleteButton.setOnClickListener { keyPressed(KeyEvent.KEYCODE_DEL, null) }
|
||||
dialpadView.deleteButton.setOnLongClickListener {
|
||||
clear()
|
||||
true
|
||||
}
|
||||
val tintColor = ThemeUtils.getColor(requireContext(), im.vector.app.R.attr.vctr_content_secondary)
|
||||
val tintColor = ThemeUtils.getColor(requireContext(), R.attr.vctr_content_secondary)
|
||||
ImageViewCompat.setImageTintList(dialpadView.deleteButton, ColorStateList.valueOf(tintColor))
|
||||
} else {
|
||||
dialpadView.deleteButton.isVisible = false
|
||||
}
|
||||
|
||||
// if region code is null, no formatting is performed
|
||||
formatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(if (formatAsYouType) regionCode else "")
|
||||
|
||||
val fabOk = view.findViewById<View>(R.id.fab_ok)
|
||||
if (enableFabOk) {
|
||||
fabOk.setOnClickListener {
|
||||
callback?.onOkClicked(digits?.text.toString(), input)
|
||||
}
|
||||
fabOk.setOnClickListener { onOkClicked() }
|
||||
} else {
|
||||
fabOk.isVisible = false
|
||||
}
|
||||
|
||||
digits?.setOnTextContextMenuClickListener {
|
||||
val string = digits?.text.toString()
|
||||
clear()
|
||||
for (element in string) {
|
||||
append(element)
|
||||
}
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
private fun onOkClicked() {
|
||||
val rawInput = getRawInput()
|
||||
if (rawInput.isEmpty()) {
|
||||
val clipboard = requireContext().getSystemService<ClipboardManager>()
|
||||
val textToPaste = clipboard?.primaryClip?.getItemAt(0)?.text ?: return
|
||||
val formatted = formatNumber(textToPaste.toString())
|
||||
digits.setText(formatted)
|
||||
digits.setSelection(digits.text!!.length)
|
||||
} else {
|
||||
val formatted = digits.text.toString()
|
||||
callback?.onOkClicked(formatted, rawInput)
|
||||
}
|
||||
}
|
||||
|
||||
fun getRawInput(): String {
|
||||
return PhoneNumberUtils.normalizeNumber(digits.text.toString())
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putString(EXTRA_REGION_CODE, regionCode)
|
||||
|
@ -145,40 +159,35 @@ class DialPadFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun poll() {
|
||||
if (input.isNotEmpty()) {
|
||||
input = input.substring(0, input.length - 1)
|
||||
formatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(regionCode)
|
||||
if (formatAsYouType) {
|
||||
digits?.setText("")
|
||||
for (c in input.toCharArray()) {
|
||||
digits?.setText(formatter?.inputDigit(c))
|
||||
}
|
||||
} else {
|
||||
digits?.setText(input)
|
||||
}
|
||||
private fun keyPressed(keyCode: Int, digitString: String?) {
|
||||
val event = KeyEvent(KeyEvent.ACTION_DOWN, keyCode)
|
||||
// Disable cursor and enable it again after onKeyDown otherwise DigitsEditText force replacing cursor at the end
|
||||
digits.isCursorVisible = false
|
||||
digits.onKeyDown(keyCode, event)
|
||||
digits.isCursorVisible = cursorVisible
|
||||
digitString?.also {
|
||||
callback?.onDigitAppended(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun clear() {
|
||||
formatter?.clear()
|
||||
digits?.setText("")
|
||||
input = ""
|
||||
digits.setText("")
|
||||
}
|
||||
|
||||
private fun append(c: Char) {
|
||||
callback?.onDigitAppended(c.toString())
|
||||
input += c
|
||||
if (formatAsYouType) {
|
||||
digits?.setText(formatter?.inputDigit(c))
|
||||
} else {
|
||||
digits?.setText(input)
|
||||
private fun formatNumber(dialString: String): String {
|
||||
val networkPortion = PhoneNumberUtils.extractNetworkPortion(dialString)
|
||||
if (TextUtils.isEmpty(networkPortion)) {
|
||||
return ""
|
||||
}
|
||||
val number = PhoneNumberUtils.formatNumber(networkPortion, null, regionCode) ?: networkPortion
|
||||
// Also retrieve the post dial portion of the provided data, so that the entire dial string can be reconstituted
|
||||
val postDial = PhoneNumberUtils.extractPostDialPortion(dialString)
|
||||
return number + postDial
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
fun onOkClicked(formatted: String?, raw: String?) = Unit
|
||||
fun onDigitAppended(digit: String) = Unit
|
||||
fun onOkClicked(formatted: String?, raw: String?) = Unit
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -193,4 +202,20 @@ class DialPadFragment : Fragment() {
|
|||
|
||||
private const val DEFAULT_REGION_CODE = "US"
|
||||
}
|
||||
|
||||
// Text watcher
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
// Noop
|
||||
}
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
// Noop
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
if (s.isEmpty()) {
|
||||
digits.clearFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,17 +86,13 @@ class CallTransferActivity : VectorBaseActivity<ActivityCallTransferBinding>(),
|
|||
}
|
||||
}
|
||||
|
||||
sectionsPagerAdapter = CallTransferPagerAdapter(this).register()
|
||||
sectionsPagerAdapter = CallTransferPagerAdapter(this)
|
||||
views.callTransferViewPager.adapter = sectionsPagerAdapter
|
||||
sectionsPagerAdapter.onDialPadOkClicked = { phoneNumber ->
|
||||
val action = CallTransferAction.ConnectWithPhoneNumber(views.callTransferConsultCheckBox.isChecked, phoneNumber)
|
||||
callTransferViewModel.handle(action)
|
||||
}
|
||||
|
||||
TabLayoutMediator(views.callTransferTabLayout, views.callTransferViewPager) { tab, position ->
|
||||
when (position) {
|
||||
0 -> tab.text = getString(R.string.call_transfer_users_tab_title)
|
||||
1 -> tab.text = getString(R.string.call_dial_pad_title)
|
||||
CallTransferPagerAdapter.USER_LIST_INDEX -> tab.text = getString(R.string.call_transfer_users_tab_title)
|
||||
CallTransferPagerAdapter.DIAL_PAD_INDEX -> tab.text = getString(R.string.call_dial_pad_title)
|
||||
}
|
||||
}.attach()
|
||||
configureToolbar(views.callTransferToolbar)
|
||||
|
@ -106,11 +102,18 @@ class CallTransferActivity : VectorBaseActivity<ActivityCallTransferBinding>(),
|
|||
|
||||
private fun setupConnectAction() {
|
||||
views.callTransferConnectAction.debouncedClicks {
|
||||
val selectedUser = sectionsPagerAdapter.userListFragment?.getCurrentState()?.getSelectedMatrixId()?.firstOrNull()
|
||||
if (selectedUser != null) {
|
||||
when (views.callTransferTabLayout.selectedTabPosition) {
|
||||
CallTransferPagerAdapter.USER_LIST_INDEX -> {
|
||||
val selectedUser = sectionsPagerAdapter.userListFragment?.getCurrentState()?.getSelectedMatrixId()?.firstOrNull() ?: return@debouncedClicks
|
||||
val action = CallTransferAction.ConnectWithUserId(views.callTransferConsultCheckBox.isChecked, selectedUser)
|
||||
callTransferViewModel.handle(action)
|
||||
}
|
||||
CallTransferPagerAdapter.DIAL_PAD_INDEX -> {
|
||||
val phoneNumber = sectionsPagerAdapter.dialPadFragment?.getRawInput() ?: return@debouncedClicks
|
||||
val action = CallTransferAction.ConnectWithPhoneNumber(views.callTransferConsultCheckBox.isChecked, phoneNumber)
|
||||
callTransferViewModel.handle(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import im.vector.app.core.extensions.toMvRxBundle
|
||||
import im.vector.app.core.platform.Restorable
|
||||
import im.vector.app.features.call.dialpad.DialPadFragment
|
||||
import im.vector.app.features.settings.VectorLocale
|
||||
import im.vector.app.features.userdirectory.UserListFragment
|
||||
|
@ -29,14 +28,17 @@ import im.vector.app.features.userdirectory.UserListFragmentArgs
|
|||
|
||||
class CallTransferPagerAdapter(
|
||||
private val fragmentActivity: FragmentActivity
|
||||
) : FragmentStateAdapter(fragmentActivity), Restorable {
|
||||
) : FragmentStateAdapter(fragmentActivity) {
|
||||
|
||||
companion object {
|
||||
const val USER_LIST_INDEX = 0
|
||||
const val DIAL_PAD_INDEX = 1
|
||||
}
|
||||
|
||||
val userListFragment: UserListFragment?
|
||||
get() = findFragmentAtPosition(0) as? UserListFragment
|
||||
get() = findFragmentAtPosition(USER_LIST_INDEX) as? UserListFragment
|
||||
val dialPadFragment: DialPadFragment?
|
||||
get() = findFragmentAtPosition(1) as? DialPadFragment
|
||||
|
||||
var onDialPadOkClicked: ((String) -> Unit)? = null
|
||||
get() = findFragmentAtPosition(DIAL_PAD_INDEX) as? DialPadFragment
|
||||
|
||||
override fun getItemCount() = 2
|
||||
|
||||
|
@ -57,10 +59,9 @@ class CallTransferPagerAdapter(
|
|||
(fragment as DialPadFragment).apply {
|
||||
arguments = Bundle().apply {
|
||||
putBoolean(DialPadFragment.EXTRA_ENABLE_DELETE, true)
|
||||
putBoolean(DialPadFragment.EXTRA_ENABLE_OK, true)
|
||||
putBoolean(DialPadFragment.EXTRA_ENABLE_OK, false)
|
||||
putString(DialPadFragment.EXTRA_REGION_CODE, VectorLocale.applicationLocale.country)
|
||||
}
|
||||
applyCallback()
|
||||
}
|
||||
}
|
||||
return fragment
|
||||
|
@ -69,20 +70,4 @@ class CallTransferPagerAdapter(
|
|||
private fun findFragmentAtPosition(position: Int): Fragment? {
|
||||
return fragmentActivity.supportFragmentManager.findFragmentByTag("f$position")
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) = Unit
|
||||
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
|
||||
dialPadFragment?.applyCallback()
|
||||
}
|
||||
|
||||
private fun DialPadFragment.applyCallback(): DialPadFragment {
|
||||
callback = object : DialPadFragment.Callback {
|
||||
override fun onOkClicked(formatted: String?, raw: String?) {
|
||||
if (raw.isNullOrEmpty()) return
|
||||
onDialPadOkClicked?.invoke(raw)
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import android.view.Menu
|
|||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.get
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.iterator
|
||||
import androidx.fragment.app.Fragment
|
||||
|
@ -122,7 +123,6 @@ class HomeDetailFragment @Inject constructor(
|
|||
super.onViewCreated(view, savedInstanceState)
|
||||
sharedActionViewModel = activityViewModelProvider.get(HomeSharedActionViewModel::class.java)
|
||||
sharedCallActionViewModel = activityViewModelProvider.get(SharedKnownCallsViewModel::class.java)
|
||||
|
||||
setupBottomNavigationView()
|
||||
setupToolbar()
|
||||
setupKeysBackupBanner()
|
||||
|
@ -351,6 +351,7 @@ class HomeDetailFragment @Inject constructor(
|
|||
}
|
||||
|
||||
private fun updateUIForTab(tab: HomeTab) {
|
||||
views.bottomNavigationView.menu.findItem(tab.toMenuId()).isChecked = true
|
||||
views.groupToolbarTitleView.setText(tab.titleRes)
|
||||
updateSelectedFragment(tab)
|
||||
invalidateOptionsMenu()
|
||||
|
|
|
@ -140,7 +140,6 @@
|
|||
android:id="@+id/roomListContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:background="?vctr_list_separator_system"
|
||||
app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/activeCallView" />
|
||||
|
||||
|
|
Loading…
Reference in New Issue