diff --git a/ring-android/app/src/main/AndroidManifest.xml b/ring-android/app/src/main/AndroidManifest.xml index ff781502e42bed815864633551550352b7ef831a..dcf7fff296041c6e8b19c93a2ce029b6c70f3cd3 100644 --- a/ring-android/app/src/main/AndroidManifest.xml +++ b/ring-android/app/src/main/AndroidManifest.xml @@ -111,7 +111,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. android:configChanges="screenSize|screenLayout|smallestScreenSize" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" - android:launchMode="singleTask" android:theme="@style/AppTheme.Navigation" android:windowSoftInputMode="adjustResize"> <intent-filter> @@ -217,6 +216,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. <activity android:name=".client.CallActivity" + android:launchMode="singleTask" android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize" android:label="@string/app_name" android:resizeableActivity="true" diff --git a/ring-android/app/src/main/java/cx/ring/client/CallActivity.kt b/ring-android/app/src/main/java/cx/ring/client/CallActivity.kt index 0fa6f6c34fb3aaa2d70dd819579fe70297e877eb..5f043279c66ab5d420c8b41211c47de2692c0895 100644 --- a/ring-android/app/src/main/java/cx/ring/client/CallActivity.kt +++ b/ring-android/app/src/main/java/cx/ring/client/CallActivity.kt @@ -96,23 +96,15 @@ class CallActivity : AppCompatActivity() { private fun handleNewIntent(intent: Intent) { val action = intent.action - if (Intent.ACTION_CALL == action || ACTION_CALL == action) { - val audioOnly = intent.getBooleanExtra(CallFragment.KEY_AUDIO_ONLY, true) + val hasVideo = intent.getBooleanExtra(CallFragment.KEY_HAS_VIDEO, false) + if (Intent.ACTION_CALL == action) { val contactId = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER) - val callFragment = CallFragment.newInstance( - CallFragment.ACTION_PLACE_CALL, - fromIntent(intent), - contactId, - audioOnly - ) + val callFragment = CallFragment.newInstance(action, fromIntent(intent), contactId, hasVideo) supportFragmentManager.beginTransaction() .replace(R.id.main_call_layout, callFragment, CALL_FRAGMENT_TAG).commit() } else if (Intent.ACTION_VIEW == action || ACTION_CALL_ACCEPT == action) { val confId = intent.getStringExtra(NotificationService.KEY_CALL_ID) - val callFragment = CallFragment.newInstance( - if (Intent.ACTION_VIEW == action) CallFragment.ACTION_GET_CALL else ACTION_CALL_ACCEPT, - confId - ) + val callFragment = CallFragment.newInstance(action, confId, hasVideo) supportFragmentManager.beginTransaction() .replace(R.id.main_call_layout, callFragment, CALL_FRAGMENT_TAG).commit() } @@ -201,7 +193,6 @@ class CallActivity : AppCompatActivity() { } companion object { - const val ACTION_CALL = BuildConfig.APPLICATION_ID + ".action.call" const val ACTION_CALL_ACCEPT = BuildConfig.APPLICATION_ID + ".action.CALL_ACCEPT" private const val CALL_FRAGMENT_TAG = "CALL_FRAGMENT_TAG" diff --git a/ring-android/app/src/main/java/cx/ring/client/ContactDetailsActivity.kt b/ring-android/app/src/main/java/cx/ring/client/ContactDetailsActivity.kt index 3b79991a9579011f8af0ddc961df1a274f8fcaae..7b05e2f2c052dc1cda4ad32a72d99b2dc68fc13e 100644 --- a/ring-android/app/src/main/java/cx/ring/client/ContactDetailsActivity.kt +++ b/ring-android/app/src/main/java/cx/ring/client/ContactDetailsActivity.kt @@ -400,7 +400,7 @@ class ContactDetailsActivity : AppCompatActivity() { binding = null } - private fun goToCallActivity(conversation: Conversation, contactUri: Uri, audioOnly: Boolean) { + private fun goToCallActivity(conversation: Conversation, contactUri: Uri, hasVideo: Boolean) { val conf = conversation.currentCall if (conf != null && conf.participants.isNotEmpty() && conf.participants[0].callStatus != Call.CallStatus.INACTIVE @@ -413,7 +413,7 @@ class ContactDetailsActivity : AppCompatActivity() { .setClass(applicationContext, CallActivity::class.java) .putExtras(ConversationPath.toBundle(conversation)) .putExtra(Intent.EXTRA_PHONE_NUMBER, contactUri.uri) - .putExtra(CallFragment.KEY_AUDIO_ONLY, audioOnly) + .putExtra(CallFragment.KEY_HAS_VIDEO, hasVideo) startActivityForResult(intent, HomeActivity.REQUEST_CODE_CALL) } } diff --git a/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.kt b/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.kt index 9b3b0587eaf4bff0eb324263edacee241062bb80..1c6bbfb7d162c9e2caeec645073162ec68ed7ec9 100644 --- a/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.kt +++ b/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.kt @@ -137,18 +137,24 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, private enum class PreviewPosition { LEFT, RIGHT } private var previewPosition = PreviewPosition.RIGHT - @Inject lateinit var mDeviceRuntimeService: DeviceRuntimeService private val mCompositeDisposable = CompositeDisposable() + override fun initPresenter(presenter: CallPresenter) { + Log.w(TAG, "DEBUG fn initPresenter [CallFragment.kt] -> chose between prepareCall and initIncomingCall") val args = requireArguments() + presenter.wantVideo = args.getBoolean(KEY_HAS_VIDEO, false) args.getString(KEY_ACTION)?.let { action -> - if (action == ACTION_PLACE_CALL) + if (action == Intent.ACTION_CALL) { + Log.w(TAG, "DEBUG fn initPresenter [CallFragment.kt] -> requesting fn prepareCall(false) ") prepareCall(false) - else if (action == ACTION_GET_CALL || action == CallActivity.ACTION_CALL_ACCEPT) - presenter.initIncomingCall(args.getString(KEY_CONF_ID)!!, action == ACTION_GET_CALL) + } + else if (action == Intent.ACTION_VIEW || action == CallActivity.ACTION_CALL_ACCEPT) { + Log.w(TAG, "DEBUG fn initPresenter [CallFragment.kt] -> requesting fn initIncomingCall( CONF_ID, GET_CALL)") + presenter.initIncomingCall(args.getString(KEY_CONF_ID)!!, action == Intent.ACTION_VIEW) + } } } @@ -197,8 +203,9 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, override fun onStart() { super.onStart() + Log.w(TAG, "DEBUG fn onStart [CallFragment.kt] -> on start, value hasVideo = ${presenter.wantVideo}, restartvideo : $restartVideo | restartPreview : $restartPreview") if (restartVideo && restartPreview) { - displayVideoSurface(true, !presenter.isPipMode) + displayVideoSurface(true, !presenter.isPipMode && presenter.wantVideo) restartVideo = false restartPreview = false } else if (restartVideo) { @@ -649,6 +656,10 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, if (requestCode != REQUEST_PERMISSION_INCOMING && requestCode != REQUEST_PERMISSION_OUTGOING) return var i = 0 val n = permissions.size + + val hasVideo = presenter.wantVideo + Log.w(TAG, "DEBUG fn onRequestPermissionsResult [CallFragment.kt] -> value hasVideo = $hasVideo") + while (i < n) { val audioGranted = mDeviceRuntimeService.hasAudioPermission() val granted = grantResults[i] == PackageManager.PERMISSION_GRANTED @@ -656,12 +667,12 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, Manifest.permission.CAMERA -> { presenter.cameraPermissionChanged(granted) if (audioGranted) { - initializeCall(requestCode == REQUEST_PERMISSION_INCOMING) + initializeCall(requestCode == REQUEST_PERMISSION_INCOMING, hasVideo) } } Manifest.permission.RECORD_AUDIO -> { presenter.audioPermissionChanged(granted) - initializeCall(requestCode == REQUEST_PERMISSION_INCOMING) + initializeCall(requestCode == REQUEST_PERMISSION_INCOMING, hasVideo) } } i++ @@ -739,15 +750,18 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, displayVideoSurface: Boolean, displayPreviewContainer: Boolean ) { - binding!!.videoSurface.visibility = - if (displayVideoSurface) View.VISIBLE else View.GONE + Log.w(TAG, "DEBUG fn displayVideoSurface [CallFragment.kt] -> changement des affichages videos displayVideoSurface: $displayVideoSurface; displayPreviewContainer: $displayPreviewContainer") + binding!!.videoSurface.visibility = if (displayVideoSurface) View.VISIBLE else View.GONE + if (isChoosePluginMode) { + Log.w(TAG, "DEBUG fn displayVideoSurface |1| inside => if (isChoosePluginMode) {displayPreviewContainer: $displayPreviewContainer}") binding!!.pluginPreviewSurface.visibility = if (displayPreviewContainer) View.VISIBLE else View.GONE binding!!.pluginPreviewContainer.visibility = if (displayPreviewContainer) View.VISIBLE else View.GONE binding!!.previewContainer.visibility = View.GONE } else { + Log.w(TAG, "DEBUG fn displayVideoSurface |2| inside => else { displayPreviewContainer : $displayPreviewContainer}") binding!!.pluginPreviewSurface.visibility = View.GONE binding!!.pluginPreviewContainer.visibility = View.GONE binding!!.previewContainer.visibility = @@ -756,6 +770,7 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, updateMenu() } + // todo Change function name, this name is misleading, this function concerns PIP preview override fun displayPreviewSurface(display: Boolean) { if (display) { binding!!.videoSurface.setZOrderOnTop(false) @@ -783,8 +798,13 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY) } - override fun switchCameraIcon(isFront: Boolean) { - binding!!.callCameraFlipBtn.setImageResource(if (isFront) R.drawable.baseline_camera_front_24 else R.drawable.baseline_camera_rear_24) + override fun switchCameraIcon() { + //binding!!.callCameraFlipBtn.setImageResource(if (isFront) R.drawable.baseline_camera_front_24 else R.drawable.baseline_camera_rear_24) + } + + fun switchCamera() { + presenter.switchOnOffCamera() + binding?.callCameraSwitchBtn?.setImageResource(if (binding?.callCameraSwitchBtn?.isChecked == true) R.drawable.baseline_videocam_off_24 else R.drawable.baseline_videocam_24) } override fun updateAudioState(state: AudioState) { @@ -828,6 +848,9 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, } ab.setDisplayShowTitleEnabled(true) } + val call = contacts[0] + val conversationUri = if (call.conversationId != null) Uri(Uri.SWARM_SCHEME, call.conversationId!!) else call.contact!!.conversationUri.blockingFirst() + activity.intent = Intent(Intent.ACTION_VIEW, ConversationPath.toUri(call.account!!, conversationUri)) } if (hasProfileName) { binding!!.contactBubbleNumTxt.visibility = View.VISIBLE @@ -918,8 +941,8 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, }) } confAdapter!!.updateFromCalls(participantInfo) - if (binding!!.confControlGroup.adapter == null) binding!!.confControlGroup.adapter = - confAdapter + if (binding!!.confControlGroup.adapter == null) + binding!!.confControlGroup.adapter = confAdapter } } @@ -944,17 +967,21 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, } } - override fun updateCallStatus(callStatus: CallStatus) { - binding!!.callStatusTxt.setText(callStateToHumanState(callStatus)) + override fun updateCallStatus(callState: CallStatus) { + binding!!.callStatusTxt.setText(callStateToHumanState(callState)) } override fun initMenu( - isSpeakerOn: Boolean, displayFlip: Boolean, canDial: Boolean, - showPluginBtn: Boolean, onGoingCall: Boolean + isSpeakerOn: Boolean, hasMultipleCamera: Boolean, canDial: Boolean, + showPluginBtn: Boolean, onGoingCall: Boolean, hasActiveVideo: Boolean ) { - if (binding != null) { - binding!!.callCameraFlipBtn.visibility = if (displayFlip) View.VISIBLE else View.GONE + // + if (!hasActiveVideo) { + binding!!.callCameraSwitchBtn.isChecked = true + binding!!.callCameraSwitchBtn.setImageResource(R.drawable.baseline_videocam_off_24) } + binding!!.callCameraFlipBtn.visibility = if (hasMultipleCamera && !binding!!.callCameraSwitchBtn.isChecked) View.VISIBLE else View.GONE + if (dialPadBtn != null) { dialPadBtn!!.isVisible = canDial } @@ -969,6 +996,7 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, binding?.apply { shapeRipple.stopRipple() callAcceptBtn.visibility = View.GONE + callAcceptAudioBtn.visibility = View.GONE callRefuseBtn.visibility = View.GONE callControlGroup.visibility = View.VISIBLE callHangupBtn.visibility = View.VISIBLE @@ -980,10 +1008,11 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, callActivity?.showSystemUI() } - override fun initIncomingCallDisplay() { + override fun initIncomingCallDisplay(hasVideo: Boolean) { Log.w(TAG, "initIncomingCallDisplay") binding?.apply { - callAcceptBtn.visibility = View.VISIBLE + if (hasVideo) callAcceptBtn.visibility = View.VISIBLE else callAcceptBtn.visibility = View.GONE + callAcceptAudioBtn.visibility = View.VISIBLE callRefuseBtn.visibility = View.VISIBLE callControlGroup.visibility = View.GONE callHangupBtn.visibility = View.GONE @@ -1117,17 +1146,15 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, */ override fun prepareCall(isIncoming: Boolean) { val audioGranted = mDeviceRuntimeService.hasAudioPermission() - val audioOnly: Boolean - val permissionType: Int - if (isIncoming) { - audioOnly = presenter.isAudioOnly - permissionType = REQUEST_PERMISSION_INCOMING - } else { - val args = arguments - audioOnly = args != null && args.getBoolean(KEY_AUDIO_ONLY) - permissionType = REQUEST_PERMISSION_OUTGOING - } - if (!audioOnly) { + val hasVideo = presenter.wantVideo + + Log.w(TAG, "DEBUG fn prepareCall -> define the permission based on hasVideo : $hasVideo and then call initializeCall($isIncoming, $hasVideo) ") + //Log.w(TAG, "fn prepareCall [CallFragment.kt] -> value of presenter.hasVideo() : $hasVideo") + + val permissionType = + if (isIncoming) REQUEST_PERMISSION_INCOMING else REQUEST_PERMISSION_OUTGOING + + if (hasVideo) { val videoGranted = mDeviceRuntimeService.hasVideoPermission() if ((!audioGranted || !videoGranted) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val perms = ArrayList<String>() @@ -1139,13 +1166,15 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, } requestPermissions(perms.toTypedArray(), permissionType) } else if (audioGranted && videoGranted) { - initializeCall(isIncoming) + Log.w(TAG, "DEBUG fn prepareCall [CallFragment.kt] -> calling initializeCall($isIncoming, $hasVideo) ") + initializeCall(isIncoming, hasVideo) } } else { if (!audioGranted && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { requestPermissions(arrayOf(Manifest.permission.RECORD_AUDIO), permissionType) } else if (audioGranted) { - initializeCall(isIncoming) + Log.w(TAG, "DEBUG fn prepareCall [CallFragment.kt] -> calling initializeCall($isIncoming, $hasVideo) ") + initializeCall(isIncoming, hasVideo) } } } @@ -1154,10 +1183,13 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, * Starts a call. Takes into account whether call is incoming or outgoing. * * @param isIncoming true if call is incoming, false for outgoing + * @param hasVideo true if we already know that conversation has video */ - private fun initializeCall(isIncoming: Boolean) { + + private fun initializeCall(isIncoming: Boolean, hasVideo: Boolean) { + Log.w(TAG, "DEBUG fn initializeCall [CallFragment.kt] -> if isIncoming ( = $isIncoming ) == true : presenter.AcceptCall(hasVideo: $hasVideo) : presenter.initOutGoing(conversation.accountId,conversation.conversationUri,args.getString(Intent.EXTRA_PHONE_NUMBER), hasVideo: $hasVideo)") if (isIncoming) { - presenter.acceptCall() + presenter.acceptCall(hasVideo) } else { arguments?.let { args -> val conversation = ConversationPath.fromBundle(args)!! @@ -1165,7 +1197,7 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, conversation.accountId, conversation.conversationUri, args.getString(Intent.EXTRA_PHONE_NUMBER), - args.getBoolean(KEY_AUDIO_ONLY) + hasVideo ) } } @@ -1182,8 +1214,10 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, } } + //todo if videomode, should mute/unmute audio output, if audio only, should switch between speaker options fun speakerClicked() { presenter.speakerClick(binding!!.callSpeakerBtn.isChecked) + //binding!!.callSpeakerBtn.setImageResource(if (binding!!.callSpeakerBtn.isChecked) R.drawable.baseline_sound_on_24 else R.drawable.baseline_sound_off_24) } private fun startScreenShare(mediaProjection: MediaProjection?) { @@ -1227,7 +1261,17 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, presenter.refuseCall() } + fun acceptAudioClicked() { + Log.w(TAG, "DEBUG fn acceptClicked() [CallFragment.kt] -> hasVideo current value is : $ (${presenter.wantVideo})") + presenter.wantVideo = false + Log.w(TAG, "DEBUG fn acceptClicked() [CallFragment.kt] -> hasVideo new value is : $ (${presenter.wantVideo})") + prepareCall(true) + } + fun acceptClicked() { + Log.w(TAG, "DEBUG fn acceptClicked() [CallFragment.kt] -> hasVideo current value is : $ (${presenter.wantVideo})") + presenter.wantVideo = true + Log.w(TAG, "DEBUG fn acceptClicked() [CallFragment.kt] -> hasVideo new value is : $ (${presenter.wantVideo})") prepareCall(true) } @@ -1370,7 +1414,7 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, } //change preview image - displayVideoSurface(true, true) + displayVideoSurface(true, presenter.wantVideo) } /** @@ -1425,32 +1469,32 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, companion object { val TAG = CallFragment::class.simpleName!! const val ACTION_PLACE_CALL = "PLACE_CALL" - const val ACTION_GET_CALL = "GET_CALL" const val KEY_ACTION = "action" const val KEY_CONF_ID = "confId" - const val KEY_AUDIO_ONLY = "AUDIO_ONLY" + const val KEY_HAS_VIDEO = "HAS_VIDEO" private const val REQUEST_CODE_ADD_PARTICIPANT = 6 private const val REQUEST_PERMISSION_INCOMING = 1003 private const val REQUEST_PERMISSION_OUTGOING = 1004 private const val REQUEST_CODE_SCREEN_SHARE = 7 - fun newInstance(action: String, path: ConversationPath?, contactId: String?, audioOnly: Boolean): CallFragment { + fun newInstance(action: String, path: ConversationPath?, contactId: String?, hasVideo: Boolean): CallFragment { val bundle = Bundle() bundle.putString(KEY_ACTION, action) path?.toBundle(bundle) bundle.putString(Intent.EXTRA_PHONE_NUMBER, contactId) - bundle.putBoolean(KEY_AUDIO_ONLY, audioOnly) + bundle.putBoolean(KEY_HAS_VIDEO, hasVideo) val countDownFragment = CallFragment() countDownFragment.arguments = bundle return countDownFragment } - fun newInstance(action: String, confId: String?): CallFragment { - val bundle = Bundle() - bundle.putString(KEY_ACTION, action) - bundle.putString(KEY_CONF_ID, confId) + fun newInstance(action: String, confId: String?, hasVideo: Boolean): CallFragment { val countDownFragment = CallFragment() - countDownFragment.arguments = bundle + countDownFragment.arguments = Bundle().apply { + putString(KEY_ACTION, action) + putString(KEY_CONF_ID, confId) + putBoolean(KEY_HAS_VIDEO, hasVideo) + } return countDownFragment } diff --git a/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.kt b/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.kt index 349c2ff1d5ff5916f8aed6639c72d6e9df5fd7ba..4a80a29e09378d8f13c8ab352370cab3e82e2208 100644 --- a/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.kt +++ b/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.kt @@ -787,10 +787,10 @@ class ConversationFragment : BaseSupportFragment<ConversationPresenter, Conversa startActivity(Intent(activity, HomeActivity::class.java)) return true } else if (itemId == R.id.conv_action_audiocall) { - presenter.goToCall(true) + presenter.goToCall(false) return true } else if (itemId == R.id.conv_action_videocall) { - presenter.goToCall(false) + presenter.goToCall(true) return true } else if (itemId == R.id.conv_contact_details) { presenter.openContact() @@ -915,11 +915,6 @@ class ConversationFragment : BaseSupportFragment<ConversationPresenter, Conversa startActivityForResult(ActionHelper.getAddNumberIntentForContact(contact), REQ_ADD_CONTACT) } - override fun goToCallActivity(conferenceId: String) { - startActivity(Intent(Intent.ACTION_VIEW) - .setClass(requireContext().applicationContext, CallActivity::class.java) - .putExtra(NotificationService.KEY_CALL_ID, conferenceId)) - } override fun goToContactActivity(accountId: String, uri: net.jami.model.Uri) { val toolbar: Toolbar = requireActivity().findViewById(R.id.main_toolbar) @@ -930,17 +925,21 @@ class ConversationFragment : BaseSupportFragment<ConversationPresenter, Conversa .toBundle()) } - override fun goToCallActivityWithResult( - accountId: String, - conversationUri: net.jami.model.Uri, - contactUri: net.jami.model.Uri, - audioOnly: Boolean - ) { + override fun goToCallActivity(conferenceId: String, withCamera: Boolean) { + Log.w(TAG, "DEBUG fn goToCallActivity") + startActivity(Intent(Intent.ACTION_VIEW) + .setClass(requireContext().applicationContext, CallActivity::class.java) + .putExtra(NotificationService.KEY_CALL_ID, conferenceId) + ) + } + + override fun goToCallActivityWithResult(accountId: String, conversationUri: net.jami.model.Uri, contactUri: net.jami.model.Uri, withCamera: Boolean) { + Log.w(TAG, "DEBUG fn goToCallActivityWithResult || hasVideo : $withCamera") val intent = Intent(Intent.ACTION_CALL) .setClass(requireContext(), CallActivity::class.java) .putExtras(ConversationPath.toBundle(accountId, conversationUri)) .putExtra(Intent.EXTRA_PHONE_NUMBER, contactUri.uri) - .putExtra(CallFragment.KEY_AUDIO_ONLY, audioOnly) + .putExtra(CallFragment.KEY_HAS_VIDEO, withCamera) startActivityForResult(intent, HomeActivity.REQUEST_CODE_CALL) } diff --git a/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.kt b/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.kt index 5909959ad9d1b7205352eeb7bc13586f0bf9aa2b..f517b053ab9005c98c57c51b33d8fe0175a86cea 100644 --- a/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.kt +++ b/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.kt @@ -251,7 +251,7 @@ class SmartListFragment : BaseSupportFragment<SmartListPresenter, SmartListView> .setTitle(R.string.choose_number) .setItems(numbers) { _: DialogInterface?, which: Int -> val selected = numbers[which] - val intent = Intent(CallActivity.ACTION_CALL) + val intent = Intent(Intent.ACTION_CALL) .setClass(context, CallActivity::class.java) .setData(android.net.Uri.parse(selected.toString())) startActivityForResult(intent, HomeActivity.REQUEST_CODE_CALL) @@ -355,10 +355,10 @@ class SmartListFragment : BaseSupportFragment<SmartListPresenter, SmartListView> } override fun goToCallActivity(accountId: String, conversationUri: Uri, contactId: String) { - val intent = Intent(CallActivity.ACTION_CALL) + val intent = Intent(Intent.ACTION_CALL) .setClass(requireContext(), CallActivity::class.java) .putExtras(ConversationPath.toBundle(accountId, conversationUri)) - .putExtra(CallFragment.KEY_AUDIO_ONLY, false) + .putExtra(CallFragment.KEY_HAS_VIDEO, true) .putExtra(Intent.EXTRA_PHONE_NUMBER, contactId) startActivityForResult(intent, HomeActivity.REQUEST_CODE_CALL) } diff --git a/ring-android/app/src/main/java/cx/ring/service/DRingService.kt b/ring-android/app/src/main/java/cx/ring/service/DRingService.kt index d14b38df81bacf0ed007276090bc4aaece53cada..0647f491e3ef68523e737d7850025f4bdba1cb11 100644 --- a/ring-android/app/src/main/java/cx/ring/service/DRingService.kt +++ b/ring-android/app/src/main/java/cx/ring/service/DRingService.kt @@ -243,6 +243,12 @@ class DRingService : Service() { return } when (action) { + ACTION_CALL_ACCEPT_AUDIO -> { + startActivity(Intent(ACTION_CALL_ACCEPT) + .putExtras(extras) + .setClass(applicationContext, CallActivity::class.java) + .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)) + } ACTION_CALL_ACCEPT -> { startActivity(Intent(ACTION_CALL_ACCEPT) .putExtras(extras) @@ -337,6 +343,7 @@ class DRingService : Service() { const val ACTION_TRUST_REQUEST_ACCEPT = BuildConfig.APPLICATION_ID + ".action.TRUST_REQUEST_ACCEPT" const val ACTION_TRUST_REQUEST_REFUSE = BuildConfig.APPLICATION_ID + ".action.TRUST_REQUEST_REFUSE" const val ACTION_TRUST_REQUEST_BLOCK = BuildConfig.APPLICATION_ID + ".action.TRUST_REQUEST_BLOCK" + const val ACTION_CALL_ACCEPT_AUDIO = BuildConfig.APPLICATION_ID + ".action.CALL_ACCEPT_AUDIO" const val ACTION_CALL_ACCEPT = BuildConfig.APPLICATION_ID + ".action.CALL_ACCEPT" const val ACTION_CALL_HOLD_ACCEPT = BuildConfig.APPLICATION_ID + ".action.CALL_HOLD_ACCEPT" const val ACTION_CALL_END_ACCEPT = BuildConfig.APPLICATION_ID + ".action.CALL_END_ACCEPT" diff --git a/ring-android/app/src/main/java/cx/ring/services/HardwareServiceImpl.kt b/ring-android/app/src/main/java/cx/ring/services/HardwareServiceImpl.kt index 3e618299b50975c2b57a4d7f7ec75823948d779f..77b0d779e1344a8b964d1db4e4c29a60e3372e43 100644 --- a/ring-android/app/src/main/java/cx/ring/services/HardwareServiceImpl.kt +++ b/ring-android/app/src/main/java/cx/ring/services/HardwareServiceImpl.kt @@ -539,7 +539,7 @@ class HardwareServiceImpl( val surfaceHolder = WeakReference(holder) videoSurfaces[id] = surfaceHolder if (shm != null && shm.window == 0L) { - shm.window = startVideo(shm.id, surfaceHolder.get()!!.surface, shm.w, shm.h) + shm.window = startVideo(shm.id, holder.surface, shm.w, shm.h) } if (shm == null || shm.window == 0L) { Log.i(TAG, "DJamiService.addVideoSurface() no window !") diff --git a/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.kt b/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.kt index 9d4b9079d40cec01b7157cbe276e39e62f06a9af..044f6035d4c318bbd21afae384fbde2b01fb265c 100644 --- a/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.kt +++ b/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.kt @@ -50,6 +50,7 @@ import cx.ring.client.CallActivity import cx.ring.client.ConversationActivity import cx.ring.client.HomeActivity import cx.ring.contactrequests.ContactRequestsFragment +import cx.ring.fragments.CallFragment import cx.ring.fragments.ConversationFragment import cx.ring.service.CallNotificationService import cx.ring.service.DRingService @@ -79,7 +80,6 @@ class NotificationServiceImpl( private val currentCalls = LinkedHashMap<String, Conference>() private val callNotifications = ConcurrentHashMap<Int, Notification>() private val dataTransferNotifications = ConcurrentHashMap<Int, Notification>() - @SuppressLint("CheckResult") fun initHelper() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -141,21 +141,42 @@ class NotificationServiceImpl( .setSound(null) .setVibrate(null) .setFullScreenIntent(viewIntent, true) - .addAction(R.drawable.baseline_call_end_24, - mContext.getText(R.string.action_call_decline), - PendingIntent.getService(mContext, random.nextInt(), - Intent(DRingService.ACTION_CALL_REFUSE) + .addAction( + R.drawable.baseline_call_end_24, mContext.getText(R.string.action_call_decline), + PendingIntent.getService(mContext, random.nextInt(), Intent(DRingService.ACTION_CALL_REFUSE) .setClass(mContext, DRingService::class.java) - .putExtra(NotificationService.KEY_CALL_ID, call.daemonIdString), - PendingIntent.FLAG_ONE_SHOT)) - .addAction(R.drawable.baseline_call_24, - mContext.getText(if (ongoingCallId == null) R.string.action_call_accept else R.string.action_call_end_accept), - PendingIntent.getService(mContext, random.nextInt(), - Intent(if (ongoingCallId == null) DRingService.ACTION_CALL_ACCEPT else DRingService.ACTION_CALL_END_ACCEPT) + .putExtra(NotificationService.KEY_CALL_ID, call.daemonIdString), PendingIntent.FLAG_ONE_SHOT + ) + ) + Log.w(TAG, "DEBUG fn buildCallNotification [NotificationServiceImpl.kt] -> conference.hasvideo = ${conference.hasVideo()} ") + + if (conference.hasVideo()){ + messageNotificationBuilder + .addAction(R.id.btnAcceptAudio, if (ongoingCallId == null) mContext.getText(R.string.action_call_accept_audio) else mContext.getText(R.string.action_call_end_accept), + PendingIntent.getService(mContext, random.nextInt(), Intent(if (ongoingCallId == null) DRingService.ACTION_CALL_ACCEPT else DRingService.ACTION_CALL_END_ACCEPT) + .setClass(mContext, DRingService::class.java) + .putExtra(NotificationService.KEY_END_ID, ongoingCallId) + .putExtra(NotificationService.KEY_CALL_ID, call.daemonIdString) + .putExtra(CallFragment.KEY_HAS_VIDEO, false), PendingIntent.FLAG_ONE_SHOT) + ) + .addAction(R.id.btnAcceptVideo, if (ongoingCallId == null) mContext.getText(R.string.action_call_accept_video) else mContext.getText(R.string.action_call_end_accept), + PendingIntent.getService(mContext, random.nextInt(), Intent(if (ongoingCallId == null) DRingService.ACTION_CALL_ACCEPT else DRingService.ACTION_CALL_END_ACCEPT) + .setClass(mContext, DRingService::class.java) + .putExtra(NotificationService.KEY_END_ID, ongoingCallId) + .putExtra(NotificationService.KEY_CALL_ID, call.daemonIdString) + .putExtra(CallFragment.KEY_HAS_VIDEO, true), PendingIntent.FLAG_ONE_SHOT) + ) + + } else { + messageNotificationBuilder.addAction( + R.id.btnAcceptAudio, if (ongoingCallId == null) mContext.getText(R.string.action_call_accept_audio) else mContext.getText(R.string.action_call_end_accept), + PendingIntent.getService(mContext, random.nextInt(), Intent(if (ongoingCallId == null) DRingService.ACTION_CALL_ACCEPT else DRingService.ACTION_CALL_END_ACCEPT) .setClass(mContext, DRingService::class.java) .putExtra(NotificationService.KEY_END_ID, ongoingCallId) - .putExtra(NotificationService.KEY_CALL_ID, call.daemonIdString), - PendingIntent.FLAG_ONE_SHOT)) + .putExtra(NotificationService.KEY_CALL_ID, call.daemonIdString) + .putExtra(CallFragment.KEY_HAS_VIDEO, false), PendingIntent.FLAG_ONE_SHOT) + ) + } if (ongoingCallId != null) { messageNotificationBuilder.addAction(R.drawable.baseline_call_24, mContext.getText(R.string.action_call_hold_accept), @@ -555,6 +576,7 @@ class NotificationServiceImpl( } } + @SuppressLint("RestrictedApi") override fun showFileTransferNotification(conversation: Conversation, info: DataTransfer) { val event = info.status ?: return if (event == InteractionStatus.FILE_AVAILABLE) diff --git a/ring-android/app/src/main/java/cx/ring/tv/call/TVCallActivity.kt b/ring-android/app/src/main/java/cx/ring/tv/call/TVCallActivity.kt index 9c011f9c8a6aea2f27a65bad352bbde1e15db1a5..17a94b98afb9f36ae5b3d47d531bc2f060037234 100644 --- a/ring-android/app/src/main/java/cx/ring/tv/call/TVCallActivity.kt +++ b/ring-android/app/src/main/java/cx/ring/tv/call/TVCallActivity.kt @@ -59,7 +59,7 @@ class TVCallActivity : FragmentActivity() { if (path != null) { Log.d(TAG, "onCreate: outgoing call $path ${intent.action}") callFragment = TVCallFragment.newInstance(intent.action!!, path.accountId, path.conversationId, - intent.extras!!.getString(Intent.EXTRA_PHONE_NUMBER, path.conversationId), false) + intent.extras!!.getString(Intent.EXTRA_PHONE_NUMBER, path.conversationId), true) fragmentTransaction.replace(R.id.main_call_layout, callFragment!!, CALL_FRAGMENT_TAG) .commit() } else { diff --git a/ring-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.kt b/ring-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.kt index 7943c454eb9b7ffe8a9153824b93c22f0bfb761e..0e027af9651f7c4a86cacd3426b7ffe821bd3384 100644 --- a/ring-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.kt +++ b/ring-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.kt @@ -300,7 +300,7 @@ class TVCallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView } override fun displayDialPadKeyboard() {} - override fun switchCameraIcon(isFront: Boolean) {} + override fun switchCameraIcon() {} override fun updateAudioState(state: AudioState) {} override fun updateMenu() {} override fun updateTime(duration: Long) { @@ -378,7 +378,7 @@ class TVCallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView override fun initMenu( isSpeakerOn: Boolean, displayFlip: Boolean, canDial: Boolean, - showPluginBtn: Boolean, onGoingCall: Boolean + showPluginBtn: Boolean, onGoingCall: Boolean, hasActiveVideo: Boolean ) { } @@ -395,7 +395,8 @@ class TVCallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView handleVisibilityTimer() } - override fun initIncomingCallDisplay() { + + override fun initIncomingCallDisplay(hasVideo: Boolean) { mSession!!.isActive = true binding?.apply { callAcceptBtn.visibility = View.VISIBLE @@ -482,16 +483,16 @@ class TVCallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView */ override fun prepareCall(isIncoming: Boolean) { val audioGranted = mDeviceRuntimeService.hasAudioPermission() - val audioOnly: Boolean + val hasVideo: Boolean val permissionType: Int if (isIncoming) { - audioOnly = presenter.isAudioOnly + hasVideo = presenter.wantVideo permissionType = REQUEST_PERMISSION_INCOMING } else { - audioOnly = requireArguments().getBoolean(CallFragment.KEY_AUDIO_ONLY) + hasVideo = requireArguments().getBoolean(CallFragment.KEY_HAS_VIDEO) permissionType = REQUEST_PERMISSION_OUTGOING } - if (!audioOnly) { + if (!hasVideo) { val videoGranted = mDeviceRuntimeService.hasVideoPermission() if ((!audioGranted || !videoGranted) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val perms = ArrayList<String>() @@ -520,9 +521,10 @@ class TVCallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView * @param isIncoming true if call is incoming, false for outgoing */ private fun initializeCall(isIncoming: Boolean) { - Log.w(TAG, "initializeCall $isIncoming") + val hasVideo : Boolean = presenter.wantVideo + Log.w(TAG, "DEBUG fn initializeCall() -> isIncoming = $isIncoming and hasVideo = $hasVideo") if (isIncoming) { - presenter.acceptCall() + presenter.acceptCall(hasVideo) } else { arguments?.let { args -> Log.w(TAG, "initializeCall presenter.initOutGoing") @@ -531,7 +533,7 @@ class TVCallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView conversation.accountId, conversation.conversationUri, args.getString(Intent.EXTRA_PHONE_NUMBER), - args.getBoolean(CallFragment.KEY_AUDIO_ONLY) + args.getBoolean(CallFragment.KEY_HAS_VIDEO) ) } } @@ -651,6 +653,7 @@ class TVCallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView } fun acceptClicked() { + presenter.wantVideo = true prepareCall(true) } @@ -717,12 +720,12 @@ class TVCallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView private const val REQUEST_PERMISSION_INCOMING = 1003 private const val REQUEST_PERMISSION_OUTGOING = 1004 - fun newInstance(action: String, accountId: String, conversationId: String, contactUri: String, audioOnly: Boolean): TVCallFragment { + fun newInstance(action: String, accountId: String, conversationId: String, contactUri: String, hasVideo: Boolean): TVCallFragment { return TVCallFragment().apply { arguments = Bundle().apply { putString(CallFragment.KEY_ACTION, action) putAll(ConversationPath.toBundle(accountId, conversationId)) putString(Intent.EXTRA_PHONE_NUMBER, contactUri) - putBoolean(CallFragment.KEY_AUDIO_ONLY, audioOnly) + putBoolean(CallFragment.KEY_HAS_VIDEO, hasVideo) }} } diff --git a/ring-android/app/src/main/java/cx/ring/tv/contact/TVContactFragment.kt b/ring-android/app/src/main/java/cx/ring/tv/contact/TVContactFragment.kt index fd71d5fa7562d1fbb7295b1fa4c2f79e6d84b00b..9fce6330f4d0bf41cd880053a24aaad2a47b04e6 100644 --- a/ring-android/app/src/main/java/cx/ring/tv/contact/TVContactFragment.kt +++ b/ring-android/app/src/main/java/cx/ring/tv/contact/TVContactFragment.kt @@ -27,6 +27,7 @@ import android.view.View import androidx.core.content.ContextCompat import androidx.leanback.widget.* import cx.ring.R +import cx.ring.fragments.CallFragment import cx.ring.tv.call.TVCallActivity import cx.ring.tv.contact.more.TVContactMoreActivity import cx.ring.tv.contact.more.TVContactMoreFragment @@ -144,7 +145,8 @@ class TVContactFragment : BaseDetailFragment<TVContactPresenter>(), TVContactVie startActivity(Intent(Intent.ACTION_CALL) .setClass(requireContext(), TVCallActivity::class.java) .putExtras(ConversationPath.toBundle(accountId, conversationUri)) - .putExtra(Intent.EXTRA_PHONE_NUMBER, uri.uri)) + .putExtra(Intent.EXTRA_PHONE_NUMBER, uri.uri) + .putExtra(CallFragment.KEY_HAS_VIDEO, true)) } override fun goToCallActivity(id: String) { diff --git a/ring-android/app/src/main/java/cx/ring/tv/conversation/TvConversationFragment.kt b/ring-android/app/src/main/java/cx/ring/tv/conversation/TvConversationFragment.kt index d8796797a980f6c0b0b171ba3cb4588a49d042f4..c51aa5f316d127b1255312bfa90a484caef82994 100644 --- a/ring-android/app/src/main/java/cx/ring/tv/conversation/TvConversationFragment.kt +++ b/ring-android/app/src/main/java/cx/ring/tv/conversation/TvConversationFragment.kt @@ -631,7 +631,7 @@ class TvConversationFragment : BaseSupportFragment<ConversationPresenter, Conver override fun clearMsgEdit() {} override fun goToHome() {} override fun goToAddContact(contact: Contact) {} - override fun goToCallActivity(conferenceId: String) {} + override fun goToCallActivity(conferenceId: String, hasVideo: Boolean) {} override fun goToCallActivityWithResult( accountId: String, conversationUri: net.jami.model.Uri, diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.kt b/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.kt index e09dcfffb67404bb9c884de7ed24d3830947c8eb..6e794627d016bb3800effc7155c0cbb3ea451934 100644 --- a/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.kt +++ b/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.kt @@ -62,6 +62,7 @@ import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.schedulers.Schedulers import net.jami.model.Account +import net.jami.model.Profile import net.jami.navigation.HomeNavigationViewModel import net.jami.smartlist.SmartListViewModel import net.jami.utils.QRCodeUtils @@ -221,34 +222,28 @@ class MainFragment : BaseBrowseFragment<MainPresenter>(), MainView { } override fun displayAccountInfo(viewModel: HomeNavigationViewModel) { - updateModel(viewModel.account) + updateModel(viewModel.account, viewModel.profile) } - override fun updateModel(account: Account) { + private fun updateModel(account: Account, profile: Profile) { val context = requireContext() val address = account.displayUsername - mDisposable.clear() - mDisposable.add(VCardServiceImpl.loadProfile(context, account) - .observeOn(AndroidSchedulers.mainThread()) - .doOnNext { profile -> - val name = profile.displayName - if (name != null && name.isNotEmpty()) { - mTitleView?.setAlias(name) - title = address ?: "" - } else { - mTitleView?.setAlias(address) - } - } - .map { p -> AvatarDrawable.build(context, account, p, true) } - .subscribe { a -> - mTitleView?.apply { - settingsButton.visibility = View.VISIBLE - logoView.visibility = View.VISIBLE - logoView.setImageDrawable(a) - } - }) - qrCard?.setDrawable(prepareAccountQr(context, account.uri)!!) - accountSettingsRow?.adapter!!.notifyItemRangeChanged(QR_ITEM_POSITION, 1) + val name = profile.displayName + if (name != null && name.isNotEmpty()) { + mTitleView?.setAlias(name) + title = address ?: "" + } else { + mTitleView?.setAlias(address) + } + mTitleView?.apply { + settingsButton.visibility = View.VISIBLE + logoView.visibility = View.VISIBLE + logoView.setImageDrawable(AvatarDrawable.build(context, account, profile, true)) + } + prepareAccountQr(context, account.uri)?.let { qr -> + qrCard?.setDrawable(qr) + accountSettingsRow?.adapter?.notifyItemRangeChanged(QR_ITEM_POSITION, 1) + } } override fun showExportDialog(pAccountID: String, hasPassword: Boolean) { diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.kt b/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.kt index 84037c0f3cc033daa1e59200495e81cbec469fcc..8684ce5ba3d06a20e85c329d174cefebbdc9b947 100644 --- a/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.kt +++ b/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.kt @@ -78,11 +78,6 @@ class MainPresenter @Inject constructor( .observeOn(mUiScheduler) .subscribe({ accountProfile -> view?.displayAccountInfo(HomeNavigationViewModel(accountProfile.first, accountProfile.second)) }) { e: Throwable -> Log.d(TAG, "reloadAccountInfos getProfileAccountList onError", e) }) - - mCompositeDisposable.add(mAccountService.observableAccounts - .observeOn(mUiScheduler) - .subscribe({ account: Account -> view?.updateModel(account) }) - { e: Throwable -> Log.e(TAG, "Error loading account list !", e) }) } fun onExportClicked() { diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/MainView.kt b/ring-android/app/src/main/java/cx/ring/tv/main/MainView.kt index 96e3f0025d2ee2868661a112ea74b01ae5732fe4..e5a1a9abf05fec15542e2e13a630f93059db7777 100644 --- a/ring-android/app/src/main/java/cx/ring/tv/main/MainView.kt +++ b/ring-android/app/src/main/java/cx/ring/tv/main/MainView.kt @@ -30,7 +30,6 @@ interface MainView { fun showContactRequests(contactRequests: List<SmartListViewModel>) fun callContact(accountID: String, ringID: String) fun displayAccountInfo(viewModel: HomeNavigationViewModel) - fun updateModel(account: Account) fun showExportDialog(pAccountID: String, hasPassword: Boolean) fun showProfileEditing() fun showAccountShare() diff --git a/ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchFragment.kt b/ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchFragment.kt index fe5843efc1ba9871304cdf3423150b3bf0335eae..6b092fc81e01a556ba2a06aeff5b666aeb2f0dda 100644 --- a/ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchFragment.kt +++ b/ring-android/app/src/main/java/cx/ring/tv/search/ContactSearchFragment.kt @@ -109,7 +109,7 @@ class ContactSearchFragment : BaseSearchFragment<ContactSearchPresenter>(), } override fun startCall(accountID: String, number: String) { - val intent = Intent(CallActivity.ACTION_CALL, toUri(accountID, number), activity, TVCallActivity::class.java) + val intent = Intent(Intent.ACTION_CALL, toUri(accountID, number), activity, TVCallActivity::class.java) intent.putExtra(Intent.EXTRA_PHONE_NUMBER, number) startActivity(intent) activity?.finish() diff --git a/ring-android/app/src/main/java/cx/ring/tv/views/CustomTitleView.kt b/ring-android/app/src/main/java/cx/ring/tv/views/CustomTitleView.kt index ad365f7d6218d9f19811fbb55bca54e2dcd33f87..1a37021fd383a77a2ca0d2855ad0bfdeb664cdd2 100644 --- a/ring-android/app/src/main/java/cx/ring/tv/views/CustomTitleView.kt +++ b/ring-android/app/src/main/java/cx/ring/tv/views/CustomTitleView.kt @@ -57,11 +57,11 @@ class CustomTitleView @JvmOverloads constructor(context: Context?, attrs: Attrib return mSearchOrbView } - override fun setTitle(titleText: CharSequence) { + override fun setTitle(titleText: CharSequence?) { this@CustomTitleView.setTitle(titleText) } - override fun setBadgeDrawable(drawable: Drawable) { + override fun setBadgeDrawable(drawable: Drawable?) { //NOOP } diff --git a/ring-android/app/src/main/java/cx/ring/utils/ConversationPath.kt b/ring-android/app/src/main/java/cx/ring/utils/ConversationPath.kt index 24619fcbd1fbafd611ca12ba5579b9fddb1cccee..9007b9b4c6493b87b3b07eeffe264595e6f73454 100644 --- a/ring-android/app/src/main/java/cx/ring/utils/ConversationPath.kt +++ b/ring-android/app/src/main/java/cx/ring/utils/ConversationPath.kt @@ -96,7 +96,7 @@ class ConversationPath { .build() } - fun toUri(accountId: String?, conversationUri: Uri): android.net.Uri { + fun toUri(accountId: String, conversationUri: Uri): android.net.Uri { return ContentUriHandler.CONVERSATION_CONTENT_URI.buildUpon() .appendEncodedPath(accountId) .appendEncodedPath(conversationUri.uri) @@ -109,9 +109,9 @@ class ConversationPath { fun toUri(interaction: Interaction): android.net.Uri { return if (interaction.conversation is Conversation) - toUri(interaction.account, (interaction.conversation as Conversation).uri) + toUri(interaction.account!!, (interaction.conversation as Conversation).uri) else - toUri(interaction.account, Uri.fromString(interaction.conversation!!.participant!!)) + toUri(interaction.account!!, Uri.fromString(interaction.conversation!!.participant!!)) } fun toBundle(accountId: String, uri: String): Bundle { diff --git a/ring-android/app/src/main/res/drawable/background_call_menu.xml b/ring-android/app/src/main/res/drawable/background_call_menu.xml new file mode 100644 index 0000000000000000000000000000000000000000..3d9d6abad22d07f4a9ecdce785f53b551165c59c --- /dev/null +++ b/ring-android/app/src/main/res/drawable/background_call_menu.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="#99323232"/> + <padding + android:left="5dp" + android:top="5dp" + android:right="5dp" + android:bottom="5dp"/> + <corners + android:topLeftRadius="20dp" + android:topRightRadius="20dp"/> +</shape> + diff --git a/ring-android/app/src/main/res/drawable/baseline_call_24_primary.xml b/ring-android/app/src/main/res/drawable/baseline_call_24_primary.xml new file mode 100644 index 0000000000000000000000000000000000000000..9bf1b3d78c6e447b4b892ab5252c4bd09ceb32b9 --- /dev/null +++ b/ring-android/app/src/main/res/drawable/baseline_call_24_primary.xml @@ -0,0 +1,11 @@ + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@color/colorPrimary" + android:pathData="M20.01,15.38c-1.23,0 -2.42,-0.2 -3.53,-0.56 -0.35,-0.12 -0.74,-0.03 -1.01,0.24l-1.57,1.97c-2.83,-1.35 -5.48,-3.9 -6.89,-6.83l1.95,-1.66c0.27,-0.28 0.35,-0.67 0.24,-1.02 -0.37,-1.11 -0.56,-2.3 -0.56,-3.53 0,-0.54 -0.45,-0.99 -0.99,-0.99H4.19C3.65,3 3,3.24 3,3.99 3,13.28 10.73,21 20.01,21c0.71,0 0.99,-0.63 0.99,-1.18v-3.45c0,-0.54 -0.45,-0.99 -0.99,-0.99z"/> +</vector> \ No newline at end of file diff --git a/ring-android/app/src/main/res/drawable/baseline_call_end_24_primary.xml b/ring-android/app/src/main/res/drawable/baseline_call_end_24_primary.xml new file mode 100644 index 0000000000000000000000000000000000000000..eef40e80476ab99f3a0e670a67a118763a7e25c4 --- /dev/null +++ b/ring-android/app/src/main/res/drawable/baseline_call_end_24_primary.xml @@ -0,0 +1,11 @@ + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@color/colorPrimary" + android:pathData="M12,9c-1.6,0 -3.15,0.25 -4.6,0.72v3.1c0,0.39 -0.23,0.74 -0.56,0.9 -0.98,0.49 -1.87,1.12 -2.66,1.85 -0.18,0.18 -0.43,0.28 -0.7,0.28 -0.28,0 -0.53,-0.11 -0.71,-0.29L0.29,13.08c-0.18,-0.17 -0.29,-0.42 -0.29,-0.7 0,-0.28 0.11,-0.53 0.29,-0.71C3.34,8.78 7.46,7 12,7s8.66,1.78 11.71,4.67c0.18,0.18 0.29,0.43 0.29,0.71 0,0.28 -0.11,0.53 -0.29,0.71l-2.48,2.48c-0.18,0.18 -0.43,0.29 -0.71,0.29 -0.27,0 -0.52,-0.11 -0.7,-0.28 -0.79,-0.74 -1.69,-1.36 -2.67,-1.85 -0.33,-0.16 -0.56,-0.5 -0.56,-0.9v-3.1C15.15,9.25 13.6,9 12,9z"/> +</vector> \ No newline at end of file diff --git a/ring-android/app/src/main/res/drawable/baseline_flip_camera_24.xml b/ring-android/app/src/main/res/drawable/baseline_flip_camera_24.xml new file mode 100644 index 0000000000000000000000000000000000000000..d0b4daa426cd2897443064835250322786b2376c --- /dev/null +++ b/ring-android/app/src/main/res/drawable/baseline_flip_camera_24.xml @@ -0,0 +1,15 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M22.2,11.4c-0.3,-0.2 -0.8,-0.1 -1,0.2c-0.2,0.3 -0.1,0.8 0.2,1c0.6,0.3 1.1,0.9 1.2,1.6c0,1.5 -3.1,3.2 -8.3,3.7c-0.4,0 -0.7,0.4 -0.7,0.8s0.4,0.7 0.8,0.7h0.1c4.8,-0.4 9.7,-2.1 9.7,-5.2C23.9,13 23.2,12 22.2,11.4z" + android:fillColor="#FFFFFF"/> + <path + android:pathData="M8.8,15.2c-0.3,-0.3 -0.8,-0.3 -1.1,0c-0.3,0.3 -0.3,0.8 0,1.1l1.6,1.6c-4.7,-0.5 -7.8,-2.2 -7.8,-3.6C1.6,13.6 2,13 2.7,12.6C2.9,12.5 3,12.2 3,12s-0.2,-0.5 -0.4,-0.6s-0.5,-0.1 -0.7,0.1C0.8,12 0.1,13 0,14.2c0,2.9 4.6,4.7 9.5,5.1l-1.8,1.8c-0.2,0.2 -0.3,0.5 -0.2,0.7s0.3,0.5 0.5,0.5c0.3,0.1 0.5,0 0.7,-0.2l3,-3c0.3,-0.3 0.3,-0.8 0,-1.1L8.8,15.2z" + android:fillColor="#FFFFFF"/> + <path + android:pathData="M6,13.5h12c0.8,0 1.5,-0.7 1.5,-1.5V4.5C19.5,3.7 18.8,3 18,3h-2.7L14,1.7c-0.1,-0.1 -0.3,-0.2 -0.5,-0.2h-3c-0.2,0 -0.4,0.1 -0.5,0.2L8.7,3H6C5.2,3 4.5,3.7 4.5,4.5V12C4.5,12.8 5.2,13.5 6,13.5zM12,5.2c1.7,0 3,1.3 3,3s-1.3,3 -3,3s-3,-1.3 -3,-3S10.3,5.2 12,5.2z" + android:fillColor="#FFFFFF"/> +</vector> diff --git a/ring-android/app/src/main/res/drawable/baseline_mic_24.xml b/ring-android/app/src/main/res/drawable/baseline_mic_24.xml index d699f0954da85fb2c920125be6d18b915d442070..0d88b269802e97456f64ce6bf2d43f113a8822e6 100644 --- a/ring-android/app/src/main/res/drawable/baseline_mic_24.xml +++ b/ring-android/app/src/main/res/drawable/baseline_mic_24.xml @@ -2,10 +2,11 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="24"> <path - android:fillColor="@android:color/white" - android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"/> + android:pathData="M11.7,14.1c2.2,0 4.1,-1.7 4.1,-4.1V4.1c0,-2.2 -1.7,-4.1 -4.1,-4.1S7.6,1.9 7.6,4.1v6C7.6,12.4 9.5,14.1 11.7,14.1L11.7,14.1z" + android:fillColor="#FFFFFF"/> + <path + android:pathData="M15.8,13.5c-1,1.1 -2.5,1.7 -4,1.7c-1.6,0 -3,-0.6 -4,-1.7c-0.5,-0.6 -1.6,-0.8 -2.2,-0.2C5,14 4.9,14.9 5.5,15.6c1.3,1.4 3,2.4 4.9,2.7v4.1c0,0.8 0.6,1.6 1.6,1.6c0.8,0 1.6,-0.6 1.6,-1.6l0,0v-4.1c1.9,-0.3 3.7,-1.3 4.9,-2.7c0.5,-0.6 0.5,-1.6 -0.2,-2.2C17.3,12.7 16.3,12.7 15.8,13.5L15.8,13.5z" + android:fillColor="#FFFFFF"/> </vector> - diff --git a/ring-android/app/src/main/res/drawable/baseline_mic_off_24.xml b/ring-android/app/src/main/res/drawable/baseline_mic_off_24.xml index 6109c69b3fea555b6fe6be85d981f390d4d13df6..3ff2537146e86278a124424a0580583b2f76ea8c 100755 --- a/ring-android/app/src/main/res/drawable/baseline_mic_off_24.xml +++ b/ring-android/app/src/main/res/drawable/baseline_mic_off_24.xml @@ -1,10 +1,18 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0" - android:tint="?attr/colorControlNormal"> - <path - android:fillColor="@android:color/white" - android:pathData="M19,11h-1.7c0,0.74 -0.16,1.43 -0.43,2.05l1.23,1.23c0.56,-0.98 0.9,-2.09 0.9,-3.28zM14.98,11.17c0,-0.06 0.02,-0.11 0.02,-0.17L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v0.18l5.98,5.99zM4.27,3L3,4.27l6.01,6.01L9.01,11c0,1.66 1.33,3 2.99,3 0.22,0 0.44,-0.03 0.65,-0.08l1.66,1.66c-0.71,0.33 -1.5,0.52 -2.31,0.52 -2.76,0 -5.3,-2.1 -5.3,-5.1L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c0.91,-0.13 1.77,-0.45 2.54,-0.9L19.73,21 21,19.73 4.27,3z"/> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M16.2,13.5c-1,1.1 -2.5,1.7 -4,1.7c-1.6,0 -3,-0.6 -4,-1.7c-0.5,-0.6 -1.6,-0.8 -2.2,-0.2c-0.6,0.6 -0.8,1.6 -0.2,2.2c1.3,1.4 3,2.4 4.9,2.7v4.1c0,0.8 0.6,1.6 1.6,1.6c0.8,0 1.6,-0.6 1.6,-1.6l0,0v-4.1c1.9,-0.3 3.7,-1.3 4.9,-2.7c0.5,-0.6 0.5,-1.6 -0.2,-2.2C17.6,12.7 16.7,12.7 16.2,13.5L16.2,13.5z" + android:fillColor="#CC0022"/> + <path + android:pathData="M5.4,23.4c-0.2,0 -0.3,0 -0.5,-0.2c-0.3,-0.2 -0.5,-0.8 -0.3,-1.1L18,1c0.2,-0.3 0.8,-0.5 1.1,-0.3s0.5,0.8 0.3,1.1L6.2,23C6,23.2 5.7,23.4 5.4,23.4z" + android:fillColor="#CC0022"/> + <path + android:pathData="M15.575,1.9C14.775,0.8 13.475,0 12.075,0C9.875,0 7.975,1.9 7.975,4.1v6c0,1 0.3,1.7 1,2.5L15.575,1.9z" + android:fillColor="#CC0022"/> + <path + android:pathData="M10.175,13.7c0.6,0.3 1.3,0.5 1.9,0.5c2.2,0 4.1,-1.7 4.1,-4.1L16.175,4.1c0,0 0,0 0,-0.2L10.175,13.7z" + android:fillColor="#CC0022"/> </vector> diff --git a/ring-android/app/src/main/res/drawable/baseline_mobile_screen_share_24.xml b/ring-android/app/src/main/res/drawable/baseline_mobile_screen_share_24.xml index 0349674349e2ec065a0c1f2e5b5e7a9cb9dc37dd..84f54a4e59db476a43deb5d9dca4a81e05d0e950 100755 --- a/ring-android/app/src/main/res/drawable/baseline_mobile_screen_share_24.xml +++ b/ring-android/app/src/main/res/drawable/baseline_mobile_screen_share_24.xml @@ -1,10 +1,9 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0" - android:tint="?attr/colorControlNormal"> - <path - android:fillColor="@android:color/white" - android:pathData="M17,1.01L7,1c-1.1,0 -1.99,0.9 -1.99,2v18c0,1.1 0.89,2 1.99,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19L7,19L7,5h10v14zM12.8,13.22v1.75l3.2,-2.99L12.8,9v1.7c-3.11,0.43 -4.35,2.56 -4.8,4.7 1.11,-1.5 2.58,-2.18 4.8,-2.18z"/> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M17.6,0c1,0 1.8,0.7 1.8,1.6l0,0v20.8c0,0.9 -0.8,1.6 -1.8,1.6l0,0H6.4c-1,0 -1.8,-0.7 -1.8,-1.6l0,0V1.6C4.6,0.7 5.4,0 6.4,0l0,0H17.6zM12.8,8v2c0,0 -0.1,0 -0.3,0l-0.2,0c-1.5,0 -5.4,0.5 -6.2,5.2c0,0 1.5,-3.4 6.7,-3.1v2l5.3,-3.1L12.8,8z" + android:fillColor="#FFFFFF"/> </vector> diff --git a/ring-android/app/src/main/res/drawable/baseline_person_add_24.xml b/ring-android/app/src/main/res/drawable/baseline_person_add_24.xml index cf4e563df2370b69426c3f4e35bc46c458044066..296a2aa359ed96675bdaf0f4ed75a05802b883f3 100755 --- a/ring-android/app/src/main/res/drawable/baseline_person_add_24.xml +++ b/ring-android/app/src/main/res/drawable/baseline_person_add_24.xml @@ -1,10 +1,15 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0" - android:tint="?attr/colorControlNormal"> - <path - android:fillColor="@android:color/white" - android:pathData="M15,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM6,10L6,7L4,7v3L1,10v2h3v3h2v-3h3v-2L6,10zM15,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M18,12c-3.3,0 -6,2.5 -6,6s2.5,6 6,6s6,-2.7 6,-6S21.2,12 18,12zM20.2,19.1H19v1.3c0,0.8 -0.6,1.3 -1.3,1.3c-0.6,0 -1,-0.6 -1,-1.4V19h-1.3c-0.6,0 -1.3,-0.5 -1.3,-1.3c0,-0.6 0.6,-1.3 1.3,-1.3h1.3v-1.3c0,-0.6 0.6,-1.3 1.3,-1.3c0.6,0 1.3,0.6 1.3,1.3v1.6h1.3c0.6,0 1.3,0.6 1.3,1.3C21.5,18.6 20.9,19.1 20.2,19.1z" + android:fillColor="#FFFFFF"/> + <path + android:pathData="M9.6,18c0,0.8 0.2,1.6 0.3,2.4H0.1c0,-5.2 4.3,-9.5 9.5,-9.5c1.3,0 2.2,0.2 3.2,0.6C10.9,12.8 9.6,15.1 9.6,18z" + android:fillColor="#FFFFFF"/> + <path + android:pathData="M9.6,4.8m-4.8,0a4.8,4.8 0,1 1,9.6 0a4.8,4.8 0,1 1,-9.6 0" + android:fillColor="#FFFFFF"/> </vector> diff --git a/ring-android/app/src/main/res/drawable/baseline_sound_off_24.xml b/ring-android/app/src/main/res/drawable/baseline_sound_off_24.xml new file mode 100644 index 0000000000000000000000000000000000000000..0cfd45bda772ca050f9b58d82bbab7fb3ff794b5 --- /dev/null +++ b/ring-android/app/src/main/res/drawable/baseline_sound_off_24.xml @@ -0,0 +1,21 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M0.7,22.3c-0.1,0 -0.3,0 -0.4,-0.1C0,22 -0.2,21.4 0,21.2L17.5,2c0.1,-0.3 0.7,-0.4 1,-0.3c0.3,0.3 0.4,0.7 0.1,1.1L1.2,22C1.2,22.1 0.9,22.3 0.7,22.3z" + android:fillColor="#CC0022"/> + <path + android:pathData="M17.1,8.9c-0.7,0.3 -1,1 -0.7,1.6c0.1,0.5 0.3,1 0.3,1.5c0,0.5 -0.1,1.1 -0.3,1.5c-0.3,0.7 0.1,1.4 0.7,1.6c0.1,0 0.3,0.1 0.4,0.1c0.5,0 1,-0.3 1.2,-0.8c0.3,-0.8 0.4,-1.6 0.4,-2.5c0,-0.8 -0.1,-1.6 -0.4,-2.5C18.5,9 17.8,8.6 17.1,8.9z" + android:fillColor="#CC0022"/> + <path + android:pathData="M21.9,5.3c-0.4,-0.5 -1.2,-0.7 -1.8,-0.3c-0.5,0.4 -0.7,1.2 -0.3,1.8c1.1,1.5 1.6,3.4 1.6,5.3s-0.5,3.7 -1.6,5.3c-0.4,0.5 -0.3,1.4 0.3,1.8c0.1,0.1 0.4,0.1 0.7,0.1c0.4,0 0.8,-0.1 1.1,-0.5c1.4,-2.1 2.1,-4.4 2.1,-6.8S23.2,7.2 21.9,5.3z" + android:fillColor="#CC0022"/> + <path + android:pathData="M1.5,16.8h1L13.9,4.1v-1c0,-1.1 -1.2,-1.8 -2.2,-1.1L4.9,6.9C4.6,7.2 4.4,7.2 4.1,7.2H1.5c-0.8,0 -1.4,0.7 -1.4,1.4v6.7C0,16.1 0.7,16.8 1.5,16.8z" + android:fillColor="#CC0022"/> + <path + android:pathData="M4.2,16.8c0.3,0 0.5,0.1 0.8,0.3l6.7,4.9c1,0.7 2.2,0 2.2,-1.1V6.1L4.2,16.8z" + android:fillColor="#CC0022"/> +</vector> diff --git a/ring-android/app/src/main/res/drawable/baseline_sound_on_24.xml b/ring-android/app/src/main/res/drawable/baseline_sound_on_24.xml new file mode 100644 index 0000000000000000000000000000000000000000..107a8988f8811b12b884022bad3ac04b29c1c67d --- /dev/null +++ b/ring-android/app/src/main/res/drawable/baseline_sound_on_24.xml @@ -0,0 +1,15 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M17.1,8.8c-0.7,0.3 -1,1 -0.7,1.7c0.1,0.6 0.3,1 0.3,1.5c0,0.6 -0.1,1.1 -0.3,1.5c-0.3,0.7 0.1,1.4 0.7,1.7c0.1,0 0.3,0.1 0.4,0.1c0.6,0 1,-0.3 1.2,-0.8c0.3,-0.8 0.4,-1.7 0.4,-2.5s-0.1,-1.7 -0.4,-2.5C18.5,9 17.8,8.6 17.1,8.8z" + android:fillColor="#FFFFFF"/> + <path + android:pathData="M21.9,5.3c-0.4,-0.6 -1.2,-0.7 -1.8,-0.3c-0.6,0.4 -0.7,1.2 -0.3,1.8c1.1,1.5 1.7,3.4 1.7,5.4s-0.6,3.7 -1.7,5.4c-0.4,0.6 -0.3,1.4 0.3,1.8c0.1,0.1 0.4,0.1 0.7,0.1c0.4,0 0.8,-0.1 1.1,-0.6c1.4,-2.1 2.1,-4.4 2.1,-6.9S23.3,7.2 21.9,5.3z" + android:fillColor="#FFFFFF"/> + <path + android:pathData="M11.6,2l-6.8,5C4.6,7.2 4.3,7.2 4,7.2H1.4C0.6,7.2 0,7.9 0,8.6v6.8c0,0.8 0.7,1.4 1.4,1.4H4c0.3,0 0.7,0.1 1,0.3l6.8,5c1,0.7 2.2,0 2.2,-1.1V2.9C13.8,2 12.6,1.3 11.6,2z" + android:fillColor="#FFFFFF"/> +</vector> diff --git a/ring-android/app/src/main/res/drawable/baseline_videocam_24.xml b/ring-android/app/src/main/res/drawable/baseline_videocam_24.xml index 429061af062194f930a97ff06220ae6410f54a0c..0382accc5d2bd1570a93751bd98d3ca3235bb7df 100755 --- a/ring-android/app/src/main/res/drawable/baseline_videocam_24.xml +++ b/ring-android/app/src/main/res/drawable/baseline_videocam_24.xml @@ -1,10 +1,13 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0" - android:tint="?attr/colorControlNormal"> - <path - android:fillColor="@android:color/white" - android:pathData="M17,10.5V7c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4z"/> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?attr/colorControlNormal"> + <path + android:pathData="M2.1,4.7H15c1.2,0 2.1,0.9 2.1,2.1v10.3c0,1.2 -0.9,2.1 -2.1,2.1H2.1c-1.2,0 -2.1,-0.9 -2.1,-2.1V6.9C0.1,5.7 1.1,4.7 2.1,4.7z" + android:fillColor="#FFFFFF"/> + <path + android:pathData="M23.1,6.2c-0.1,0 -0.3,0 -0.4,0.1l-4.1,3v5l4.1,3c0.4,0.3 0.9,0.1 1.2,-0.4c0.1,-0.1 0.1,-0.4 0.1,-0.5V7.3C23.9,6.7 23.5,6.2 23.1,6.2L23.1,6.2L23.1,6.2z" + android:fillColor="#FFFFFF"/> </vector> diff --git a/ring-android/app/src/main/res/drawable/baseline_videocam_24_primary.xml b/ring-android/app/src/main/res/drawable/baseline_videocam_24_primary.xml new file mode 100644 index 0000000000000000000000000000000000000000..bdad9728c4b2d87c5c6da50d017ab5f707af232c --- /dev/null +++ b/ring-android/app/src/main/res/drawable/baseline_videocam_24_primary.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@color/colorPrimary" + android:pathData="M17,10.5V7c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4z"/> +</vector> \ No newline at end of file diff --git a/ring-android/app/src/main/res/drawable/baseline_videocam_off_24.xml b/ring-android/app/src/main/res/drawable/baseline_videocam_off_24.xml new file mode 100644 index 0000000000000000000000000000000000000000..b1752195028dd64c1f6bc26756457830880ee40d --- /dev/null +++ b/ring-android/app/src/main/res/drawable/baseline_videocam_off_24.xml @@ -0,0 +1,18 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M23.1,6.5c-0.1,0 -0.3,0 -0.4,0.1l-4.1,3v5l4.1,3c0.4,0.3 0.9,0.1 1.2,-0.4c0.1,-0.1 0.1,-0.4 0.1,-0.5v-9.1C23.9,7 23.5,6.5 23.1,6.5L23.1,6.5L23.1,6.5z" + android:fillColor="#CC0022"/> + <path + android:pathData="M13.5,5h-11.4c-1,0 -2,1 -2.1,2.2v10.2c0,0.3 0.1,0.6 0.2,0.9L13.5,5z" + android:fillColor="#CC0022"/> + <path + android:pathData="M15,5L15,5l-14.2,14.1c0.3,0.3 0.8,0.4 1.3,0.4h12.9c1.2,0 2.1,-0.9 2.1,-2.1v-10.3C17.1,5.9 16.2,5 15,5z" + android:fillColor="#CC0022"/> + <path + android:pathData="M1.1,20c-0.1,0 -0.3,0 -0.4,-0.1c-0.2,-0.2 -0.2,-0.5 0,-0.7L15.8,4.1c0.2,-0.2 0.5,-0.2 0.7,0s0.2,0.5 0,0.7L1.4,19.9C1.3,20 1.2,20 1.1,20z" + android:fillColor="#CC0022"/> +</vector> diff --git a/ring-android/app/src/main/res/drawable/drawer_handlebar.xml b/ring-android/app/src/main/res/drawable/drawer_handlebar.xml new file mode 100644 index 0000000000000000000000000000000000000000..2c8ebdf960936fc8ddd7c88a27fb386f1e8ae9a0 --- /dev/null +++ b/ring-android/app/src/main/res/drawable/drawer_handlebar.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="@color/grey_600" /> + <corners + android:topLeftRadius="25dp" + android:topRightRadius="25dp" + android:bottomLeftRadius="25dp" + android:bottomRightRadius="25dp"/> +</shape> \ No newline at end of file diff --git a/ring-android/app/src/main/res/layout/call_notification.xml b/ring-android/app/src/main/res/layout/call_notification.xml new file mode 100644 index 0000000000000000000000000000000000000000..93a331ed58123a65007e696cd03d1bb555e89335 --- /dev/null +++ b/ring-android/app/src/main/res/layout/call_notification.xml @@ -0,0 +1,126 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:orientation="vertical"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:orientation="vertical" + android:paddingHorizontal="16dp" + android:paddingVertical="4dp"> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:paddingVertical="4dp" + tools:ignore="UseCompoundDrawables"> + <ImageView + android:id="@+id/ivAppIcon" + android:layout_width="15dp" + android:layout_height="15dp" + android:background="@drawable/ic_ring_logo_white" + android:backgroundTint="@color/colorPrimary" + android:contentDescription="@string/app_name" /> + <TextView + android:id="@+id/tvNotificationAppName" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="2dp" + android:text="@string/app_name" + android:textColor="@color/colorPrimary" + android:textSize="12sp" /> + </LinearLayout> + <RelativeLayout + android:id="@+id/notification_main" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:layout_centerVertical="true" + android:layout_toStartOf="@+id/ivAvatar" + android:orientation="vertical"> + <TextView + android:id="@+id/tvNotificationTitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="end" + android:lines="1" + android:textColor="@android:color/black" + android:textSize="16sp" /> + <TextView + android:id="@+id/tvNotificationContent" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="end" + android:lines="1" + android:textAppearance="@style/TextAppearance.Compat.Notification.Title" + android:textColor="@color/grey_700" /> + </LinearLayout> + <ImageView + android:id="@+id/ivAvatar" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_alignParentEnd="true" + android:contentDescription="@string/profile" + android:padding="8dp" /> + </RelativeLayout> + </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:paddingHorizontal="6dp" + android:weightSum="3"> + <Button + android:id="@+id/btnRefuse" + style="@style/Widget.AppCompat.Button.Borderless" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:drawableStart="@drawable/baseline_call_end_24_primary" + android:drawablePadding="2dp" + android:ellipsize="end" + android:lines="1" + android:paddingHorizontal="2dp" + android:text="@string/action_call_decline" + android:textColor="@color/colorPrimary" + android:textSize="13sp" /> + <Button + android:id="@+id/btnAcceptAudio" + style="@style/Widget.AppCompat.Button.Borderless" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:drawableStart="@drawable/baseline_call_24_primary" + android:ellipsize="end" + android:lines="2" + android:paddingHorizontal="2dp" + android:text="@string/action_call_accept_audio" + android:textColor="@color/colorPrimary" + android:textSize="13sp" + android:visibility="gone" + tools:visibility="visible" /> + <Button + android:id="@+id/btnAcceptVideo" + style="@style/Widget.AppCompat.Button.Borderless" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:drawableStart="@drawable/baseline_videocam_24_primary" + android:ellipsize="end" + android:lines="2" + android:paddingHorizontal="2dp" + android:text="@string/action_call_accept_video" + android:textColor="@color/colorPrimary" + android:textSize="13sp" + android:visibility="gone" + tools:visibility="visible" /> + </LinearLayout> +</LinearLayout> \ No newline at end of file diff --git a/ring-android/app/src/main/res/layout/frag_call.xml b/ring-android/app/src/main/res/layout/frag_call.xml index 52b021247191863f7f9a8d5b866b763b80163ff0..ef5d20c9ae147c8f322f64bea7ddda449aebe0d9 100644 --- a/ring-android/app/src/main/res/layout/frag_call.xml +++ b/ring-android/app/src/main/res/layout/frag_call.xml @@ -142,9 +142,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. android:layout_centerInParent="true" android:orientation="vertical" android:visibility="gone" + app:alignItems="stretch" app:flexDirection="column" app:flexWrap="wrap" - app:alignItems="stretch" tools:visibility="visible"> <RelativeLayout @@ -235,12 +235,30 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. app:useCompatPadding="true" tools:visibility="visible" /> + <com.google.android.material.floatingactionbutton.FloatingActionButton + android:id="@+id/call_accept_audio_btn" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="16dp" + android:contentDescription="@string/action_call_accept_audio" + android:onClick="@{() -> presenter.acceptAudioClicked()}" + android:tint="@color/white" + android:visibility="gone" + app:backgroundTint="@color/green_500" + app:elevation="6dp" + app:fabSize="normal" + app:pressedTranslationZ="12dp" + app:rippleColor="@android:color/white" + app:srcCompat="@drawable/baseline_call_24" + app:useCompatPadding="true" + tools:visibility="visible" /> + <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/call_accept_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" - android:contentDescription="@string/action_call_accept" + android:contentDescription="@string/action_call_accept_video" android:onClick="@{() -> presenter.acceptClicked()}" android:visibility="gone" app:backgroundTint="@color/green_500" @@ -248,7 +266,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. app:fabSize="normal" app:pressedTranslationZ="12dp" app:rippleColor="@android:color/white" - app:srcCompat="@drawable/baseline_call_24" + app:srcCompat="@drawable/baseline_videocam_24" app:useCompatPadding="true" tools:visibility="visible" /> </LinearLayout> @@ -268,9 +286,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. android:orientation="vertical" android:visibility="gone" app:flexDirection="column_reverse" + android:animateLayoutChanges="true" app:flexWrap="wrap" tools:visibility="visible"> + <cx.ring.views.CheckableImageButton + android:id="@+id/call_screenshare_btn" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="12dp" + android:background="@drawable/call_button_background" + android:contentDescription="@string/ab_action_speakerphone" + android:onClick="@{(v) -> presenter.shareScreenClicked(((android.widget.Checkable)v).isChecked())}" + android:padding="16dp" + app:srcCompat="@drawable/baseline_mobile_screen_share_24" /> + <ImageButton android:id="@+id/call_camera_flip_btn" android:layout_width="wrap_content" @@ -280,19 +310,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. android:contentDescription="@string/ab_action_flipcamera" android:onClick="@{() -> presenter.cameraFlip()}" android:padding="16dp" - android:tint="@color/white" - app:srcCompat="@drawable/baseline_camera_front_24" /> + app:srcCompat="@drawable/baseline_flip_camera_24" /> - <ImageButton - android:id="@+id/call_conference_add" + <cx.ring.views.CheckableImageButton + android:id="@+id/call_camera_switch_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="12dp" android:background="@drawable/call_button_background" - android:onClick="@{() -> presenter.addParticipant()}" + android:contentDescription="@string/ab_action_flipcamera" + android:onClick="@{() -> presenter.switchCamera()}" android:padding="16dp" - android:tint="@color/white" - app:srcCompat="@drawable/baseline_person_add_24" /> + android:visibility="visible" + app:srcCompat="@drawable/baseline_videocam_24" /> <cx.ring.views.CheckableImageButton android:id="@+id/call_mic_btn" @@ -303,7 +333,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. android:contentDescription="@string/action_call_mic_mute" android:onClick="@{() -> presenter.micClicked()}" android:padding="16dp" - android:tint="@color/white" app:srcCompat="@drawable/baseline_mic_24" tools:visibility="visible" /> @@ -316,20 +345,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. android:contentDescription="@string/ab_action_speakerphone" android:onClick="@{() -> presenter.speakerClicked()}" android:padding="16dp" - android:tint="@color/white" - app:srcCompat="@drawable/baseline_volume_up_24" /> + app:srcCompat="@drawable/baseline_sound_on_24" /> - <cx.ring.views.CheckableImageButton - android:id="@+id/call_screenshare_btn" + + <ImageButton + android:id="@+id/call_conference_add" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="12dp" android:background="@drawable/call_button_background" - android:contentDescription="@string/ab_action_speakerphone" - android:onClick="@{(v) -> presenter.shareScreenClicked(((android.widget.Checkable)v).isChecked())}" + android:onClick="@{() -> presenter.addParticipant()}" android:padding="16dp" - android:tint="@color/white" - app:srcCompat="@drawable/baseline_mobile_screen_share_24" /> + app:srcCompat="@drawable/baseline_person_add_24" /> </com.google.android.flexbox.FlexboxLayout> @@ -374,11 +401,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. android:id="@+id/record_layout" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_margin="16dp" - android:gravity="center_vertical" android:layout_alignParentLeft="true" + android:layout_marginStart="16dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="16dp" + android:layout_marginBottom="16dp" android:layout_toLeftOf="@+id/conf_control_group" + android:gravity="center_vertical" + android:orientation="horizontal" android:visibility="invisible" tools:visibility="visible"> @@ -386,8 +416,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. android:id="@+id/record_indicator" android:layout_width="13dp" android:layout_height="13dp" - android:backgroundTint="#BF0046" - android:background="@drawable/item_color_background" /> + android:background="@drawable/item_color_background" + android:backgroundTint="#BF0046" /> <TextView android:id="@+id/record_name" @@ -395,7 +425,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. android:layout_height="wrap_content" android:layout_marginStart="8dp" android:textSize="12sp" - tools:text="Thomas"/> + tools:text="Thomas" /> </LinearLayout> diff --git a/ring-android/app/src/main/res/values-fr-rFR/strings.xml b/ring-android/app/src/main/res/values-fr-rFR/strings.xml index 6748d792ec6ac71269a255440856e1282e0555c4..f2beb2ba2e508d60024f480fe7e1f2d556ccf151 100644 --- a/ring-android/app/src/main/res/values-fr-rFR/strings.xml +++ b/ring-android/app/src/main/res/values-fr-rFR/strings.xml @@ -95,6 +95,8 @@ <string name="start_error_title">Impossible de démarrer Jami !</string> <string name="start_error_mic_required">Jami requiert la permission d\'accéder à votre microphone pour fonctionner.</string> <string name="action_call_accept">Prendre un appel</string> + <string name="action_call_accept_audio">Répondre en audio</string> + <string name="action_call_accept_video">Répondre en video</string> <string name="action_call_hold_accept">Mettre en attente et prendre l\'appel</string> <string name="action_call_end_accept">Terminer et prendre l\'appel</string> <string name="action_call_decline">Refuser</string> diff --git a/ring-android/app/src/main/res/values/strings.xml b/ring-android/app/src/main/res/values/strings.xml index 888b0faa267cb8ad2783aa4d99bfc31df7eead67..03483258bf1fd0b3355607ae0d03e76e528e84c5 100644 --- a/ring-android/app/src/main/res/values/strings.xml +++ b/ring-android/app/src/main/res/values/strings.xml @@ -150,6 +150,8 @@ along with this program; if not, write to the Free Software <string name="start_error_title">Can\'t start Jami !</string> <string name="start_error_mic_required">Jami requires the microphone permission to work.</string> <string name="action_call_accept">Take call</string> + <string name="action_call_accept_audio">Answer in audio</string> + <string name="action_call_accept_video">Answer in video</string> <string name="action_call_hold_accept">Hold and take call</string> <string name="action_call_end_accept">End and take call</string> <string name="action_call_decline">Decline</string> diff --git a/ring-android/libjamiclient/src/main/kotlin/net/jami/call/CallPresenter.kt b/ring-android/libjamiclient/src/main/kotlin/net/jami/call/CallPresenter.kt index 1fb3394605cb2ad9346552aa2202d75b77dc0288..daa02e14b70a1aa329066f4c4819f979df7f5bde 100644 --- a/ring-android/libjamiclient/src/main/kotlin/net/jami/call/CallPresenter.kt +++ b/ring-android/libjamiclient/src/main/kotlin/net/jami/call/CallPresenter.kt @@ -56,7 +56,8 @@ class CallPresenter @Inject constructor( private val mPendingCalls: MutableList<Call> = ArrayList() private val mPendingSubject: Subject<List<Call>> = BehaviorSubject.createDefault(mPendingCalls) private var mOnGoingCall = false - var isAudioOnly = true + var wantVideo = false + var videoIsMuted = false private set private var permissionChanged = false var isPipMode = false @@ -86,12 +87,12 @@ class CallPresenter @Inject constructor( } } - override fun unbindView() { - if (!isAudioOnly) { + /*override fun unbindView() { + if (wantVideo) { mHardwareService.endCapture() } super.unbindView() - } + }*/ override fun bindView(view: CallView) { super.bindView(view) @@ -118,20 +119,21 @@ class CallPresenter @Inject constructor( }));*/ } - fun initOutGoing(accountId: String?, conversationUri: Uri?, contactUri: String?, audioOnly: Boolean) { + fun initOutGoing(accountId: String?, conversationUri: Uri?, contactUri: String?, hasVideo: Boolean) { Log.e(TAG, "initOutGoing") - var audioOnly = audioOnly + var pHasVideo = hasVideo if (accountId == null || contactUri == null) { Log.e(TAG, "initOutGoing: null account or contact") hangupCall() return } if (!mHardwareService.hasCamera()) { - audioOnly = true + pHasVideo = false } + Log.w(TAG, "DEBUG fn initOutGoing() -> value of pHasVideo : $pHasVideo") //getView().blockScreenRotation(); val callObservable = mCallService - .placeCall(accountId, conversationUri, fromString(toNumber(contactUri)!!), audioOnly) + .placeCall(accountId, conversationUri, fromString(toNumber(contactUri)!!), pHasVideo) //.map(mCallService::getConference) .flatMapObservable { call: Call -> mCallService.getConfUpdates(call) } .share() @@ -155,6 +157,7 @@ class CallPresenter @Inject constructor( */ fun initIncomingCall(confId: String, actionViewOnly: Boolean) { //getView().blockScreenRotation(); + Log.w(TAG, "DEBUG fn initIncomingCall [CallPresenter.kt] -> if actionViewOnly ( = $actionViewOnly ) == true then getConfUpdate() who return a conference \n ,then if (!ActionViewOnly) call confUpdate() and prepareCall(isIncoming: true) or ") // if the call is incoming through a full intent, this allows the incoming call to display incomingIsFullIntent = actionViewOnly @@ -208,12 +211,14 @@ class CallPresenter @Inject constructor( fun prepareOptionMenu() { val isSpeakerOn: Boolean = mHardwareService.isSpeakerphoneOn //boolean hasContact = mSipCall != null && null != mSipCall.getContact() && mSipCall.getContact().isUnknown(); - val canDial = mOnGoingCall && mConference != null + val conference = mConference + val canDial = mOnGoingCall && conference != null + val hasActiveVideo = conference?.hasActiveVideo() == true // get the preferences val displayPluginsButton = view!!.displayPluginsButton() - val showPluginBtn = displayPluginsButton && mOnGoingCall && mConference != null - val hasMultipleCamera = mHardwareService.cameraCount > 1 && mOnGoingCall && !isAudioOnly - view?.initMenu(isSpeakerOn, hasMultipleCamera, canDial, showPluginBtn, mOnGoingCall) + val showPluginBtn = displayPluginsButton && mOnGoingCall && conference != null + val hasMultipleCamera = mHardwareService.cameraCount > 1 && mOnGoingCall && hasActiveVideo + view?.initMenu(isSpeakerOn, hasMultipleCamera, canDial, showPluginBtn, mOnGoingCall, hasActiveVideo) } fun chatClick() { @@ -246,7 +251,14 @@ class CallPresenter @Inject constructor( fun switchVideoInputClick() { val conference = mConference ?: return mHardwareService.switchInput(conference.id, false) - view?.switchCameraIcon(mHardwareService.isPreviewFromFrontCamera) + //view?.switchCameraIcon(mHardwareService.isPreviewFromFrontCamera) + view?.switchCameraIcon() + } + + fun switchOnOffCamera() { + val conference = mConference ?: return + videoIsMuted = !videoIsMuted + mCallService.requestVideoMedia(conference, !videoIsMuted) } fun configurationChanged(rotation: Int) { @@ -257,8 +269,8 @@ class CallPresenter @Inject constructor( view?.displayDialPadKeyboard() } - fun acceptCall() { - mConference?.let { mCallService.accept(it.id) } + fun acceptCall(hasVideo: Boolean) { + mConference?.let { mCallService.accept(it.id, hasVideo) } } fun hangupCall() { @@ -425,36 +437,39 @@ class CallPresenter @Inject constructor( if (status === CallStatus.HOLD) { if (call.isSimpleCall) mCallService.unhold(call.id) else JamiService.addMainParticipant(call.id) } - isAudioOnly = !call.hasVideo() + val hasVideo = call.hasVideo() + val hasActiveVideo = call.hasActiveVideo() + videoIsMuted = !hasActiveVideo val view = view ?: return view.updateMenu() if (call.isOnGoing) { mOnGoingCall = true - view.initNormalStateDisplay(isAudioOnly, isMicrophoneMuted) + view.initNormalStateDisplay(!hasVideo, isMicrophoneMuted) view.updateMenu() - if (!isAudioOnly) { + if (hasVideo) { mHardwareService.setPreviewSettings() mHardwareService.updatePreviewVideoSurface(call) videoSurfaceUpdateId(call.id) pluginSurfaceUpdateId(call.pluginId) - view.displayVideoSurface(true, mDeviceRuntimeService.hasVideoPermission()) + view.displayVideoSurface(true, hasActiveVideo && mDeviceRuntimeService.hasVideoPermission()) if (permissionChanged) { - mHardwareService.switchInput(mConference!!.id, permissionChanged) + mHardwareService.switchInput(call.id, permissionChanged) permissionChanged = false } } timeUpdateTask?.dispose() timeUpdateTask = mUiScheduler.schedulePeriodicallyDirect({ updateTime() }, 0, 1, TimeUnit.SECONDS) } else if (call.isRinging) { + //Log.w(TAG, "DEBUG fn confUpdate [CallPresenter] -> [ELSEIF] checking value of call.hasVideo() : ${call.hasVideo()}") val scall = call.call!! - view.handleCallWakelock(isAudioOnly) + view.handleCallWakelock(!hasVideo) if (scall.isIncoming) { if (mAccountService.getAccount(scall.account!!)!!.isAutoanswerEnabled) { Log.w(TAG, "Accept because of autoanswer") - mCallService.accept(scall.daemonIdString!!) + mCallService.accept(scall.daemonIdString!!, wantVideo) // only display the incoming call screen if the notification is a full screen intent } else if (incomingIsFullIntent) { - view.initIncomingCallDisplay() + view.initIncomingCallDisplay(hasVideo) } } else { mOnGoingCall = false @@ -493,25 +508,33 @@ class CallPresenter @Inject constructor( } private fun onVideoEvent(event: VideoEvent) { + Log.w(TAG, "DEBUG fn onVideoEvent") Log.d(TAG, "VIDEO_EVENT: " + event.start + " " + event.callId + " " + event.w + "x" + event.h) val view = view ?: return + val conference = mConference if (event.start) { - view.displayVideoSurface(true, !isPipMode && mDeviceRuntimeService.hasVideoPermission()) - } else if (mConference != null && mConference!!.id == event.callId) { - view.displayVideoSurface(event.started, event.started && !isPipMode && mDeviceRuntimeService.hasVideoPermission()) + Log.w(TAG, "DEBUG fn onVideoEvent |1| inside => if (event.start) ") + view.displayVideoSurface(true, !isPipMode && mDeviceRuntimeService.hasVideoPermission() && conference?.hasActiveVideo() == true) + } else if (conference != null && conference.id == event.callId) { + Log.w(TAG, "DEBUG fn onVideoEvent |2| inside => else if (mConference != null && mConference!!.id == event.callId)") + Log.w(TAG, "DEBUG fn onVideoEvent |2| inside => event.started ${event.started} && !isPipMode ${!isPipMode} && mDeviceRuntimeService.hasVideoPermission() ${mDeviceRuntimeService.hasVideoPermission()} && hasVideo $wantVideo && !videoIsMuted ${!videoIsMuted}") + view.displayVideoSurface(event.started, + event.started && !isPipMode && mDeviceRuntimeService.hasVideoPermission() && conference.hasActiveVideo()) if (event.started) { videoWidth = event.w videoHeight = event.h view.resetVideoSize(videoWidth, videoHeight) } } else if (event.callId == null) { + Log.w(TAG, "DEBUG fn onVideoEvent |3| inside => else if (event.callId == null)") if (event.started) { previewWidth = event.w previewHeight = event.h view.resetPreviewVideoSize(previewWidth, previewHeight, event.rot) } } - if (mConference != null && mConference!!.pluginId == event.callId) { + if (conference != null && conference.pluginId == event.callId) { + Log.w(TAG, "DEBUG fn onVideoEvent |4| inside => if (mConference != null && mConference!!.pluginId == event.callId)") if (event.started) { previewWidth = event.w previewHeight = event.h @@ -525,7 +548,7 @@ class CallPresenter @Inject constructor( fun positiveButtonClicked() { if (mConference!!.isRinging && mConference!!.isIncoming) { - acceptCall() + acceptCall(true) } else { hangupCall() } @@ -554,10 +577,12 @@ class CallPresenter @Inject constructor( fun pipModeChanged(pip: Boolean) { isPipMode = pip if (pip) { + Log.w(TAG, "DEBUG fn pipModeChanged |1| entering pipMode -> hangupButton : false; previewSurface: false; displayVideoSurface(true, false)") view!!.displayHangupButton(false) view!!.displayPreviewSurface(false) view!!.displayVideoSurface(true, false) } else { + Log.w(TAG, "DEBUG fn pipModeChanged |2| entering pipMode -> previewSurface: true; displayVideoSurface(true, mDeviceRuntimeService.hasVideoPermission() && hasVideo && !videoIsMuted)") view!!.displayPreviewSurface(true) view!!.displayVideoSurface(true, mDeviceRuntimeService.hasVideoPermission()) } @@ -565,6 +590,7 @@ class CallPresenter @Inject constructor( fun toggleCallMediaHandler(id: String, toggle: Boolean) { val conference = mConference ?: return + //Log.w(TAG, "fn toggleMediaHandler [CallPresenter] -> check value of conference.hasVideo : ${conference.hasVideo()}") if (conference.isOnGoing && conference.hasVideo()) { view?.toggleCallMediaHandler(id, conference.id, toggle) } @@ -577,8 +603,8 @@ class CallPresenter @Inject constructor( fun addConferenceParticipant(accountId: String, uri: Uri) { mCompositeDisposable.add(mConversationFacade.startConversation(accountId, uri) .subscribe { conversation: Conversation -> - val confs: List<Conference> = conversation.currentCalls - if (confs.isEmpty()) { + val conf = conversation.currentCall + if (conf == null) { val pendingObserver: Observer<Call> = object : Observer<Call> { private var call: Call? = null override fun onSubscribe(d: Disposable) {} @@ -602,7 +628,7 @@ class CallPresenter @Inject constructor( val contactUri = if (uri.isSwarm) conversation.contact!!.uri else uri // Place new call, join to conference when answered - val newCall = mCallService.placeCallObservable(accountId, null, contactUri, isAudioOnly) + val newCall = mCallService.placeCallObservable(accountId, null, contactUri, wantVideo) .doOnEach(pendingObserver) .filter(Call::isOnGoing) .firstElement() @@ -616,21 +642,17 @@ class CallPresenter @Inject constructor( mCallService.joinParticipant(id, call.daemonIdString!!).subscribe() } }) - } else { - // Selected contact already in call or conference, join it to current conference - val selectedConf = confs[0] - if (selectedConf !== mConference) { - if (mConference!!.isConference) { - if (selectedConf.isConference) - mCallService.joinConference(mConference!!.id, selectedConf.id) - else - mCallService.addParticipant(selectedConf.id, mConference!!.id) - } else { - if (selectedConf.isConference) - mCallService.addParticipant(mConference!!.id, selectedConf.id) - else - mCallService.joinParticipant(mConference!!.id, selectedConf.id).subscribe() - } + } else if (conf !== mConference) { + if (mConference!!.isConference) { + if (conf.isConference) + mCallService.joinConference(mConference!!.id, conf.id) + else + mCallService.addParticipant(conf.id, mConference!!.id) + } else { + if (conf.isConference) + mCallService.addParticipant(mConference!!.id, conf.id) + else + mCallService.joinParticipant(mConference!!.id, conf.id).subscribe() } } }) diff --git a/ring-android/libjamiclient/src/main/kotlin/net/jami/call/CallView.kt b/ring-android/libjamiclient/src/main/kotlin/net/jami/call/CallView.kt index 62843bbe30c27c4414a78d24d91fef65cec59f00..d6970fa4098cc8a351b477f95d8024580b801075 100644 --- a/ring-android/libjamiclient/src/main/kotlin/net/jami/call/CallView.kt +++ b/ring-android/libjamiclient/src/main/kotlin/net/jami/call/CallView.kt @@ -19,6 +19,7 @@ */ package net.jami.call +import com.j256.ormlite.field.converter.BooleanNumberFieldConverter import net.jami.model.Call import net.jami.model.Call.CallStatus import net.jami.model.Conference.ParticipantInfo @@ -32,15 +33,15 @@ interface CallView { fun displayPreviewSurface(display: Boolean) fun displayHangupButton(display: Boolean) fun displayDialPadKeyboard() - fun switchCameraIcon(isFront: Boolean) + fun switchCameraIcon() fun updateAudioState(state: AudioState) fun updateMenu() fun updateTime(duration: Long) fun updateContactBubble(contact: List<Call>) fun updateCallStatus(callState: CallStatus) - fun initMenu(isSpeakerOn: Boolean, displayFlip: Boolean, canDial: Boolean, showPluginBtn: Boolean, onGoingCall: Boolean) + fun initMenu(isSpeakerOn: Boolean, hasMultipleCamera: Boolean, canDial: Boolean, showPluginBtn: Boolean, onGoingCall: Boolean, hasActiveVideo: Boolean) fun initNormalStateDisplay(audioOnly: Boolean, muted: Boolean) - fun initIncomingCallDisplay() + fun initIncomingCallDisplay(hasVideo: Boolean) fun initOutGoingCallDisplay() fun resetPreviewVideoSize(previewWidth: Int, previewHeight: Int, rot: Int) fun resetPluginPreviewVideoSize(previewWidth: Int, previewHeight: Int, rot: Int) diff --git a/ring-android/libjamiclient/src/main/kotlin/net/jami/conversation/ConversationPresenter.kt b/ring-android/libjamiclient/src/main/kotlin/net/jami/conversation/ConversationPresenter.kt index db178f86a8250e5f44d188548061e8838ab84483..69aeaef91280410973a7421182996dd38b54387d 100644 --- a/ring-android/libjamiclient/src/main/kotlin/net/jami/conversation/ConversationPresenter.kt +++ b/ring-android/libjamiclient/src/main/kotlin/net/jami/conversation/ConversationPresenter.kt @@ -309,14 +309,16 @@ class ConversationPresenter @Inject constructor( fun clickOnGoingPane() { val conf = mConversation?.currentCall if (conf != null) { - view?.goToCallActivity(conf.id) + view?.goToCallActivity(conf.id, conf.hasActiveVideo()) } else { view?.displayOnGoingCallPane(false) } } - fun goToCall(audioOnly: Boolean) { - if (audioOnly && !mHardwareService.hasMicrophone()) { + fun goToCall(withCamera: Boolean) { + + Log.w(TAG, "DEBUG fn goToCall || hasVideo: $withCamera") + if (!withCamera && !mHardwareService.hasMicrophone()) { view!!.displayErrorToast(Error.NO_MICROPHONE) return } @@ -329,9 +331,9 @@ class ConversationPresenter @Inject constructor( if (conf != null && conf.participants.isNotEmpty() && conf.participants[0].callStatus !== Call.CallStatus.INACTIVE && conf.participants[0].callStatus !== Call.CallStatus.FAILURE) { - view.goToCallActivity(conf.id) + view.goToCallActivity(conf.id, conf.hasActiveVideo()) } else { - view.goToCallActivityWithResult(conversation.accountId, conversation.uri, conversation.contact!!.uri, audioOnly) + view.goToCallActivityWithResult(conversation.accountId, conversation.uri, conversation.contact!!.uri, withCamera) } } }) diff --git a/ring-android/libjamiclient/src/main/kotlin/net/jami/conversation/ConversationView.kt b/ring-android/libjamiclient/src/main/kotlin/net/jami/conversation/ConversationView.kt index 9eb95ec75a5a9cc96dfb64ec4ae6ddd0e8ba9f14..8b04bfc1ce1e4c3201bef9654c3766b04f8a2817 100644 --- a/ring-android/libjamiclient/src/main/kotlin/net/jami/conversation/ConversationView.kt +++ b/ring-android/libjamiclient/src/main/kotlin/net/jami/conversation/ConversationView.kt @@ -22,6 +22,7 @@ package net.jami.conversation import net.jami.model.* import net.jami.model.Account.ComposingStatus import java.io.File +import javax.swing.text.StyledEditorKit interface ConversationView { fun refreshView(conversation: List<Interaction>) @@ -35,8 +36,8 @@ interface ConversationView { fun clearMsgEdit() fun goToHome() fun goToAddContact(contact: Contact) - fun goToCallActivity(conferenceId: String) - fun goToCallActivityWithResult(accountId: String, conversationUri: Uri, contactUri: Uri, audioOnly: Boolean) + fun goToCallActivity(conferenceId: String, withCamera: Boolean) + fun goToCallActivityWithResult(accountId: String, conversationUri: Uri, contactUri: Uri, withCamera: Boolean) fun goToContactActivity(accountId: String, uri: Uri) fun switchToUnknownView(name: String) fun switchToIncomingTrustRequestView(message: String) diff --git a/ring-android/libjamiclient/src/main/kotlin/net/jami/model/Call.kt b/ring-android/libjamiclient/src/main/kotlin/net/jami/model/Call.kt index 6e7026b775aeb58ff3e79acdaf8bf1f72c9cf7a0..39dd51cfa49e8cba67c0ca557234e1debf1d6501 100644 --- a/ring-android/libjamiclient/src/main/kotlin/net/jami/model/Call.kt +++ b/ring-android/libjamiclient/src/main/kotlin/net/jami/model/Call.kt @@ -19,13 +19,12 @@ */ package net.jami.model -import net.jami.utils.StringUtils.isEmpty -import net.jami.utils.ProfileChunk -import ezvcard.VCard -import net.jami.utils.VCardUtils import ezvcard.Ezvcard +import ezvcard.VCard import net.jami.utils.Log -import java.lang.Exception +import net.jami.utils.ProfileChunk +import net.jami.utils.StringUtils.isEmpty +import net.jami.utils.VCardUtils import java.util.* class Call : Interaction { @@ -35,8 +34,6 @@ class Call : Interaction { private set private var isVideoMuted = false private val isRecording = false - var isAudioOnly = false - private set var callStatus = CallStatus.NONE private set var timestampEnd: Long = 0 @@ -73,6 +70,9 @@ class Call : Interaction { var contactNumber: String? = null private set var confId: String? = null + + var mediaList: List<Media>? = null + private var mProfileChunk: ProfileChunk? = null constructor( @@ -81,7 +81,8 @@ class Call : Interaction { account: String?, conversation: ConversationHistory?, contact: Contact?, - direction: Direction + direction: Direction, + mediaList: List<Media>, ) { daemonIdString = daemonId try { @@ -97,6 +98,8 @@ class Call : Interaction { mType = InteractionType.CALL.toString() this.contact = contact mIsRead = 1 + this.mediaList = mediaList + } constructor(interaction: Interaction) { @@ -145,16 +148,14 @@ class Call : Interaction { } fun setDetails(details: Map<String, String>) { - isPeerHolding = "true" == details[KEY_PEER_HOLDING] - isAudioMuted = "true" == details[KEY_AUDIO_MUTED] - isVideoMuted = "true" == details[KEY_VIDEO_MUTED] - isAudioOnly = "true" == details[KEY_AUDIO_ONLY] + isPeerHolding = details[KEY_PEER_HOLDING].toBoolean() + isAudioMuted = details[KEY_AUDIO_MUTED].toBoolean() + isVideoMuted = details[KEY_VIDEO_MUTED].toBoolean() audioCodec = details[KEY_AUDIO_CODEC] videoCodec = details[KEY_VIDEO_CODEC] val confId = details[KEY_CONF_ID] this.confId = if (isEmpty(confId)) null else confId } - val isConferenceParticipant: Boolean get() = confId != null @@ -230,6 +231,24 @@ class Call : Interaction { return null } + fun hasMedia(label: Media.MediaType): Boolean { + val mediaList = mediaList ?: return false + for (media in mediaList) { + // todo check -> isEnabled est il utile ? Si le media n'est pas activé alors le daemon ne nous le transmets pas ? + if (media.isEnabled && media.mediaType == label) { + return true + } + } + return false + } + fun hasActiveMedia(label: Media.MediaType): Boolean { + val mediaList = mediaList ?: return false + for (media in mediaList) + if (media.isEnabled && !media.isMuted && media.mediaType == label) + return true + return false + } + enum class CallStatus { NONE, SEARCHING, CONNECTING, RINGING, CURRENT, HUNGUP, BUSY, FAILURE, HOLD, UNHOLD, INACTIVE, OVER; @@ -275,7 +294,7 @@ class Call : Interaction { companion object { val TAG = Call::class.simpleName!! const val KEY_ACCOUNT_ID = "ACCOUNTID" - const val KEY_AUDIO_ONLY = "AUDIO_ONLY" + const val KEY_HAS_VIDEO = "HAS_VIDEO" const val KEY_CALL_TYPE = "CALL_TYPE" const val KEY_CALL_STATE = "CALL_STATE" const val KEY_PEER_NUMBER = "PEER_NUMBER" diff --git a/ring-android/libjamiclient/src/main/kotlin/net/jami/model/Conference.kt b/ring-android/libjamiclient/src/main/kotlin/net/jami/model/Conference.kt index 5fd6757569534dba013270070037251aaca2b155..e08e40ed09d8bca60bb4b089d2c1dc5f753a2819 100644 --- a/ring-android/libjamiclient/src/main/kotlin/net/jami/model/Conference.kt +++ b/ring-android/libjamiclient/src/main/kotlin/net/jami/model/Conference.kt @@ -25,6 +25,7 @@ import io.reactivex.rxjava3.subjects.BehaviorSubject import io.reactivex.rxjava3.subjects.Subject import net.jami.model.Call.CallStatus import net.jami.model.Call.CallStatus.Companion.fromConferenceString +import net.jami.utils.Log import java.util.* import kotlin.math.min @@ -135,8 +136,28 @@ class Conference { val isOnGoing: Boolean get() = mParticipants.size == 1 && mParticipants[0].isOnGoing || mParticipants.size > 1 + + fun getMediaList(): List<Media>? { + return if (mParticipants.size == 1) mParticipants[0].mediaList else ArrayList() + } + + fun hasAudioMedia(): Boolean { + return mParticipants.size == 1 && mParticipants[0].hasMedia(Media.MediaType.MEDIA_TYPE_AUDIO) + } + fun hasVideo(): Boolean { - for (call in mParticipants) if (!call.isAudioOnly) return true + for (call in mParticipants) if (call.hasMedia(Media.MediaType.MEDIA_TYPE_VIDEO)) return true + return false + } + + /* fun hasVideoMedia(): Boolean { + return mParticipants.size == 1 && mParticipants[0].hasMedia(Media.MediaType.MEDIA_TYPE_VIDEO) + }*/ + + fun hasActiveVideo(): Boolean { + for (call in mParticipants) + if (call.hasActiveMedia(Media.MediaType.MEDIA_TYPE_VIDEO)) + return true return false } @@ -168,4 +189,5 @@ class Conference { } mParticipantRecording.onNext(mParticipantRecordingSet) } + } \ No newline at end of file diff --git a/ring-android/libjamiclient/src/main/kotlin/net/jami/model/Conversation.kt b/ring-android/libjamiclient/src/main/kotlin/net/jami/model/Conversation.kt index 389ecfac2382ad51831cef492be11ff2139cba36..5899800542e9a4c3b6749f848f4d9a0357088f9a 100644 --- a/ring-android/libjamiclient/src/main/kotlin/net/jami/model/Conversation.kt +++ b/ring-android/libjamiclient/src/main/kotlin/net/jami/model/Conversation.kt @@ -39,7 +39,7 @@ class Conversation : ConversationHistory { val uri: Uri val contacts: MutableList<Contact> val rawHistory: NavigableMap<Long, Interaction> = TreeMap() - val currentCalls = ArrayList<Conference>() + private val currentCalls = ArrayList<Conference>() val aggregateHistory = ArrayList<Interaction>(32) private var lastDisplayed: Interaction? = null private val updatedElementSubject: Subject<Tuple<Interaction, ElementStatus>> = PublishSubject.create() diff --git a/ring-android/libjamiclient/src/main/kotlin/net/jami/model/Media.kt b/ring-android/libjamiclient/src/main/kotlin/net/jami/model/Media.kt new file mode 100644 index 0000000000000000000000000000000000000000..f88729999ab21b0a3e04333770b33be3edb1c6d3 --- /dev/null +++ b/ring-android/libjamiclient/src/main/kotlin/net/jami/model/Media.kt @@ -0,0 +1,72 @@ +package net.jami.model + +import net.jami.daemon.StringMap +import java.util.* + +data class Media(val source: String?, + val mediaType: MediaType?, + val label: String?, + val isEnabled: Boolean, + val isOnHold: Boolean, + val isMuted: Boolean +) { + constructor(mediaMap: Map<String, String>) : this( + source = mediaMap[SOURCE_KEY], + mediaType = MediaType.parseMediaType(mediaMap[MEDIA_TYPE_KEY]!!), + label = mediaMap[LABEL_KEY], + isEnabled = java.lang.Boolean.parseBoolean(mediaMap[ENABLED_KEY]), + isOnHold = java.lang.Boolean.parseBoolean(mediaMap[ON_HOLD_KEY]), + isMuted = java.lang.Boolean.parseBoolean(mediaMap[MUTED_KEY]) + ) + + constructor(type: MediaType, label: String) : this( + source = "", + mediaType = type, + label = label, + isEnabled = true, + isOnHold = false, + isMuted = false + ) + + enum class MediaType { + MEDIA_TYPE_AUDIO, + MEDIA_TYPE_VIDEO; + + companion object { + fun parseMediaType(mediaType: String): MediaType? { + for (mt in values()) { + if (mt.name.contains(mediaType)) { + return mt + } + } + return null + } + + fun getMediaTypeString(mediaType: MediaType?): String { + return mediaType?.name ?: "NULL" + } + } + } + + fun toMap(): StringMap { + val map = StringMap() + if (source != null) map[SOURCE_KEY] = source + map[MEDIA_TYPE_KEY] = MediaType.getMediaTypeString(mediaType) + if (label != null) map[LABEL_KEY] = label + map[ENABLED_KEY] = java.lang.Boolean.toString(isEnabled) + map[ON_HOLD_KEY] = java.lang.Boolean.toString(isOnHold) + map[MUTED_KEY] = java.lang.Boolean.toString(isMuted) + return map + } + + companion object { + private const val SOURCE_KEY = "SOURCE" + const val MEDIA_TYPE_KEY = "MEDIA_TYPE" + private const val LABEL_KEY = "LABEL" + private const val ENABLED_KEY = "ENABLED" + private const val ON_HOLD_KEY = "ON_HOLD" + const val MUTED_KEY = "MUTED" + val DEFAULT_AUDIO = Media(MediaType.MEDIA_TYPE_AUDIO, "audio_0") + val DEFAULT_VIDEO = Media(MediaType.MEDIA_TYPE_VIDEO, "video_0") + } +} diff --git a/ring-android/libjamiclient/src/main/kotlin/net/jami/services/CallService.kt b/ring-android/libjamiclient/src/main/kotlin/net/jami/services/CallService.kt index a4240fffe5d0a128d000aa5a0bdf3c13055d57a4..21ad4b92be601d489c2ea114af6a1e4602e312d9 100644 --- a/ring-android/libjamiclient/src/main/kotlin/net/jami/services/CallService.kt +++ b/ring-android/libjamiclient/src/main/kotlin/net/jami/services/CallService.kt @@ -28,16 +28,26 @@ import io.reactivex.rxjava3.subjects.PublishSubject import net.jami.daemon.Blob import net.jami.daemon.JamiService import net.jami.daemon.StringMap +import net.jami.daemon.VectMap import net.jami.model.Call import net.jami.model.Call.CallStatus import net.jami.model.Conference import net.jami.model.Conference.ParticipantInfo +import net.jami.model.Media import net.jami.model.Uri import net.jami.utils.Log import net.jami.utils.StringUtils.isEmpty +import net.jami.utils.SwigNativeConverter +import org.w3c.dom.stylesheets.MediaList import java.util.* import java.util.concurrent.Callable import java.util.concurrent.ScheduledExecutorService +import javax.xml.transform.Source +import kotlin.collections.ArrayList + + + + class CallService( private val mExecutor: ScheduledExecutorService, @@ -208,24 +218,25 @@ class CallService( SipCall call = getCurrentCallForId(callId); return call == null ? Observable.error(new IllegalArgumentException()) : getCallUpdates(call); }*/ - fun placeCallObservable(accountId: String, conversationUri: Uri?, number: Uri, audioOnly: Boolean): Observable<Call> { - return placeCall(accountId, conversationUri, number, audioOnly) + fun placeCallObservable(accountId: String, conversationUri: Uri?, number: Uri, hasVideo: Boolean): Observable<Call> { + return placeCall(accountId, conversationUri, number, hasVideo) .flatMapObservable { call: Call -> getCallUpdates(call) } } - fun placeCall(account: String, conversationUri: Uri?, number: Uri, audioOnly: Boolean): Single<Call> { + fun placeCall(account: String, conversationUri: Uri?, number: Uri, hasVideo: Boolean): Single<Call> { return Single.fromCallable<Call> { - Log.i(TAG, "placeCall() thread running... $number audioOnly: $audioOnly") - val volatileDetails = HashMap<String, String>() - volatileDetails[Call.KEY_AUDIO_ONLY] = audioOnly.toString() - val callId = JamiService.placeCall(account, number.uri, StringMap.toSwig(volatileDetails)) + Log.i(TAG, "placeCall() thread running... $number hasVideo: $hasVideo") + val media = VectMap() + media.reserve(if (hasVideo) 2L else 1L) + media.add(Media.DEFAULT_AUDIO.toMap()) + if (hasVideo) + media.add(Media.DEFAULT_VIDEO.toMap()) + val callId = JamiService.placeCallWithMedia(account, number.uri, media) if (callId == null || callId.isEmpty()) return@fromCallable null - if (audioOnly) { - JamiService.muteLocalMedia(callId, "MEDIA_TYPE_VIDEO", true) - } - val call = addCall(account, callId, number, Call.Direction.OUTGOING) + Log.w(TAG, "DEBUG fn placeCall() -> La valeur de hasVideo est $hasVideo et la valeur de !hasVideo est ${!hasVideo}") + + val call = addCall(account, callId, number, Call.Direction.OUTGOING, if (hasVideo) listOf(Media.DEFAULT_AUDIO, Media.DEFAULT_VIDEO) else listOf(Media.DEFAULT_AUDIO)) if (conversationUri != null && conversationUri.isSwarm) call.setSwarmInfo(conversationUri.rawRingId) - call.muteVideo(audioOnly) updateConnectionCount() call }.subscribeOn(Schedulers.from(mExecutor)) @@ -239,12 +250,33 @@ class CallService( } } - fun accept(callId: String) { +/* fun accept(callId: String) { mExecutor.execute { Log.i(TAG, "accept() running... $callId") JamiService.muteCapture(false) JamiService.accept(callId) } + }*/ + + fun accept(callId: String, hasVideo: Boolean = false) { + Log.w(TAG, "DEBUG fn accept [CallService.kt] -> based on value of hasvideo ( $hasVideo ) [IF] false then mute media type VIDEO and JamiService.accept [ELSE] add Media in VectMap and JamiService.acceptWithMedia() ") + mExecutor.execute { + Log.i(TAG, "accept() running... $callId") + val call = currentCalls[callId] ?: return@execute + val mediaList = call.mediaList ?: return@execute + val vectMapMedia = mediaList.mapTo(VectMap().apply { reserve(mediaList.size.toLong()) }, { media -> + if (!hasVideo && media.mediaType == Media.MediaType.MEDIA_TYPE_VIDEO) + media.copy(isMuted = true).toMap() + else + media.toMap() + }) + + for (i in vectMapMedia){ + Log.w(TAG, "DEBUG fn accept [CallService.kt] -> $i") + } + Log.w(TAG, "DEBUG fn accept [CallService.kt] -> value of hasvideo : $hasVideo => on accept un appel avec media") + JamiService.acceptWithMedia(callId, vectMapMedia) + } } fun hangUp(callId: String) { @@ -443,8 +475,9 @@ class CallService( } } - private fun addCall(accountId: String, callId: String, from: Uri, direction: Call.Direction): Call { + private fun addCall(accountId: String, callId: String, from: Uri, direction: Call.Direction, mediaList: List<Media>): Call { synchronized(currentCalls) { + Log.w(TAG, "DEBUG fn addCall() [CallService] -> if call == null ? create a new call instance and return it : update medialist of the call and return it => we go back to previous fn who will push the call as the updated subject ") var call = currentCalls[callId] if (call == null) { val account = mAccountService.getAccount(accountId)!! @@ -452,10 +485,11 @@ class CallService( val conversationUri = contact.conversationUri.blockingFirst() val conversation = if (conversationUri.equals(from)) account.getByUri(from) else account.getSwarm(conversationUri.rawRingId) - call = Call(callId, from.uri, accountId, conversation, contact, direction) + call = Call(callId, from.uri, accountId, conversation, contact, direction, mediaList) currentCalls[callId] = call } else { Log.w(TAG, "Call already existed ! $callId $from") + call.mediaList = mediaList } return call } @@ -534,13 +568,51 @@ class CallService( } } - fun incomingCall(accountId: String, callId: String, from: String) { + fun incomingCallWithMedia(accountId: String, callId: String, from: String, mediaList: VectMap?) { Log.d(TAG, "incoming call: $accountId, $callId, $from") - val call = addCall(accountId, callId, Uri.fromStringWithName(from).first, Call.Direction.INCOMING) + val nMediaList = mediaList ?: emptyList() + val medias = nMediaList.mapTo(ArrayList(nMediaList.size)) { mediaMap -> Media(mediaMap) } + val call = addCall(accountId, callId, Uri.fromStringWithName(from).first, Call.Direction.INCOMING, medias) callSubject.onNext(call) updateConnectionCount() } + fun mediaChangeRequested(accountId: String, callId: String, mediaList: VectMap) { + Log.w(TAG, "DEBUG fn mediaChangeRequested $accountId $callId $mediaList") + currentCalls[callId]?.let { call -> + if (!call.hasActiveMedia(Media.MediaType.MEDIA_TYPE_VIDEO)) { + for (e in mediaList) + if (e[Media.MEDIA_TYPE_KEY]!! == MEDIA_TYPE_VIDEO) + e[Media.MUTED_KEY] = true.toString() + } + JamiService.answerMediaChangeRequest(callId, mediaList) + } + } + + fun mediaNegotiationStatus(callId: String, event: String, mediaList: VectMap) { + Log.w(TAG, "DEBUG fn mediaNegotiationStatus $callId $event $mediaList") + synchronized(currentCalls) { + currentCalls[callId]?.let { call -> + call.mediaList = mediaList.mapTo(ArrayList(mediaList.size), { media -> Media(media)}) + callSubject.onNext(call) + } + } + } + + fun requestVideoMedia(conf: Conference, enable: Boolean) { + Log.w(TAG, "DEBUG fn requestVideoMedia: ${conf.id} $enable") + if (conf.isConference || conf.hasVideo()) { + JamiService.muteLocalMedia(conf.id, Media.MediaType.MEDIA_TYPE_VIDEO.name, !enable) + } else if (enable) { + val call = conf.firstCall ?: return + val mediaList = call.mediaList ?: return + JamiService.requestMediaChange(call.daemonIdString, mediaList.mapTo(VectMap() + .apply { reserve(mediaList.size.toLong() + 1L) }, + { media -> media.toMap() }) + .apply { add(Media.DEFAULT_VIDEO.toMap()) }) + } + } + fun incomingMessage(callId: String, from: String, messages: Map<String, String>) { val call = currentCalls[callId] if (call == null) { diff --git a/ring-android/libjamiclient/src/main/kotlin/net/jami/services/ConversationFacade.kt b/ring-android/libjamiclient/src/main/kotlin/net/jami/services/ConversationFacade.kt index 4bad9d94cea6393b53e4ac01e16e960d068df1a8..abb7ec8be0f85c020ff90360ca2259f0609937f7 100644 --- a/ring-android/libjamiclient/src/main/kotlin/net/jami/services/ConversationFacade.kt +++ b/ring-android/libjamiclient/src/main/kotlin/net/jami/services/ConversationFacade.kt @@ -489,13 +489,16 @@ class ConversationFacade( Log.d(TAG, "onCallStateChange Thread id: " + Thread.currentThread().id) val newState = call.callStatus val incomingCall = newState === CallStatus.RINGING && call.isIncoming - mHardwareService.updateAudioState(newState, incomingCall, !call.isAudioOnly) + mHardwareService.updateAudioState(newState, incomingCall, call.hasMedia(Media.MediaType.MEDIA_TYPE_VIDEO)) val account = mAccountService.getAccount(call.account!!) ?: return val contact = call.contact val conversationId = call.conversationId Log.w(TAG, "CallStateChange " + call.daemonIdString + " conversationId:" + conversationId) val conversation = if (conversationId == null) - if (contact == null) null else account.getByUri(contact.uri) + if (contact == null) + null + else + account.getByUri(contact.conversationUri.blockingFirst()) else account.getSwarm(conversationId) val conference = if (conversation != null) (conversation.getConference(call.daemonIdString) ?: Conference(call).apply { diff --git a/ring-android/libjamiclient/src/main/kotlin/net/jami/services/DaemonService.kt b/ring-android/libjamiclient/src/main/kotlin/net/jami/services/DaemonService.kt index 7ff7f380c70d877c45217ad3502c1adb448472e5..b481784e323c2c112f10ebbaecc21fe5dbeea69a 100644 --- a/ring-android/libjamiclient/src/main/kotlin/net/jami/services/DaemonService.kt +++ b/ring-android/libjamiclient/src/main/kotlin/net/jami/services/DaemonService.kt @@ -19,6 +19,7 @@ */ package net.jami.services +import net.jami.call.CallPresenter import net.jami.daemon.* import net.jami.model.Uri import net.jami.utils.Log @@ -204,7 +205,23 @@ class DaemonService( } override fun incomingCall(accountId: String, callId: String, from: String) { - mCallService.incomingCall(accountId, callId, from) + // Should be kept while multi-stream is not enabled for Android by default + mCallService.incomingCallWithMedia(accountId, callId, from, null) + } + + override fun incomingCallWithMedia(accountId: String, callId: String, from: String, mediaList: VectMap) { + for (i in mediaList){ + Log.w(CallPresenter.TAG, "DEBUG fn incomingCallWithMedia [DaemonService] -> media.toMap() : ${i}") + } + mCallService.incomingCallWithMedia(accountId, callId, from, mediaList) + } + + override fun mediaChangeRequested(accountId: String, callId: String, mediaList: VectMap) { + mCallService.mediaChangeRequested(accountId, callId, mediaList) + } + + override fun mediaNegotiationStatus(callId: String, event: String, mediaList: VectMap) { + mCallService.mediaNegotiationStatus(callId, event, mediaList) } override fun connectionUpdate(id: String, state: Int) {