Use in-app call overlay if system overlay permission wasn't granted
This commit is contained in:
parent
4e6eb852e7
commit
f0d4b8c34e
9 changed files with 255 additions and 66 deletions
|
@ -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 -> {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,7 +85,11 @@ class RecordingsFragment : MasterFragment<RecordingsFragmentBinding, RecordingsL
|
|||
videoY = v.y - event.rawY
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
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()
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<Boolean>()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
<variable
|
||||
name="callOverlayViewModel"
|
||||
type="org.linphone.activities.main.viewmodels.CallOverlayViewModel" />
|
||||
</data>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentBottom="true">
|
||||
|
@ -14,6 +21,10 @@
|
|||
android:layout_height="match_parent"
|
||||
android:layout_toRightOf="@id/tabs_fragment">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/nav_host_fragment"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -23,6 +34,13 @@
|
|||
app:navGraph="@navigation/main_nav_graph"
|
||||
tools:layout="@layout/dialer_fragment" />
|
||||
|
||||
<include
|
||||
android:id="@+id/call_overlay"
|
||||
layout="@layout/call_overlay"
|
||||
tools:visibility="@{callOverlayViewModel.displayCallOverlay}"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
|
@ -34,3 +52,5 @@
|
|||
tools:layout="@layout/tabs_fragment"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</layout>
|
|
@ -1,7 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View" />
|
||||
<variable
|
||||
name="visibility"
|
||||
type="Boolean" />
|
||||
</data>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="@{visibility ? View.VISIBLE : View.GONE}">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="60dp"
|
||||
|
@ -13,3 +23,5 @@
|
|||
android:src="@drawable/call_back" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</layout>
|
|
@ -10,6 +10,9 @@
|
|||
<variable
|
||||
name="viewModel"
|
||||
type="org.linphone.activities.main.viewmodels.SharedMainViewModel" />
|
||||
<variable
|
||||
name="callOverlayViewModel"
|
||||
type="org.linphone.activities.main.viewmodels.CallOverlayViewModel" />
|
||||
</data>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
|
@ -35,7 +38,10 @@
|
|||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/status_fragment">
|
||||
|
||||
<include android:id="@+id/content" layout="@layout/main_activity_content" />
|
||||
<include
|
||||
android:id="@+id/content"
|
||||
layout="@layout/main_activity_content"
|
||||
tools:callOverlayViewModel="@{callOverlayViewModel}"/>
|
||||
|
||||
<!-- Side Menu -->
|
||||
<RelativeLayout
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
<variable
|
||||
name="callOverlayViewModel"
|
||||
type="org.linphone.activities.main.viewmodels.CallOverlayViewModel" />
|
||||
</data>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentBottom="true">
|
||||
|
@ -14,6 +21,10 @@
|
|||
android:layout_height="match_parent"
|
||||
android:layout_above="@id/tabs_fragment">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/nav_host_fragment"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -23,6 +34,13 @@
|
|||
app:navGraph="@navigation/main_nav_graph"
|
||||
tools:layout="@layout/dialer_fragment" />
|
||||
|
||||
<include
|
||||
android:id="@+id/call_overlay"
|
||||
layout="@layout/call_overlay"
|
||||
tools:visibility="@{callOverlayViewModel.displayCallOverlay}" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
|
@ -34,3 +52,5 @@
|
|||
tools:layout="@layout/tabs_fragment"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</layout>
|
Loading…
Reference in a new issue