Merge pull request #3516 from vector-im/feature/fga/dial_pad_improvements

Feature/fga/dial pad improvements
This commit is contained in:
Benoit Marty 2021-06-17 14:31:48 +02:00 committed by GitHub
commit da19992f3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 122 additions and 111 deletions

1
newsfragment/3516.misc Normal file
View File

@ -0,0 +1 @@
Some improvements on DialPad (cursor edition, paste number, small fixes).

View File

@ -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()

View File

@ -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()
}
}
}

View File

@ -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)
}
}
}
}

View File

@ -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
}
}

View File

@ -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()

View File

@ -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" />