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
|
previewY = v.y - event.rawY
|
||||||
}
|
}
|
||||||
MotionEvent.ACTION_MOVE -> {
|
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()
|
.start()
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import androidx.databinding.DataBindingUtil
|
import androidx.databinding.DataBindingUtil
|
||||||
|
@ -37,6 +38,7 @@ import androidx.navigation.findNavController
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import java.io.UnsupportedEncodingException
|
import java.io.UnsupportedEncodingException
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
|
import kotlin.math.abs
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||||
import org.linphone.LinphoneApplication.Companion.corePreferences
|
import org.linphone.LinphoneApplication.Companion.corePreferences
|
||||||
|
@ -45,6 +47,7 @@ import org.linphone.activities.GenericActivity
|
||||||
import org.linphone.activities.SnackBarActivity
|
import org.linphone.activities.SnackBarActivity
|
||||||
import org.linphone.activities.assistant.AssistantActivity
|
import org.linphone.activities.assistant.AssistantActivity
|
||||||
import org.linphone.activities.call.CallActivity
|
import org.linphone.activities.call.CallActivity
|
||||||
|
import org.linphone.activities.main.viewmodels.CallOverlayViewModel
|
||||||
import org.linphone.activities.main.viewmodels.SharedMainViewModel
|
import org.linphone.activities.main.viewmodels.SharedMainViewModel
|
||||||
import org.linphone.activities.navigateToDialer
|
import org.linphone.activities.navigateToDialer
|
||||||
import org.linphone.compatibility.Compatibility
|
import org.linphone.compatibility.Compatibility
|
||||||
|
@ -57,6 +60,7 @@ import org.linphone.utils.FileUtils
|
||||||
class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestinationChangedListener {
|
class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestinationChangedListener {
|
||||||
private lateinit var binding: MainActivityBinding
|
private lateinit var binding: MainActivityBinding
|
||||||
private lateinit var sharedViewModel: SharedMainViewModel
|
private lateinit var sharedViewModel: SharedMainViewModel
|
||||||
|
private lateinit var callOverlayViewModel: CallOverlayViewModel
|
||||||
|
|
||||||
private val listener = object : ContactsUpdatedListenerStub() {
|
private val listener = object : ContactsUpdatedListenerStub() {
|
||||||
override fun onContactsUpdated() {
|
override fun onContactsUpdated() {
|
||||||
|
@ -72,6 +76,12 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
|
||||||
private lateinit var tabsFragment: FragmentContainerView
|
private lateinit var tabsFragment: FragmentContainerView
|
||||||
private lateinit var statusFragment: 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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
@ -81,6 +91,9 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
|
||||||
sharedViewModel = ViewModelProvider(this).get(SharedMainViewModel::class.java)
|
sharedViewModel = ViewModelProvider(this).get(SharedMainViewModel::class.java)
|
||||||
binding.viewModel = sharedViewModel
|
binding.viewModel = sharedViewModel
|
||||||
|
|
||||||
|
callOverlayViewModel = ViewModelProvider(this).get(CallOverlayViewModel::class.java)
|
||||||
|
binding.callOverlayViewModel = callOverlayViewModel
|
||||||
|
|
||||||
sharedViewModel.toggleDrawerEvent.observe(this, {
|
sharedViewModel.toggleDrawerEvent.observe(this, {
|
||||||
it.consume {
|
it.consume {
|
||||||
if (binding.sideMenu.isDrawerOpen(Gravity.LEFT)) {
|
if (binding.sideMenu.isDrawerOpen(Gravity.LEFT)) {
|
||||||
|
@ -112,6 +125,8 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
|
||||||
|
|
||||||
tabsFragment = findViewById(R.id.tabs_fragment)
|
tabsFragment = findViewById(R.id.tabs_fragment)
|
||||||
statusFragment = findViewById(R.id.status_fragment)
|
statusFragment = findViewById(R.id.status_fragment)
|
||||||
|
|
||||||
|
initOverlay()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent?) {
|
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")
|
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
|
videoY = v.y - event.rawY
|
||||||
}
|
}
|
||||||
MotionEvent.ACTION_MOVE -> {
|
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 -> {
|
else -> {
|
||||||
v.performClick()
|
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)
|
context.startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onCallStarted() {
|
fun onCallStarted() {
|
||||||
if (corePreferences.preventInterfaceFromShowingUp) {
|
if (corePreferences.preventInterfaceFromShowingUp) {
|
||||||
Log.w("[Context] We were asked to not show the call screen")
|
Log.w("[Context] We were asked to not show the call screen")
|
||||||
return
|
return
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
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_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_alignParentBottom="true">
|
android:layout_alignParentBottom="true">
|
||||||
|
@ -14,6 +21,10 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_toRightOf="@id/tabs_fragment">
|
android:layout_toRightOf="@id/tabs_fragment">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
<androidx.fragment.app.FragmentContainerView
|
||||||
android:id="@+id/nav_host_fragment"
|
android:id="@+id/nav_host_fragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -23,6 +34,13 @@
|
||||||
app:navGraph="@navigation/main_nav_graph"
|
app:navGraph="@navigation/main_nav_graph"
|
||||||
tools:layout="@layout/dialer_fragment" />
|
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.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
<androidx.fragment.app.FragmentContainerView
|
||||||
|
@ -33,4 +51,6 @@
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
tools:layout="@layout/tabs_fragment"/>
|
tools:layout="@layout/tabs_fragment"/>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
</layout>
|
|
@ -1,7 +1,17 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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_width="wrap_content"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="@{visibility ? View.VISIBLE : View.GONE}">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="60dp"
|
android:layout_width="60dp"
|
||||||
|
@ -12,4 +22,6 @@
|
||||||
android:padding="10dp"
|
android:padding="10dp"
|
||||||
android:src="@drawable/call_back" />
|
android:src="@drawable/call_back" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
</layout>
|
|
@ -10,6 +10,9 @@
|
||||||
<variable
|
<variable
|
||||||
name="viewModel"
|
name="viewModel"
|
||||||
type="org.linphone.activities.main.viewmodels.SharedMainViewModel" />
|
type="org.linphone.activities.main.viewmodels.SharedMainViewModel" />
|
||||||
|
<variable
|
||||||
|
name="callOverlayViewModel"
|
||||||
|
type="org.linphone.activities.main.viewmodels.CallOverlayViewModel" />
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
@ -35,7 +38,10 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_below="@id/status_fragment">
|
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 -->
|
<!-- Side Menu -->
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
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_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_alignParentBottom="true">
|
android:layout_alignParentBottom="true">
|
||||||
|
@ -14,6 +21,10 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_above="@id/tabs_fragment">
|
android:layout_above="@id/tabs_fragment">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
<androidx.fragment.app.FragmentContainerView
|
||||||
android:id="@+id/nav_host_fragment"
|
android:id="@+id/nav_host_fragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -23,6 +34,13 @@
|
||||||
app:navGraph="@navigation/main_nav_graph"
|
app:navGraph="@navigation/main_nav_graph"
|
||||||
tools:layout="@layout/dialer_fragment" />
|
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.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
<androidx.fragment.app.FragmentContainerView
|
||||||
|
@ -33,4 +51,6 @@
|
||||||
android:layout_alignParentBottom="true"
|
android:layout_alignParentBottom="true"
|
||||||
tools:layout="@layout/tabs_fragment"/>
|
tools:layout="@layout/tabs_fragment"/>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
</layout>
|
Loading…
Reference in a new issue