add silent call feature

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
Marcel Hibbe 2022-05-24 12:36:12 +02:00 committed by Marcel Hibbe (Rebase PR Action)
parent 6669c9fecc
commit a23d4ef692
7 changed files with 120 additions and 35 deletions

View File

@ -231,6 +231,7 @@ public class CallActivity extends CallBaseActivity {
private boolean microphoneOn = false;
private boolean isVoiceOnlyCall;
private boolean isCallWithoutNotification;
private boolean isIncomingCallFromNotification;
private Handler callControlHandler = new Handler();
private Handler callInfosHandler = new Handler();
@ -287,6 +288,7 @@ public class CallActivity extends CallBaseActivity {
conversationPassword = extras.getString(BundleKeys.INSTANCE.getKEY_CONVERSATION_PASSWORD(), "");
conversationName = extras.getString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), "");
isVoiceOnlyCall = extras.getBoolean(BundleKeys.INSTANCE.getKEY_CALL_VOICE_ONLY(), false);
isCallWithoutNotification = extras.getBoolean(BundleKeys.INSTANCE.getKEY_CALL_WITHOUT_NOTIFICATION(), false);
if (extras.containsKey(BundleKeys.INSTANCE.getKEY_FROM_NOTIFICATION_START_CALL())) {
isIncomingCallFromNotification = extras.getBoolean(BundleKeys.INSTANCE.getKEY_FROM_NOTIFICATION_START_CALL());
@ -1356,7 +1358,11 @@ public class CallActivity extends CallBaseActivity {
int apiVersion = ApiUtils.getCallApiVersion(conversationUser, new int[]{ApiUtils.APIv4, 1});
ncApi.joinCall(credentials, ApiUtils.getUrlForCall(apiVersion, baseUrl, roomToken), inCallFlag)
ncApi.joinCall(
credentials,
ApiUtils.getUrlForCall(apiVersion, baseUrl, roomToken),
inCallFlag,
isCallWithoutNotification)
.subscribeOn(Schedulers.io())
.retry(3)
.observeOn(AndroidSchedulers.mainThread())
@ -1825,11 +1831,11 @@ public class CallActivity extends CallBaseActivity {
int apiVersion = ApiUtils.getCallApiVersion(conversationUser, new int[]{ApiUtils.APIv4, 1});
ncApi.getPeersForCall(
credentials,
ApiUtils.getUrlForCall(
apiVersion,
baseUrl,
roomToken))
credentials,
ApiUtils.getUrlForCall(
apiVersion,
baseUrl,
roomToken))
.subscribeOn(Schedulers.io())
.subscribe(new Observer<ParticipantsOverall>() {
@Override
@ -2468,7 +2474,7 @@ public class CallActivity extends CallBaseActivity {
mediaPlayer.setDataSource(this, ringtoneUri);
mediaPlayer.setLooping(true);
AudioAttributes audioAttributes = new AudioAttributes.Builder().setContentType(
AudioAttributes.CONTENT_TYPE_SONIFICATION)
AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
.build();
mediaPlayer.setAudioAttributes(audioAttributes);

View File

@ -211,7 +211,8 @@ public interface NcApi {
@FormUrlEncoded
@POST
Observable<GenericOverall> joinCall(@Nullable @Header("Authorization") String authorization, @Url String url,
@Field("flags") Integer inCall);
@Field("flags") Integer inCall,
@Field("silent") Boolean callWithoutNotification);
/*
Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /call/callToken
@ -239,8 +240,8 @@ public interface NcApi {
*/
@GET
Observable<SignalingOverall> pullSignalingMessages(@Nullable @Header("Authorization") String authorization, @Url
String
url);
String
url);
/*
QueryMap items are as follows:
@ -259,7 +260,7 @@ public interface NcApi {
@FormUrlEncoded
@PUT
Observable<GenericOverall> setUserData(@Header("Authorization") String authorization, @Url String url,
@Field("key") String key, @Field("value") String value);
@Field("key") String key, @Field("value") String value);
/*
@ -281,14 +282,14 @@ public interface NcApi {
@POST
Observable<PushRegistrationOverall> registerDeviceForNotificationsWithNextcloud(@Header("Authorization")
String authorization,
String authorization,
@Url String url,
@QueryMap Map<String,
String> options);
String> options);
@DELETE
Observable<GenericOverall> unregisterDeviceForNotificationsWithNextcloud(@Header("Authorization")
String authorization,
String authorization,
@Url String url);
@FormUrlEncoded
@ -438,10 +439,10 @@ public interface NcApi {
@FormUrlEncoded
@POST
Observable<GenericOverall> sendLocation(@Header("Authorization") String authorization,
@Url String url,
@Field("objectType") String objectType,
@Field("objectId") String objectId,
@Field("metaData") String metaData);
@Url String url,
@Field("objectType") String objectType,
@Field("objectId") String objectId,
@Field("metaData") String metaData);
@DELETE
Observable<GenericOverall> clearChatHistory(@Header("Authorization") String authorization, @Url String url);
@ -484,23 +485,23 @@ public interface NcApi {
@FormUrlEncoded
@PUT
Observable<GenericOverall> setPredefinedStatusMessage(@Header("Authorization") String authorization,
@Url String url,
@Field("messageId") String selectedPredefinedMessageId,
@Field("clearAt") Long clearAt);
@Url String url,
@Field("messageId") String selectedPredefinedMessageId,
@Field("clearAt") Long clearAt);
@FormUrlEncoded
@PUT
Observable<GenericOverall> setCustomStatusMessage(@Header("Authorization") String authorization,
@Url String url,
@Field("statusIcon") String statusIcon,
@Field("message") String message,
@Field("clearAt") Long clearAt);
@Url String url,
@Field("statusIcon") String statusIcon,
@Field("message") String message,
@Field("clearAt") Long clearAt);
@FormUrlEncoded
@PUT
Observable<GenericOverall> setStatusType(@Header("Authorization") String authorization,
@Url String url,
@Field("statusType") String statusType);
@Url String url,
@Field("statusType") String statusType);
@GET
Observable<StatusesOverall> getUserStatuses(@Header("Authorization") String authorization, @Url String url);
@ -508,7 +509,7 @@ public interface NcApi {
@POST
Observable<GenericOverall> sendReaction(@Header("Authorization") String authorization, @Url String url,
@Query("reaction") String reaction);
@Query("reaction") String reaction);
@DELETE
Observable<GenericOverall> deleteReaction(@Header("Authorization") String authorization, @Url String url,

View File

@ -103,7 +103,6 @@ import com.nextcloud.talk.BuildConfig
import com.nextcloud.talk.R
import com.nextcloud.talk.activities.CallActivity
import com.nextcloud.talk.activities.MainActivity
import com.nextcloud.talk.shareditems.activities.SharedItemsActivity
import com.nextcloud.talk.activities.TakePhotoActivity
import com.nextcloud.talk.adapters.messages.IncomingLocationMessageViewHolder
import com.nextcloud.talk.adapters.messages.IncomingPreviewMessageViewHolder
@ -143,6 +142,7 @@ import com.nextcloud.talk.models.json.conversations.RoomsOverall
import com.nextcloud.talk.models.json.generic.GenericOverall
import com.nextcloud.talk.models.json.mention.Mention
import com.nextcloud.talk.presenters.MentionAutocompletePresenter
import com.nextcloud.talk.shareditems.activities.SharedItemsActivity
import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet
import com.nextcloud.talk.ui.dialog.AttachmentDialog
import com.nextcloud.talk.ui.dialog.MessageActionsDialog
@ -886,6 +886,35 @@ class ChatController(args: Bundle) :
popupMenu.show()
}
private fun showCallButtonMenu(isVoiceOnlyCall: Boolean) {
val anchor: View? = if (isVoiceOnlyCall) {
activity?.findViewById(R.id.conversation_voice_call)
} else {
activity?.findViewById(R.id.conversation_video_call)
}
if (anchor != null) {
val popupMenu = PopupMenu(
ContextThemeWrapper(view?.context, R.style.CallButtonMenu),
anchor,
Gravity.END
)
popupMenu.inflate(R.menu.chat_call_menu)
popupMenu.setOnMenuItemClickListener { item: MenuItem ->
when (item.itemId) {
R.id.call_without_notification -> startACall(isVoiceOnlyCall, true)
}
true
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
popupMenu.setForceShowIcon(true)
}
popupMenu.show()
}
}
private fun startPlayback(message: ChatMessage) {
if (!this.isAttached) {
@ -1827,7 +1856,7 @@ class ChatController(args: Bundle) :
}
if (startCallFromNotification != null && startCallFromNotification ?: false) {
startCallFromNotification = false
startACall(voiceOnly)
startACall(voiceOnly, false)
}
}
@ -2403,6 +2432,22 @@ class ChatController(args: Bundle) :
if (CapabilitiesUtil.isAbleToCall(conversationUser)) {
conversationVoiceCallMenuItem = menu.findItem(R.id.conversation_voice_call)
conversationVideoMenuItem = menu.findItem(R.id.conversation_video_call)
if (CapabilitiesUtil.hasSpreedFeatureCapability(conversationUser, "silent-call")) {
Handler().post {
activity?.findViewById<View?>(R.id.conversation_voice_call)?.setOnLongClickListener {
showCallButtonMenu(true)
true
}
}
Handler().post {
activity?.findViewById<View?>(R.id.conversation_video_call)?.setOnLongClickListener {
showCallButtonMenu(false)
true
}
}
}
} else {
menu.removeItem(R.id.conversation_video_call)
menu.removeItem(R.id.conversation_voice_call)
@ -2425,11 +2470,11 @@ class ChatController(args: Bundle) :
return true
}
R.id.conversation_video_call -> {
startACall(false)
startACall(false, false)
return true
}
R.id.conversation_voice_call -> {
startACall(true)
startACall(true, false)
return true
}
R.id.conversation_info -> {
@ -2493,19 +2538,19 @@ class ChatController(args: Bundle) :
currentMessage.value.systemMessageType == ChatMessage.SystemMessageType.REACTION_REVOKED
}
private fun startACall(isVoiceOnlyCall: Boolean) {
private fun startACall(isVoiceOnlyCall: Boolean, callWithoutNotification: Boolean) {
if (currentConversation?.canStartCall == false && currentConversation?.hasCall == false) {
Toast.makeText(context, R.string.startCallForbidden, Toast.LENGTH_LONG).show()
} else {
ApplicationWideCurrentRoomHolder.getInstance().isDialing = true
val callIntent = getIntentForCall(isVoiceOnlyCall)
val callIntent = getIntentForCall(isVoiceOnlyCall, callWithoutNotification)
if (callIntent != null) {
startActivity(callIntent)
}
}
}
private fun getIntentForCall(isVoiceOnlyCall: Boolean): Intent? {
private fun getIntentForCall(isVoiceOnlyCall: Boolean, callWithoutNotification: Boolean): Intent? {
currentConversation?.let {
val bundle = Bundle()
bundle.putString(KEY_ROOM_TOKEN, roomToken)
@ -2518,6 +2563,9 @@ class ChatController(args: Bundle) :
if (isVoiceOnlyCall) {
bundle.putBoolean(BundleKeys.KEY_CALL_VOICE_ONLY, true)
}
if (callWithoutNotification) {
bundle.putBoolean(BundleKeys.KEY_CALL_WITHOUT_NOTIFICATION, true)
}
return if (activity != null) {
val callIntent = Intent(activity, CallActivity::class.java)

View File

@ -55,6 +55,7 @@ object BundleKeys {
val KEY_INVITED_EMAIL = "KEY_INVITED_EMAIL"
val KEY_CONVERSATION_NAME = "KEY_CONVERSATION_NAME"
val KEY_CALL_VOICE_ONLY = "KEY_CALL_VOICE_ONLY"
val KEY_CALL_WITHOUT_NOTIFICATION = "KEY_CALL_WITHOUT_NOTIFICATION"
val KEY_ACTIVE_CONVERSATION = "KEY_ACTIVE_CONVERSATION"
val KEY_SERVER_CAPABILITIES = "KEY_SERVER_CAPABILITIES"
val KEY_FROM_NOTIFICATION_START_CALL = "KEY_FROM_NOTIFICATION_START_CALL"

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Nextcloud Talk application
~
~ @author Marcel Hibbe
~ Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
~
~ 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/>.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/call_without_notification"
android:icon="@drawable/ic_baseline_notifications_off_24"
android:title="@string/call_without_notification" />
</menu>

View File

@ -522,5 +522,6 @@
<string name="reactions_tab_all">All</string>
<string name="send_without_notification">Send without notification</string>
<string name="call_without_notification">Call without notification</string>
</resources>

View File

@ -63,6 +63,8 @@
<item name="iconTint">@color/fontAppbar</item>
</style>
<style name="CallButtonMenu" parent="@style/ChatSendButtonMenu"></style>
<style name="BottomNavigationView" parent="@style/Widget.MaterialComponents.BottomNavigationView">
<item name="elevation">1dp</item>
</style>