diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java index cdb31d2e8..1b78b4714 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java @@ -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() { @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); diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApi.java b/app/src/main/java/com/nextcloud/talk/api/NcApi.java index 20a1969a5..e160ebfda 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -211,7 +211,8 @@ public interface NcApi { @FormUrlEncoded @POST Observable 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 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 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 registerDeviceForNotificationsWithNextcloud(@Header("Authorization") - String authorization, + String authorization, @Url String url, @QueryMap Map options); + String> options); @DELETE Observable unregisterDeviceForNotificationsWithNextcloud(@Header("Authorization") - String authorization, + String authorization, @Url String url); @FormUrlEncoded @@ -438,10 +439,10 @@ public interface NcApi { @FormUrlEncoded @POST Observable 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 clearChatHistory(@Header("Authorization") String authorization, @Url String url); @@ -484,23 +485,23 @@ public interface NcApi { @FormUrlEncoded @PUT Observable 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 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 setStatusType(@Header("Authorization") String authorization, - @Url String url, - @Field("statusType") String statusType); + @Url String url, + @Field("statusType") String statusType); @GET Observable getUserStatuses(@Header("Authorization") String authorization, @Url String url); @@ -508,7 +509,7 @@ public interface NcApi { @POST Observable sendReaction(@Header("Authorization") String authorization, @Url String url, - @Query("reaction") String reaction); + @Query("reaction") String reaction); @DELETE Observable deleteReaction(@Header("Authorization") String authorization, @Url String url, diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt index cfba72955..b1a9e50fd 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -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(R.id.conversation_voice_call)?.setOnLongClickListener { + showCallButtonMenu(true) + true + } + } + + Handler().post { + activity?.findViewById(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) diff --git a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt index 58d60e445..eac2cf190 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt @@ -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" diff --git a/app/src/main/res/menu/chat_call_menu.xml b/app/src/main/res/menu/chat_call_menu.xml new file mode 100644 index 000000000..03bf83900 --- /dev/null +++ b/app/src/main/res/menu/chat_call_menu.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 298d2625f..83b3cceb4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -522,5 +522,6 @@ All Send without notification + Call without notification diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 6d010315e..d16b13034 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -63,6 +63,8 @@ @color/fontAppbar + +