diff --git a/app/src/main/java/org/linphone/activities/call/CallActivity.kt b/app/src/main/java/org/linphone/activities/call/CallActivity.kt index 2a38f1484..ffd82da0a 100644 --- a/app/src/main/java/org/linphone/activities/call/CallActivity.kt +++ b/app/src/main/java/org/linphone/activities/call/CallActivity.kt @@ -86,7 +86,10 @@ class CallActivity : ProximitySensorActivity() { previewY = v.y - event.rawY } MotionEvent.ACTION_MOVE -> { - v.animate().x(event.rawX + previewX).y(event.rawY + previewY).setDuration(0) + v.animate() + .x(event.rawX + previewX) + .y(event.rawY + previewY) + .setDuration(0) .start() } else -> { diff --git a/app/src/main/java/org/linphone/activities/main/MainActivity.kt b/app/src/main/java/org/linphone/activities/main/MainActivity.kt index 490756c66..6148be565 100644 --- a/app/src/main/java/org/linphone/activities/main/MainActivity.kt +++ b/app/src/main/java/org/linphone/activities/main/MainActivity.kt @@ -25,6 +25,7 @@ import android.net.Uri import android.os.Bundle import android.os.Parcelable import android.view.Gravity +import android.view.MotionEvent import android.view.View import android.view.inputmethod.InputMethodManager import androidx.databinding.DataBindingUtil @@ -37,6 +38,7 @@ import androidx.navigation.findNavController import com.google.android.material.snackbar.Snackbar import java.io.UnsupportedEncodingException import java.net.URLDecoder +import kotlin.math.abs import kotlinx.coroutines.* import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.corePreferences @@ -45,6 +47,7 @@ import org.linphone.activities.GenericActivity import org.linphone.activities.SnackBarActivity import org.linphone.activities.assistant.AssistantActivity import org.linphone.activities.call.CallActivity +import org.linphone.activities.main.viewmodels.CallOverlayViewModel import org.linphone.activities.main.viewmodels.SharedMainViewModel import org.linphone.activities.navigateToDialer import org.linphone.compatibility.Compatibility @@ -57,6 +60,7 @@ import org.linphone.utils.FileUtils class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestinationChangedListener { private lateinit var binding: MainActivityBinding private lateinit var sharedViewModel: SharedMainViewModel + private lateinit var callOverlayViewModel: CallOverlayViewModel private val listener = object : ContactsUpdatedListenerStub() { override fun onContactsUpdated() { @@ -72,6 +76,12 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin private lateinit var tabsFragment: FragmentContainerView private lateinit var statusFragment: FragmentContainerView + private var overlayX = 0f + private var overlayY = 0f + private var initPosX = 0f + private var initPosY = 0f + private var overlay: View? = null + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -81,6 +91,9 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin sharedViewModel = ViewModelProvider(this).get(SharedMainViewModel::class.java) binding.viewModel = sharedViewModel + callOverlayViewModel = ViewModelProvider(this).get(CallOverlayViewModel::class.java) + binding.callOverlayViewModel = callOverlayViewModel + sharedViewModel.toggleDrawerEvent.observe(this, { it.consume { if (binding.sideMenu.isDrawerOpen(Gravity.LEFT)) { @@ -112,6 +125,8 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin tabsFragment = findViewById(R.id.tabs_fragment) statusFragment = findViewById(R.id.status_fragment) + + initOverlay() } override fun onNewIntent(intent: Intent?) { @@ -403,4 +418,39 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin Log.e("[Main Activity] Failed to parse shortcut/locus id: $id") } } + + private fun initOverlay() { + overlay = binding.root.findViewById(R.id.call_overlay) + val callOverlay = overlay + callOverlay ?: return + + callOverlay.setOnTouchListener { view, event -> + when (event.action) { + MotionEvent.ACTION_DOWN -> { + overlayX = view.x - event.rawX + overlayY = view.y - event.rawY + initPosX = view.x + initPosY = view.y + } + MotionEvent.ACTION_MOVE -> { + view.animate() + .x(event.rawX + overlayX) + .y(event.rawY + overlayY) + .setDuration(0) + .start() + } + MotionEvent.ACTION_UP -> { + if (abs(initPosX - view.x) < 5 && abs(initPosY - view.y) < 5) { + view.performClick() + } + } + else -> return@setOnTouchListener false + } + true + } + + callOverlay.setOnClickListener { + coreContext.onCallStarted() + } + } } diff --git a/app/src/main/java/org/linphone/activities/main/recordings/fragments/RecordingsFragment.kt b/app/src/main/java/org/linphone/activities/main/recordings/fragments/RecordingsFragment.kt index 83f4f5959..d3669d891 100644 --- a/app/src/main/java/org/linphone/activities/main/recordings/fragments/RecordingsFragment.kt +++ b/app/src/main/java/org/linphone/activities/main/recordings/fragments/RecordingsFragment.kt @@ -85,7 +85,11 @@ class RecordingsFragment : MasterFragment { - v.animate().x(event.rawX + videoX).y(event.rawY + videoY).setDuration(0).start() + v.animate() + .x(event.rawX + videoX) + .y(event.rawY + videoY) + .setDuration(0) + .start() } else -> { v.performClick() diff --git a/app/src/main/java/org/linphone/activities/main/viewmodels/CallOverlayViewModel.kt b/app/src/main/java/org/linphone/activities/main/viewmodels/CallOverlayViewModel.kt new file mode 100644 index 000000000..07e573b0b --- /dev/null +++ b/app/src/main/java/org/linphone/activities/main/viewmodels/CallOverlayViewModel.kt @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2010-2021 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.activities.main.viewmodels + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import org.linphone.LinphoneApplication.Companion.coreContext +import org.linphone.LinphoneApplication.Companion.corePreferences +import org.linphone.core.Call +import org.linphone.core.Core +import org.linphone.core.CoreListenerStub + +class CallOverlayViewModel : ViewModel() { + val displayCallOverlay = MutableLiveData() + + private val listener = object : CoreListenerStub() { + override fun onCallStateChanged( + core: Core, + call: Call, + state: Call.State, + message: String + ) { + if (state == Call.State.IncomingReceived || state == Call.State.OutgoingInit) { + createCallOverlay() + } else if (state == Call.State.End || state == Call.State.Error || state == Call.State.Released) { + if (core.callsNb == 0) { + removeCallOverlay() + } + } + } + } + + init { + displayCallOverlay.value = coreContext.core.callsNb > 0 + + coreContext.core.addListener(listener) + } + + override fun onCleared() { + coreContext.core.removeListener(listener) + + super.onCleared() + } + + private fun createCallOverlay() { + // If system-wide call overlay is enabled or if already visible, abort + if (corePreferences.showCallOverlay) { + return + } + + displayCallOverlay.value = true + } + + private fun removeCallOverlay() { + displayCallOverlay.value = false + } +} diff --git a/app/src/main/java/org/linphone/core/CoreContext.kt b/app/src/main/java/org/linphone/core/CoreContext.kt index ad4ef3abc..d0f42699e 100644 --- a/app/src/main/java/org/linphone/core/CoreContext.kt +++ b/app/src/main/java/org/linphone/core/CoreContext.kt @@ -654,7 +654,7 @@ class CoreContext(val context: Context, coreConfig: Config) { context.startActivity(intent) } - private fun onCallStarted() { + fun onCallStarted() { if (corePreferences.preventInterfaceFromShowingUp) { Log.w("[Context] We were asked to not show the call screen") return diff --git a/app/src/main/res/layout-land/main_activity_content.xml b/app/src/main/res/layout-land/main_activity_content.xml index 4eb3af483..8025ec968 100644 --- a/app/src/main/res/layout-land/main_activity_content.xml +++ b/app/src/main/res/layout-land/main_activity_content.xml @@ -1,36 +1,56 @@ - + xmlns:tools="http://schemas.android.com/tools"> - - + + + + + android:layout_alignParentBottom="true"> - + + android:layout_toRightOf="@id/tabs_fragment"> - + - + - \ No newline at end of file + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/call_overlay.xml b/app/src/main/res/layout/call_overlay.xml index 87dff99aa..3baf5d08a 100644 --- a/app/src/main/res/layout/call_overlay.xml +++ b/app/src/main/res/layout/call_overlay.xml @@ -1,15 +1,27 @@ - + - + + + + - \ No newline at end of file + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/main_activity.xml b/app/src/main/res/layout/main_activity.xml index abdc88b5d..46e3e268f 100644 --- a/app/src/main/res/layout/main_activity.xml +++ b/app/src/main/res/layout/main_activity.xml @@ -10,6 +10,9 @@ + - + - + xmlns:tools="http://schemas.android.com/tools"> - - + + + + + android:layout_alignParentBottom="true"> - + + android:layout_above="@id/tabs_fragment"> - + - + - \ No newline at end of file + + + + + + + + + + + \ No newline at end of file