From 973afe08c5bb3a81ca611770d021480218d92fa2 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Wed, 30 Aug 2023 11:46:16 +0200 Subject: [PATCH] Added numpad --- app/build.gradle | 2 +- .../main/calls/fragment/StartCallFragment.kt | 33 +++ .../ui/main/calls/model/NumpadModel.kt | 50 +++++ .../calls/viewmodel/StartCallViewModel.kt | 49 +++++ .../org/linphone/utils/DataBindingUtils.kt | 14 +- .../shape_circle_green_background.xml | 5 + .../main/res/drawable/shape_drawer_handle.xml | 6 + .../res/drawable/shape_numpad_background.xml | 5 + .../res/layout-land/calls_list_fragment.xml | 2 +- .../layout-land/contacts_list_fragment.xml | 2 +- app/src/main/res/layout/account_list_cell.xml | 2 +- .../res/layout/assistant_login_fragment.xml | 6 +- .../assistant_qr_code_scanner_fragment.xml | 4 +- .../layout/assistant_register_fragment.xml | 4 +- app/src/main/res/layout/call_fragment.xml | 4 +- app/src/main/res/layout/call_list_cell.xml | 4 +- .../main/res/layout/call_start_fragment.xml | 44 +++- app/src/main/res/layout/call_start_numpad.xml | 191 ++++++++++++++++++ .../main/res/layout/calls_list_fragment.xml | 2 +- .../main/res/layout/chat_bubble_incoming.xml | 2 +- .../layout/contact_device_trust_list_cell.xml | 4 +- .../res/layout/contact_new_or_edit_cell.xml | 4 +- .../res/layout/contacts_list_fragment.xml | 2 +- app/src/main/res/layout/toast_blue.xml | 4 +- app/src/main/res/layout/toast_green.xml | 4 +- app/src/main/res/layout/toast_red.xml | 4 +- .../res/layout/voip_active_call_fragment.xml | 4 +- .../res/layout/voip_call_main_actions.xml | 2 +- .../layout/voip_incoming_call_fragment.xml | 4 +- .../layout/voip_outgoing_call_fragment.xml | 8 +- app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/dimen.xml | 2 + app/src/main/res/values/styles.xml | 8 + 33 files changed, 436 insertions(+), 46 deletions(-) create mode 100644 app/src/main/java/org/linphone/ui/main/calls/model/NumpadModel.kt create mode 100644 app/src/main/res/drawable/shape_circle_green_background.xml create mode 100644 app/src/main/res/drawable/shape_drawer_handle.xml create mode 100644 app/src/main/res/drawable/shape_numpad_background.xml create mode 100644 app/src/main/res/layout/call_start_numpad.xml diff --git a/app/build.gradle b/app/build.gradle index 5892549a2..a6d041406 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -100,7 +100,7 @@ dependencies { // https://github.com/tommybuonomo/dotsindicator/blob/master/LICENSE Apache v2.0 implementation("com.tbuonomo:dotsindicator:5.0") - implementation platform('com.google.firebase:firebase-bom:30.3.2') + implementation platform('com.google.firebase:firebase-bom:32.2.3') implementation 'com.google.firebase:firebase-messaging' implementation 'org.linphone:linphone-sdk-android:5.3+' diff --git a/app/src/main/java/org/linphone/ui/main/calls/fragment/StartCallFragment.kt b/app/src/main/java/org/linphone/ui/main/calls/fragment/StartCallFragment.kt index 3b9f3bc8c..d99360c6e 100644 --- a/app/src/main/java/org/linphone/ui/main/calls/fragment/StartCallFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/calls/fragment/StartCallFragment.kt @@ -43,6 +43,9 @@ import org.linphone.ui.main.contacts.viewmodel.ContactsListViewModel import org.linphone.ui.main.fragment.GenericFragment import org.linphone.ui.main.model.isInSecureMode import org.linphone.utils.DialogUtils +import org.linphone.utils.hideKeyboard +import org.linphone.utils.setKeyboardInsetListener +import org.linphone.utils.showKeyboard @UiThread class StartCallFragment : GenericFragment() { @@ -103,6 +106,10 @@ class StartCallFragment : GenericFragment() { goBack() } + binding.setHideNumpadClickListener { + viewModel.hideNumpad() + } + contactsAdapter = ContactsListAdapter(viewLifecycleOwner, disableLongClick = true) binding.contactsList.setHasFixedSize(true) binding.contactsList.adapter = contactsAdapter @@ -140,6 +147,32 @@ class StartCallFragment : GenericFragment() { contactsListViewModel.applyFilter(filter) viewModel.applyFilter(filter) } + + viewModel.appendDigitToSearchBarEvent.observe(viewLifecycleOwner) { + it.consume { digit -> + val newValue = "${binding.searchBar.text}$digit" + binding.searchBar.setText(newValue) + binding.searchBar.setSelection(newValue.length) + } + } + + viewModel.requestKeyboardVisibilityChangedEvent.observe(viewLifecycleOwner) { + it.consume { show -> + if (show) { + // To automatically open keyboard + binding.searchBar.showKeyboard(requireActivity().window) + } else { + binding.searchBar.requestFocus() + binding.searchBar.hideKeyboard() + } + } + } + + binding.root.setKeyboardInsetListener { keyboardVisible -> + if (keyboardVisible) { + viewModel.isNumpadVisible.value = false + } + } } private fun startCall(model: ContactAvatarModel) { diff --git a/app/src/main/java/org/linphone/ui/main/calls/model/NumpadModel.kt b/app/src/main/java/org/linphone/ui/main/calls/model/NumpadModel.kt new file mode 100644 index 000000000..f39be1540 --- /dev/null +++ b/app/src/main/java/org/linphone/ui/main/calls/model/NumpadModel.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of linphone-android + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.linphone.ui.main.calls.model + +import androidx.annotation.UiThread +import org.linphone.core.tools.Log + +class NumpadModel @UiThread constructor( + private val onDigitClicked: (value: String) -> (Unit), + private val onCallClicked: () -> (Unit) +) { + companion object { + private const val TAG = "[Numpad Model]" + } + + @UiThread + fun onDigitClicked(value: String) { + Log.i("$TAG Clicked on digit [$value]") + onDigitClicked.invoke(value) + } + + fun onDigitLongClicked(value: String): Boolean { + Log.i("$TAG Long clicked on digit [$value]") + onDigitClicked.invoke(value) + return true + } + + @UiThread + fun onCallClicked() { + Log.i("$TAG Starting call") + onCallClicked.invoke() + } +} diff --git a/app/src/main/java/org/linphone/ui/main/calls/viewmodel/StartCallViewModel.kt b/app/src/main/java/org/linphone/ui/main/calls/viewmodel/StartCallViewModel.kt index 6705a9033..67a34baa6 100644 --- a/app/src/main/java/org/linphone/ui/main/calls/viewmodel/StartCallViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/calls/viewmodel/StartCallViewModel.kt @@ -33,6 +33,7 @@ import org.linphone.core.MagicSearch import org.linphone.core.MagicSearchListenerStub import org.linphone.core.SearchResult import org.linphone.core.tools.Log +import org.linphone.ui.main.calls.model.NumpadModel import org.linphone.ui.main.calls.model.SuggestionModel import org.linphone.ui.main.model.isInSecureMode import org.linphone.utils.Event @@ -49,6 +50,18 @@ class StartCallViewModel @UiThread constructor() : ViewModel() { val suggestionsList = MutableLiveData>() + val numpadModel: NumpadModel + + val isNumpadVisible = MutableLiveData() + + val appendDigitToSearchBarEvent: MutableLiveData> by lazy { + MutableLiveData>() + } + + val requestKeyboardVisibilityChangedEvent: MutableLiveData> by lazy { + MutableLiveData>() + } + val onSuggestionClickedEvent: MutableLiveData> by lazy { MutableLiveData>() } @@ -81,6 +94,31 @@ class StartCallViewModel @UiThread constructor() : ViewModel() { } init { + isNumpadVisible.value = false + numpadModel = NumpadModel( + { digit -> + // onDigitClicked + appendDigitToSearchBarEvent.value = Event(digit) + // Don't do that, cursor will stay at start + // searchFilter.value = "${searchFilter.value.orEmpty()}$digit" + }, + { // OnCallClicked + val suggestion = searchFilter.value.orEmpty() + if (suggestion.isNotEmpty()) { + Log.i("$TAG Using numpad dial button to call [$suggestion]") + coreContext.postOnCoreThread { core -> + val address = core.interpretUrl(suggestion, true) + if (address != null) { + Log.i("$TAG Calling [${address.asStringUriOnly()}]") + onSuggestionClickedEvent.postValue(Event(address)) + } else { + Log.e("$TAG Failed to parse [$suggestion] as SIP address") + } + } + } + } + ) + coreContext.postOnCoreThread { core -> val defaultAccount = core.defaultAccount limitSearchToLinphoneAccounts = defaultAccount?.isInSecureMode() ?: false @@ -108,6 +146,17 @@ class StartCallViewModel @UiThread constructor() : ViewModel() { searchFilter.value = "" } + @UiThread + fun switchBetweenKeyboardAndNumpad() { + requestKeyboardVisibilityChangedEvent.value = Event(isNumpadVisible.value == true) + isNumpadVisible.value = isNumpadVisible.value == false + } + + @UiThread + fun hideNumpad() { + isNumpadVisible.value = false + } + @WorkerThread fun processMagicSearchResults(results: Array) { Log.i("$TAG Processing [${results.size}] results") diff --git a/app/src/main/java/org/linphone/utils/DataBindingUtils.kt b/app/src/main/java/org/linphone/utils/DataBindingUtils.kt index 053b13087..110544c37 100644 --- a/app/src/main/java/org/linphone/utils/DataBindingUtils.kt +++ b/app/src/main/java/org/linphone/utils/DataBindingUtils.kt @@ -40,6 +40,7 @@ import androidx.core.view.doOnLayout import androidx.databinding.BindingAdapter import androidx.databinding.DataBindingUtil import androidx.databinding.ViewDataBinding +import androidx.lifecycle.LifecycleOwner import coil.load import coil.transform.CircleCropTransformation import io.getstream.avatarview.AvatarView @@ -120,7 +121,7 @@ fun View.setKeyboardInsetListener(lambda: (visible: Boolean) -> Unit) { lambda(isKeyboardVisible) } catch (ise: IllegalStateException) { Log.e( - "[Databinding Utils] Failed to called lambda after keyboard visibility changed: $ise" + "[Data Binding Utils] Failed to called lambda after keyboard visibility changed: $ise" ) } @@ -137,7 +138,7 @@ fun View.setKeyboardInsetListener(lambda: (visible: Boolean) -> Unit) { lambda(isKeyboardVisible) } catch (ise: IllegalStateException) { Log.e( - "[Databinding Utils] Failed to called lambda after keyboard visibility changed: $ise" + "[Data Binding Utils] Failed to called lambda after keyboard visibility changed: $ise" ) } } @@ -284,3 +285,12 @@ fun setConstraintLayoutBottomMargin(view: View, margins: Float) { params.setMargins(params.leftMargin, params.topMargin, params.rightMargin, m) view.layoutParams = params } + +@BindingAdapter("inflatedLifecycleOwner") +fun setInflatedViewStubLifecycleOwner(view: View, enable: Boolean) { + val binding = DataBindingUtil.bind(view) + // This is a bit hacky... + if (view.context is LifecycleOwner) { + binding?.lifecycleOwner = view.context as? LifecycleOwner + } +} diff --git a/app/src/main/res/drawable/shape_circle_green_background.xml b/app/src/main/res/drawable/shape_circle_green_background.xml new file mode 100644 index 000000000..d36f85dd7 --- /dev/null +++ b/app/src/main/res/drawable/shape_circle_green_background.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_drawer_handle.xml b/app/src/main/res/drawable/shape_drawer_handle.xml new file mode 100644 index 000000000..7946b3661 --- /dev/null +++ b/app/src/main/res/drawable/shape_drawer_handle.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_numpad_background.xml b/app/src/main/res/drawable/shape_numpad_background.xml new file mode 100644 index 000000000..334cf259c --- /dev/null +++ b/app/src/main/res/drawable/shape_numpad_background.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-land/calls_list_fragment.xml b/app/src/main/res/layout-land/calls_list_fragment.xml index 659da509d..5b4e3a2c5 100644 --- a/app/src/main/res/layout-land/calls_list_fragment.xml +++ b/app/src/main/res/layout-land/calls_list_fragment.xml @@ -47,7 +47,7 @@ - + + @@ -16,7 +19,7 @@ + android:background="@color/gray_7"> + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/call_start_numpad.xml b/app/src/main/res/layout/call_start_numpad.xml new file mode 100644 index 000000000..5c3eb9ada --- /dev/null +++ b/app/src/main/res/layout/call_start_numpad.xml @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/calls_list_fragment.xml b/app/src/main/res/layout/calls_list_fragment.xml index 0a245880b..63dc048b6 100644 --- a/app/src/main/res/layout/calls_list_fragment.xml +++ b/app/src/main/res/layout/calls_list_fragment.xml @@ -47,7 +47,7 @@ #FFEACB #FFB266 #22334D + #C0D1D9 #EEF7F8 diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml index a2e74c5c8..612d669be 100644 --- a/app/src/main/res/values/dimen.xml +++ b/app/src/main/res/values/dimen.xml @@ -8,6 +8,8 @@ 355dp 300dp + 24dp + 45dp 50dp 100dp diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 1b7939621..4ba503000 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -50,6 +50,14 @@ @color/red_danger 8dp +